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を使用する場合も、この先の手順は同じになります。
この続きは次回にしたいと思います。










コメントを残す