Node-REDを使用して、Arduinoの入出力をコンピュータからGUI操作する方法を紹介します。例として、今回はLED制御(ON/OFF、明るさ調整、アナログ値の時系列グラフ表示)をブラウザ上のユーザーインターフェースで行います。Node-REDは、やってみるとそれほど難しくなく、IoT機器/アプリを作ってみたいという方にはおすすめです。
MQTTブローカーソフトウェア
この実験ではコンピュータがブローカーとクライアントの両方の役割を持ちます。
クライアントの1つ「MQTT.fx」は通信プロセスを理解できるよう、MQTTの通信トラフィックを表示するために使用します。
もう1つのクライアント「Node-RED」はArduino/ESPで読み取ったアナログ値の表示やLEDの制御を行うブラウザ上のユーザーインターフェースを提供するために使用します。
最初に、3つのプログラムをインストールします。
1つ目はMQTTブローカーとなるMosquittoです。ダウンロードページからインストーラファイルをダウンロードしてください。
https://mosquitto.org/download/
Windowsの場合は、インストーラの実行ファイル(.exe)を実行します。設定はデフォルトのまま、変更は必要ありません。
続いて、MQTT通信のトラフィックを表示する、MQTT.fxをインストールします。
同様にダウンロードページからインストーラファイルをダウンロード・インストールしてください。
https://mqttfx.jensd.de/index.php/download
最後に、MQTTクライアントとしてNode-REDをインストールします。
Node-REDを実行するために、Node-jsというプラットフォームが必要なので、こちらを先にインストールします。
ダウンロードページからインストーラファイルをダウンロードしてください。
https://mosquitto.org/download/
Windowsの場合は、インストーラの実行ファイル(.msi)を実行します。設定はデフォルトのまま、変更は必要ありません。
https://nodejs.org/ja/download/
Node-jsプラットフォームのインストールが完了したら、Node-REDアプリケーションをインストールします。
コマンドプロンプトを開きます。Windowsではスタートメニューを開き、「cmd」と入力します。
コマンドプロンプトで次のように入力します。
node –version && npm -version
Enterキーを押して、バージョンが表示されればNode-jsが正しくインストールできています。
エラーメッセージが出た場合は、再度インストールを試してください。
次のコマンドで、Node-REDのインストールを開始します。
npm install -g –unsafe-perm node-red
Enterキーを押すと、Node Package ManagerがNode-REDをダウンロード・インストールしてくれます。
これで、MQTTの実験の準備が整いました。ここからはArduino/ESPをプログラミングしていきます。
プログラミングの準備
まず、Arduino IDEのライブラリマネージャから必要なライブラリ「PubSubCluent.h」をダウンロード・インストールします。
作者名は「by Nick O’Leary」となっているはずです。
スケッチを作成する前に、MQTTブローカー(ここではパソコン)のIPアドレスを調べておきます。
コマンドプロンプトでコマンド「ipconfig」を入力し、Enterキーを押します。
ネットワーク接続が一覧で表示されます。
このうち、「IPv4アドレス」を今回は使用します。図8では「192.168.1.137」です。これで、MQTTプラットフォームを利用した実験を進められます。
前回までの実験と同様、ArduinoやESPで可変抵抗から読み取ったアナログ値を表示し、ArduinoやESPに接続したLEDのON/OFF制御、ここでは加えてPWMによる明るさの調整を行います。
イーサネットシールド用のプログラム
まずはArduinoとEthernet Shieldを使用する場合のスケッチを見てみましょう。
後にESP8266とESP32のスケッチも示します。これらは少しだけプログラムが異なっています。
#define LEDPIN 3 #define ANALOGPIN 0 #define MQTT_BROKER "192.168.1.137" #include "SPI.h" #include "Ethernet.h" #include "PubSubClient.h" //(1) byte MAC[] = {0x00, 0x08, 0xDC, 0x12, 0x34, 0x56}; EthernetClient ethClient; PubSubClient mqttClient(ethClient); //(2) long Timer = 0; char Buffer[50]; void setup() { pinMode(LEDPIN, OUTPUT); Serial.begin(115200); delay(100); Serial.println("My IP-Adresse: "); Ethernet.begin(MAC); delay(3000); Serial.println(Ethernet.localIP()); mqttClient.setServer(MQTT_BROKER, 1883); //(3) } void loop() { if (!mqttClient.connected()) //(4) reconnect(); mqttClient.loop(); //(5) if(millis() > Timer) //(6) { String AnalogValue = String(analogRead(ANALOGPIN)); AnalogValue.toCharArray(Buffer, 20); //(7) mqttClient.publish("Arduino/AnalogValue", Buffer); //(8) Serial.println("Published Value: " + AnalogValue); Timer = millis() + 5000; } Ethernet.maintain(); } void subscribeReceive(char* Topic, byte* UserData, unsigned int Length) //(9) { Serial.println("### Receive ###"); Serial.print("Topic: "); Serial.println(Topic); //(10) Serial.print("User Data: "); for(int i = 0; i < Length; i ++) Serial.print(char(UserData[i])); Serial.println(""); byte Brightness = atoi(UserData); //(11) Serial.print("Brightness: "); Serial.println(Brightness); analogWrite(LEDPIN, Brightness); Serial.println("-----------------"); } void reconnect() //(12) { while(!mqttClient.connected()) //(13) { Serial.print("Connect with MQTT."); if(!mqttClient.connect("EthernetClient")) //(14) { Serial.print("Error: "); Serial.print(mqttClient.state()); Serial.println("Try again in 5 seconds."); delay(5000); } mqttClient.setCallback(subscribeReceive); //(15) mqttClient.subscribe("Arduino/LED"); //(16) } }
(1) MQTTライブラリを読み込みます。
(2) オブジェクトmqttClientがクライアントを表します。
(3) 「setServer()」関数でクライアントに接続するブローカーを決定します。
パソコンにインストールしたMosquittoを利用するので、パソコンのIPアドレスを指定します。Mosquittoはデフォルトでポート1883を使用します。
(4) メインの処理を行う前に、ブローカーに接続できているかチェックします。接続されていなければ「reconnect()」関数を呼び出し、ブローカーへの接続を試みます。電源を入れたり、リセットしたりした直後にも一度実行します。
(5) PubSubCliantライブラリではメインループの任意の場所でクライアントオブジェクトの「loop()」関数を呼び出して、ブローカーが新しいメッセージを送信したかどうかをチェックします。
(6) このif条件は5秒ごとにtrueとなって内部の処理を実行し、可変抵抗のアナログ読み取り値をブローカーに送信します。
(7) このライブラリでは送信する値をchar配列で送る必要があるので、オブジェクト関数「toCharArray()」で文字列オブジェクトのアナログ値をchar配列にコピーします。
(8) 「publish()」関数でトピックとメッセージをMQTTネットワークに送信します。引数の1つ目にトピック、2つ目にメッセージを指定します。ここでは省略していますが、3つ目にtrueを指定するとリテイナーが有効になります。
(9) 「subscribeReceive」関数はブローカーからメッセージを受信したときにライブラリから呼び出されます。
(10) この例ではトピックによって動作を変えることは行っておらず、ただシリアルモニタに表示しているだけです。
(11) メッセージの内容は文字列として受け取ります。「atoi()」関数(ACSII to Integer)で整数に変換することで、LEDのPWM制御に利用できるようになります。
(12) 「reconnect」関数はMQTTブローカーに最初に接続するときや、接続しなおすときに呼び出されます。
(13) MQTT接続ができるまで、接続動作を繰り返します。
(14) 「connect()」関数を使用して接続を行います。引数にはネットワーク接続を表すオブジェクトの名前を指定します。
(15) 接続が確立されると、(9)の「subscribeReceive」関数が呼び出されます。
(16) トピック「Arduino/LED」を受け取ります。
ESP8266用のプログラム
続いて、ESP8266のプログラムです。
#define LEDPIN D3 #define ANALOGPIN A0 #define NETWORKNAME "MyWLAN" #define PASSWORD "Password" #define MQTT_BROKER "192.168.1.137" #include "ESP8266WiFi.h" #include "PubSubClient.h" WiFiClient espClient; PubSubClient mqttClient(espClient); long Timer = 0; char Buffer[50]; void setup() { pinMode(LEDPIN, OUTPUT); Serial.begin(115200); delay(100); Serial.println(); Serial.print("Connect with WLAN: "); Serial.println("My WLAN");//Serial.println(NETWORKNAME); WiFi.begin(NETWORKNAME, PASSWORD); while (WiFi.status() != WL_CONNECTED) //(1) { delay(500); Serial.print("."); } Serial.println(""); Serial.print("Successfully. My IPaddress: "); Serial.println(WiFi.localIP()); mqttClient.setServer(MQTT_BROKER, 1883); } void loop() { if (!mqttClient.connected()) reconnect(); mqttClient.loop(); if(millis() > Timer) { String AnalogValue = String(analogRead(A0)); AnalogValue.toCharArray(Buffer, 20); mqttClient.publish("Arduino/AnalogValue", Buffer); Serial.println("Published Value: " + AnalogValue); Timer = millis() + 5000; } } void subscribeReceive(char* Topic, byte* UserData, unsigned int Length) { Serial.println("### Receive ###"); Serial.print("Topic: "); Serial.println(Topic); Serial.print("UserData: "); for(int i = 0; i < Length; i ++) Serial.print(char(UserData[i])); Serial.println(""); byte Brightness = (UserData[0]-'0')*100 + (UserData[1]-'0')*10 + (UserData[2]-'0'); //(2) Serial.print("Brightness: "); Serial.println(Brightness); analogWrite(LEDPIN, Brightness); Serial.println("-----------------"); } void reconnect() { while (!mqttClient.connected()) { Serial.print("Connect with MQTT."); if (!mqttClient.connect("ESP8266Client")) { Serial.print("Error: "); Serial.print(mqttClient.state()); Serial.println("Try again in 5 seconds."); delay(5000); } } mqttClient.setCallback(subscribeReceive); mqttClient.subscribe("Arduino/LED"); }
(1) webサーバーの例の時と同様、WiFi接続が確立するまでそれ以降の処理の実行を待ちます。
(2) ESP8266のコンパイラではatoi()関数が使用できません。ここでは、手動で文字列を数値に変換しています。
ESP32用のプログラム
ESP32では、analogRead()関数やanalogWrite()関数が使えないため、さらに変更を行います。
#define LEDPIN 4 #define PWM_CHANNEL 0 #define ANALOGPIN ADC1_CHANNEL_4 #define NETWORKNAME "MyWLAN" #define PASSWORD "Password" #define MQTT_BROKER "192.168.1.137" #include "WiFi.h" #include "driver/adc.h" //(1) #include "PubSubClient.h" WiFiClient espClient; PubSubClient mqttClient(espClient); long Timer = 0; char Buffer[50]; void setup() { pinMode(LEDPIN, OUTPUT); Serial.begin(115200); delay(100); Serial.println(); Serial.print("Connect with WLAN: "); Serial.println("My WLAN");//Serial.println(NETWORKNAME); WiFi.begin(NETWORKNAME, PASSWORD); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.print("Successfully. My IPaddress: "); Serial.println(WiFi.localIP()); mqttClient.setServer(MQTT_BROKER, 1883); adc1_config_width(ADC_WIDTH_BIT_10); //(2) adc1_config_channel_atten(ANALOGPIN,ADC_ATTEN_DB_11); ledcSetup(PWM_CHANNEL, 5000, 8); //(3) ledcAttachPin(LEDPIN, PWM_CHANNEL); //(4) } void loop() { if (!mqttClient.connected()) reconnect(); mqttClient.loop(); if(millis() > Timer) { String AnalogValue = String(adc1_get_raw(ANALOGPIN)); AnalogValue.toCharArray(Buffer, 20); mqttClient.publish("Arduino/AnalogValue", Buffer); Serial.println("Published Value: " + AnalogValue); Timer = millis() + 5000; } } void subscribeReceive(char* Topic, byte* UserData, unsigned int Length) { Serial.println("### Receive ###"); Serial.print("Topic: "); Serial.println(Topic); Serial.print("UserData: "); for(int i = 0; i < Length; i ++) Serial.print(char(UserData[i])); Serial.println(""); byte Brightness = (UserData[0]-'0')*100 + (UserData[1]-'0')*10 + (UserData[2]-'0'); Serial.print("Brightness: "); Serial.println(Brightness); ledcWrite(PWM_CHANNEL, Brightness); //(5) Serial.println("-----------------"); } void reconnect() { while (!mqttClient.connected()) { Serial.print("Connect with MQTT."); if (!mqttClient.connect("ESP8266Client")) { Serial.print("Error: "); Serial.print(mqttClient.state()); Serial.println("Try again in 5 seconds."); delay(5000); } } mqttClient.setCallback(subscribeReceive); mqttClient.subscribe("Arduino/LED"); }
(1) アナログ入力の追加ライブラリについてはwebサーバーの例で使用したのと同じものを使用します。
(2) webサーバーの例と同様に、アナログ入力に関する設定を行います。
(3) ESP32では16チャンネルのPWMが用意されています。(LEDコントローラと呼ばれています)
LEDコントローラを使用するには3つの引数を用意して「ledcSetup()」関数を呼び出します。
1つ目の引数でチャンネル、2つ目でPWMの周波数、3つ目で分解能をそれぞれ指定します。
(4) LEDコントローラを出力端子に割り当てます。
(5) 「ledcWrite()」関数がArduinoでいうところのanalogWrite()関数に対応します。
スケッチのアップロード後、パソコン上でクライアントMQTT.fxを使用して、クライアントが動作するかテストを行います。
Arduino+Ethernet Shieldを使用する場合も、ESP8266/ESP32を使用する場合も、この先の手順は同じになります。
この続きは次回にしたいと思います。
コメントを残す