OMRON USB型環境センサー 2JCIE-BUをLinux(debian9/OpenBlocks IoT)からUSB接続して使用する

OMRON USB型環境センサー 2JCIE-BUは、USBにより電源供給される、オムロン環境センサーのUSB版となります。
本製品の使い方としては、電源供給をUSBで、通信は従来品と同様にBLE通信を行えば、従来品同様通信が可能ですが、合わせてUSB経由でのデータ取得が可能です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/316451/b2bfbd53-3964-a0a0-9af2-96b90b5d32be.png)

https://www.omron.co.jp/ecb/product-detail?partId=73063

OpenBlocks IoTシリーズのFW3.3ではBLEによる接続は標準サポートしていますが、USBでの有線接続には対応していません。
今回は、本センサーからUSB経由でデータを取得するための方法についてNode-REDによる実装を行ってみました。
なお、本記事はOpenBlocks IoT(debian9)上で検証を行っていますが、debian9はもとより、他の汎用Linuxデストリビューションでも利用可能かと思います。

## Linuxにおけるデバイスの扱いと設定について

本センサーのUSB接続はUSBシリアル接続で、対応するのはftdi_sioです。
ただし、本チップのvendor_idとproduct_idはftdi_sioには含まれていませんので、ドライバを再構築するか、以下の手順でnew_idを書き込むことによりftdi社のUSBシリアルデバイスとして認識します。

```
modprobe ftdi_sio
echo "0590 00d4" > /sys/bus/usb-serial/drivers/ftdi_sio/new_id
```
Linux(debian9)起動時の設定方法の例としては、以下の内容のファイルをrules.dに追加します。
/etc/udev/rules.d/80-rename-2JCIE-BU.rules

```
ACTION=="add", ATTRS{idVendor}=="0590", ATTRS{idProduct}=="00d4", RUN+="/sbin/modprobe ftdi_sio" RUN+="/bin/sh -c 'echo 0590 00d4 > /sys/bus/usb-serial/drivers/ftdi_sio/new_id'", SYMLINK+="2JCIE-BU"
```
再起動し、/dev/2JCIE-BUが/dev/ttyUSB*(*は0以上の数字)のシンボリックリンクとして作成されていることを確認してください。

この状態で、環境センサはシリアルデバイスとして利用できる状態になっています。
2JCIE-BUのユーザーズマニュアルに従い、各種設定や、データの取り出しを行うことが可能となります。
マニュアルは、上記の製品情報ページから入手してください。

## Node-REDによるセンサーデータの取得例

センサーデータの取得や、JSON形式への展開は、Node-REDを利用するとシリアル通信周りのコーディングを簡略化することができます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/316451/1fa779eb-8185-7e3a-a5e6-63be3a89ef4e.png)

Node-RED によるサンプルフロー

