読者です 読者をやめる 読者になる 読者になる

脳汁portal

アメリカ在住(だった)新米エンジニアがその日学んだIT知識を書き綴るブログ

motionを使ってRaspberryPiからUSBカメラの映像をストリーミングする方法

電子工作

motionのインストール

sudo apt-get install motion

設定の変更

sudo vi /etc/motion/motion.conf

 ### USBカメラの設定をする(解像度やFramerate)
 89 # Image width (pixels). Valid range: Camera dependent, default: 352
 90 width 320
 91
 92 # Image height (pixels). Valid range: Camera dependent, default: 288
 93 height 240
 94
 95 # Maximum number of frames to be captured per second.
 96 # Valid range: 2-100. Default: 100 (almost no limit).
 97 framerate 2

 ### 画像ファイルとして保存する機能は今回はoffにする
 250 output_pictures off

 ### Streamingの設定をする
 473 # Maximum framerate for stream streams (default: 1)
 474 stream_maxrate 2
 475
 476 # Restrict stream connections to localhost only (default: on)
 477 stream_localhost off

motion起動

sudo motion -n

確認

以下のURLにアクセスして映像が確認できれば完了

http://{raspberryPiのIP}:8081

ただしMJPG-stremerに比べて3秒前後の遅延が発生する。

USBカメラの解像度等の情報を確認する方法

電子工作

deviceの確認

まずはカメラを接続したらlsusbで認識されているか確認します

$ lsusb
Bus 001 Device 005: ID 288c:0002  # <== これが追加された
Bus 001 Device 007: ID 05ac:0222 Apple, Inc. Aluminum Keyboard (JIS)
Bus 001 Device 006: ID 04ca:0061 Lite-On Technology Corp.
Bus 001 Device 004: ID 05ac:1006 Apple, Inc. Hub in Aluminum Keyboard
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

今回はBus 001 Device 005にUSBカメラが設定されています。
このときカメラの製造元や会社の名前が出てくるときと出てこない場合があります

各項目の確認

概要確認
$ v4l2-ctl -d 0 --info
Driver Info (not using libv4l2):
        Driver name   : uvcvideo
        Card type     : MS-M103HU
        Bus info      : usb-3f980000.usb-1.5
        Driver version: 4.4.11
        Capabilities  : 0x84200001
                Video Capture
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps   : 0x04200001
                Video Capture
                Streaming
                Extended Pix Format
全項目確認
$ v4l2-ctl -d 0 --all
Driver Info (not using libv4l2):
        Driver name   : uvcvideo
        Card type     : MS-M103HU
        Bus info      : usb-3f980000.usb-1.5
        Driver version: 4.4.11
        Capabilities  : 0x84200001
                Video Capture
                Streaming
                Extended Pix Format
・
・
・
対応解像度&FPS確認
$ v4l2-ctl -d 0 --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
        Index       : 0
        Type        : Video Capture
        Pixel Format: 'YUYV'
        Name        : YUYV 4:2:2
                Size: Discrete 1280x720
                        Interval: Discrete 0.083s (12.000 fps)

        Index       : 1
        Type        : Video Capture
        Pixel Format: 'MJPG' (compressed)
        Name        : Motion-JPEG
                Size: Discrete 1280x720
                        Interval: Discrete 0.033s (30.000 fps)
lsusbでも確認できる
$ lsusb -s 001:005 -v | egrep "Width|Height"
Couldn't open device, some information will be missing
        wWidth                           1280
        wHeight                           720
        wWidth( 0)                       1280
        wHeight( 0)                       720
        wWidth                           1280
        wHeight                           720
        wWidth( 0)                       1280
        wHeight( 0)                       720

low voltage detected, date/time is not reliable.

電子工作

エラー文

RaspberryPiでRTCから時刻合わせをしようとすると以下のようなエラーが出ることがある。

Jan 01 23:11:11 raspberrypi kernel: [  131.098038] rtc-pcf8563 1-0051: low voltage detected, date/time is not reliable. 

