ESP8266(ESP-WROOM-02)で、OTA使いながら、UDP(もといOSC)で送受信できるようにするテスト

<img src="https://docid81hrs3j1.cloudfront.net/contents/large/25001_tTK67dP.jpg" />

# 背景
各所で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点灯、それ以外だと消灯)