0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

M5StickCの書き換えが面倒だったので、Node.jsでArduinoっぽくしてみた

Last updated at Posted at 2020-10-18

M5StickCのように、ESP32にGroveが付いているのが多いで、いろんな周辺デバイスをいじりたいのですが、そのたびに、リコンパイル&書き込みをするのはすごく面倒で時間がかかるので、MQTTを使ってリモートから操作できるようにして、クライアント側はNode.jsでたたけるようにしました。

M5StickC側では、I2C、Serial、Lcd、Gpioを操作できるようにしましたので、一度バイナリを書き込んでしまえばあとは、クライアント側のNode.jsで周辺デバイスのドライバを書くことができます。
ですが、動作はそこまで速くない(特にLcd描画系)ので、お遊び程度に思ってください。

で、Node.jsになって開発しやすくなったので、手元にあったI2Cデバイスを一気に実装しました。

・M5Stack ENVユニット
 https://www.switch-science.com/catalog/5690/
・M5Stack TVOC/eCO2ユニット
 https://www.switch-science.com/catalog/6619/
・Grove OLED Display 0.96”
 https://www.switch-science.com/catalog/829/
・Grove Digital Light Sensor
 https://www.switch-science.com/catalog/1174/

以下のGitHubに上げておきました。

poruruba/RemoteArduino
 https://github.com/poruruba/RemoteArduino

#MQTTトピック名

2つのMQTTトピックを使います。
1つは、Node.js側からM5StickCに要求するコマンド送信のためのトピック
もう一つは、M5StickCから処理結果をNode.jsに返すためのトピック

とりあえず、
前者は、m5lite/cmd【M5StickCのMacアドレスの16進数表記】、
後者は、m5lite/rsp【M5StickCのMacアドレスの16進数表記】
にしています。

Arduino側

以下のライブラリを使わせていただいています。

tanakamasayuki/ESP32LitePack
 https://github.com/tanakamasayuki/ESP32LitePack

もちろん、それに含まれる以下も利用させていただきました。
lovyan03/LovyanGFX
 https://github.com/lovyan03/LovyanGFX

これらのおかげで、描画系が整備され、マルチデバイスで動くようになりましたっ!!

また、以下も利用させていただいています。

knolleary/pubsubclient
 https://github.com/knolleary/pubsubclient

ArduinoJson
 https://arduinojson.org/

##MQTTで受信されるデータフォーマット

こんなJSONがM5StickCに届きます。

{
 “client_id”: “【クライアントID】”,
 “tx_id”: 【トランザクションID】,
 “device_type”: “【デバイスタイプ】”,
 “cmd”: “【コマンド名】”,
 “params”: {
  “param1”: 【1番目のパラメータ】,
  “param2”: 【2番目のパラメータ】,
  “param3”: 【3番目のパラメータ】,
  “param4”: 【4番目のパラメータ】,
  “param5”: 【5番目のパラメータ】,
  “param6”: 【6番目のパラメータ】,
  “param7”: 【7番目のパラメータ】
 }
}

クライアントIDとトランザクションIDは、送信側(Node.js側)が自由に付けて送ります。M5StickC側はそのまま同じ値をレスポンスに返します。トランザクションIDを毎回インクリメントすれば、送ったレスポンスかどうかがわかることになります。
デバイスタイプは今のところ以下の4種類です。

  • Serial
  • Gpio
  • Lcd
  • Wire、Wire1

コマンド名は、デバイスタイプごとにサポートする名前が異なります。
整理する意味でざっと上げてみました。

デバイス名 コマンド名
Serial begin
^ end
^ available
^ read
^ peek
^ flush
^ print
^ println
^ write
^ write_str
^ write_buf
Gpio pinMode
^ digitalWrite
^ digitalRead
^ analogRead
^ analogReadResolution
Wire begin
^ requestFrom
^ beginTransmission
^ endTransmission
^ write
^ write_str
^ write_buf
^ available
^ read
^ read_buf
Lcd setRotation
^ setTextColor
^ setBrightness
^ drawPixel
^ drawLine
^ drawRect
^ fillRect
^ fillScreen
^ drawTriangle
^ fillTriangle
^ drawCircle
^ fillCircle
^ drawEllipse
^ fillEllipse
^ drawBmpData
^ getRange

およそArduinoに合わせてあるので、イメージしやすいかと思います。
M5StickCでは、受け取ったMQTTパケットを解析し、ArduinoのAPI呼び出しに渡す処理をしているだけです。
処理結果は、MQTTトピックにPublishして戻しています。

レスポンスのフォーマットは以下の通りです。