```
[
    {
        "id": "938af1b1.33e5c",
        "type": "inject",
        "z": "21982ae5.5c1f96",
        "name": "",
        "repeat": "",
        "crontab": "",
        "once": false,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 260,
        "y": 80,
        "wires": [
            [
                "d76add5b.d7ef5"
            ]
        ]
    },
    {
        "id": "1e28cad3.7b43e5",
        "type": "serial in",
        "z": "21982ae5.5c1f96",
        "name": "OMRON ENV USB SERIAL",
        "serial": "e4eeb6ae.937598",
        "x": 260,
        "y": 260,
        "wires": [
            [
                "26598d01.ad0e82",
                "458e56ec.fe6d78"
            ]
        ]
    },
    {
        "id": "649587d3.0b5f38",
        "type": "serial out",
        "z": "21982ae5.5c1f96",
        "name": "OMRON ENV USB Serial",
        "serial": "e4eeb6ae.937598",
        "x": 750,
        "y": 80,
        "wires": []
    },
    {
        "id": "d76add5b.d7ef5",
        "type": "function",
        "z": "21982ae5.5c1f96",
        "name": "テストコマ",
        "func": "var buf = new Buffer([0x52, 0x42, 5, 0,0x01,0x22,0x50,0xe2,0xbb ]);\nmsg.payload = buf;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 470,
        "y": 80,
        "wires": [
            [
                "649587d3.0b5f38",
                "daa302a6.bddb"
            ]
        ]
    },
    {
        "id": "daa302a6.bddb",
        "type": "debug",
        "z": "21982ae5.5c1f96",
        "name": "",
        "active": false,
        "console": "false",
        "complete": "payload",
        "x": 760,
        "y": 160,
        "wires": []
    },
    {
        "id": "26598d01.ad0e82",
        "type": "function",
        "z": "21982ae5.5c1f96",
        "name": "BuffertoJSON",
        "func": "var crc = 0xFFFF;\nvar odd;\n\nfor (var i = 0; i < msg.payload.readInt16LE(2)+2; i++) {\n    crc = crc ^ msg.payload[i];\n\n    for (var j = 0; j < 8; j++) {\n        odd = crc & 0x0001;\n        crc = crc >> 1;\n        if (odd) {\n            crc = crc ^ 0xA001;\n        }\n    }\n}\nif ( crc== msg.payload.readUInt16LE(msg.payload.readInt16LE(2)+2)){\n    temperature= msg.payload.readInt16LE(8)/100;\n    humidity= msg.payload.readInt16LE(10)/100;\n    light= msg.payload.readInt16LE(12);\n    pressure= msg.payload.readInt32LE(14)/1000;\n    sound= msg.payload.readInt16LE(18)/100;\n    etvoc= msg.payload.readInt16LE(20);\n    eco2= msg.payload.readInt16LE(22);\n    discomfortindex= msg.payload.readInt16LE(24)/100;\n    heatstroke= msg.payload.readInt16LE(26)/100;\n    msg.payload=\n    {\n        temperature: temperature,\n        humidity: humidity,\n        light: light,\n        pressure: pressure,\n        sound: sound,\n        etvoc: etvoc,\n        eco2,\n        discomfortindex: discomfortindex,\n        heatstroke: heatstroke\n    }\n    return msg;\n}",
        "outputs": "1",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 540,
        "y": 260,
        "wires": [
            [
                "15f63fd3.03a5c"
            ]
        ]
    },
    {
        "id": "15f63fd3.03a5c",
        "type": "debug",
        "z": "21982ae5.5c1f96",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "false",
        "x": 830,
        "y": 260,
        "wires": []
    },
    {
        "id": "458e56ec.fe6d78",
        "type": "debug",
        "z": "21982ae5.5c1f96",
        "name": "",
        "active": false,
        "console": "false",
        "complete": "false",
        "x": 500,
        "y": 340,
        "wires": []
    },
    {
        "id": "e4eeb6ae.937598",
        "type": "serial-port",
        "z": "",
        "serialport": "/dev/2JCIE-BU",
        "serialbaud": "115200",
        "databits": "8",
        "parity": "none",
        "stopbits": "1",
        "newline": "100",
        "bin": "bin",
        "out": "time",
        "addchar": false
    }
]
```

サンプルでは、
USB Original Address 0x5022 (Latest data short)を指定して、以下のデータ取得コマンドを発行し、
受信したレスポンスデータのCRC16チェックを行い、正しい場合のみJSON形式に展開しています。

```
0x52, 0x42, 0x05, 0x00, 0x01, 0x22, 0x50, 0xe2,0xbb
```
Node-REDでシリアルデバイスへのデータリクエストは、functionノードで以下の記述を行っています。

```
var buf = new Buffer([0x52, 0x42, 5, 0,0x01,0x22,0x50,0xe2,0xbb ]);
msg.payload = buf;
return msg;
```
シリアルデバイスの設定は、

```
Serial Port: /dev/2JCIE-BU
Setting: 115200bps / 8bit / NonParity / 1stopbit			
Input: Split input  after a timeout 100ms
 and deliver binary buffers
```
としています。
injectボタンを押すとコマンドがシリアルデバイスに出力され、レスポンスをJSON形式でパースしたものが、debugノードで確認できます。

起動時より自動的に一定時間毎のデータ計測を行いたい場合は、injectノードを編集し、「Node-RED起動時に実行」「繰り返し」等の設定を行ってください。
JSON形式にしていますので、Node-REDで利用できるノードを用いて、サーバやクラウドのIoT Hubへ送信したり、データベースへの書き込みを行うことも容易かと思います。
また、OpenBlocks IoTシリーズの場合、ユーザーデバイス登録を行い、IPC Outへパスを設定することで、パースしたJSONデータをPD repeaterで扱うことにより、各種クラウドやAzure IoT Edgeのコンテナモジュールへの送信について、WebUIで設定/リモート管理出来ます。

## 最後に
本センサーはUSB経由でログデータの取り出しなども行えますので、いろいろ試してみてください。
また、CRC16チェック以外のエラーハンドリングは実装していません。実運用の場合は最低でもレスポンスのチェックとエラー時のデータ再取得などのフローが必要になります。

なお、本センサーはUSBポートに直結出来ますが、PC本体の発熱により温度センサーの値が上昇しますので、環境センサーとして利用する場合は、延長ケーブルを用意して測定場所を離す必要があります。
また、精度の高い振動検出などを行う場合は、非常に細い線材によりUSB配線を行う必要もあるかと思います。
設置場所や配線材については、設置する場所やモノに合わせて工夫する必要があります。

--
(ご)　　Toshiya Goto
　　　　goto@plathome.co.jp