Skip to content

Instantly share code, notes, and snippets.

@voluntas
Last active November 26, 2017 12:11
Show Gist options
  • Save voluntas/23132cd3848af5b3ee1e to your computer and use it in GitHub Desktop.
Save voluntas/23132cd3848af5b3ee1e to your computer and use it in GitHub Desktop.
時雨堂 MQTT ゲートウェイ Fuji 開発ログ

時雨堂 MQTT ゲートウェイ Fuji 開発ログ

日時:2016-04-09
作:時雨堂
バージョン:5.1.1
url:https://shiguredo.jp/

いろいろな場所で説明されてるので省略します

時雨堂では無料で使える MQTT as a Service 提供中です。

MQTT as a Service sango

時雨堂では商用の MQTT ブローカーを販売中です。

MQTT Broker Akane

  • 時雨堂が開発している MQTT ゲートウェイ Fuji の開発ログです
  • 興味がある方は contact at shiguredo.jp まで

この定義は時雨堂独自の解釈です

センサーからのデータを受信して MQTT ブローカーに送る仕組みです。

構成:

<センサー> -> ZigBee  ->             +------------+
<センサー> -> EnOcean -> シリアル -> |ゲートウェイ| -> MQTT -> <MQTT ブローカー>
<センサー> -> USB     ->             +------------+

MQTT ゲートウェイは送受信機にインストールして使用することを想定しています。

URL:http://fuji.shiguredo.jp

MQTT サービス、MQTT ブローカーと開発をしてきましたが、センサーなどのデータを気軽に MQTT ブローカーへ送る仕組みの決定打がなく、 MQTT を使ったサービスを動かすまでの距離を感じていました。

そこで今まで培った MQTT のノウハウを凝縮したセンサー送受信機向けのシンプルな MQTT ゲートウェイを開発し、オープンソースで公開とすることで気軽に使って貰えないだろうかと考えました。

リリース:2015-04-22
ライセンス:Apache License, Version 2.0
著者:Shiguredo Inc.
コピーライト:Shiguredo Inc.
GitHub:https://github.com/shiguredo/fuji

ソースコードから自分でバイナリを作成することも可能です

ダウンロード:https://github.com/shiguredo/fuji/releases

Edison と Raspberry Pi に対してはパッケージを提供します。

  • センサーデータを生データのまま MQTT ブローカーへ送信する
    • センサーデータの処理は全て MQTT ブローカーの先で行う思想
    • ゲートウェイのカスタマイズをなるべく減らす
  • センサーからのデータはシリアルポート経由であれば TOML ファイルに設定するだけで MQTT ブローカーへ送信可能
  • MQTT ブローカーへの接続に対して細かい設定が可能
    • MQTT ブローカーを複数設定できる
    • MQTT ブローカーの冗長構成に対応
    • MQTT ブローカーへ送信するデータは QoS の設定や Retain を設定できる
    • MQTT ブローカーからメッセージを受信し、センサーへ送ることが出来る
      • アプリからセンサーへメッセージを送ることが出来る
  • 送受信機の状態を $SYS/gateway で取得する事ができる

時雨堂 MQTT ゲートウェイの設定ファイル例です。

この設定ファイルは検討中のものを含みます

  • バイナリを指定する場合は \x00\x01\x02 のように記述します
    • payload = \x00\x01\x02
  • name
    • topic に使用できる文字は全て使用可能
[gateway]

name = "fuji"
  • ブローカーは複数指定できます
  • ブローカーは冗長構成に対応します
    • 優先順位は上からの順番となります
    • 個々のブローカーに対して無限に再接続を行います
  • 切り替え
    • ブローカーごとに、パケットのリトライ回数とリトライインターバルを指定出来ます
    • 指定回数リトライを行っても接続ができない場合、送信先を次のブローカーに切り替えます
      • リトライ回数の最大値の設定を可能にします
      • リトライ間隔の最大値の設定を可能にします
    • 次の切り替え先ブローカーが存在しない場合はパケットを破棄します
  • will message をブローカー単位で指定出来る
    • will message を定義することで有効になる
    • topic は固定で <Gateway-Name>/will に送られる
    • 未実装
      • will_topic でトピックを設定可能にする
  • subscribe
    • デバイスに対してメッセージを配信できるようになる
    • topic は固定で <Gateway-Name>/<Device-Name> に送られます
  • 切り戻し
    • 未実装
    • 一定時間過ぎた後、優先順位が高い方に接続を切り戻します
[[broker."sango"]]

host = "192.0.2.10"
port = 1883

username = "sayo"
password = "123"

topic_prefix = "[email protected]"
retry_count = 7
#
retry_interval = 3

# will の動作は固定する
will_message = "hello world"

[[broker."sango"]]

