音声関連クラス化
見通しが悪くなりそうなので音声周りは別クラスに。多分まだ予想してない改造をしそなのでパラメーター渡すとか考えずに単純な別ファイル化だけ。(StackSpeaker.h)
#ifndef STACK_SPEAKER_H
#define STACK_SPEAKER_H
#include <AudioFileSourceSD.h>
#include <AudioFileSourceBuffer.h>
#include <AudioGeneratorMP3.h>
#include "EpeaAudioOutputM5Speaker.h"
static epea::AudioOutputM5Speaker out(&M5.Speaker);
static AudioGeneratorMP3 mp3;
static AudioFileSourceSD *file = nullptr;
static AudioFileSourceBuffer *buff = nullptr;
const int preallocateBufferSize = 50 * 1024;
uint8_t *preallocateBuffer;
class StackSpeaker {
public:
StackSpeaker() {}
~StackSpeaker(void){};
boolean init(void) {
auto spk_cfg = M5.Speaker.config();
spk_cfg.sample_rate = 26000;
spk_cfg.task_priority = 23;
spk_cfg.dma_buf_count = 20;
spk_cfg.dma_buf_len = 128;
M5.Speaker.config(spk_cfg);
M5.Speaker.begin();
M5.Speaker.setVolume(255);
preallocateBuffer = (uint8_t *)malloc(preallocateBufferSize);
return preallocateBuffer;
}
void stopVoice() {
if (file == nullptr) return;
out.stop();
mp3.stop();
file->close();
delete file;
file = nullptr;
}
void startVoice(const char *filename) {
if (file != nullptr) { stopVoice(); }
file = new AudioFileSourceSD(filename);
buff = new AudioFileSourceBuffer(file, preallocateBuffer, preallocateBufferSize);
mp3.begin(buff, &out);
delay(10);
while (mp3.isRunning()) {
while (mp3.loop()) {}
mp3.stop();
file->close();
delete file;
delete buff;
file = nullptr;
buff = nullptr;
}
}
};
#endif /* STACK_SPEAKER_H */
呼び出しは
#include "StackSpeaker.h" //インクルード
static StackSpeaker speaker; // 宣言
void setup() {
M5.begin();
speaker.init(); //初期化
しておいて使いたいところで
void loop() {
M5.update();
if (M5.BtnA.wasPressed()) {
Serial.println("BtnA");
speaker.stopVoice(); // 停止
speaker.startVoice("/mp3/010.mp3"); // 起動
}
通信
次に外部から呼び出せるようにserver機能を持たせる。店の外から呼び出す計画のでまずはhttpでそのまま外からつなげられるようする。そのうちリバースプロキシの後ろに隠すかもしれないけどとりあえずルーターにNAPTでさばかせておく。
wifi周り
スマートコンフィグ使おうかと考えたけど、ip指定で起動するときにデフォゲとサブネット与えないといけなかった。そいつらをしっかりと渡すとなると外から与えてスマートコンフィグの意味なくなるか、一回目の接続で取れた値を使用と面倒になりそう。というわけでおとなしく明示的に指定することにした。
#include <WiFi.h>
// 見えているけど店の公開wifiなので気にしない
#define STASSID "rocher_guest";
#define STAPSK "sd5ks86vn5nti";
const char *ssid = STASSID;
const char *pass = STAPSK;
const IPAddress ip(192, 168, 11, 26);
const IPAddress gateway(192, 168, 11, 1);
const IPAddress subnet(255, 255, 255, 0);
WiFi.mode(WIFI_STA);
if (!WiFi.config(ip, gateway, subnet)) {
Serial.println("Fail WiFi.config");
for (;;) { delay(1000); }
}
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
と、サンプルにありそうなやつそのまま
server周り
これもよくありそうなやつ。
WebServer.hつかって、呼び出し処理があったら音声を流す処理を行う。
handleYobidashiの中で、speaker#stopVoice/speaker#startVoiceを直接呼んでいるので同期処理になっている。音声が流れ終わってから呼び出し側にはレスポンス返る。使うケース次第では非同期呼び出しにするためにさらにスレッドを呼ぶ形にする方が良い場合は多そう。
#include <WebServer.h>
WebServer server(80);
void handleYobidashi() {
speaker.stopVoice();
speaker.startVoice("/mp3/010.mp3");
server.send(200, "text/plain", "call shop!");
}
void serverInit() {
server.on("/yobidashi", handleYobidashi);
server.onNotFound([]() {
server.send(404, "text/plain", "not found");
});
server.begin();
Serial.println("server Init!");
}
void setup() {
connectWifi();
serverInit();
}
void loop() {
server.handleClient();
delay(2);
}
続き 呼び出し側の自宅端末バージョンアップ