Raspberry Pi + Go で IoT した話
2019-09-25 Takeshi Yaegashi
golang.tokyo #26
自己紹介
八重樫 剛史 Takeshi Yaegashi
● 株式会社バンダイナムコスタジオ所属
● Linux・Unix・OSS・低レベルなことが好きなエンジニア
● ホームページ・ブログ https://l0w.dev
● Go のお仕事
○ Raspberry Pi を使った IoT 案件
○ スマホゲームアプリのサーバ
● Go のお話
○ golang.tokyo #25「golang binary hacks」
○ Go Conference 2019 Autumn「MS Graph API Library for Go (仮)」
Raspberry Pi + Go で IoT した事例を紹介します
※ 2018年の事例です。案件について具体的なお話はできません。申しわけありません。
本日のお話
Wi-Fi Router Cloud Services
Edge DevicesThings
IoT エッジデバイス・組み込みシステムの開発に適した Go 言語
● マイコン用のクロスコンパイルが驚くほど簡単
$ GOARCH=arm GOARM=6 go get ./cmd/hello
● シングルバイナリが扱いやすい
● 言語レベルの並行処理サポート: goroutine, chan
● システム構築に使えるライブラリ、パッケージの充実
○ 様々なネットワークプロトコルのサポート
○ Linux カーネルなどのシステムプログラミング
本日のお話の背景
Raspberry Pi の GPIO・ストレージ・Wi-Fi を使ったデータ収集システム
● エッジデバイス (Raspberry Pi 0/1/2/3)
○ Wi-Fi によるインターネット接続
○ GPIO などでイベント発生を監視、サーバに逐次送信 (MQTT)
○ サーバ接続不良時は発生イベントをMicroSDに蓄積、復旧時に再送
○ ソフトウェアアップデートに対応
● サーバ (AWS)
○ IoT + Lambda + DynamoDB (イベントデータ蓄積)
○ API Gateway + Lambda (デバイス登録APIなど)
○ S3 + CloudFront (アップデート配信)
事例紹介:システム概要
事例紹介:エッジデバイス (Raspberry Pi) のシステム構成
pigpiod
MQTT
wpa_
supplicant
I/O System
Event Queue
(LevelDB)
AWS IoT
dhcpcd
GPIO EthernetWi-Fi
timedated
timesyncd
ClockMicroSD
System
Daemons
Go Process
Hardware
💡💡💡
Goroutine
Library
mutex
chan
named pipe unix socketD-Bus
TLS
組み込み Linux システムを Go で動かす
● 組み込みシステム向けに改造した Raspbian stretch 上で動作
○ 圧縮・耐障害ファイルシステム (squashfs + overlayfs)
○ 圧縮後のシステムイメージサイズは 100MB 程度
● システムコンポーネントと仲良く
○ systemd, dhcpcd, wpa_supplicant などのデーモンを活用
○ IPC (named pipe, unix socket, D-Bus) を利用して Go から制御
● goroutine, chan の活用
○ 基本はリソース専用 goroutine 割り当てと chan による相互通信・同期
○ イベントキューは DB ライブラリ関数内部の mutex 同期を活用
事例紹介:エッジデバイスのシステム設計のポイント
コンポーネント紹介:MQTT
Eclipse Paho MQTT Go client
● https://github.com/eclipse/paho.mqtt.golang
● 定番の MQTT 通信ライブラリ
● X.509 証明書によりクライアントの認証
● QoS=1 でサーバへのデータ到達を保証
コンポーネント紹介:イベントキュー
LevelDB によるイベントキューの実装
● goleveldb - https://github.com/syndtr/goleveldb
○ LevelDB の Go による実装
● goque - https://github.com/beeker1121/goque
○ goleveldb による stack/queue/priority queue
○ イベントキュー実装において参考にした
● DB 障害対策
○ Micro SD に専用のパーティション・ファイルシステム (ext4) を作る
○ DB が壊れたら Recover() で修復、だめなら mkfs して初期化
コンポーネント紹介:Wi-Fi 制御
wpa_supplicant / dhcpcd
● wpasupplicant - https://github.com/dpifke/golang-wpasupplicant
○ wpa_supplicant と unix socket で通信するライブラリ
○ WPAのSSID/PSKの設定やAPスキャンなどができる
● dhcpcd - WPA 接続が確立すると自動的に IP アドレスを取得してくれる
● この他、エッジデバイスの設置と運用を支援するため、定期的にAPスキャンを行い
Wi-Fi電波情報をイベントキューに入れてサーバに送信している
コンポーネント紹介:システム時刻設定
systemd-timedated / systemd-timesyncd
● システム時刻、タイムゾーン、NTP 設定は
systemd により管理されている
● 基本的な NTP クライアントは systemd に内
蔵されている
● timedatectl - CLI
○ タイムゾーンや時刻設定に使用
● godbus - https://github.com/godbus/dbus
○ D-Bus で制御することもできる
$ timedatectl
Local time: Wed 2019-09-25 05:03:11 JST
Universal time: Tue 2019-09-24 20:03:11 UTC
RTC time: n/a
Time zone: Asia/Tokyo (JST, +0900)
Network time on: yes
NTP synchronized: yes
RTC in local TZ: no
$ timedatectl set-timezone Japan
$ timedatectl set-ntp false
$ timedatectl set-time "2019-09-25 19:00:00"
コンポーネント紹介:GPIO 制御
pigpio による GPIO 制御
● pigpio - http://abyz.me.uk/rpi/pigpio/
○ Raspberry Pi の GPIO を内蔵ハードで制御する C 言語のライブラリ
○ PCM/PWM クロックと GPU DMA で高速・高精度・低負荷な入出力
○ スクリプティングエンジン搭載
● pigpiod - pigpio デーモン
○ 名前つきパイプやソケットで別プロセスから制御
● pigs - pigpiod のクライアント CLI
● apt-get install pigpio でインストール可能
コンポーネント紹介:GPIO 制御
# GPIO 5 状態読み取り
$ pigs "R 5"
0
# GPIO 4 出力モード 500ms "1" パルス出力
$ pigs "M 4 W W 4 0"
$ pigs "W 4 1 MILS 500 W 4 0"
# GPIO 0-3 状態変化を /dev/pigpio0 に通知
$ pigs "NO"
0
$ pigs "NB 0 0x0000000F"
$ cat /dev/pigpio0
@?2$8??!@*??;??!@9DK???!@B??B??!@QWrF??!
pigpio スクリプト http://abyz.me.uk/rpi/pigpio/pigs.html
コンポーネント紹介:GPIO 制御
pin, err := os.OpenFile("/dev/pigpio", os.O_RDWR, 0)
if err != nil { return err}
defer pin.Close()
pout, err := os.Open("/dev/pigout")
if err != nil { return err }
defer pout.Close()
pin.Write([]byte("W 4 1 MILS 500 W 4 0")) // 3命令のスクリプトを実行
buf := make([]byte, 128) // 3命令の実行結果を取得
n, err := pout.Read(buf)
if err != nil { return err }
fmt.Printf("%sn", string(buf[:n])) // "0n0n0n"を表示(成功×3)
Go + pigpiod:named pipe (文字列) や socket (バイナリ)で制御可能
コンポーネント紹介:GPIO 制御
Go + pigpiod:用途を選ぶが、はまれば非常に便利
● 利点
○ Go だけでは難しいレベルのリアルタイム I/O が可能になる
○ 全 GPIO 端子で解像度 5μs (200kHz) の入出力を実現
○ 強力なスクリプトで様々な機能を実装可能
■ チャタリング除去つきの入力パルス検出
■ プロシージャによるインジケータLED自動点滅制御
● 欠点
○ 遅延が大きい (スクリプト制御は必ずコンテキストスイッチを伴う)
○ pigpiod の CPU 負荷が高い (3B+ で 7〜10%)
コンポーネント紹介:GPIO 制御
その他の Go I/O ライブラリの紹介
● gobot.io - https://gobot.io
○ Raspberry Pi 以外もサポートする汎用のライブラリ
○ メモリマップ I/O による低遅延な入出力
● periph.io - https://periph.io
○ gobot.io と同様な汎用ライブラリ
○ 徹底した高速化・最適化
○ pigpio のような GPU DMA 入出力を pure Go で実装
○ 開発が活発、現時点で一番おすすめできるライブラリ
まとめ
Raspberry Pi + Go で IoT エッジデバイスを作った事例を紹介しました
IoT や組み込みの分野で Go をもっと活用していきましょう!