host = "192.0.2.11"
port = 1883

username = "sayo"
password = "123"

topic_prefix = "[email protected]"
retry_count = 7
#
retry_interval = 3

[[broker."akane"]]

host = "192.0.2.20"
port = 8883
tls = true
# PEM 形式
cacert = "/path/to/cacert"

username = "sayo"
password = "456"

シリアルポートからの通信を前提として、デバイス単位で MQTT ブローカーを指定出来ます

TOML ファイルに設定するだけで対応が可能です。

  • broker
    • [[broker.""]] で指定しているものが無ければエラー
  • qos
    • 0,1,2 で対応 SUBSCRIBE したタイミングで 0x80 が帰ってきたらエラーログを出力する
  • retain
    • true / false でデフォルトは false
  • type
    • seiral を指定します
  • subscribe
    • true / false でデフォルトは false
  • baud
    • 必要な値を指定
  • size
    • 取得するデータサイズ
[device."spam"]

type = "serial"
broker = "sango"
qos = 0
retain = true
subscribe = true

serial = "/dev/tty.ble"
baud = 9600
size = 4

[device."beacon"]

type = "serial"
broker = "sango"
qos = 2

serial = "/dev/tty.enocean"
baud = 115200
size = 8

検討中

EnOcean は USB の無線受信機があり、さらにシリアル通信で気軽に扱うことが出来ます。

Fuji ではさらに EnOcean パケットの CRC チェックを行う事により、より安全に MQTT ブローカーにぱけっとを送れるような仕組みを追加する予定です。

これ以外にも EnOcean との相性をよりよくしていく方向で進めています。

[device."yaki"]

type = "EnOcean"

broker = "spam"
qos = 2
retain = true

serial = "/dev/tty.enocean"

crc8 = true

テスト用ダミーデバイスを作る事ができます。

このダミーデバイスは名前がかぶらない限り、好きなだけ増やすことが可能です。

[device."dora"]

type = "dummy"
broker = "spam"
qos = 2
retain = true

interval = 10
payload = "Hello world."
  • 送受信機の状態を定期的に MQTT ブローカーに投げることができます
  • interval を 0 に設定した場合はサーバからの PUBLISH を契機として状態を PUBLISH します
  • $SYS/gateway/<Gateway-Name>/cpu/cpu_times/user
  • $SYS/gateway/<Gateway-Name>/cpu/cpu_times/system
  • $SYS/gateway/<Gateway-Name>/cpu/cpu_times/idle
  • $SYS/gateway/<Gateway-Name>/memory/virtual_memory/total
  • $SYS/gateway/<Gateway-Name>/memory/virtual_memory/available
  • $SYS/gateway/<Gateway-Name>/ip_address/interface/all
    • $SYS/gateway/<Gateway-Name>/ip_address/interface/en0
[status]

broker = "sango"
# qos は 0 固定
interval = 3600

[status."cpu"]

cpu_times = [ "user", "system", "idle", "nice", "iowait", "irq", "softirq", "guest"]

[status."memory"]

# interval を上書きできる
interval = 200

virtual_memory = [ "total", "available", "percent", "used", "free" ]

[status."ip_address"]

interface = ["eth0"]

[status."process"]

# https://github.com/fukata/golang-stats-api-handler からそのまま
go = [ "version", "os", "arch", "gc_num", "gc_last", "gc_next", "cgo_call_num", "gomaxprocs", "goroutine_num" ]
memory = [ "lookups", "sys", "total_alloc", "alloc", "mallocs", "frees" ]
heap = [ "alloc", "sys", "idle", "inuse", "released", "objects" ]

MQTT Publish Topic は 自動的に 生成されます。ゲートウェイ名とデバイス名から自動で生成されます。

たとえばゲートウェイ名が spam で、デバイス名が egg だとします。

その場合の Topic は spam/egg/publish です。 payload はセンサーがから送られてきた値そのままが送られます。

Topic 固定:

<Gateway-Name>/<Device-Name>/publish

データ送信トピックは基本的に固定しますが、プレフィックスを付けられる仕組みを考えています。

topic_prefix 例

[[broker."sango"]]

topic_prefix = "sayo@github"

topic_prefix 適用後の Topic:

sayo@github/<Gateway-Name>/<Device-Name>/publish

MQTT ブローカーはマルチテナントでの使用を考えられることが多いと考えられます。さらにパーミッションは topic ベースがほとんどではないかと考え、この機能を用意しました。

MQTT Subscribe Topic は 自動的に 生成されます。Gateway-Name や Device-Name や topic_prefix については publish と同一なので省略します。

Topic 固定:

<Gateway-Name>/<Device-Name>/subscribe