ちなみに使ったRTCはこちら
リアルタイムクロック(RTC)モジュール: 半導体 秋月電子通商 電子部品 ネット通販
f:id:portaltan:20170113194609j:plain
エラーの意味としては『電圧低下が検知されたよ。だから今の時刻データは信頼できないよ。』って感じの警告だ。

調査

アプリケーションノートを読んでみると、RTCへの電源供給の電圧が一定以下になった場合、VLflag(Voltage Low Flag)が立つらしい。
f:id:portaltan:20170113100032p:plain

対応

ちなみにこのVLフラグは秒数を管理しているメモリ区域で管理されている
f:id:portaltan:20170113100209p:plain

なので上記のエラー文を出したくなければ、VLフラグを0クリアしてやればエラーは出なくなる。

RubyのThreadのステータスに関して

ruby

class Thread (Ruby 2.4.0)
instance method Thread#status (Ruby 2.4.0)
instance method Thread#alive? (Ruby 2.4.0)

Rubyのスレッドに関して各ケースのステータスまとめ

各項目がとる値

  • thread object(thread.inspect)
    • run
    • sleep
    • aborting
    • dead
  • thread.status
    • run
    • sleep
    • false
    • nil
  • thread.alive?
    • true
    • false

調査

通常時(スレッドが生きているとき)

ソース
t1 = Thread.start{
  loop do
    sleep 1
  end
}

loop do
  p t1 # run, sleep, aborting, dead
  puts t1.status || t1.status.class # run, sleep, false, nil
  puts t1.alive? # true, false
  puts "--------------------------------"
  sleep 1
end
実行結果
#<Thread:0x55e80d30 sleep>
run
true
--------------------------------
#<Thread:0x55e80d30 sleep>
run
true
--------------------------------
#<Thread:0x55e80d30 sleep>
run
true
--------------------------------
各ステータス
  • thread object : (run), sleep
  • thread.status : run, (sleep)
  • thread.alive? : true

正常終了時

ソース
t1 = Thread.start{
  2.times{|n|
    sleep 1
    puts n+1
  }
}

loop do
  p t1
  puts t1.status || t1.status.class # run, sleep, aborting, false, nil
  puts t1.alive? # true, false
  puts "--------------------------------"
  sleep 1
end
実行結果
#<Thread:0x566a0c48 run>
sleep
true
--------------------------------
1
#<Thread:0x566a0c48 sleep>
sleep
true
--------------------------------
2
#<Thread:0x566a0c48 dead>
FalseClass
false
--------------------------------
#<Thread:0x566a0c48 dead>
FalseClass
false
--------------------------------
#<Thread:0x566a0c48 dead>
FalseClass
false
--------------------------------
各ステータス
  • thread object : dead
  • thread.status : false
  • thread.alive? : false

異常終了時

ソース
t1 = Thread.start{
  2.times{|n|
    sleep 1
    puts n+1
  }
  raise
}

loop do
  p t1
  puts t1.status || t1.status.class # run, sleep, aborting, false, nil
  puts t1.alive? # true, false
  puts "--------------------------------"
  sleep 1
end
実行結果
#<Thread:0x558e8bf0 run>
sleep
true
--------------------------------
#<Thread:0x558e8bf0 run>
sleep
1
true
--------------------------------
#<Thread:0x558e8bf0 run>
2
sleep
false
--------------------------------
#<Thread:0x558e8bf0 dead>
NilClass
false
--------------------------------
#<Thread:0x558e8bf0 dead>
NilClass
false
各ステータス
  • thread object : dead
  • thread.status : nil
  • thread.alive? : false

まとめると

  • thread.statusはスレッドが生きている間はrunかsleepで、正常終了するとfalseになり、異常終了するとnilになる
  • thread.alive?はスレッドが生きている間はtrueで、終了すると正常・異常に関わらずfalseになる
  • thread情報を直接見ると、スレッドが生きている場合はrunかsleepで、終了処理中はabortingになり、完全に終了するとdeadになる

raspberryPiでRTCを使おうとしたときにargument Errorになる

電子工作

