「AE2100」内蔵の920MHz帯無線「SmartHop」のモジュールを活用してみよう(2)　 ―データ収集編―

# 要約
* この記事では、OKIのAIエッジコンピューター「AE2100」に内蔵されている920MHz帯マルチホップ無線「SmartHop」のモジュールを使用して、遠隔地にあるセンサーデータを収集、AE2100上で可視化する方法を紹介します。

* 第２回の「データ収集編」では、AE2100にNode-REDを導入し、SmartHopモジュールの通信フォーマットでバイナリ(電文)を送受信することで、Modbus RTU対応のセンサーデータの収集・見える化を行う方法を解説します。
※SmartHopのユニットタイプを使用したデータ収集方法は[こちら](https://qiita.com/shimatak/items/0c6250348ed9fdae3f5c)。



# はじめに
本記事は、「AE2100」内蔵の920MHz帯無線「SmartHop」のモジュールを活用する2つ目の記事「データ収集編」です。

●[AE2100内蔵の920MHz帯無線モジュールを活用してみよう(1)―無線ネットワーク構築編―](https://qiita.com/shimatak/items/ae87081694d1bcbb3cf5)
●AE2100内蔵の920MHz帯無線モジュールを活用してみよう(2)―データ収集編―　(本記事)
●[AE2100内蔵の920MHz帯無線モジュールを活用してみよう(3)―パトランプ発報編―](https://qiita.com/shimatak/items/270be8410ee3374a1223)

前回の記事では、SmartHopの無線ネットワーク構築が完了しました。
今回は、いよいよSmartHop経由でのセンサーデータの収集・見える化です。
アプリケーションの実装は、Node-REDを使用して直観的かつ可能な限りローコードで行っていきます。



# 環境
実行環境は次の図のような構成を前提とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1620360/5278408c-ec0e-23b1-fde3-c393d96dccd6.png)


Windows PCからAE2100（LANポート2）へTeraTermによって接続、AE2100（LANポート1）からインターネットへ接続が可能なことを確認してください。
その他の環境は[「AE2100」内蔵の920MHz帯無線「SmartHop」のモジュールを活用してみよう(1) ―無線ネットワーク構築編―](https://qiita.com/shimatak/items/ae87081694d1bcbb3cf5)をご参照ください。



# AE2100へのNode-REDの導入
Node-REDの構成およびAE2100に導入する方法については、[OKI AE2100 & Node-REDでローコードIoTしてみた。その２ 構築編](https://qiita.com/marufumio/items/f6bd3088c9c60e5e7f57)に分かりやすく記載されているので、そちらをご参照ください。
本記事では、AE2100内蔵のSmartHopモジュールのインターフェースで通信を行うため、「Dockerコンテナの実行」の章に記載されている「--device=/dev/ttyRS485:/dev/ttyRS485」のオプションを「--device=/dev/tty920M:/dev/tty920M」に変更してください。
なお、本記事ではMQTTは使用しないので、「Dockerコンテナの実行」の章に記載されている1883番ポートの設定は不要です。

```
root@ae2100:~#docker run -it -p 1880:1880 --device=/dev/tty920M:/dev/tty920M -v /home/root/.node-red:/data --name node-red-1 node-red_v1:appf
```
Node-REDをAE2100に導入したら、Webブラウザで http://192.168.100.1:1880 にアクセスします。おなじみのフローエディタが表示されるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1620360/85aa540a-c8c3-7ac1-8222-7e9d622c243c.png)



# ノードの全体構成
Node-REDのフローエディタで各種ノードをワークスペースに配置し、ノード間を線で結ぶことによってフローを作成します。
本記事では、SmartHopモジュールの通信フォーマットに従ったバイナリ(電文)をセンサーに送信する必要があるので、CO2濃度を収集するフローを以下のように作成しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1620360/ea6aee52-29be-7c22-1468-a76d5f2cda9b.png)


全体的な流れとしては、1秒間隔でfunctionノードにてバイナリ(電文)を作成し、serial requestノードでAE2100から送信。serial requestノードでCO2コントローラーのデータ（CO2濃度）を受信して、2つ目のfunctionノードで必要なデータを抜き出し、ダッシュボードでCO2濃度の見える化まで行っています。
次の見出しから、実装のポイントを図の吹き出しの番号に沿って解説します。


# SmartHopモジュールの通信フォーマットに従ったデータ収集の実装
まずは、②functionノードでSmartHopモジュールの通信フォーマットに従ったバイナリ(電文)を作成します。
SmartHopモジュールの通信フォーマットは、[無線通信モジュール/ホスト CPU間　API仕様書](https://www.oki.com/jp/920M/support/#tab02)に詳しく記載されているので、そちらを参照します。

以下、[無線通信モジュール/ホスト CPU間　API仕様書](https://www.oki.com/jp/920M/support/#tab02)　P.18、P.19の抜粋
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1620360/ea838eab-5aed-a3ff-fd1b-faf6ef2f5356.png)
#### ■SmartHopモジュールの 通信フォーマット情報
項目|バイト数|値|備考|
:-:|:-:| :-:| :-:|
SYN|1|0x7E|固定|
ProcessID|1|0x11|ホストCPU間の透過データ|
Length|2|設定値|payloadサイズ|
Payload|可変長|可変|CO2コントローラーのデータを収集するための<br>Modbus RTUの電文|
SYN|1|0x7E||固定

ここで、Payloadには、CO2コントローラーのデータを収集するためのModbus RTUの電文を入れます。
Modbus RTUの通信フォーマットは以下のようになっているようです。

[CO2コントローラー RS485(Modbus-RTU)タイプ 通信マニュアルの抜粋](https://group.chcsys.net/wp-content/uploads/2021/09/CO2%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9modbus%E3%82%BF%E3%82%A4%E3%83%95%E3%82%9A-%E9%80%9A%E4%BF%A1%E3%83%9E%E3%83%8B%E3%83%A5%E3%82%A2%E3%83%AB.pdf)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1620360/b0245b3a-68a0-e784-1915-ade314ee6c7b.png)

実際に、Modbus RTUの通信フォーマットをSmartHopモジュールの通信フォーマットに当てはめると以下の表のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1620360/0f89e29c-4419-f430-caaf-ef3283210f10.png)
なお、[AI2100⇔センサー間の電文のやりとり](https://qiita.com/shimatak/items/0c6250348ed9fdae3f5c)やCRC-16の計算方法については、既に説明している記事がありますので、本記事では割愛します。
<br>
表を参考にバイナリを作成するプログラムをnode.jsで実装します。

```diff_jsl
var buf = new Buffer(13); // バイナリの配列を定義
//SmartHopのモジュールとホストCPU間APIの
//通信フォーマット(UARTインターフェースの通信フォーマット)に従って
//電文(バイナリ)を送る
buf[0]=0x7E;//SYN(0x7E固定) 1byte

buf[1]=0x11;//ProcessID：モジュールとホストCPU間の透過データ　 1byte
           //API仕様書P.19の表5参照

buf[2]=0x00;//Length(payloadサイズ) 2byte
buf[3]=0x08;//ここでは、8byteを指定

//CO2コントローラーのデータ取得のためのModubus RTUの電文(8byte)を指定
//CO2コントローラーの仕様書を参照
buf[4]=0x01;//スレーブアドレス(局番) 1byte

buf[5]=0x03;//Modubus機能コード(0x03固定)　1byte

buf[6]=0x00;//データ項目 2byte
buf[7]=0x44;//ここでは、CO2濃度を指定

buf[8]=0x00;//データ数　2byte
buf[9]=0x01;//ここでは、１つのデータを指定

buf[10]=0xC4;//Modbus電文のCRC　2byte
buf[11]=0x1F;
//CO2コントローラーのデータ取得のためのModubus RTUの電文(8byte)終了

buf[12]=0x7E;//SYN(0x7E固定) 1byte

 
msg.payload = buf;
return msg;
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1620360/797f56bd-357b-c52d-dba9-d8a2560bce6d.png)



<br>
あとは、③serial requestノードをSmartHop(UARTシリアル)の通信設定値に合わせて設定すれば、SmartHop経由で取得したCO2濃度のセンサーデータが④デバックエディターに表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1620360/f57f7399-8d06-6ca6-f6d4-123ec891ea23.png)
#### ■ シリアルポート(プロパティ)
項目|設定内容|備考|
:-:|:-:| :-:
シリアルポート|/dev/tty920M|AE2100のSmartHopモジュールのインタフェース名|
ボーレート|115200|SmartHopモジュールのUART（固定値）|
データビット|8 |SmartHopモジュールのUART（固定値）|
パリティ|なし|SmartHopモジュールのUART（固定値）|
終了ビット|1|SmartHopモジュールのUART（固定値）|
分割後の配信データ|バイナリバッファ||
※他の項目はデフォルトでOKです。
<br>
デバックエディターには以下のように表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1620360/26d7e952-2ca9-6cd3-d38c-cb83570fdbb6.png)






# センサーデータの可視化
センサーデータをグラフ表示するために、CO2コントローラーから受信したバイナリからCO2濃度のデータの部分を抜き出す必要があります。具体的には、④デバックエディターで表示されているバイナリのmsg.payload[7]とmsg.payload[8]を2byteの16進数にして、16進数を10進数にするプログラムを⑤functionノードに実装します。

```diff_jsl
var dec = new Buffer(1);//新しい配列を作成
var s1=msg.payload[7].toString(16);//[7]に格納されている数値を16進数の文字型に変換
var s2=msg.payload[8].toString(16);//[8]に格納されている数値を16進数の文字型に変換
var sum=s1+s2;//[7]に格納されている文字と[8]に格納されている文字を結合(2byteの16進数にする)
dec= parseInt(sum,16);//16進数の文字列を10進数の数字に変換
msg.payload = dec;//msg.payloadに数値を格納
return msg;
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1620360/ae99f0e5-fcd6-1efd-b531-344e0ca7765d.png)


デバックエディターで表示されている数値とCO2コントローラーに表示されている数値と一致していればＯＫです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1620360/ff73bfc2-910f-9a7c-12e3-b9ec8f673691.png)





最後に、gaugeノードとchartノードを繋ぐと下図のようにCO2濃度のゲージグラフおよび時間推移がダッシュボードに表示できました。
なお、gaugeノードとchartノードの細かい設定やダッシュボードの開き方については、[OKI AE2100 & Node-REDでローコードIoTしてみた。その３ 実践編 1](https://qiita.com/marufumio/items/7e04006f48036cc37f21)に分かりやすく記載されているので、そちらをご参照ください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1620360/86fcd4cb-c179-9593-f6d4-2d3c2eac3599.png)



# 参考
念のため、ノードフローのjsonファイルを公開します。この内容をコピペしたjsonファイルを作成して、Node-REDのパレットにドラック&ドロップすれば、一連の動作は問題なく動くはずです。もしセンサーデータが得られない場合は、[「AE2100」内蔵の920MHz帯無線「SmartHop」のモジュールを活用してみよう(1) ―無線ネットワーク構築編―](https://qiita.com/shimatak/items/ae87081694d1bcbb3cf5)を再度ご確認ください。

```flows.json
[{"id":"d55d9079.29bed","type":"tab","label":"フロー 1","disabled":false,"info":""},{"id":"e80027f1.e4ba88","type":"debug","z":"d55d9079.29bed","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":240,"wires":[]},{"id":"c57639f0.91ab98","type":"debug","z":"d55d9079.29bed","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":950,"y":240,"wires":[]},{"id":"54e5e0f3.d2eb1","type":"serial request","z":"d55d9079.29bed","name":"serial request","serial":"5a7b30e9.8a0bb","x":500,"y":400,"wires":[["564d4ba0.ff0dd4","e80027f1.e4ba88"]]},{"id":"4d80c7a5.b693d8","type":"function","z":"d55d9079.29bed","name":"","func":"var buf = new Buffer(13); // バイナリの配列をつくる\n//SmartHopのモジュールとホストCPU間APIの\n//通信フォーマット(UARTインターフェースの通信フォーマット)に従って\n//電文(バイナリ)を送る\nbuf[0]=0x7E;//SYN(0x7E固定) 1byte\n\nbuf[1]=0x11;//ProcessID：モジュールとホストCPU間の透過データ　 1byte\n           //仕様書P.19の表5参照\n\nbuf[2]=0x00;//Length(payloadサイズ) 2byte\nbuf[3]=0x08;//ここでは、8byteを指定\n\n//CO2コントローラーの測定データ取得コマンド(8byte)を送る\nbuf[4]=0x01;//スレーブアドレス(局番) 1byte\n\nbuf[5]=0x03;//機能コード(0x03固定)　1byte\n\nbuf[6]=0x00;//データ項目 2byte\nbuf[7]=0x44;//ここでは、CO2濃度を指定\n\nbuf[8]=0x00;//データ数　2byte\nbuf[9]=0x01;//ここでは、１つのデータを指定\n\nbuf[10]=0xC4;//エラーチェック　2byte\nbuf[11]=0x1F;\n//CO2コントローラーの測定データ取得コマンド(8byte)終了\n\nbuf[12]=0x7E;//SYN(0x7E固定) 1byte\n\n \nmsg.payload = buf;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":340,"y":400,"wires":[["54e5e0f3.d2eb1"]]},{"id":"564d4ba0.ff0dd4","type":"function","z":"d55d9079.29bed","name":"","func":"var dec = new Buffer(1);//新しい配列を作成\nvar s1=msg.payload[7].toString(16);//[7]に格納されている数値を文字型に変換\nvar s2=msg.payload[8].toString(16);//[8]に格納されている数値を文字型に変換\nvar sum=s1+s2;//[7]に格納されている文字と[8]に格納されている文字を結合する\ndec= parseInt(sum,16);//16進数の文字を10進数の数字に変換\nmsg.payload = dec;//msg.payloadに数値を格納\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":680,"y":400,"wires":[["c57639f0.91ab98","251df414.d0183c","fa263f4b.29865"]]},{"id":"251df414.d0183c","type":"ui_gauge","z":"d55d9079.29bed","name":"gauge","group":"ddfec04a.0cf5f","order":1,"width":6,"height":6,"gtype":"gage","title":"CO2濃度","label":"ppm","format":"{{value}}","min":0,"max":"2000","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":930,"y":340,"wires":[]},{"id":"fa263f4b.29865","type":"ui_chart","z":"d55d9079.29bed","name":"chart","group":"ddfec04a.0cf5f","order":4,"width":9,"height":6,"label":"CO2濃度推移","chartType":"line","legend":"false","xformat":"HH:mm","interpolate":"linear","nodata":"","dot":false,"ymin":"400","ymax":"800","removeOlder":"10","removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"x":930,"y":440,"wires":[[]]},{"id":"cbe762b5.1dff7","type":"inject","z":"d55d9079.29bed","name":"inject","props":[{"p":"payload"}],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":210,"y":400,"wires":[["4d80c7a5.b693d8"]]},{"id":"5a7b30e9.8a0bb","type":"serial-port","z":"","serialport":"/dev/tty920M","serialbaud":"115200","databits":"8","parity":"none","stopbits":"1","waitfor":"","dtr":"none","rts":"none","cts":"none","dsr":"none","newline":"/n","bin":"bin","out":"char","addchar":"","responsetimeout":"1000"},{"id":"ddfec04a.0cf5f","type":"ui_group","z":"","name":"ダッシュボード","tab":"d2e076eb.aafbd8","order":1,"disp":true,"width":18,"collapse":false},{"id":"d2e076eb.aafbd8","type":"ui_tab","z":"","name":"Qiitaデモ","icon":"dashboard","order":1,"disabled":false,"hidden":false}]
```


# まとめ
今回は、AE2100内蔵の920MHz帯マルチホップ無線「SmartHop」モジュールを活用して、センサーデータの収集・見える化を行いました。Node-REDを活用することで、ローコードなプログラムの実装が実現し、直感的に実装内容をご理解いただけたのではないでしょうか。
次回は、本記事で収集したデータの閾値に合わせてパトランプの制御を行っていきたいと思います。

次回の記事:
[「AE2100」内蔵の920MHz帯無線「SmartHop」のモジュールを活用してみよう(3)―パトランプ発報編―](https://qiita.com/shimatak/items/270be8410ee3374a1223)
前回の記事:
[「AE2100」内蔵の920MHz帯無線「SmartHop」のモジュールを活用してみよう(1) ―無線ネットワーク構築編―](https://qiita.com/shimatak/items/ae87081694d1bcbb3cf5)