subscribe を true にするとデバイスは <Gateway-Name>/<Device-Name>/subscribe に対して SUBSCRIBE を行います。

[device."spam"]

type = "serial"

broker = "sango"
qos = 0
retain = true
subscribe = true

Broker を経由して <Gateway-Name>/<Device-Name>/subscribe にメッセージを送ると MQTT-GW は指定したデバイスに対して受け取った Payload を書き込みます。

MQTT Payload はセンサーから受信した値そのものを使用します。

そのため何かしら処理を MQTT ブローカーの先で行う必要があります。

  • シリアルから受け取ったバイナリそのままを MQTT の PUBLISH の Payload へ入れて転送する

未実装

MQTT ブローカーに接続出来ない場合ローカルのファイルに書き出します。

ファイル最大サイズや、ログローテーションサイズなどが指定できる予定です。

また、バッファリングしたデータを送信する場合は通常のトピックの後ろに buffering などの topic を設定して送信することができるようになる。

1.1.0 で実装予定

Fuji は HTTP リクエストを代理で送信してくれる機能を持っています。

決められたTopic に対して、決められた JSON フォーマットで HTTP リクエスト要求を送ることが出来ます。 また、その HTTP リクエストの戻り値を SUBSCRIBE することで受け取ることもできます。

HTTP リクエスト依頼用の JSON

{
    "id": "<UUID>",
    "url": "http://127.0.0.1:5000/spam",
    "method": "POST",
    "body": {"msg": "spam"}
}
  • body は JSON である必要があります
  • response を false にすると戻り値が返ってきません
  • method はデフォルトが "POST" です
  • body はデフォルトが空です
  • headers が必要?
{
    "id": "<UUID>",
    "status": 200,
    "body": {"result": "bacon"}
}

この JSON を <Gateway-Name>/http/request に対して投げます。 Fuji は送られてきた JSON を使って HTTP を指定された URL に送ります。 もし JSON では無い場合は 502 を返します。

Fuji に対して HTTP リクエストの依頼を出すための Topic:

<Gateway-Name>/http/request

Fuji が MQTT Broker にたいして SUBSCRIBE する Topic:

<Gateway-Name>/http/request

Fuji が HTTP リクエストの戻り値を MQTT Broker に PUBLISH する Topic:

// id を設定して送ってくる
<Gateway-Name>/http/response/<リクエストに含まれる id>
[http]

enabled = true

MQTT For Sensor Networks (MQTT-SN)

MQTT の派生で MQTT-SN というセンサーネットワーク向けのプロトコルも定義されています。

このプロトコルは UDP/NoIP での使用が想定されています。

基本的には MQTT-SN -> MQTT-SN Gateway -> MQTT という流れになります。

URL:http://git.eclipse.org/c/paho.incubator/smidge.git/

このライブラリに貢献しつつ時雨堂なりの MQTT-SN の使い方を提案していきます。

  • MQTT-SN を MQTT に変換して送る

MQTT-SN は Gateway の先が対応している必要があるため、何かしらの仕組みが必要です。

リリース:未定

MQTT-SN 対応

  • [ ] HTTP リクエスト機能
リリース:2016-02-29
  • Golang 1.6 を使用する
リリース:2016-02-15

安定化と仕様固定

  • [x] Golang 1.5.3 を使用する
  • [x] クライアント認証
    • client_cert/client_key で証明書を設定できるようにする
  • [x] 設定ファイルを TOML に変更する
    • 下位互換なし
    • [x] デバイスの種類をセクションから type に変更する
      • [device "spam/serial"] は [device."spam"] と type = serial に変更する
  • [x] publish 時の topic 変更
    • 下位互換なし
    • <Gateway-Name>/<Device-Name>/publish に変更する
  • [x] subscribe 時の topic 変更
    • 下位互換なし
    • <Gateway-Name>/<Device-Name>/subscribe に変更する
  • [x] will_topic の設定
  • [x] [status."ip_address"] にて IP アドレスの配信を追加
リリース:2015-10-08
  • [x] Paho ライブラリのアップデート
  • [x] gopsutil ライブラリのアップデート
  • [x] Golang 1.5.1 対応

OSS にて公開

サポートとカスタマイズ受付開始

