今度はLoRaWANデバイスに向かってデータを送信してみましょう。
LoRaWANの下り通信おさらい
3つのクラス
まずはLoRaWANの下り通信についておさらいしてみます。LoRaWANでは3つのモード(クラス)が定義されており、ごく大雑把にまとめると
- Class A
- デバイスがデータを送信したときだけ受信が可能
- Class B
- ゲートウェイからデバイスの受信動作を制御
- Class C
- デバイスが常に受信状態で待ち受け
のようになっています。Class Aだと消費電力が少なく電池が長持ちするものの、いつデータが届くかわかりません。一方、Class Cではいつでもデータを受け取れる反面、消費電力が大きいため電池駆動による動作は推奨されていません。それらの中間となるClass Bは実用化が遅れており、一般的に利用されるようになるのはまだ先のようです。
サポートされているクラス
センスウェイのサービスがどのクラスをサポートしているか、については明文化された情報が見つけられませんでした。一方で、今回使用しているLoRaWANデバイス(Kiwi Technology ADB922Sの機能省略版のようです)ではClass A、Cがサポートされています。
デバイスがどのモードで動作しているかを確認するコマンドが用意されているので試してみましょう。前回記事同様、デバイスをシリアルコンソールコンソールから叩いてみます。
> lorawan get_class
>> A
ということで、デバイスはClass Aで動作していることが分かりました。つまり、デバイスがデータを送信したときだけ下りのデータが受信できるということになります。
下り通信を試してみよう
LoRaWANサーバの仕様
センスウェイのLoRaWANサービスでは、下りの通信を行う際にはLoRaWANサーバにmqtt接続し、トピック lora/<username>/<devEUI>/tx
に対して所定のJSONデータを送信します。JSONに含まれるべき内容は
項目 | 内容 | 省略時 |
---|---|---|
cnf | ACKを要求するかどうか(true/false) | false |
ref | 返ってきたACKど送信データを対応させるためのID | 空文字列 |
port | ポート番号 | 省略不可 |
data | 送信データ | 省略不可 |
となっています。例えば
{"cnf":true,"ref":"abcd1234","port":14,"data":"00112233"}
こんなJSONデータを送信するわけですね。cnfとrefは省略が可能で、それぞれfalseと空文字列になります。
データを送ってみよう
まずはACK要求なしでデータを送ってみましょう。前回作成したmosquittoのテスト環境では、docker runコマンドの引数としてmosquitto_pubコマンドを送ることができます。
$ docker run --rm -it test/mosquitto-client mosquitto_pub -h mqtt.senseway.net -p 1883 -u <username> -P <password> -t lora/akirat/<devEUI>/tx -m '{"port":14,"data":"00112233"}' -d
Client mosqpub|1-92a89559dd16 sending CONNECT
Client mosqpub|1-92a89559dd16 received CONNACK
Client mosqpub|1-92a89559dd16 sending PUBLISH (d0, q0, r0, m1, 'lora/<username>/<devEUI>/tx', ... (29 bytes))
Client mosqpub|1-92a89559dd16 sending DISCONNECT
$
このようにデータを送信してみましたが、Class A動作なのでこのままではデータが受信できません。そこでデバイスからデータを送信してみます。
> lorawan tx ucnf 1 0102030405060708090a
>> Ok
>> rx 14 00112233
>> tx_ok
>
送信完了を示すOk
に続き、受信データを示すrx 14 00112233
が送られてきました。これはポート14に対してデータ"00112233"が送られてきたことを示しており、送信データと一致しています。
というわけで、無事デバイスに対して下りの通信を行うことができました。
もう少し深く見てみよう
後々の理解を助けるため、もう少し動作を深く見てみましょう。既に紹介したtx
、rx
の他にも、LoRaWANサーバとの間では種々のtopicで情報をやり取りすることができます。lora/<username>/<devEUI>/
に続くパスごとに整理してみます。
パス | 内容 |
---|---|
rx | 上りのデータを受信する |
tx | 下りのデータを送信する |
tx_send | 下りのデータがデバイスに届いた時に受信する |
ack | デバイスがACKを返した時に受信する |
error | エラーが発生した時に受信する |
ack、errorについては後で試してみましょう。ここで気になるのは、tx_send
がどの時点で返ってくるかです。実験してみると、このような順番になっていました。
- アプリケーション側からデータを送信(mqttで
/tx
に送信) - デバイス側がデータを送信
- アプリケーションがデータを受信(mqttで
/rx
を受信) - アプリケーションが送信確認を受信(mqttで
/tx_send
を受信)
つまり、tx_send
はデータが「送られた」時ではなく、デバイスが「受信した後」に送られてくることになります。何度か試してみたところ、3.から4.の間の間隔は400〜500 ms程度のようでした(DR値によって変わるかもしれません)。
複数データを連続して送ったら
さて、アプリケーション側から連続して複数のデータを送るとどうなるでしょうか。
$ docker run --rm -it test/mosquitto-client mosquitto_pub -h mqtt.senseway.net -p 1883 -u <username> -P <password> -t lora/<username>/<devEUI>/tx -m '{"port":14,"data":"01","ref":"1"}'
$ docker run --rm -it test/mosquitto-client mosquitto_pub -h mqtt.senseway.net -p 1883 -u <username> -P <password> -t lora/<username>/<devEUI>/tx -m '{"port":14,"data":"02","ref":"2"}'
$ docker run --rm -it test/mosquitto-client mosquitto_pub -h mqtt.senseway.net -p 1883 -u <username> -P <password> -t lora/<username>/<devEUI>/tx -m '{"port":14,"data":"03","ref":"3"}'
のように連続して3つのデータを送信した後、デバイスからデータを送信してみます。すると
> lorawan tx ucnf 1 0102030405060708090a
>> Ok
>> rx 14 03
>> tx_ok
> lorawan tx ucnf 1 0102030405060708090a
>> Ok
>> tx_ok
>
となり、3番目に送ったデータだけがデバイスに届きました。この時、/tx_send
にも3番目のデータに相当する返信だけが送られてきます。
lora/<username>/<devEUI>/rx {"gw":[{"date":"2018-04-22T11:25:23.329366Z","rssi":-45,"snr":11.3,"gwid":"<gwid>"}],"mod":{"fq":927.6,"cnt":50,"data":"0102030405060708090a","mt":"ucnf","devEUI":"<devEUI>","dr":"2","port":1}}
lora/<username>/<devEUI>/tx_send {"cnt":16,"cnf":false,"devEUI":"<devEUI>","date":"2018-04-22T11:25:23.746103Z","ref":"3","port":14,"size":1}
lora/<username>/<devEUI>/rx {"gw":[{"date":"2018-04-22T11:25:30.568609Z","rssi":-42,"snr":10,"gwid":"<gwid>"}],"mod":{"fq":923.2,"cnt":51,"data":"0102030405060708090a","mt":"ucnf","devEUI":"<devEUI>","dr":"2","port":1}}
デバイスがデータを受信する前に次のデータを送信してしまうと、それ以前のデータが消滅するようです。
ACK要求付きの場合
ACK要求付き("cnf": true
)の場合はもう少し複雑な動作になります。頭の中を整理してから改めて紹介したいと思います。
まとめ
まとめると、Class Aの場合は
- デバイスにデータを送る時は
/tx
にメッセージを送信する- デバイスがデータを送信した際にデータが受信される
- データが受信された後に
/tx_send
が返ってくる -
/tx_send
を受信する前に次のデータを送ると、それ以前のデータが消滅する
という動作をすることが分かりました。なので、データの消滅を避けるためには対応する/tx_send
メッセージが送られてきたことを確認してから次のデータを投げるか、あるいは再送するなどの仕組みを準備しなければなりません。