Raspberry Pi + Go で IoT した話

  • 1.
    Raspberry Pi +Go で IoT した話 2019-09-25 Takeshi Yaegashi golang.tokyo #26
  • 2.
    自己紹介 八重樫 剛史 TakeshiYaegashi ● 株式会社バンダイナムコスタジオ所属 ● Linux・Unix・OSS・低レベルなことが好きなエンジニア ● ホームページ・ブログ https://l0w.dev ● Go のお仕事 ○ Raspberry Pi を使った IoT 案件 ○ スマホゲームアプリのサーバ ● Go のお話 ○ golang.tokyo #25「golang binary hacks」 ○ Go Conference 2019 Autumn「MS Graph API Library for Go (仮)」
  • 3.
    Raspberry Pi +Go で IoT した事例を紹介します ※ 2018年の事例です。案件について具体的なお話はできません。申しわけありません。 本日のお話 Wi-Fi Router Cloud Services Edge DevicesThings
  • 4.
    IoT エッジデバイス・組み込みシステムの開発に適した Go言語 ● マイコン用のクロスコンパイルが驚くほど簡単 $ GOARCH=arm GOARM=6 go get ./cmd/hello ● シングルバイナリが扱いやすい ● 言語レベルの並行処理サポート: goroutine, chan ● システム構築に使えるライブラリ、パッケージの充実 ○ 様々なネットワークプロトコルのサポート ○ Linux カーネルなどのシステムプログラミング 本日のお話の背景
  • 5.
    Raspberry Pi のGPIO・ストレージ・Wi-Fi を使ったデータ収集システム ● エッジデバイス (Raspberry Pi 0/1/2/3) ○ Wi-Fi によるインターネット接続 ○ GPIO などでイベント発生を監視、サーバに逐次送信 (MQTT) ○ サーバ接続不良時は発生イベントをMicroSDに蓄積、復旧時に再送 ○ ソフトウェアアップデートに対応 ● サーバ (AWS) ○ IoT + Lambda + DynamoDB (イベントデータ蓄積) ○ API Gateway + Lambda (デバイス登録APIなど) ○ S3 + CloudFront (アップデート配信) 事例紹介:システム概要
  • 6.
    事例紹介:エッジデバイス (Raspberry Pi)のシステム構成 pigpiod MQTT wpa_ supplicant I/O System Event Queue (LevelDB) AWS IoT dhcpcd GPIO EthernetWi-Fi timedated timesyncd ClockMicroSD System Daemons Go Process Hardware 💡💡💡 Goroutine Library mutex chan named pipe unix socketD-Bus TLS
  • 7.
    組み込み Linux システムをGo で動かす ● 組み込みシステム向けに改造した Raspbian stretch 上で動作 ○ 圧縮・耐障害ファイルシステム (squashfs + overlayfs) ○ 圧縮後のシステムイメージサイズは 100MB 程度 ● システムコンポーネントと仲良く ○ systemd, dhcpcd, wpa_supplicant などのデーモンを活用 ○ IPC (named pipe, unix socket, D-Bus) を利用して Go から制御 ● goroutine, chan の活用 ○ 基本はリソース専用 goroutine 割り当てと chan による相互通信・同期 ○ イベントキューは DB ライブラリ関数内部の mutex 同期を活用 事例紹介:エッジデバイスのシステム設計のポイント
  • 8.
    コンポーネント紹介:MQTT Eclipse Paho MQTTGo client ● https://github.com/eclipse/paho.mqtt.golang ● 定番の MQTT 通信ライブラリ ● X.509 証明書によりクライアントの認証 ● QoS=1 でサーバへのデータ到達を保証
  • 9.
    コンポーネント紹介:イベントキュー LevelDB によるイベントキューの実装 ● goleveldb- https://github.com/syndtr/goleveldb ○ LevelDB の Go による実装 ● goque - https://github.com/beeker1121/goque ○ goleveldb による stack/queue/priority queue ○ イベントキュー実装において参考にした ● DB 障害対策 ○ Micro SD に専用のパーティション・ファイルシステム (ext4) を作る ○ DB が壊れたら Recover() で修復、だめなら mkfs して初期化
  • 10.
    コンポーネント紹介:Wi-Fi 制御 wpa_supplicant /dhcpcd ● wpasupplicant - https://github.com/dpifke/golang-wpasupplicant ○ wpa_supplicant と unix socket で通信するライブラリ ○ WPAのSSID/PSKの設定やAPスキャンなどができる ● dhcpcd - WPA 接続が確立すると自動的に IP アドレスを取得してくれる ● この他、エッジデバイスの設置と運用を支援するため、定期的にAPスキャンを行い Wi-Fi電波情報をイベントキューに入れてサーバに送信している
  • 11.
    コンポーネント紹介:システム時刻設定 systemd-timedated / systemd-timesyncd ●システム時刻、タイムゾーン、NTP 設定は systemd により管理されている ● 基本的な NTP クライアントは systemd に内 蔵されている ● timedatectl - CLI ○ タイムゾーンや時刻設定に使用 ● godbus - https://github.com/godbus/dbus ○ D-Bus で制御することもできる $ timedatectl Local time: Wed 2019-09-25 05:03:11 JST Universal time: Tue 2019-09-24 20:03:11 UTC RTC time: n/a Time zone: Asia/Tokyo (JST, +0900) Network time on: yes NTP synchronized: yes RTC in local TZ: no $ timedatectl set-timezone Japan $ timedatectl set-ntp false $ timedatectl set-time "2019-09-25 19:00:00"
  • 12.
    コンポーネント紹介:GPIO 制御 pigpio によるGPIO 制御 ● pigpio - http://abyz.me.uk/rpi/pigpio/ ○ Raspberry Pi の GPIO を内蔵ハードで制御する C 言語のライブラリ ○ PCM/PWM クロックと GPU DMA で高速・高精度・低負荷な入出力 ○ スクリプティングエンジン搭載 ● pigpiod - pigpio デーモン ○ 名前つきパイプやソケットで別プロセスから制御 ● pigs - pigpiod のクライアント CLI ● apt-get install pigpio でインストール可能
  • 13.
    コンポーネント紹介:GPIO 制御 # GPIO5 状態読み取り $ pigs "R 5" 0 # GPIO 4 出力モード 500ms "1" パルス出力 $ pigs "M 4 W W 4 0" $ pigs "W 4 1 MILS 500 W 4 0" # GPIO 0-3 状態変化を /dev/pigpio0 に通知 $ pigs "NO" 0 $ pigs "NB 0 0x0000000F" $ cat /dev/pigpio0 @?2$8??!@*??;??!@9DK???!@B??B??!@QWrF??! pigpio スクリプト http://abyz.me.uk/rpi/pigpio/pigs.html
  • 14.
    コンポーネント紹介:GPIO 制御 pin, err:= os.OpenFile("/dev/pigpio", os.O_RDWR, 0) if err != nil { return err} defer pin.Close() pout, err := os.Open("/dev/pigout") if err != nil { return err } defer pout.Close() pin.Write([]byte("W 4 1 MILS 500 W 4 0")) // 3命令のスクリプトを実行 buf := make([]byte, 128) // 3命令の実行結果を取得 n, err := pout.Read(buf) if err != nil { return err } fmt.Printf("%sn", string(buf[:n])) // "0n0n0n"を表示(成功×3) Go + pigpiod:named pipe (文字列) や socket (バイナリ)で制御可能
  • 15.
    コンポーネント紹介:GPIO 制御 Go +pigpiod:用途を選ぶが、はまれば非常に便利 ● 利点 ○ Go だけでは難しいレベルのリアルタイム I/O が可能になる ○ 全 GPIO 端子で解像度 5μs (200kHz) の入出力を実現 ○ 強力なスクリプトで様々な機能を実装可能 ■ チャタリング除去つきの入力パルス検出 ■ プロシージャによるインジケータLED自動点滅制御 ● 欠点 ○ 遅延が大きい (スクリプト制御は必ずコンテキストスイッチを伴う) ○ pigpiod の CPU 負荷が高い (3B+ で 7〜10%)
  • 16.
    コンポーネント紹介:GPIO 制御 その他の GoI/O ライブラリの紹介 ● gobot.io - https://gobot.io ○ Raspberry Pi 以外もサポートする汎用のライブラリ ○ メモリマップ I/O による低遅延な入出力 ● periph.io - https://periph.io ○ gobot.io と同様な汎用ライブラリ ○ 徹底した高速化・最適化 ○ pigpio のような GPU DMA 入出力を pure Go で実装 ○ 開発が活発、現時点で一番おすすめできるライブラリ
  • 17.
    まとめ Raspberry Pi +Go で IoT エッジデバイスを作った事例を紹介しました IoT や組み込みの分野で Go をもっと活用していきましょう!