ESP8266 > UDPでコマンドを受けて、音を鳴らす

```txt:動作確認
ESP-WROOM-02
圧電ブザー LF-PB30W25C
```

[ESP8266 > IPアドレスを圧電ブザーで通知](
http://qiita.com/7of9/items/0fc2bb13c1bfca26fdc9)
の先にやろうとしていたことができた。

UDPでコマンドを受けて、それに基づき音を鳴らす。

## 通信プロトコル

- UDP
- port: 7000
- コマンド
  + alert,[番号1..9]
  + 例として `alert,3`

## 振る舞い

1. ESP8266が起動時にIPアドレスの4つめに対応する音を鳴らす
   - 192.168.10.7の場合は、「ピー、ピッピッ」
2. 起動後は、alertコマンドを受けて、alert,3の場合は3に対応する音を鳴らす。
3. 2の繰り返し

とりあえず、鳴らす音は長音と短音の組合せとしている。

## code

.inoファイル(コードの色付のため.cとしている）

```eps8266_160130_udpBuzzerAlert.c
#include <ESP8266WiFi.h>
#include <string.h>
#include <stdlib.h> // for strtol
#include <stdint.h>
#include <WiFiUDP.h>
#include "wifiConfig.h"
/*
 * Define your SSID and WiFi password at [wifiConfig.h] using [TemplateWifiConfig.h]
 * 
 */

/*
 * v0.3 2016/01/30
 *   - can handle UDP command
 *     + add getAlertIdx()
 *     + add readNumber_loopRead()
 *     + add readNumber_strtol()
 * v0.2 2016/01/23
 *   - can notify 4th IP address by sound 
 * v0.1 2016/01/23a
 *   - can beep 
 */

//#define USE_STRTOL // to use strtol in reading number

static const int kPinSound = 4;
static IPAddress s_myIpAddress;
static const int kLocalPort = 7000;
static WiFiUDP wifiUdp;
static char receivedBuffer[255];

//--------------------- sound realated -----------------
void sound5()
{
  digitalWrite(kPinSound, HIGH);
  delay(500);
  digitalWrite(kPinSound, LOW);
  delay(100);
}

void sound1(int maxloop)
{
  for(int loop=0; loop < maxloop; loop++) {
    digitalWrite(kPinSound, HIGH);
    delay(100);
    digitalWrite(kPinSound, LOW);
    delay(100);
  }
}

void soundLastIp(int lastIp)
{
  int workIp = lastIp;
  if (workIp >= 5) {
    sound5(); 
    workIp -= 5;
  }
  sound1(workIp);
}
void soundNumber(int nmb) 
{
  soundLastIp(nmb);
}

//--------------------- UDP command related -----------------
int16_t readNumber_loopRead(char *ptr)
{
  if (ptr == NULL) {
    return -6; // error
  }

  static const int kMaxLoop = 5;
  int res = 0;
  int loop = 0;
  while(loop < kMaxLoop){
    if (*ptr >= '0' && *ptr <= '9') {
       res *= 10;
       res = res + (*ptr - '0');
    } else {
      break;
    }
    ptr++;
    loop++;
  }
  
  if (loop == (kMaxLoop - 1)) {
    return -7; // error;
  }
  return res;
  
}

int16_t readNumber_strtol(char *ptr)
{
  if (ptr == NULL) {
    return -3; // error
  }
  char *endptr;
  int res = 0;

  res = (int)strtol(ptr, &endptr, /* base=*/10);
  
#if 0 // did not work correctly for Arduino IDE with ESP8266 but worked on ideone
  if (*endptr != '\0') {
    Serial.println(res);
    return -4; // error
  }
#endif  

  return res;
}

int16_t getAlertIndex(char *ptr)
{
  // format example
  //   alert,3
  //
  
  if (ptr == NULL) {
    return -1; // error
  }

  char *commaPtr;
  commaPtr = strchr(ptr, ',');
  printf("%s", ptr);
  if (commaPtr == NULL) {
    return -2; // error
  }

  char *numPtr = commaPtr + 1;
  printf("found: %s", numPtr);
#ifdef USE_STRTOL
  return readNumber_strtol(numPtr);
#else
  return readNumber_loopRead(numPtr);
#endif  
}
//-----------------------------------------------------------

static void WiFi_setup()
{
  WiFi.begin(kWifiSsid, kWifiPass);
  while( WiFi.status() != WL_CONNECTED) {
    delay(500);  
  }
  wifiUdp.begin(kLocalPort);
}

void setup() {
  WiFi_setup();
  Serial.begin(115200);
  
  pinMode(kPinSound, OUTPUT);

  s_myIpAddress = WiFi.localIP();
  soundLastIp(s_myIpAddress[3]);  
}

void handleAlert(int16_t alertIdx)
{
  Serial.print("Your alert index is ");
  Serial.println(alertIdx); 
  if (alertIdx >= 1 && alertIdx < 10) {
    soundNumber(alertIdx);
  }
}

void loop() {
  int rcvdSize = wifiUdp.parsePacket();
  if (rcvdSize == 0) {
    delay(100);
    return; 
  }

  Serial.println("received");

  memset(receivedBuffer, 0, sizeof(receivedBuffer));
  int len = wifiUdp.read(receivedBuffer, 255);
  Serial.println(); // *** from my experience, this may be required to avoid Exception(2) after wifiUdp.read() ***
  if (len == 0) {
    return;
  }
  receivedBuffer[len] = 0x00;
  
  int16_t altIdx = getAlertIndex(receivedBuffer);
  if (altIdx < 0) {
    return;
  }
  
  handleAlert(altIdx);  
}
```

### wifiConfig.h

wifiConfigで自分の環境のSSIDとパスワードを設定する。
<font color=red>gitにpushしないこと</font>

参考 http://qiita.com/7of9/items/a61999a914ae02673b8d


### 補足

USE_STRTOLマクロを有効にするとstrotol()で処理、無効の場合は自作の数値読込み処理（だいたい確認済み）で処理。

一応、両方で動作確認済

コードはint16_tを使ってしまっている、など細かい点では改善点はいくつかある。bestコードではない。


## 使い方

### CentOS / MacOS X

ESP8266を起動後、ターミナルにて以下を実行すると音が鳴る。
192.168.10.7はESP8266のIPアドレスとする。

```
echo "alert,7" | nc -w 1 -u 192.168.10.7 7000
```

### PowerShell

http://qiita.com/7of9/items/fe9e5b160b14fd1f631c
を参考に実行する。

今の僕には、ワンライナーで書けない。

### vivado処理終了時に使う

これでコマンドにより音を鳴らすことができるようになった。
これをvivadoの処理終了通知に使う。

単純にGPIOを叩いているだけなので、夜に静かな環境で使いたい場合は、LEDに付け替えれば良い。


## 画像

![DSC_0048.JPG](https://qiita-image-store.s3.amazonaws.com/0/32870/f88c6de1-607d-4bde-67a4-64405ef3df03.jpeg)

LEDとした場合

LEDの場合でも、５という値と１をN回というのは一応分かるタイミングになっている。

![DSC_0050.JPG](https://qiita-image-store.s3.amazonaws.com/0/32870/6b20262d-11bf-a08f-6f67-c6d44347a3c7.jpeg)