M5STACKおよびM5STICKのボタンを押すと値をUDP経由で送信し、受信したデバイスで受信した値を表示する。
構成
プロジェクト名:Wifi_UDP_Master_M5Stack.ino/Wifi_UDP_Slave_M5StickCPlus.ino
デバイス:M5STACK、M5STICK CPLUS
プログラム
M5STACK
//--------------------------------------------
// ライブラリ
//--------------------------------------------
#include <M5Stack.h> //ver 0.2.9
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
//双方向通信
#include <WiFiUdp.h>
//---------------------------------------------
//
//---------------------------------------------
const char *ssid = "yourAP";
const char *password = "yourpasswd";
const char * to_udp_address = "192.168.4.2"; //送信先のIPアドレス
const int to_udp_port = 55556; //送信相手のポート番号
const int my_server_udp_port = 55555; //開放する自ポート
WiFiUDP udp;
TaskHandle_t task_handl; //マルチタスクハンドル定義
uint8_t receive_data[3] ; //
boolean connected=false;
boolean isDisp_ok = false; //ディスプレイ表示フラグ
boolean isSet_send_data = false;
//
uint32_t now_time = 0;
int16_t interval = 100; //UDPデータ送信間隔
IPAddress myIP;
int16_t trans_count=0;
//---------------------------------------------
//wifiアクセスポイント
//---------------------------------------------
void setup_wifi() {
WiFi.softAP(ssid, password);
myIP = WiFi.softAPIP();
}
//---------------------------------------------
//********* core 1 task ************
//---------------------------------------------
void setup() {
Serial.begin(115200);
delay(1000);
//wifiアクセスポイント
setup_wifi();
//
setupWiFiUDPserver();
//M5Stackはデュアルコア
//LCDディスプレイはWiFi, UDPの送受信とは別のCPU core で動かした方が遅延が少ない。
//FreeRTOS関数で、マルチコアおよびマルチタスクで動作させます。
//メインloop関数はcore 1のタスク。
//LCDディスプレイ表示はcore 0のタスク。
/*
xTaskCreatePinnedToCore(
タスク名,
"タスク名",
スタックメモリサイズ,
NULL,
タスク優先順位,
タスクハンドルポインタ,
Core ID
);
*/
xTaskCreatePinnedToCore(&taskDisplay, "taskDisplay", 8192, NULL, 10, &task_handl, 0);
delay(500); //別タスクでM5.begin関数が起動するまで待つ。
//
}
//---------------------------------------------
// ******** core 1 task *************
//UDPからのデータを受信
//UDPデータを相手先へ送信
//---------------------------------------------
void loop() {
receiveUDP();
sendUDP();
}
//---------------------------------------------
//******** core 0 task *************
//UDP受信して、isDisp_ok = true になったら、ディスプレイに表示。
//M5Stackのボタン操作も制御
//---------------------------------------------
void taskDisplay(void *pvParameters){
M5.begin();
//画面初期化
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(2);
M5.Lcd.setCursor(0, 0);
M5.Lcd.print("AP IP address: ");
M5.Lcd.println(myIP);
/*
M5.Lcd.clear(WHITE);
M5.Lcd.setTextColor(BLUE);
M5.Lcd.setTextSize(2);
M5.Lcd.setCursor(55, 100);
M5.Lcd.println("Wifi_UDP_Master");
*/
while(true){
M5.update(); //Update M5Stack button state
button_action();
// データが届いていたら表示を更新
if(isDisp_ok){
M5.Lcd.fillRect(0, 32, 160, 60, BLACK);
M5.Lcd.setCursor(0, 32);
M5.Lcd.setTextColor(WHITE); // 背景指定なしでもOK
M5.Lcd.printf("1st: %c \n", receive_data[0]);
M5.Lcd.printf("2nd: %c \n", receive_data[1]);
M5.Lcd.printf("3rd: %c \n", receive_data[2]);
isDisp_ok = false; // ★ここでフラグをリセット!
}
delay(1);
}
}
//---------------------------------------------
//UDPデータを受信する関数です。
//55555ポートを開放しているので、届いたデータをudp.parsePacket関数で受信して、
//ライブラリ内の受信バッファに一時保存します。
//udp.read(buf, size);
//という形式にすると、そのサイズ分だけ読込み位置が進みます。
//これは多量のデータを受信する分にはとても便利ですが、ソースコードをパッと見ただけでは意味不
/*
udp.read() の戻り値の範囲は、-1 〜 255 です。
0 〜 255: 正常に読み取れたデータの値(1バイト分)
-1: 読み取るデータがもう残っていない(End of File / Error)
もし戻り値が byte 型(0〜255)だと、「データとしての255」なのか「エラー(-1)」なのかを区別できません。そのため、1バイトよりも大きな型である int を使って、データとエラー状態を両方表現できるように設計されています。
int len = udp.read(packetBuffer, 255); と書いたとき:
packetBuffer(第1引数): 届いたデータそのものが保存される「箱(配列)」です。型は char[] や uint8_t[] です。
255(第2引数): 「最大で255バイトまで読み取ります」という制限です。
戻り値(len): 「実際に何バイト読み取って、bufferに書き込んだか」 という数字(int型)が返ってきます。
int packetSize = udp.parsePacket(); // 受信データがあるか確認
if (packetSize) {
char packetBuffer[255];
// udp.readの戻り値を変数「len」に格納
int len = udp.read(packetBuffer, 255);
if (len > 0) {
packetBuffer[len] = 0; // 文字列として扱うために終端を追加
}
Serial.printf("受信サイズ: %d バイト\n", len);
}
*/
//---------------------------------------------
void receiveUDP(){
if(!isDisp_ok){ // 表示待ち状態でない場合のみ処理
if(connected){
int packetSize = udp.parsePacket();
// 3バイト('A','B','C')届いているか確認
if(packetSize >= 3){
// 1バイトずつ読み出す
receive_data[0] = udp.read(); // 'A' (65) が入る
receive_data[1] = udp.read(); // 'B' (66) が入る
receive_data[2] = udp.read(); // 'C' (67) が入る
// LCDに表示
// 処理完了フラグを立てる
isDisp_ok = true;
}
}
}
}
//---------------------------------------------
//udp.beginPacket関数で、UDP送信バッファ領域を1460byte確保します。
//このbeginPacket関数は相手の送信先のIPアドレスとUDPポートを指定すること。
//送信バッファにはwrite関数で順番に貯め込みます。
//udp.endPacket関数で実際にデータを一気に送信します。
//バッファは1460byte以上になると捨てられますので注意です。
//---------------------------------------------
void sendUDP(){
if(isSet_send_data){
udp.beginPacket(to_udp_address, to_udp_port);
// printを使うと数値が自動的に「文字」として送信されます
udp.print(trans_count); // 例:0 なら "0" が送られる
udp.print(trans_count + 1); // 例:1 なら "1" が送られる
udp.print(trans_count + 2); // 例:2 なら "2" が送られる
udp.endPacket();
// デバッグ用にシリアルに出力して、本当に増えているか確認
Serial.printf("Sent UDP: %d, %d, %d\n", trans_count%10, (trans_count+1)%10, (trans_count+2)%10);
trans_count++;
// 0-9の範囲でループさせたい場合は以下を追加
if(trans_count > 7) trans_count = 0;
isSet_send_data = false;
}
}
//---------------------------------------------
//M5Stackをアクセスポイントおよびルーターにする。
//SoftAPモードで起動し、UDPサーバーとしてポートを開放して
//待ち受け状態にします。
//---------------------------------------------
void setupWiFiUDPserver(){
Serial.println("Setting up SoftAP...");
WiFi.disconnect(true, true); // 以前の設定をクリア
delay(100);
// SoftAPとして起動
if(WiFi.softAP(ssid, password)) {
Serial.println("SoftAP started!");
} else {
Serial.println("SoftAP failed to start.");
return;
}
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
// UDP待ち受け開始(ポート番号のみ指定が標準的です)
if(udp.begin(my_server_udp_port)) {
Serial.printf("UDP Server started at port %d\n", my_server_udp_port);
connected = true; // 通信準備完了!
} else {
Serial.println("UDP begin failed.");
}
delay(1000);
}
//---------------------------------------------
//ボタン操作関数です。
//---------------------------------------------
void button_action(){
//左ボタン(Botton A)
if (M5.BtnA.wasReleased()) {
isSet_send_data = true;
}
//中央ボタン(Botton B)
else if (M5.BtnB.wasReleased()) {
isSet_send_data = true;
}
//右ボタン(Botton C)
else if (M5.BtnC.wasReleased()) {
isSet_send_data = true;
}
}
M5STICK CPLUS
//============================================
//
// Device:M5Stick-CPLUS
//
// 参考サイト
// wifi双方向通信
// https://www.mgo-tec.com/blog-entry-udp-wifi-m5stack.html/3
//
// wifiアクセスポイント
// M5StackでWifiをAPとSTAとで使い分ける
// https://qiita.com/nnn112358/items/fbb4f24505c6ef8082cd
//=
//============================================
//--------------------------------------------
// ライブラリ
//--------------------------------------------
//#include <M5StickC.h>
#include <M5StickCPlus.h>
#include <WiFi.h>
#include <WiFiUdp.h>
const char* ssid = "yourAP";
const char* password = "yourpasswd";
const char * to_udp_address = "192.168.4.1"; //送信先のIPアドレス
const int to_udp_port = 55555; //送信相手のポート番号
const int my_server_udp_port = 55556; //開放する自ポート
WiFiUDP udp;
TaskHandle_t task_handl; //マルチタスクハンドル定義
uint8_t receive_data[3] ; //受信図形の座標位置
boolean connected = false;
boolean isDisp_ok = false; //ディスプレイ表示フラグ
boolean isSet_send_data = false;
uint32_t now_time = 0;
int16_t interval = 100; //UDPデータ送信間隔
int16_t trans_count=0;
//********* core 1 task ***************
//====================================================
// 初期化処理
//====================================================
void setup(){
Serial.begin(115200);
delay(1000);
connectToWiFi();
while(!connected){
delay(1);
}
xTaskCreatePinnedToCore(&taskDisplay, "taskDisplay", 8192, NULL, 10, &task_handl, 0);
delay(500); //別タスクでM5.begin関数が起動するまで待つ。
}
//---------------------------------------------
// ******** core 1 task *************
//UDPからのデータを受信
//UDPデータを相手先へ送信
//---------------------------------------------
void loop() {
receiveUDP();
sendUDP();
}
//******** core 0 task *************
void taskDisplay(void *pvParameters){
M5.begin();
// 画面を縦持ち(ボタンが下)にする場合は 0、横持ちにする場合は 1 または 3
M5.Lcd.setRotation(3);
M5.Lcd.fillScreen(BLACK); // clear()より確実に画面をリフレッシュ
M5.Lcd.setTextSize(2); // StickCは画面が小さいのでサイズ2くらいが見やすいです
while(true){
M5.update(); //Update M5Stack button state
button_action();
// データが届いていたら表示を更新
if(isDisp_ok){
M5.Lcd.fillRect(0, 32, 160, 60, BLACK);
M5.Lcd.setCursor(0, 32);
M5.Lcd.setTextColor(WHITE); // 背景指定なしでもOK
M5.Lcd.printf("1st: %c \n", receive_data[0]);
M5.Lcd.printf("2nd: %c \n", receive_data[1]);
M5.Lcd.printf("3rd: %c \n", receive_data[2]);
isDisp_ok = false; // ★ここでフラグをリセット!
}
delay(1);
}
}
//---------------------------------------------
//UDPデータを受信する関数です。
//55555ポートを開放しているので、届いたデータをudp.parsePacket関数で受信して、
//ライブラリ内の受信バッファに一時保存します。
//udp.read(buf, size);
//という形式にすると、そのサイズ分だけ読込み位置が進みます。
//これは多量のデータを受信する分にはとても便利ですが、ソースコードをパッと見ただけでは意味不
/*
udp.read() の戻り値の範囲は、-1 〜 255 です。
0 〜 255: 正常に読み取れたデータの値(1バイト分)
-1: 読み取るデータがもう残っていない(End of File / Error)
もし戻り値が byte 型(0〜255)だと、「データとしての255」なのか「エラー(-1)」なのかを区別できません。そのため、1バイトよりも大きな型である int を使って、データとエラー状態を両方表現できるように設計されています。
int len = udp.read(packetBuffer, 255); と書いたとき:
packetBuffer(第1引数): 届いたデータそのものが保存される「箱(配列)」です。型は char[] や uint8_t[] です。
255(第2引数): 「最大で255バイトまで読み取ります」という制限です。
戻り値(len): 「実際に何バイト読み取って、bufferに書き込んだか」 という数字(int型)が返ってきます。
int packetSize = udp.parsePacket(); // 受信データがあるか確認
if (packetSize) {
char packetBuffer[255];
// udp.readの戻り値を変数「len」に格納
int len = udp.read(packetBuffer, 255);
if (len > 0) {
packetBuffer[len] = 0; // 文字列として扱うために終端を追加
}
Serial.printf("受信サイズ: %d バイト\n", len);
}
*/
//---------------------------------------------
void receiveUDP(){
if(!isDisp_ok){ // 表示待ち状態でない場合のみ処理
if(connected){
int packetSize = udp.parsePacket();
// 3バイト('A','B','C')届いているか確認
if(packetSize >= 3){
// 1バイトずつ読み出す
receive_data[0] = udp.read(); // 'A' (65) が入る
receive_data[1] = udp.read(); // 'B' (66) が入る
receive_data[2] = udp.read(); // 'C' (67) が入る
// 処理完了フラグを立てる
isDisp_ok = true;
}
}
}
}
//---------------------------------------------
//udp.beginPacket関数で、UDP送信バッファ領域を1460byte確保します。
//このbeginPacket関数は相手の送信先のIPアドレスとUDPポートを指定すること。
//送信バッファにはwrite関数で順番に貯め込みます。
//udp.endPacket関数で実際にデータを一気に送信します。
//バッファは1460byte以上になると捨てられますので注意です。
//---------------------------------------------
void sendUDP(){
if(isSet_send_data){
udp.beginPacket(to_udp_address, to_udp_port);
// printを使うと数値が自動的に「文字」として送信されます
udp.print(trans_count); // 例:0 なら "0" が送られる
udp.print(trans_count + 1); // 例:1 なら "1" が送られる
udp.print(trans_count + 2); // 例:2 なら "2" が送られる
udp.endPacket();
// デバッグ用にシリアルに出力して、本当に増えているか確認
Serial.printf("Sent UDP: %d, %d, %d\n", trans_count%10, (trans_count+1)%10, (trans_count+2)%10);
trans_count++;
// 0-9の範囲でループさせたい場合は以下を追加
if(trans_count > 7) trans_count = 0;
isSet_send_data = false;
}
}
//---------------------------------------------
//---------------------------------------------
void connectToWiFi(){
Serial.println("Connecting to WiFi network: " + String(ssid));
WiFi.disconnect(true, true);
delay(1000);
WiFi.onEvent(WiFiEvent);
WiFi.begin(ssid, password);
Serial.println("Waiting for WIFI connection...");
}
//---------------------------------------------
//---------------------------------------------
void WiFiEvent(WiFiEvent_t event){
IPAddress myIP = WiFi.localIP();
switch(event) {
case SYSTEM_EVENT_STA_GOT_IP:
Serial.println("WiFi connected!");
Serial.print("My IP address: ");
Serial.println(myIP);
//udp.begin関数は自サーバーの待ち受けポート開放する関数である
udp.begin(myIP, my_server_udp_port);
delay(1000);
connected = true;
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
Serial.println("WiFi lost connection");
connected = false;
break;
default:
break;
}
}
//---------------------------------------------
//ボタン操作関数です。
//---------------------------------------------
void button_action(){
if (M5.BtnA.wasReleased()) {
isSet_send_data = true;
}
if (M5.BtnB.wasReleased()) {
isSet_send_data = true;
}
}




コメント