{
 “client_id”: “【クライアントID】”,
 “tx_id”: 【トランザクションID】,
 “device_type”: “【デバイスタイプ】”,
 “rsp”: “【コマンド名】”,
 “status”: “【処理結果】”,
 “params”: {
  “param1”: 【1番目のパラメータ】,
  “param2”: 【2番目のパラメータ】,
  “param3”: 【3番目のパラメータ】,
  “param4”: 【4番目のパラメータ】,
  “param5”: 【5番目のパラメータ】,
  “param6”: 【6番目のパラメータ】,
  “param7”: 【7番目のパラメータ】
 }
}

cmdがrspに代わっただけです。
クライアントIDとトランザクションIDは、コマンドにあったものをそのまま返しています。
処理結果には、”OK”か”NG”が入ります。NGの場合には、reasonも一緒に理由が入って帰ります。
paramsは処理結果です。デバイスタイプとコマンド名で決まる処理内容によって異なります。

複雑な処理はしておらず、ただただ、各コマンドをArduinoのAPIに変換する処理をえんえんと記述しています。

#Node.js側

以下のファイル構成になっています。

arduino.js
 これがメインとなるクラスです。これをrequireすると、Serial、Gpio、Lcd、WireおよびWire1が一緒にインスタンス化されます。

arduino_device/Gpio.js
 Gpioのコマンド送信のためのクラスです。

arduino_device/Serial.js
 Serialのコマンド送信のためのクラスです。

arduino_device/Lcd.js
 Lcdのコマンド送信のためのクラスです。

arduino_device/Wire.js
 Wireのコマンド送信のためのクラスです。
 周辺デバイスはI2Cデバイスが多いので、こちらを多用しました。

#使い方(Node.js側)

まずは、メインとなるArduinoクラスをインスタンス化します。

index.js
const Arduino = require('./arduino');
const arduino = new Arduino(MQTT_CLIENT_ID, MQTT_HOST_URL, MQTT_TOPIC_CMD, MQTT_TOPIC_RSP);

その際に、接続するMQTTのクライアントID、MQTTブローカのURL、接続するMQTTトピック名の送信用と受信用を指定します。トピック名は、接続するM5StickCの設定に合わせます。

MQTTブローカのURLは以下のような感じに指定します。
 tcp://【MQTTブローカのホスト名】:1883

そして、以下のようにしてMQTTに接続し、内部でLcdの画面サイズを取得しています。

index.js
 await arduino.connect();

あとは、Wireを使いたい場合は、arduino.Wireにあります。

index.js
  var wire = arduino.Wire;
  await wire.begin();

#周辺デバイスの制御

手持ちにある、I2Cデバイスの周辺デバイスを一通り実装しました。
I2Cアドレスが違うのであれば、I2Cハブを介して同時に接続できるかと思います。以下では4つ同時につないでいます。SSD 1308のOLED Displayは少々遅いですが、それ以外はあまり遅さは気になりませんかね。

・M5Stack ENVユニット
  device/DHT12.js
  device/BME280.js
・M5Stack TVOC/eCO2ユニット
  device/SGC30.js
・Grove OLED Display 0.96”
  device/SSD1308.js
・Grove Digital Light Sensor
  device/TSL2561.js

M5StackやAdafruitやseeedが提供するサンプルを参考にさせていただきました。っていうより、ほぼそのままNode.jsにポーティングです。
以下のようにして使います。

index.js
const SGC30 = require('./device/SGC30');
const DHT12 = require('./device/DHT12');
const TSL2561 = require('./device/TSL2561');
const BME280 = require('./device/BME280');
const SSD1308 = require('./device/SSD1308');

  var bme280 = new BME280(wire);
  await bme280.begin();
  var ret = await bme280.readTemperature();
  console.log(ret);
  var ret = await bme280.readPressure();
  console.log(ret);

  var dht12 = new DHT12(wire);
  var ret = await dht12.readTemperature();
  console.log(ret);
  var ret = await dht12.readHumidity();
  console.log(ret);

  var tsl2561 = new TSL2561(wire);
  await tsl2561.init();
  var ret = await tsl2561.readVisibleLux();
  console.log(ret);

  var sgc30 = new SGC30(wire);
  var ret = await sgc30.begin();
  console.log(ret);
  await sgc30.IAQmeasure();
  console.log(sgc30.TVOC);
  console.log(sgc30.eCO2);

  var ssd1308 = new SSD1308(wire);
  await ssd1308.init();
  await ssd1308.clear();
  await ssd1308.put_pixel(1, 1, true);
  await ssd1308.update();

#終わりに

自分はNode.jsに慣れているので、周辺デバイスのドライバ作成のデバッグが非常にはかどりました。修正・再実行が一瞬(数秒)なので。

ただ、動いたっぽく見えただけなので、いろいろバグがあるかもしれません。
さらに、I2C以外は動作確認していません。ほんと機械的な実装なので。。。

以上

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?