リリース:2015-04-22
  • [x] MQTT 3.1.1 対応
    • Paho Golang への貢献を含む
  • [x] MQTT over TLS 対応
  • [x] 送受信機情報の取得
    • gopsutil を使用する
    • CPU とメモリを取得可能
    • インターバルを指定可能
  • [x] will メッセージの対応
    • will_message = ""
    • topic_prefix にも対応
    • <Gateway-Name>/will に送られる
  • [x] Retain の対応
    • retain = true
  • [x] バイナリメッセージ
    • \x00\x12 を指定することでバイナリで送信が可能になる
  • [x] Armadillo-IoT での動作検証
    • 3G 回線での MQTT over TLS の検証
  • [x] インテル Edison モジュールでの動作検証
    • センサー類は確認せず、起動のみ
  • [x] 無限リトライの対応
    • サーバが応答しなくなった、または接続が切れた場合は無限に再接続を行う
  • [x] Subscribe 機能
    • subscribe = true
    • デバイスに対してメッセージを送る
    • <Gateway-Name>/<Device-Name>
  • [x] Armadillo-IoT 向けバイナリファイルの提供
    • ARM5 向け
  • [x] Model B+ | Raspberry Pi 向けパッケージファイルの提供
    • Raspbian 向け
    • ARM6 向け
  • [x] インテル Edison 向けパッケージファイルの提供
    • Yocto Linux 向け
  • [x] パケットリトライ
    • 接続が失敗した場合一定回数を試みる
  • [x] 冗長構成時の切り替え
    • 一定回数のリトライを試した場合、他のブローカーへ接続を試みる
    • [device "sango/1"] と [device "sango/2"]
  • [x] 複数ブローカーへの対応
    • それぞれのブローカーに対して接続を行う
    • [device "sango/1"] と [device "akane/1"]
  • [x] 日本語ドキュメント
リリース:2015-02-13

社内リリース

  • [x] シリアルポート経由デバイス
    • EnOcean を使って実際にセンサーデータを取得
  • [x] ダミーデバイス
  • [x] MQTT 3.1 対応
    • Paho Golang が 3.1.1 未対応のため
  • [x] MQTT ブローカー対応
    • シングルのみ
  • [x] 日本語ドキュメント
  • [x] topic_prefix への対応
  • [x] dpkg install - Raspbian への対応
  • [x] Raspberry Pi での動作検証
  • [x] Raspberry Pi 向けのパッケージファイルの提供
  • [ ] セッション対応
  • [ ] Web 管理画面
  • [ ] 切り戻し
  • [ ] バッファリング
    • 接続が失敗した場合、データをローカルファイルに書き出す
    • 接続が成功した際、通常のデータとともにバッファリングデータも送信する

年間契約での提供をです。基本的にはお問い合わせベースです。

気軽に契約できるように最小コストにしています

年間契約:60 時間/年で 60 万円/年
問い合わせ:平日 10:00-17:00 専用の E メールアドレスから
  • バグの優先的修正
  • MQTT ゲートウェイ Fuji に関する技術的問い合わせ
開発費用:要件次第
メンテナンス費用(OSS):無償
メンテナンス費用(非OSS):開発費用の 50% / 年

カスタマイズ内容がお客様が OSS で公開してもよいと判断され、さらに時雨堂が公開するメリットを感じた場合はメンテナンス費用を無償とします。

時雨堂では一番簡単に始められるセンサーとして EnOcean をお薦めしています。

EnOcean はエナジーハーベストという屋内の微弱な光でも充電することができる仕組みがそろっています。 つまり電池が不要で動くセンサーです。そのため屋内に置いて試すセンサーとしてはお薦めです。

使用する電波帯域が 928 MHz であり、Wifi や ZigBee や BLE が使用する 2.4 GHZ ではないため電波干渉が起こりにくいです。

時雨堂の MQTT as a Service である sango と MQTT Gateway Fuji と EnOcean のセンサーと受信機さえ用意できれば、すぐにセンサーデータを MQTT 経由で配信できます。

後で書く

BLE は接続が不安定で、古い端末での動作しない事から、一度開発休止とすることにしました。 ただし、実際に動作する実装を用意はできますので、興味がある方はご連絡ください。

  • serial 経由ではなく直接 Bluetooth と設定できるようにする
  • 設定ファイルにいくつか設定をする必要あり
  • 使用するライブラリが Linux カーネル 3.14 以降でしか使えないことがわかり、一端 3.14 以降への対応とする
    • Edison は 2015-08 現在カーネルが 3.10 のため使用不可能
[device."beacon"]

type = "BLE"

broker = "sango"
qos = 1

# または UUID形式 900d4ff61c61445d8c20776359858f4f
peripheral_id = "E7:6F:8B:B6:6D:33"
service_uuid = "713d0000503e4c75ba943148f18d941e"
characteristic_uuid = "713d0003503e4c75ba943148f18d941e"
  • 再開に向けて
    • Edison の Kernel が 3.1.4 以上になったタイミングで再検討する

ZigBee も 2.4G 帯域を使用するため、Wifi が多いところで不安定な可能性があると判断したため、一度保留としました。

[device."yaki"]

type = "ZigBee"

broker = "spam"
qos = 2
retain = true

serial = "/dev/tty.zigbee"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment