ESP-WROOM-02(ESP8266)でAdafruit Audio FX Sound Boardを制御する

<img src="https://cdn-shop.adafruit.com/970x728/2133-00.jpg" />

<a href="https://www.adafruit.com/product/2133">Audio FX Sound Board</a>は、3.3Vでも制御できる偉い子なので、
ESP8266で音制御するのに使えないかなと思って、試してみました。

# 1. Example、うごかへん...。
Adafruit_Soundboardのexamplesにある、menucommands.inoを実行したが動かなかった。


## 原因: 無限ループ

サンプルのsetup関数は、下記のようになってます。

```menucommands.ino
// we'll be using software serial
SoftwareSerial ss = SoftwareSerial(SFX_TX, SFX_RX);

// pass the software serial to Adafruit_soundboard, the second
// argument is the debug port (not used really) and the third 
// arg is the reset pin
Adafruit_Soundboard sfx = Adafruit_Soundboard(&ss, NULL, SFX_RST);
// can also try hardware serial with
// Adafruit_Soundboard sfx = Adafruit_Soundboard(&Serial1, NULL, SFX_RST);

void setup() {
  Serial.begin(115200);
  Serial.println("Adafruit Sound Board!");
  
  // softwareserial at 9600 baud
  ss.begin(9600);
  // can also do Serial1.begin(9600)

  if (!sfx.reset()) { // ← こいつがわるさしてる
    Serial.println("Not found");
    while (1);
  }
  Serial.println("SFX board found");
}
```

1. sfx.reset()のところで、常にfalseが帰ってきてしまい、while(1)が走り続けます。
2. ESP8266のWDTが1000msで、1000ms以内に関数内の処理をおえないとリセットがかかります。
3. 1に戻ります。


## 解消方法: reset()関数のreturnを無視 + SoftwareSerialからの卒業

というわけで、sfx.reset()がtrueを返すかとかを無視することにしました。
ついでに、SoftwareSerialが不安定なので、ESP8266ではIO2をSerial1のTXピンとして使えるので、下記のように処理を変更しました。

```menucommands.ino
// pass the software serial to Adafruit_soundboard, the second
// argument is the debug port (not used really) and the third 
// arg is the reset pin
Adafruit_Soundboard sfx = Adafruit_Soundboard(&Serial1, NULL, SFX_RST);

void setup() {
  Serial.begin(115200);
  Serial.println("Adafruit Sound Board!");
  
  // softwareserial at 9600 baud
  Serial1.begin(9600);

  // just reset!
  sfx.reset();
}
```

これだけで良かったんや！！


# 2. 割込再生をやりたいんや（やりたいんや）

<a href="https://www.adafruit.com/product/2133">Audio FX Sound Board</a>さんは、<a href="https://www.switch-science.com/catalog/2626/">MP3 Trigger</a>さんみたいに割込で楽曲の切替ができません。
でも、やりたいんやで。


## パターン1: １曲目が勝手にとまって2曲目を再生できたら嬉しいけどできないパターン
たとえば、下記のようなコードを書いても、1曲目が勝手に止まって2番目が再生…とはいきません。
※ ただ、1曲目が2.5　秒以内の場合は2曲目は問題なく再生されます

``` sample.ino
// 1曲目を再生するます
sfx.playTrack(1);

// 2.5秒待ってから、、、
delay(2500);

// 1曲目を止めて、2曲目を再生したい、、、がしかし再生されず
sfx.playTrack(2);

```

## パターン2: 1曲目を手動でとめて2曲目再生するが、ラグがかなりあるパターン
たとえば、下記のようにstop関数を入れてからやるという方法があるのですが、以下の理由からあまり使えません。
- 何も再生してない時にstop関数を走らせるとバグる
- stop関数からの次の再生までのラグが異様にある

``` sample.ino
// 1曲目を再生するます
sfx.playTrack(1);

// 2.5秒待ってから、、、
delay(2500);

// 手動でストップ(割と時間かかる)
sfx.stop();

// 2曲目を再生
sfx.playTrack(2);
```

　何も再生していない時にstop()関数を走らせないようにするには、ACKピン(再生中はLOW、ストップ中はHIGHを出力)を見ながらやるという方法もあるのですが、いずれにせよstop()関数を挟んでから別の楽曲再生となると、ちと遅くなってしまいます。

## パターン3: たとえばstop()ではなく、reset()

強制的に楽曲を止めるといえば、reset()関数つかえばいいのかな？と思ってやってみたのですが、
Adafruit_Soundboardの元ソースでは、リセット後1000ms待つ処理が入ってました。
そりゃ遅いわ。

``` sample.ino
// 1曲目を再生するます
sfx.playTrack(1);

// 2.5秒待ってから、、、
delay(2500);

// 強制的に止めるために、リセット(しかしこれがおそい)
sfx.reset();

// 2曲目を再生
sfx.playTrack(2);
```

ちなみに、ライブラリの該当箇所のソース。

```Adafruit_Soundboard.cpp
boolean Adafruit_Soundboard::reset(void) {
  digitalWrite(reset_pin, LOW);
  pinMode(reset_pin, OUTPUT);
  delay(10);
  pinMode(reset_pin, INPUT);
  delay(1000); // give a bit of time to 'boot up'
```

## パターン4: 手動リセット(最終型)

というわけで、リセット後の起き上がり時間を直接操作できるように、
下記のようにしたところ、割と現実的な待ち時間になりました(1/4!)。

```sample.ino
// 1曲目を再生するます
sfx.playTrack(1);

// 2.5秒待ってから、、、
delay(2500);

// 手動リセット(250msですむ)
digitalWrite(SFX_RST, LOW);
pinMode(SFX_RST, OUTPUT);
delay(10);
pinMode(SFX_RST, INPUT);
delay(250); // ここがミソ

// 2曲目を再生
sfx.playTrack(2);
```

ちなみに、200msとかにすると楽曲再生できなかったです。
以上。