背景
各所でESP8266でUDPを使うソースが出回っているのだけど、
若干バージョンの違いにより使えなかったり、使えたり、みたいなのがあったので、
現行で動くことが確認できたソースを残しておきます。
検証環境
- Mac OS X 10.11.6(El Capitan)
- Arduino IDE 1.6.8
- ESPr Developer: https://www.switch-science.com/catalog/2500/
仕様
- OTA経由でファームを更新できる
- UDPでデータの送受信ができる
ソース
メインのプログラム
testOTA.ino
/**
* ESP8266(ESP-WROOM-02)で、
* OTAでネットワーク越しにファイルをアップデートできる仕組みにしつつ
* UDPのデータの送受信を可能にするテスト用プログラム
**/
#include <OSCBundle.h>
#include <OSCData.h>
#include <OSCMessage.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
const char* ssid = "xxxxx";
const char* password = "xxxxxx";
void setup() {
Serial.begin(115200);
Serial.println("Booting");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("Connection Failed! Rebooting...");
delay(5000);
ESP.restart();
}
// switch off all the PWMs during upgrade
ArduinoOTA.onStart([]() {
Serial.println("start");
});
// do a fancy thing with our board led at end
ArduinoOTA.onEnd([]() {
Serial.println("end");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
// setup the OTA server
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// setup UDP port
oscSetup();
mainSetup();
}
void loop() {
ArduinoOTA.handle();
oscLoop();
mainLoop();
}
Arduino的な処理をする部分のソース
MainOTA.ino
/**
* 一般的なArduinoで処理するような処理をココに書く
*
**/
int ledPin = 13;
int ledState = LOW;
unsigned long previousMillis = 0;
const long interval = 2500;
void mainSetup() {
// configure dimmers, and OTA server events
// via http://makers-with-myson.blog.so-net.ne.jp/2017-01-14
analogWriteRange(1000);
}
void mainLoop() {
// 一定周期でLEDを点滅
/*if (millis() - previousMillis >= interval) {
if (ledState == LOW) {
ledState = HIGH;
}
else {
ledState = LOW;
}
analogWrite(ledPin, ledState == HIGH ? 990 : 0);
previousMillis = millis();
}*/
}
UDPの処理をする部分のソース
ESP8266UDP.ino
/**
* ESP8266でUDP操作するテスト
*
**/
// 自分のポート番号
char myIP[16];
#define MY_OSC_PORT 8091
// 相手のIPアドレスとポート番号
const IPAddress TARGET_IP(192, 168, 11, 3);
#define TARGET_PORT 8090
WiFiUDP Udp;
OSCErrorCode error;
/**
* 初期設定
*/
void oscSetup() {
Udp.begin(MY_OSC_PORT);
// 送信テスト(自分のIPアドレスを通知)
{
sprintf(myIP, "IP is %d.%d.%d.%d", WiFi.localIP()[0], WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3] );
OSCMessage smsg("/ip");
smsg.add(myIP);
Udp.beginPacket(TARGET_IP, TARGET_PORT);
smsg.send(Udp);
Udp.endPacket();
smsg.empty();
}
}
/**
* dispatchのテスト(/tone宛に来た場合のみ処理)
*/
void dispatchTone(OSCMessage &msg) {
int ledState = msg.getInt(0);
if (ledState == 1) {
analogWrite(13, 990);
}
else {
analogWrite(13, 0);
}
// 受信できたよ応答
OSCMessage _msg("/got_it");
_msg.add(ledState);
Udp.beginPacket(TARGET_IP, TARGET_PORT);
_msg.send(Udp);
Udp.endPacket();
_msg.empty();
}
/**
* 繰り返し処理
*/
void oscLoop() {
// 受信処理
// ここがキモ(OSCBundleではなく、OSCMessageにする)
// rmsg.routeではなく、rmgs.dispatchなとこも重要
OSCMessage rmsg;
int size = Udp.parsePacket();
if (size > 0) {
while (size--) {
rmsg.fill(Udp.read());
}
if (!rmsg.hasError()) {
// アドレスごとに処理を振り分ける
rmsg.dispatch("/tone", dispatchTone, 0);
//rmsg.dispatch("/led", dispatchLED, 0);
rmsg.empty();
}
else {
error = rmsg.getError();
Serial.print("error: ");
Serial.println(error);
}
}
}
補足説明
このプログラムをOTA経由で書き込んで、
同一ネットワークに接続したPCのIPアドレスを192.168.11.3に設定して、
UDPのポートを8090にして待ち受けていると、
ESP-WROOM-02が起動したタイミングでその子のIPアドレスが/ip宛に送られてきます。
なので、そのIPアドレスの8091ポート宛に、
/tone ,i 1000
みたいな感じのOSCを送ってあげると、LEDの明るさが変わるようになります。
(現状では、1000のとこを1とするとLED点灯、それ以外だと消灯)