WiFi環境が整っているところでは、ルーターを経由した通信が一般的だと思います。今回は、ルーターに接続できない場所でデータ伝送をする必要があったので、プロトコルにUDPを使ったESP32同士の通信について試してみました。
備忘録的な記事になりますが、良かったらご覧ください。
プログラムについては、ceramie様のサイトを参考にさせて頂きました。
子機(client):データを取得して、データを送信します。
親機(server):子機から送られたデータを受信します。
UDPは、通信開始前に相手との間で事前のやりとりが必要ないコネクションレス型通信で、TCPに比べると信頼性は劣りますが高速の通信が可能です。
まず、親機(server)側のプログラムです。
#include <WiFi.h>
#include <WiFiUdp.h>
const char ssid[] = "ESP32_Server"; // SSID
const char password[] = "1234567890"; // password
const int localPort = 10000; // ポート番号
const IPAddress ip(192, 168, 4, 1); // ServerのIPアドレス
const IPAddress gateway(192, 168, 4, 1); // gatewayのIPアドレス
const IPAddress subnet(255, 255, 255, 0); // サブネットマスク
WiFiUDP udp;
char packetBuffer[256];
void setup() {
Serial.begin(115200);
WiFi.softAP(ssid, password);
delay(100);
WiFi.softAPConfig(ip, gateway, subnet);
Serial.println("Starting UDP");
udp.begin(localPort);
}
void loop() {
int i;
while (1) {
int packetSize = udp.parsePacket();
if (packetSize) {
for ( i = 0 ; i < 256 ; i++ ) packetBuffer[i] = 0;
udp.read(packetBuffer, 256);
Serial.println(packetBuffer);
}
}
}
4行目のssid、5行目のpasswordは、任意のもの(アルファベットと数字、一部の記号で構成)で構いません。子機から、親機に接続するときに必要になります。
const char ssid[] = "ESP32_Server"; // SSID
const char password[] = "1234567890"; // password
6行目のlocalPortには、使用するUDPのポート番号を指定してください。他の通信で、競合するポート番号がないか気を付けてください。
const int localPort = 10000; // ポート番号
17行目のsoftAPで、ESP32をルーターとして設定します。IPアドレス等は、19行目のsoftAPConfigで設定します。
WiFi.softAP(ssid, password);
delay(100);
WiFi.softAPConfig(ip, gateway, subnet);
22行目のudp.begin(localPort)で、localPortで指定したポート番号でUDP通信を開始します。
udp.begin(localPort);
29行目のudp.parsePacket()で、UDPで送られてきたパケット数(データ長)を取得します。
int packetSize = udp.parsePacket();
32行目のudp.read(packetBuffer, 256)で、データを受信します。受信したデータは、変数packetBufferに格納されます。一度に受信できる最大のバッファサイズ(ここでは256)を指定しています。
packetBufferの変数は、12行目でchar型で宣言しており、31行目で中身をクリアしています。
udp.read(packetBuffer, 256);
次に、子機(client)のプログラムです。
5秒ごとに、親機に”test data”という文字列を送るプログラムです。変数”WiFi_FLG”は、親機に接続できたかを判断する変数です。親機と接続できなかった場合は、何もしません。
#include <WiFi.h>
#include <WiFiUdp.h>
const char ssid[] = "ESP32_Server"; // SSID
const char password[] = "1234567890"; // password
static WiFiUDP wifiUdp;
static const char *kRemoteIpadr = "192.168.4.1"; //送信先のIPアドレス
static const int kRmoteUdpPort = 10000; //送信先のポート番号
static const int kLocalPort = 5000; //自身のポート番号
bool WiFi_FLG;
void setup() {
int i;
Serial.begin(115200);
WiFi_FLG = true;
i = 0;
WiFi.begin(ssid, password);
while ( WiFi.status() != WL_CONNECTED) {
i++;
if ( i > 30 ) {
WiFi_FLG = false;
break;
}
delay(500);
}
if ( WiFi_FLG == true ) wifiUdp.begin(kLocalPort);
}
void loop()
{
if ( WiFi_FLG == true ) {
wifiUdp.beginPacket(kRemoteIpadr, kRmoteUdpPort);
wifiUdp.print("test data");
wifiUdp.endPacket();
}
delay(5000);
}
4行目のssid、5行目のpasswordは、親機に設定したものを設定します。
const char ssid[] = "ESP32_Server"; // SSID
const char password[] = "1234567890"; // password
8行目の”kRemoteIpadr”には、親機に設定したIPアドレスを指定します。
9行目の”kRmoteUdpPort”には、親機に設定したUDPポート番号を指定します。
10行目の”kLocalPort”には、子機で使用するUDPのポート番号を指定してください。他の通信で、競合するポート番号がないか気を付けてください。
static const char *kRemoteIpadr = "192.168.4.1"; //送信先のIPアドレス
static const int kRmoteUdpPort = 10000; //送信先のポート番号
static const int kLocalPort = 5000; //自身のポート番号
19行目で、親機に接続を試みます。
WiFi.begin(ssid, password);
20行目から27行目で親機との接続を待ちます。500ms×30回待っても接続が確立できない場合は、変数”WiFi_FLG”をfalseにしてループを抜けます。
while ( WiFi.status() != WL_CONNECTED) {
i++;
if ( i > 30 ) {
WiFi_FLG = false;
break;
}
delay(500);
}
親機との接続が確立できていたら、UDP通信を開始します。このときのUDPポート番号は、子機のポート番号です。
if ( WiFi_FLG == true ) wifiUdp.begin(kLocalPort);
34行目のbeginPacket(kRemoteIpadr, kRmoteUdpPort)で、親機に対してUDPデータの書き込みを開始します。kRemoteIpadrは親機のIPアドレス、kRmoteUdpPortは親機のUDPポートです。
35行目のprintで、親機にデータを送ります。
36行目のendPacketで、UDPデータの書込み終了を伝えます。
wifiUdp.beginPacket(kRemoteIpadr, kRmoteUdpPort);
wifiUdp.print("test data");
wifiUdp.endPacket();
計測データをUDP経由でサーバーに送りたい場合は、wifiUdp.print()の中身に計測結果を入れることで可能となります。
以下は、超音波距離センサーHC-SR04を使って計測したデータを、10秒ごとにサーバーに送るプログラムです。距離の計測には、温度センサDS18B20を使って補正をしています。
制御は、M5StampC3を使っています。
#include <Adafruit_NeoPixel.h>
#include <WiFi.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <WiFiUdp.h>
#define LED_PIN 2
#define ONE_WIRE_BUS 4
#define Zero_SW 5
const char ssid[] = "ESP32_server"; // SSID
const char pass[] = "1234567890"; // password
static WiFiUDP wifiUdp;
static const char *kRemoteIpadr = "192.168.4.1"; //送信先のIPアドレス
static const int kRmoteUdpPort = 10000; //送信先のポート
int TRIG = 8;
int ECHO = 7;
int RX_PIN = 18;
int TX_PIN = 19;
bool WiFi_FLG;
Adafruit_NeoPixel pixels(1, LED_PIN, NEO_GRB + NEO_KHZ800);
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
void setup() {
Serial.begin(115200);
Serial1.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN);
pixels.begin();
pixels.setPixelColor(0, pixels.Color(0, 0, 0)); //明るさを3原色全て0にして消灯
sensors.begin();
pinMode(ECHO, INPUT );
pinMode(TRIG, OUTPUT );
pinMode(Zero_SW, INPUT_PULLUP);
WiFi_FLG = true;
WiFi_setup();
}
void loop() {
int sw;
int icount;
int i;
char MODE;
String D_DATA;
bool FLG;
unsigned long ttime, stime;
float temp_c = 0.0;
double duration = 0.0;
double distance = 0.0;
double dif = 0.0;
double speed_of_sound;
MODE = 0;
ttime = millis();
stime = millis();
sensors.requestTemperatures();
temp_c = sensors.getTempCByIndex(0);
Serial.print("temp : ");
Serial.println(temp_c);
speed_of_sound = 331.5 + 0.6 * temp_c; // 温度補正値
FLG = false;
while (1) {
// 10秒毎に補正用温度計測
if ( millis() - ttime > 10000 ) {
FLG = true;
pixels.setPixelColor(0, pixels.Color(255, 0, 0)); //Colorメソッド内の引数の順番は赤、緑、青
pixels.show();
sensors.requestTemperatures();
temp_c = sensors.getTempCByIndex(0);
Serial.print("temp : ");
Serial.println(temp_c);
speed_of_sound = 331.5 + 0.6 * temp_c;
ttime = millis();
pixels.setPixelColor(0, pixels.Color(0, 0, 0)); //Colorメソッド内の引数の順番は赤、緑、青
pixels.show();
}
// 距離測定
digitalWrite(TRIG, LOW);
delayMicroseconds(2);
digitalWrite( TRIG, HIGH );
delayMicroseconds( 10 );
digitalWrite( TRIG, LOW );
duration = pulseIn( ECHO, HIGH ); // 往復にかかった時間が返却される[マイクロ秒]
if (duration > 0) {
duration = duration / 2; // 往路にかかった時間
distance = duration * speed_of_sound * 100 / 1000000;
Serial.print("Distance:");
Serial.print(distance);
Serial.println(" cm");
}
if ( MODE == 0 ) {
D_DATA = String(distance, 1);
} else {
D_DATA = String((dif - distance), 1);
}
Serial1.println(D_DATA);
if ( FLG == true && WiFi_FLG == true ) {
Serial.println("UDP begin");
wifiUdp.beginPacket(kRemoteIpadr, kRmoteUdpPort);
wifiUdp.print(D_DATA);
wifiUdp.endPacket();
Serial.println("UDP end");
FLG = false;
}
delay(200);
icount = 0;
sw = digitalRead(Zero_SW);
while ( sw == LOW ) {
sw = digitalRead(Zero_SW);
delay(100);
icount++;
}
if ( icount > 1 && icount < 30 ) {
Serial.println("Set Diff");
dif = distance;
MODE = 1;
}
if ( icount > 30 ) {
Serial.println("Reset Diff");
dif = 0.0;
MODE = 0;
}
}
}
static void WiFi_setup()
{
int i;
i = 0;
static const int kLocalPort = 5000; //自身のポート
WiFi.disconnect();
WiFi.begin(ssid, pass);
while ( WiFi.status() != WL_CONNECTED) {
i++;
if ( i > 30 ){
WiFi_FLG = false;
break;
}
delay(500);
}
if ( WiFi_FLG == true ) wifiUdp.begin(kLocalPort);
}
コメント