前提条件として、raspberryPiにはRTCがついていないため、シャットダウンしてしまうと時間を保持しておくことが出来ない。

なので通常は起動時にntpでシステムの時刻をあわせるのだが、当然インターネット環境がない場合はその時間合わせをすること自体が出来ない。

そういう場合などのために個別にRTCを取り付けることが出来るのだが、以下のようにエラーが出てRTCへアクセスが出来ないことがたまにある。

hwclock: ioctl(RTC_RD_TIME) to /dev/rtcX to read the time failed: Invalid argument

調べていくと割とメジャーなつまづきポイントらしい。
Raspberry Pi • View topic - Wierd RTC(ds1307) problem

調べていくとfake_hwclockというものが悪さをしているらしく、上の方でraspberryPiにはRTC(hwclock)はついていないといったのだが、その代わりにのっているのがfake_hwclockらしい。

これは何かというと、普通ならば時刻データを保持できないのでネット環境がない場合は起動するたびに初期値の1970年1月1日0時0分0秒になってしまうのだが、
このfake_hwclockというシステムが有効化されていると、起動している時間を定期的(shutdown時にも?)にtxtとして保存しておき、次回起動時にこの時間を現在時刻として
使おうという機能である。

今回は本物のhwclockであるRTCとこのfake_hwclockが競合してしまったために上記のエラーが出たものと思われる。
解決方法としてはこいつを無効化してやればよい。

sudo update-rc.d fake-hwclock disable

これで競合することはなくなり、上記のエラーが出ることもなくなった。

raspberryPi3のIPを固定する方法(static ip)

電子工作

raspberryPiは通常自動でIPをアサインしてくれますが、それでは困る場合などに固定IPアドレスにする方法です。
raspbianのversion upに伴い、raspberryPi2の時とは方法が変わっているようです。

ちなみにversionは8.0です

$ cat /etc/debian_version
8.0

/etc/network/interfacesの確認

$ cat /etc/network/interfaces
# interfaces(5) file used by ifup(8) and ifdown(8)

# Please note that this file is written to be used with dhcpcd
# For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'

# Include files from /etc/network/interfaces.d:
source-directory /etc/network/interfaces.d

auto lo
iface lo inet loopback

iface eth0 inet manual

allow-hotplug wlan0
iface wlan0 inet manual
    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

allow-hotplug wlan1
iface wlan1 inet manual
    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

上のほうのコメントを見ると、staticIPにするにはdhcpcd.confを編集すればよいことがわかります。
とりあえず書かれている通りにmanを見ます

$ man dhcpcd.conf
     static value
             Configures a static value.  If you set ip_address then dhcpcd will not attempt to obtain a lease
             and just use the value for the address with an infinite lease time.

             Here is an example which configures a static address, routes and dns.
                   interface eth0
                   static ip_address=192.168.0.10/24
                   static routers=192.168.0.1
                   static domain_name_servers=192.168.0.1

             Here is an example for PPP which gives the destination a default route.  It uses the special desti-
             nation keyword to insert the destination address into the value.
                   interface ppp0
                   static ip_address=
                   destination routers

staticで検索すると上記のような記入例が出てくるので、自分のIPに変更して実際に書き込みます

/etc/dhcpcd.confの編集

$ vi /etc/dhcpcd.conf
### ファイル末尾に追加
interface eth0
static ip_address=192.168.0.10/24
static routers=192.168.0.1
static domain_name_servers=192.168.0.1

これで完了です。
後はraspberryPiを再起動して設定したIPでアクセスできることが確認できればOKです。

Rubyでシリアル通信する方法(serialport gem)

ruby

Rubyでシリアル通信する場合は、serialport gemを使うと簡単にできます。

install serial port

$ gem install serialport

How to Use

require 'serialport'

sp = SerialPort.new('/dev/ttyS0', 115200, 8, 1, 0) # device, rate, data, stop, parity

# 送信
sp.puts("foobar")

# 受信
sp.gets
sp.readline.chomp.strip # こっちだと空白とか余分な情報をそぎ落としてくれる