From ae34d15168ce25838aaa29e2db10ba021d82aaaf Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Tue, 7 Sep 2021 23:43:20 +0200 Subject: [PATCH 01/15] Add a Play state for every state and add Error --- PlatformIO/src/General.hpp | 27 ++-- PlatformIO/src/Satellite.cpp | 2 +- PlatformIO/src/StateMachine.hpp | 236 ++++++++++++++++++++------------ 3 files changed, 166 insertions(+), 99 deletions(-) diff --git a/PlatformIO/src/General.hpp b/PlatformIO/src/General.hpp index a28f94a..7b7dc48 100644 --- a/PlatformIO/src/General.hpp +++ b/PlatformIO/src/General.hpp @@ -67,7 +67,7 @@ bool mqttInitialized = false; int retryCount = 0; int I2SMode = -1; bool mqttConnected = false; -bool DEBUG = false; +bool DEBUG = true; std::string audioFrameTopic = std::string("hermes/audioServer/") + config.siteid + std::string("/audioFrame"); std::string playBytesTopic = std::string("hermes/audioServer/") + config.siteid + std::string("/playBytes/#"); @@ -79,6 +79,7 @@ std::string debugTopic = config.siteid + std::string("/debug"); std::string restartTopic = config.siteid + std::string("/restart"); std::string sayTopic = "hermes/tts/say"; std::string sayFinishedTopic = "hermes/tts/sayFinished"; +std::string errorTopic = "hermes/nlu/intentNotRecognized"; AsyncMqttClient asyncClient; WiFiClient net; PubSubClient audioServer(net); @@ -92,24 +93,33 @@ static EventGroupHandle_t audioGroup; SemaphoreHandle_t wbSemaphore; TaskHandle_t i2sHandle; +struct WifiConnected; struct WifiDisconnected; +struct MQTTConnected; struct MQTTDisconnected; -struct HotwordDetected; +struct Listening; +struct ListeningPlay; struct Idle; -struct Speaking; +struct IdlePlay; +struct Tts; +struct TtsPlay; +struct Error; +struct ErrorPlay; struct Updating; -struct PlayAudio; struct WifiDisconnectEvent : tinyfsm::Event { }; struct WifiConnectEvent : tinyfsm::Event { }; struct MQTTDisconnectedEvent : tinyfsm::Event { }; struct MQTTConnectedEvent : tinyfsm::Event { }; struct IdleEvent : tinyfsm::Event { }; -struct SpeakEvent : tinyfsm::Event { }; -struct OtaEvent : tinyfsm::Event { }; +struct TtsEvent : tinyfsm::Event { }; +struct ErrorEvent : tinyfsm::Event { }; +struct UpdateEvent : tinyfsm::Event { }; +struct BeginPlayAudioEvent : tinyfsm::Event {}; +struct EndPlayAudioEvent : tinyfsm::Event {}; struct StreamAudioEvent : tinyfsm::Event { }; -struct PlayAudioEvent : tinyfsm::Event {}; -struct HotwordDetectedEvent : tinyfsm::Event { }; +struct PlayBytesEvent : tinyfsm::Event {}; +struct ListeningEvent : tinyfsm::Event { }; void onMqttConnect(bool sessionPresent); void onMqttDisconnect(AsyncMqttClientDisconnectReason reason); @@ -290,6 +300,7 @@ void handleRequest ( AsyncWebServerRequest* request ) } void publishDebug(const char* message) { + Serial.println(message); if (DEBUG) { asyncClient.publish(debugTopic.c_str(), 0, false, message); } diff --git a/PlatformIO/src/Satellite.cpp b/PlatformIO/src/Satellite.cpp index a02eeb7..da62cc8 100644 --- a/PlatformIO/src/Satellite.cpp +++ b/PlatformIO/src/Satellite.cpp @@ -183,7 +183,7 @@ void setup() { ArduinoOTA .onStart([]() { Serial.println("Uploading..."); - send_event(OtaEvent()); + send_event(UpdateEvent()); }) .onEnd([]() { Serial.println("\nEnd"); diff --git a/PlatformIO/src/StateMachine.hpp b/PlatformIO/src/StateMachine.hpp index 786b1ab..c1e6b59 100644 --- a/PlatformIO/src/StateMachine.hpp +++ b/PlatformIO/src/StateMachine.hpp @@ -7,41 +7,71 @@ class StateMachine : public tinyfsm::Fsm { public: - virtual void react(WifiDisconnectEvent const &) {}; - virtual void react(WifiConnectEvent const &) {}; - virtual void react(MQTTConnectedEvent const &) {}; - virtual void react(MQTTDisconnectedEvent const &) {}; - virtual void react(StreamAudioEvent const &) {}; + virtual void react(WifiDisconnectEvent const &) { + transit(); + }; + virtual void react(WifiConnectEvent const &) { + transit(); + }; + virtual void react(MQTTConnectedEvent const &) { + transit(); + }; + virtual void react(MQTTDisconnectedEvent const &) { + transit(); + }; + virtual void react(BeginPlayAudioEvent const &) {}; + virtual void react(EndPlayAudioEvent const &) {}; + virtual void react(StreamAudioEvent const &) { + xEventGroupClearBits(audioGroup, PLAY); + xEventGroupSetBits(audioGroup, STREAM); + }; virtual void react(IdleEvent const &) {}; - virtual void react(SpeakEvent const &) {}; - virtual void react(OtaEvent const &) {}; - virtual void react(PlayAudioEvent const &) {}; - virtual void react(HotwordDetectedEvent const &) {}; + virtual void react(ErrorEvent const &) {}; + virtual void react(TtsEvent const &) {}; + virtual void react(UpdateEvent const &) {}; + virtual void react(PlayBytesEvent const &) { + xEventGroupClearBits(audioGroup, STREAM); + xEventGroupSetBits(audioGroup, PLAY); + }; + virtual void react(ListeningEvent const &) {}; virtual void entry(void) {}; virtual void run(void) {}; void exit(void) {}; }; -class Speaking : public StateMachine +class Tts : public StateMachine { + void entry(void) override { + publishDebug("Enter Tts"); + } + void react(IdleEvent const &) override { + publishDebug("IdleEvent in Tts"); transit(); } - void react(HotwordDetectedEvent const &) override { - transit(); + void react(ListeningEvent const &) override { + publishDebug("ListeningEvent in Tts"); + transit(); } - void react(StreamAudioEvent const &) override { - xEventGroupClearBits(audioGroup, PLAY); - xEventGroupSetBits(audioGroup, STREAM); - }; + void react(BeginPlayAudioEvent const &) override { + publishDebug("BeginPlayAudioEvent in Tts"); + transit(); + } +}; - void react(PlayAudioEvent const &) override { - xEventGroupClearBits(audioGroup, STREAM); - xEventGroupSetBits(audioGroup, PLAY); - }; +class TtsPlay : public StateMachine +{ + void entry(void) override { + publishDebug("Enter TtsPlay"); + } + + void react(EndPlayAudioEvent const &) override { + publishDebug("EndPlayAudioEvent in TtsPlay"); + transit(); + } }; class Updating : public StateMachine @@ -56,10 +86,10 @@ class Updating : public StateMachine } }; -class HotwordDetected : public StateMachine +class Listening : public StateMachine { void entry(void) override { - Serial.println("Enter HotwordDetected"); + publishDebug("Enter Listening"); xEventGroupClearBits(audioGroup, PLAY); xEventGroupClearBits(audioGroup, STREAM); device->updateBrightness(config.hotword_brightness); @@ -70,27 +100,31 @@ class HotwordDetected : public StateMachine xEventGroupSetBits(audioGroup, STREAM); } - void react(StreamAudioEvent const &) override { - xEventGroupClearBits(audioGroup, PLAY); - xEventGroupSetBits(audioGroup, STREAM); - }; - - void react(PlayAudioEvent const &) override { - xEventGroupClearBits(audioGroup, STREAM); - xEventGroupSetBits(audioGroup, PLAY); - }; - void react(IdleEvent const &) override { + publishDebug("IdleEvent in Listening"); transit(); } - void react(SpeakEvent const &) override { - transit(); + void react(TtsEvent const &) override { + transit(); } - void react(WifiDisconnectEvent const &) override { - transit(); - }; + void react(BeginPlayAudioEvent const &) override { + publishDebug("BeginPlayAudioEvent in Listening"); + transit(); + } +}; + +class ListeningPlay : public StateMachine +{ + void entry(void) override { + publishDebug("Enter ListeningPlay"); + } + + void react(EndPlayAudioEvent const &) override { + publishDebug("EndPlayAudioEvent in ListeningPlay"); + transit(); + } }; class Idle : public StateMachine @@ -98,7 +132,7 @@ class Idle : public StateMachine bool hotwordDetected = false; void entry(void) override { - Serial.println("Enter Idle"); + publishDebug("Enter Idle"); hotwordDetected = false; xEventGroupClearBits(audioGroup, PLAY); xEventGroupClearBits(audioGroup, STREAM); @@ -119,38 +153,70 @@ class Idle : public StateMachine } } - void react(WifiDisconnectEvent const &) override { - transit(); + void react(ListeningEvent const &) override { + transit(); } - void react(MQTTDisconnectedEvent const &) override { - transit(); + void react(BeginPlayAudioEvent const &) override { + publishDebug("BeginPlayAudioEvent in Idle"); + transit(); } - void react(HotwordDetectedEvent const &) override { - transit(); + void react(TtsEvent const &) override { + transit(); } - void react(StreamAudioEvent const &) override { - xEventGroupClearBits(audioGroup, PLAY); - xEventGroupSetBits(audioGroup, STREAM); - }; + void react(ErrorEvent const &) override { + transit(); + } + + void react(UpdateEvent const &) override { + transit(); + } +}; - void react(PlayAudioEvent const &) override { - xEventGroupClearBits(audioGroup, STREAM); - xEventGroupSetBits(audioGroup, PLAY); - }; +class IdlePlay : public StateMachine +{ + void entry(void) override { + publishDebug("Enter IdlePlay"); + } - void react(SpeakEvent const &) override { - transit(); + void react(EndPlayAudioEvent const &) override { + publishDebug("EndPlayAudioEvent in IdlePlay"); + transit(); } - +}; + +class Error : public StateMachine +{ + void entry(void) override { + publishDebug("Enter Error"); + device->updateBrightness(config.brightness); + xSemaphoreTake(wbSemaphore, portMAX_DELAY); + device->updateColors(COLORS_OTA); + xSemaphoreGive(wbSemaphore); + } + void react(IdleEvent const &) override { + publishDebug("IdleEvent in Error"); transit(); } - void react(OtaEvent const &) override { - transit(); + void react(BeginPlayAudioEvent const &) override { + publishDebug("BeginPlayAudioEvent in Error"); + transit(); + } +}; + +class ErrorPlay : public StateMachine +{ + void entry(void) override { + publishDebug("Enter ErrorPlay"); + } + + void react(EndPlayAudioEvent const &) override { + publishDebug("EndPlayAudioEvent in ErrorPlay"); + transit(); } }; @@ -162,21 +228,14 @@ class MQTTConnected : public StateMachine { asyncClient.subscribe(playBytesTopic.c_str(), 0); asyncClient.subscribe(hotwordTopic.c_str(), 0); asyncClient.subscribe(audioTopic.c_str(), 0); - asyncClient.subscribe(debugTopic.c_str(), 0); + //asyncClient.subscribe(debugTopic.c_str(), 0); asyncClient.subscribe(ledTopic.c_str(), 0); asyncClient.subscribe(restartTopic.c_str(), 0); asyncClient.subscribe(sayTopic.c_str(), 0); asyncClient.subscribe(sayFinishedTopic.c_str(), 0); + asyncClient.subscribe(errorTopic.c_str(), 0); transit(); } - - void react(MQTTDisconnectedEvent const &) override { - transit(); - } - - void react(WifiDisconnectEvent const &) override { - transit(); - } }; class MQTTDisconnected : public StateMachine { @@ -221,14 +280,6 @@ class MQTTDisconnected : public StateMachine { } } } - - void react(MQTTConnectedEvent const &) override { - transit(); - } - - void react(WifiDisconnectEvent const &) override { - transit(); - } }; class WifiConnected : public StateMachine @@ -247,11 +298,6 @@ class WifiConnected : public StateMachine ArduinoOTA.begin(); transit(); } - - void react(WifiDisconnectEvent const &) override { - Serial.println("DisconnectEvent"); - transit(); - }; }; class WifiDisconnected : public StateMachine @@ -355,10 +401,6 @@ class WifiDisconnected : public StateMachine #endif } - - void react(WifiConnectEvent const &) override { - transit(); - }; }; FSM_INITIAL_STATE(StateMachine, WifiDisconnected) @@ -414,7 +456,7 @@ void push_i2s_data(const uint8_t *const payload, size_t len) { if (xEventGroupGetBits(audioGroup) != PLAY) { - send_event(PlayAudioEvent()); + send_event(PlayBytesEvent()); } vTaskDelay(pdMS_TO_TICKS(50)); } while (audioData.isFull()); @@ -429,6 +471,7 @@ void handle_playBytes(const std::string& topicstr, uint8_t *payload, size_t len, // start of message if (index == 0) { + send_event(BeginPlayAudioEvent()); message_size = total; audioData.clear(); XT_Wav_Class Message((const uint8_t *)payload); @@ -452,11 +495,12 @@ void handle_playBytes(const std::string& topicstr, uint8_t *payload, size_t len, //At the end, make sure to start play in case the buffer is not full yet if (!audioData.isEmpty() && xEventGroupGetBits(audioGroup) != PLAY) { - send_event(PlayAudioEvent()); + send_event(PlayBytesEvent()); } std::vector topicparts = explode("/", topicstr); finishedMsg = "{\"id\":\"" + topicparts[4] + "\",\"siteId\":\"" + config.siteid + "\",\"sessionId\":null}"; + send_event(EndPlayAudioEvent()); } } @@ -467,7 +511,19 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties // complete or enf of message has been received if (len + index == total) { - if (topicstr.find(sayFinishedTopic.c_str()) != std::string::npos) + if (topicstr.find(errorTopic.c_str()) != std::string::npos) + { + std::string payloadstr(payload); + StaticJsonDocument<300> doc; + DeserializationError err = deserializeJson(doc, payloadstr.c_str()); + // Check if this is for us + if (!err) { + JsonObject root = doc.as(); + if (root["siteId"] == config.siteid.c_str()) { + send_event(ErrorEvent()); + } + } + } else if (topicstr.find(sayFinishedTopic.c_str()) != std::string::npos) { std::string payloadstr(payload); StaticJsonDocument<300> doc; @@ -488,7 +544,7 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties if (!err) { JsonObject root = doc.as(); if (root["siteId"] == config.siteid.c_str()) { - send_event(SpeakEvent()); + send_event(TtsEvent()); } } } else if (topicstr.find("toggleOff") != std::string::npos) @@ -501,10 +557,10 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties JsonObject root = doc.as(); if (root["siteId"] == config.siteid.c_str() && root.containsKey("reason")) { if (root["reason"] == "dialogueSession") { - send_event(HotwordDetectedEvent()); + send_event(ListeningEvent()); } if (root["reason"] == "ttsSay") { - send_event(SpeakEvent()); + send_event(TtsEvent()); } } } @@ -657,7 +713,7 @@ void I2Stask(void *p) { while (played < message_size && timeout == false) { - if (fsm::is_in_state()) { + if (fsm::is_in_state()) { xSemaphoreTake(wbSemaphore, portMAX_DELAY); device->animate(COLORS_OTA); xSemaphoreGive(wbSemaphore); From 96286a29b8043f484ca005172a6818ed147d19f2 Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Tue, 14 Sep 2021 20:18:21 +0200 Subject: [PATCH 02/15] add extra colors --- PlatformIO/src/General.hpp | 1 + PlatformIO/src/StateMachine.hpp | 23 ++++++-- PlatformIO/src/device.h | 6 ++- PlatformIO/src/devices/MatrixVoice.hpp | 74 ++++++++++++++++++++++---- 4 files changed, 89 insertions(+), 15 deletions(-) diff --git a/PlatformIO/src/General.hpp b/PlatformIO/src/General.hpp index 7b7dc48..a770268 100644 --- a/PlatformIO/src/General.hpp +++ b/PlatformIO/src/General.hpp @@ -89,6 +89,7 @@ int queueDelay = 10; int sampleRate = 16000; int numChannels = 2; int bitDepth = 16; +int current_colors = COLORS_IDLE; static EventGroupHandle_t audioGroup; SemaphoreHandle_t wbSemaphore; TaskHandle_t i2sHandle; diff --git a/PlatformIO/src/StateMachine.hpp b/PlatformIO/src/StateMachine.hpp index c1e6b59..26a49f1 100644 --- a/PlatformIO/src/StateMachine.hpp +++ b/PlatformIO/src/StateMachine.hpp @@ -44,6 +44,7 @@ class Tts : public StateMachine { void entry(void) override { publishDebug("Enter Tts"); + current_colors = COLORS_TTS; } void react(IdleEvent const &) override { @@ -95,6 +96,7 @@ class Listening : public StateMachine device->updateBrightness(config.hotword_brightness); xSemaphoreTake(wbSemaphore, portMAX_DELAY); device->updateColors(COLORS_HOTWORD); + current_colors = COLORS_HOTWORD; xSemaphoreGive(wbSemaphore); initHeader(device->readSize, device->width, device->rate); xEventGroupSetBits(audioGroup, STREAM); @@ -139,6 +141,7 @@ class Idle : public StateMachine device->updateBrightness(config.brightness); xSemaphoreTake(wbSemaphore, portMAX_DELAY); device->updateColors(COLORS_IDLE); + current_colors = COLORS_IDLE; xSemaphoreGive(wbSemaphore); initHeader(device->readSize, device->width, device->rate); xEventGroupSetBits(audioGroup, STREAM); @@ -193,7 +196,8 @@ class Error : public StateMachine publishDebug("Enter Error"); device->updateBrightness(config.brightness); xSemaphoreTake(wbSemaphore, portMAX_DELAY); - device->updateColors(COLORS_OTA); + device->updateColors(COLORS_ERROR); + current_colors = COLORS_ERROR; xSemaphoreGive(wbSemaphore); } @@ -562,6 +566,9 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties if (root["reason"] == "ttsSay") { send_event(TtsEvent()); } + if (root["reason"] == "playAudio") { + send_event(ListeningEvent()); + } } } } else if (topicstr.find("toggleOn") != std::string::npos) { @@ -572,9 +579,15 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties if (!err) { JsonObject root = doc.as(); if (root["siteId"] == config.siteid.c_str() && root.containsKey("reason")) { - if (root["reason"] == "dialogueSession" || root["reason"] == "ttsSay") { + if (root["reason"] == "dialogueSession") { send_event(IdleEvent()); } + if (root["reason"] == "ttsSay") { + send_event(IdleEvent()); + } + if (root["reason"] == "playAudio") { + // send_event(IdleEvent()); + } } } } @@ -713,11 +726,11 @@ void I2Stask(void *p) { while (played < message_size && timeout == false) { - if (fsm::is_in_state()) { + // if (fsm::is_in_state()) { xSemaphoreTake(wbSemaphore, portMAX_DELAY); - device->animate(COLORS_OTA); + device->animate(current_colors); xSemaphoreGive(wbSemaphore); - } + // } int bytes_to_write = device->writeSize; if (message_size - played < device->writeSize) { diff --git a/PlatformIO/src/device.h b/PlatformIO/src/device.h index 24d8197..265d96f 100644 --- a/PlatformIO/src/device.h +++ b/PlatformIO/src/device.h @@ -5,12 +5,16 @@ int idle_colors[4] = {0, 0, 255, 0}; int wifi_conn_colors[4] = {0, 0, 255, 0}; int wifi_disc_colors[4] = {255, 0, 0, 0}; int ota_colors[4] = {0, 0, 0, 255}; +int tts_colors[4] = {80, 10, 185, 0}; +int error_colors[4] = {150, 255, 0, 0}; enum { COLORS_HOTWORD = 0, COLORS_WIFI_CONNECTED = 1, COLORS_WIFI_DISCONNECTED = 2, COLORS_IDLE = 3, - COLORS_OTA = 4 + COLORS_OTA = 4, + COLORS_TTS = 5, + COLORS_ERROR = 6 }; enum DeviceMode { MODE_UNUSED = -1, // indicates the no explicit mode change has been stored yet diff --git a/PlatformIO/src/devices/MatrixVoice.hpp b/PlatformIO/src/devices/MatrixVoice.hpp index a763a8b..f27965a 100644 --- a/PlatformIO/src/devices/MatrixVoice.hpp +++ b/PlatformIO/src/devices/MatrixVoice.hpp @@ -108,20 +108,64 @@ void MatrixVoice::updateBrightness(int brightness) { void MatrixVoice::animate(int colors) { currentMillis = millis(); if (currentMillis - startMillis > 10) { - startMillis = millis(); int r = 0; int g = 0; int b = 0; int w = 0; + switch (colors) { + case COLORS_TTS: + r = tts_colors[0]; + g = tts_colors[1]; + b = tts_colors[2]; + w = tts_colors[3]; + break; + case COLORS_ERROR: + r = error_colors[0]; + g = error_colors[1]; + b = error_colors[2]; + w = error_colors[3]; + break; + case COLORS_HOTWORD: + r = hotword_colors[0]; + g = hotword_colors[1]; + b = hotword_colors[2]; + w = hotword_colors[3]; + break; + case COLORS_WIFI_CONNECTED: + r = wifi_conn_colors[0]; + g = wifi_conn_colors[1]; + b = wifi_conn_colors[2]; + w = wifi_conn_colors[3]; + break; + case COLORS_IDLE: + r = idle_colors[0]; + g = idle_colors[1]; + b = idle_colors[2]; + w = idle_colors[3]; + break; + case COLORS_WIFI_DISCONNECTED: + r = wifi_disc_colors[0]; + g = wifi_disc_colors[1]; + b = wifi_disc_colors[2]; + w = wifi_disc_colors[3]; + break; + case COLORS_OTA: + r = ota_colors[0]; + g = ota_colors[1]; + b = ota_colors[2]; + w = ota_colors[3]; + break; + } + startMillis = millis(); for (int i = 0; i < image1d.leds.size(); i++) { - r = ((i + 1) * brightness / image1d.leds.size()) * ota_colors[0] / 100; - g = ((i + 1) * brightness / image1d.leds.size()) * ota_colors[1] / 100; - b = ((i + 1) * brightness / image1d.leds.size()) * ota_colors[2] / 100; - w = ((i + 1) * brightness / image1d.leds.size()) * ota_colors[3] / 100; - image1d.leds[(i + position) % image1d.leds.size()].red = pgm_read_byte(&gamma8[r]); - image1d.leds[(i + position) % image1d.leds.size()].green = pgm_read_byte(&gamma8[g]); - image1d.leds[(i + position) % image1d.leds.size()].blue = pgm_read_byte(&gamma8[b]); - image1d.leds[(i + position) % image1d.leds.size()].white = pgm_read_byte(&gamma8[w]); + int red = ((i + 1) * brightness / image1d.leds.size()) * r / 100; + int green = ((i + 1) * brightness / image1d.leds.size()) * g / 100; + int blue = ((i + 1) * brightness / image1d.leds.size()) * b / 100; + int white = ((i + 1) * brightness / image1d.leds.size()) * w / 100; + image1d.leds[(i + position) % image1d.leds.size()].red = pgm_read_byte(&gamma8[red]); + image1d.leds[(i + position) % image1d.leds.size()].green = pgm_read_byte(&gamma8[green]); + image1d.leds[(i + position) % image1d.leds.size()].blue = pgm_read_byte(&gamma8[blue]); + image1d.leds[(i + position) % image1d.leds.size()].white = pgm_read_byte(&gamma8[white]); } position++; position %= image1d.leds.size(); @@ -135,6 +179,18 @@ void MatrixVoice::updateColors(int colors) { int b = 0; int w = 0; switch (colors) { + case COLORS_TTS: + r = tts_colors[0]; + g = tts_colors[1]; + b = tts_colors[2]; + w = tts_colors[3]; + break; + case COLORS_ERROR: + r = error_colors[0]; + g = error_colors[1]; + b = error_colors[2]; + w = error_colors[3]; + break; case COLORS_HOTWORD: r = hotword_colors[0]; g = hotword_colors[1]; From e904b732926e9f5c7e812b93831e310d0241e2bf Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Fri, 22 Oct 2021 20:10:18 +0200 Subject: [PATCH 03/15] Dynamically update leds --- PlatformIO/src/General.hpp | 1 + PlatformIO/src/StateMachine.hpp | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/PlatformIO/src/General.hpp b/PlatformIO/src/General.hpp index a770268..5ade78d 100644 --- a/PlatformIO/src/General.hpp +++ b/PlatformIO/src/General.hpp @@ -121,6 +121,7 @@ struct EndPlayAudioEvent : tinyfsm::Event {}; struct StreamAudioEvent : tinyfsm::Event { }; struct PlayBytesEvent : tinyfsm::Event {}; struct ListeningEvent : tinyfsm::Event { }; +struct UpdateConfigurationEvent : tinyfsm::Event { }; void onMqttConnect(bool sessionPresent); void onMqttDisconnect(AsyncMqttClientDisconnectReason reason); diff --git a/PlatformIO/src/StateMachine.hpp b/PlatformIO/src/StateMachine.hpp index e1eab50..4f52e96 100644 --- a/PlatformIO/src/StateMachine.hpp +++ b/PlatformIO/src/StateMachine.hpp @@ -34,6 +34,13 @@ class StateMachine xEventGroupSetBits(audioGroup, PLAY); }; virtual void react(ListeningEvent const &) {}; + virtual void react(UpdateConfigurationEvent const &) { + device->updateBrightness(config.brightness); + xSemaphoreTake(wbSemaphore, portMAX_DELAY); + device->updateColors(COLORS_IDLE); + current_colors = COLORS_IDLE; + xSemaphoreGive(wbSemaphore); + }; virtual void entry(void) {}; virtual void run(void) {}; @@ -648,7 +655,7 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties if (saveNeeded) { saveConfiguration(configfile, config); } - send_event(IdleEvent()); + send_event(UpdateConfigurationEvent()); } else { publishDebug(err.c_str()); } From 2ed74a8e999b8eb719fdd4e389421b9151228f37 Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Sat, 23 Oct 2021 15:22:33 +0200 Subject: [PATCH 04/15] Fix for not playing feedback sounds --- PlatformIO/src/StateMachine.hpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/PlatformIO/src/StateMachine.hpp b/PlatformIO/src/StateMachine.hpp index 4f52e96..fcad746 100644 --- a/PlatformIO/src/StateMachine.hpp +++ b/PlatformIO/src/StateMachine.hpp @@ -487,7 +487,7 @@ void handle_playBytes(const std::string& topicstr, uint8_t *payload, size_t len, // start of message if (index == 0) { - send_event(BeginPlayAudioEvent()); + // send_event(BeginPlayAudioEvent()); message_size = total; audioData.clear(); XT_Wav_Class Message((const uint8_t *)payload); @@ -516,7 +516,7 @@ void handle_playBytes(const std::string& topicstr, uint8_t *payload, size_t len, std::vector topicparts = explode("/", topicstr); finishedMsg = "{\"id\":\"" + topicparts[4] + "\",\"siteId\":\"" + config.siteid + "\",\"sessionId\":null}"; - send_event(EndPlayAudioEvent()); + //send_event(EndPlayAudioEvent()); } } @@ -524,7 +524,7 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties { const std::string topicstr(topic); - // complete or enf of message has been received + // complete or end of message has been received if (len + index == total) { if (topicstr.find(errorTopic.c_str()) != std::string::npos) @@ -575,12 +575,12 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties if (root["reason"] == "dialogueSession") { send_event(ListeningEvent()); } - if (root["reason"] == "ttsSay") { - send_event(TtsEvent()); - } - if (root["reason"] == "playAudio") { - send_event(ListeningEvent()); - } + // if (root["reason"] == "ttsSay") { + // send_event(TtsEvent()); + // } + // if (root["reason"] == "playAudio") { + // send_event(ListeningEvent()); + // } } } } else if (topicstr.find("toggleOn") != std::string::npos) { @@ -594,12 +594,12 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties if (root["reason"] == "dialogueSession") { send_event(IdleEvent()); } - if (root["reason"] == "ttsSay") { - send_event(IdleEvent()); - } - if (root["reason"] == "playAudio") { - // send_event(IdleEvent()); - } + // if (root["reason"] == "ttsSay") { + // send_event(IdleEvent()); + // } + // if (root["reason"] == "playAudio") { + // // send_event(IdleEvent()); + // } } } } From 1fe5ba96b1607dc09d7ba94c8d332d9a6962f954 Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Sat, 23 Oct 2021 15:30:14 +0200 Subject: [PATCH 05/15] dispatch events correctly --- PlatformIO/src/StateMachine.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PlatformIO/src/StateMachine.hpp b/PlatformIO/src/StateMachine.hpp index fcad746..469ae53 100644 --- a/PlatformIO/src/StateMachine.hpp +++ b/PlatformIO/src/StateMachine.hpp @@ -23,6 +23,7 @@ class StateMachine virtual void react(EndPlayAudioEvent const &) {}; virtual void react(StreamAudioEvent const &) { xEventGroupClearBits(audioGroup, PLAY); + dispatch(EndPlayAudioEvent()); xEventGroupSetBits(audioGroup, STREAM); }; virtual void react(IdleEvent const &) {}; @@ -31,6 +32,7 @@ class StateMachine virtual void react(UpdateEvent const &) {}; virtual void react(PlayBytesEvent const &) { xEventGroupClearBits(audioGroup, STREAM); + dispatch(BeginPlayAudioEvent()); xEventGroupSetBits(audioGroup, PLAY); }; virtual void react(ListeningEvent const &) {}; @@ -516,7 +518,6 @@ void handle_playBytes(const std::string& topicstr, uint8_t *payload, size_t len, std::vector topicparts = explode("/", topicstr); finishedMsg = "{\"id\":\"" + topicparts[4] + "\",\"siteId\":\"" + config.siteid + "\",\"sessionId\":null}"; - //send_event(EndPlayAudioEvent()); } } From 6f9afc2c973124a61e20b6d92dc34458e62e30d6 Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Sun, 24 Oct 2021 14:00:28 +0200 Subject: [PATCH 06/15] Serial message all over the place for debug --- PlatformIO/src/StateMachine.hpp | 40 +++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/PlatformIO/src/StateMachine.hpp b/PlatformIO/src/StateMachine.hpp index 469ae53..88041ae 100644 --- a/PlatformIO/src/StateMachine.hpp +++ b/PlatformIO/src/StateMachine.hpp @@ -23,6 +23,7 @@ class StateMachine virtual void react(EndPlayAudioEvent const &) {}; virtual void react(StreamAudioEvent const &) { xEventGroupClearBits(audioGroup, PLAY); + Serial.println("Send EndPlayAudioEvent in StreamAudioEvent"); dispatch(EndPlayAudioEvent()); xEventGroupSetBits(audioGroup, STREAM); }; @@ -32,6 +33,7 @@ class StateMachine virtual void react(UpdateEvent const &) {}; virtual void react(PlayBytesEvent const &) { xEventGroupClearBits(audioGroup, STREAM); + Serial.println("Send BeginPlayAudioEvent in PlayBytesEvent"); dispatch(BeginPlayAudioEvent()); xEventGroupSetBits(audioGroup, PLAY); }; @@ -474,11 +476,11 @@ void push_i2s_data(const uint8_t *const payload, size_t len) { if (xEventGroupGetBits(audioGroup) != PLAY) { + Serial.println("Send PlayBytesEvent"); send_event(PlayBytesEvent()); } vTaskDelay(pdMS_TO_TICKS(50)); } while (audioData.isFull()); - /// Serial.print("."); } } @@ -489,7 +491,6 @@ void handle_playBytes(const std::string& topicstr, uint8_t *payload, size_t len, // start of message if (index == 0) { - // send_event(BeginPlayAudioEvent()); message_size = total; audioData.clear(); XT_Wav_Class Message((const uint8_t *)payload); @@ -513,6 +514,7 @@ void handle_playBytes(const std::string& topicstr, uint8_t *payload, size_t len, //At the end, make sure to start play in case the buffer is not full yet if (!audioData.isEmpty() && xEventGroupGetBits(audioGroup) != PLAY) { + Serial.println("Send PlayBytesEvent"); send_event(PlayBytesEvent()); } @@ -537,6 +539,7 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties if (!err) { JsonObject root = doc.as(); if (root["siteId"] == config.siteid.c_str()) { + Serial.println("Send ErrorEvent from errorTopic"); send_event(ErrorEvent()); } } @@ -549,6 +552,7 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties if (!err) { JsonObject root = doc.as(); if (root["siteId"] == config.siteid.c_str()) { + Serial.println("Send IdleEvent from sayFinishedTopic"); send_event(IdleEvent()); } } @@ -561,6 +565,7 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties if (!err) { JsonObject root = doc.as(); if (root["siteId"] == config.siteid.c_str()) { + Serial.println("Send TtsEvent from sayTopic"); send_event(TtsEvent()); } } @@ -574,14 +579,17 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties JsonObject root = doc.as(); if (root["siteId"] == config.siteid.c_str() && root.containsKey("reason")) { if (root["reason"] == "dialogueSession") { + Serial.println("Send ListeningEvent from toggleOff (dialogueSession)"); + send_event(ListeningEvent()); + } + if (root["reason"] == "ttsSay") { + Serial.println("Send TtsEvent from toggleOff (ttsSay)"); + send_event(TtsEvent()); + } + if (root["reason"] == "playAudio") { + Serial.println("Send ListeningEvent from toggleOff (playAudio)"); send_event(ListeningEvent()); } - // if (root["reason"] == "ttsSay") { - // send_event(TtsEvent()); - // } - // if (root["reason"] == "playAudio") { - // send_event(ListeningEvent()); - // } } } } else if (topicstr.find("toggleOn") != std::string::npos) { @@ -593,14 +601,17 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties JsonObject root = doc.as(); if (root["siteId"] == config.siteid.c_str() && root.containsKey("reason")) { if (root["reason"] == "dialogueSession") { + Serial.println("Send IdleEvent from toggleOn (dialogueSession)"); send_event(IdleEvent()); } - // if (root["reason"] == "ttsSay") { - // send_event(IdleEvent()); - // } - // if (root["reason"] == "playAudio") { - // // send_event(IdleEvent()); - // } + if (root["reason"] == "ttsSay") { + Serial.println("Send IdleEvent from toggleOn (ttsSay)"); + send_event(IdleEvent()); + } + if (root["reason"] == "playAudio") { + Serial.println("Send IdleEvent from toggleOn (playAudio)"); + send_event(IdleEvent()); + } } } } @@ -784,6 +795,7 @@ void I2Stask(void *p) { xSemaphoreGive(wbSemaphore); audioData.clear(); Serial.println("Done"); + Serial.println("Send StreamAudioEvent"); send_event(StreamAudioEvent()); } if (xEventGroupGetBits(audioGroup) == STREAM && !config.mute_input) { From bc4432cc585b6f434acb524c456ce1f702a10624 Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Sun, 24 Oct 2021 14:37:24 +0200 Subject: [PATCH 07/15] Every state its own colors --- PlatformIO/src/StateMachine.hpp | 21 ++++++++++++++------- PlatformIO/src/device.h | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/PlatformIO/src/StateMachine.hpp b/PlatformIO/src/StateMachine.hpp index 88041ae..12997a9 100644 --- a/PlatformIO/src/StateMachine.hpp +++ b/PlatformIO/src/StateMachine.hpp @@ -90,11 +90,12 @@ class Updating : public StateMachine { void entry(void) override { Serial.println("Enter Updating"); + current_colors = COLORS_OTA; xEventGroupClearBits(audioGroup, PLAY); xEventGroupClearBits(audioGroup, STREAM); device->updateBrightness(config.brightness); xSemaphoreTake(wbSemaphore, portMAX_DELAY); - device->updateColors(COLORS_OTA); + device->updateColors(current_colors); xSemaphoreGive(wbSemaphore); } }; @@ -103,12 +104,12 @@ class Listening : public StateMachine { void entry(void) override { publishDebug("Enter Listening"); + current_colors = COLORS_HOTWORD; xEventGroupClearBits(audioGroup, PLAY); xEventGroupClearBits(audioGroup, STREAM); device->updateBrightness(config.hotword_brightness); xSemaphoreTake(wbSemaphore, portMAX_DELAY); - device->updateColors(COLORS_HOTWORD); - current_colors = COLORS_HOTWORD; + device->updateColors(current_colors); xSemaphoreGive(wbSemaphore); initHeader(device->readSize, device->width, device->rate); xEventGroupSetBits(audioGroup, STREAM); @@ -148,12 +149,12 @@ class Idle : public StateMachine void entry(void) override { publishDebug("Enter Idle"); hotwordDetected = false; + current_colors = COLORS_IDLE; xEventGroupClearBits(audioGroup, PLAY); xEventGroupClearBits(audioGroup, STREAM); device->updateBrightness(config.brightness); xSemaphoreTake(wbSemaphore, portMAX_DELAY); - device->updateColors(COLORS_IDLE); - current_colors = COLORS_IDLE; + device->updateColors(current_colors); xSemaphoreGive(wbSemaphore); initHeader(device->readSize, device->width, device->rate); xEventGroupSetBits(audioGroup, STREAM); @@ -206,10 +207,10 @@ class Error : public StateMachine { void entry(void) override { publishDebug("Enter Error"); + current_colors = COLORS_ERROR; device->updateBrightness(config.brightness); xSemaphoreTake(wbSemaphore, portMAX_DELAY); - device->updateColors(COLORS_ERROR); - current_colors = COLORS_ERROR; + device->updateColors(current_colors); xSemaphoreGive(wbSemaphore); } @@ -640,6 +641,12 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties hotword_colors[2] = root["hotword"][2]; hotword_colors[3] = root["hotword"][3]; } + if (root.containsKey("tts")) { + tts_colors[0] = root["tts"][0]; + tts_colors[1] = root["tts"][1]; + tts_colors[2] = root["tts"][2]; + tts_colors[3] = root["tts"][3]; + } if (root.containsKey("idle")) { idle_colors[0] = root["idle"][0]; idle_colors[1] = root["idle"][1]; diff --git a/PlatformIO/src/device.h b/PlatformIO/src/device.h index 265d96f..788903a 100644 --- a/PlatformIO/src/device.h +++ b/PlatformIO/src/device.h @@ -5,7 +5,7 @@ int idle_colors[4] = {0, 0, 255, 0}; int wifi_conn_colors[4] = {0, 0, 255, 0}; int wifi_disc_colors[4] = {255, 0, 0, 0}; int ota_colors[4] = {0, 0, 0, 255}; -int tts_colors[4] = {80, 10, 185, 0}; +int tts_colors[4] = {173, 17, 240, 0}; int error_colors[4] = {150, 255, 0, 0}; enum { COLORS_HOTWORD = 0, From a7643687cab956297d4e4445826520687001a0ad Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Mon, 25 Oct 2021 17:44:43 +0200 Subject: [PATCH 08/15] start adding animations and some refactoring --- PlatformIO/src/General.hpp | 28 +++++- PlatformIO/src/Satellite.cpp | 2 + PlatformIO/src/StateMachine.hpp | 119 +++++++++--------------- PlatformIO/src/device.h | 31 ++++++- PlatformIO/src/devices/MatrixVoice.hpp | 124 ++++++------------------- PlatformIO/src/index_html.h | 9 ++ 6 files changed, 140 insertions(+), 173 deletions(-) diff --git a/PlatformIO/src/General.hpp b/PlatformIO/src/General.hpp index 5ade78d..a28d803 100644 --- a/PlatformIO/src/General.hpp +++ b/PlatformIO/src/General.hpp @@ -42,6 +42,7 @@ struct Config { int hotword_brightness = 15; uint16_t volume = 100; int gain = 5; + int animation = SOLID; }; const char *configfile = "/config.json"; Config config; @@ -193,6 +194,11 @@ const std::map processor_values = { {"VOLUME", []() { return String(config.volume); } }, {"GAIN", []() { return String(config.gain); } }, {"SITEID", []() -> String { return config.siteid.c_str(); } }, + {"ANIMATIONSUPPORT", []() -> String { return device->animationSupported() ? "block" : "none"; } }, + {"ANIM_SOLID", []() -> String { return (config.animation == SOLID) ? "selected" : ""; } }, + {"ANIM_RUNNING", []() -> String { return device->runningSupported() ? (config.animation == RUNNING) ? "selected" : "" : "hidden"; } }, + {"ANIM_PULSING", []() -> String { return device->pulsingSupported() ? (config.animation == PULSING) ? "selected" : "" : "hidden"; } }, + {"ANIM_BLINKING", []() -> String { return device->blinkingSupported() ? (config.animation == BLINKING) ? "selected" : "" : "hidden"; } }, }; // this function supplies template variables to the template engine @@ -252,6 +258,7 @@ void handleFSf ( AsyncWebServerRequest* request, const String& route ) { saveNeeded |= processParam(p, "brightness", config.brightness); saveNeeded |= processParam(p, "hw_brightness", config.hotword_brightness); saveNeeded |= processParam(p, "hotword_detection", config.hotword_detection); + saveNeeded |= processParam(p, "animation", config.animation); saveNeeded |= processParam(p, "gain", config.gain); saveNeeded |= processParam(p, "volume", config.volume); @@ -301,6 +308,23 @@ void handleRequest ( AsyncWebServerRequest* request ) handleFSf ( request, String( "/index.html") ) ; } +void initHeader(int readSize, int width, int rate) { + strncpy(header.riff_tag, "RIFF", 4); + strncpy(header.wave_tag, "WAVE", 4); + strncpy(header.fmt_tag, "fmt ", 4); + strncpy(header.data_tag, "data", 4); + + header.riff_length = (uint32_t)sizeof(header) + (readSize * width); + header.fmt_length = 16; + header.audio_format = 1; + header.num_channels = 1; + header.sample_rate = rate; + header.byte_rate = rate * width; + header.block_align = width; + header.bits_per_sample = width * 8; + header.data_length = readSize * width; +} + void publishDebug(const char* message) { Serial.println(message); if (DEBUG) { @@ -336,6 +360,7 @@ void loadConfiguration(const char *filename, Config &config) { config.volume = doc.getMember("volume").as(); device->setVolume(config.volume); config.gain = doc.getMember("gain").as(); + config.animation = doc.getMember("animation").as(); device->setGain(config.gain); audioFrameTopic = std::string("hermes/audioServer/") + config.siteid + std::string("/audioFrame"); playBytesTopic = std::string("hermes/audioServer/") + config.siteid + std::string("/playBytes/#"); @@ -358,7 +383,7 @@ void saveConfiguration(const char *filename, Config &config) { Serial.println(F("Failed to create file")); return; } - StaticJsonDocument<256> doc; + StaticJsonDocument<512> doc; doc["siteid"] = config.siteid; doc["mqtt_host"] = config.mqtt_host; doc["mqtt_port"] = config.mqtt_port; @@ -372,6 +397,7 @@ void saveConfiguration(const char *filename, Config &config) { doc["hotword_detection"] = config.hotword_detection; doc["volume"] = config.volume; doc["gain"] = config.gain; + doc["animation"] = config.animation; if (serializeJson(doc, file) == 0) { Serial.println(F("Failed to write to file")); } diff --git a/PlatformIO/src/Satellite.cpp b/PlatformIO/src/Satellite.cpp index ff7f3c9..a0c407a 100644 --- a/PlatformIO/src/Satellite.cpp +++ b/PlatformIO/src/Satellite.cpp @@ -183,6 +183,8 @@ void setup() { device->setGain(config.gain); device->setVolume(config.volume); + initHeader(device->readSize, device->width, device->rate); + // --------------------------------------------------------------------------- // ArduinoOTA // --------------------------------------------------------------------------- diff --git a/PlatformIO/src/StateMachine.hpp b/PlatformIO/src/StateMachine.hpp index 12997a9..cd4488b 100644 --- a/PlatformIO/src/StateMachine.hpp +++ b/PlatformIO/src/StateMachine.hpp @@ -39,14 +39,21 @@ class StateMachine }; virtual void react(ListeningEvent const &) {}; virtual void react(UpdateConfigurationEvent const &) { + current_colors = COLORS_IDLE; device->updateBrightness(config.brightness); xSemaphoreTake(wbSemaphore, portMAX_DELAY); - device->updateColors(COLORS_IDLE); - current_colors = COLORS_IDLE; + device->updateColors(current_colors); xSemaphoreGive(wbSemaphore); }; - virtual void entry(void) {}; + virtual void entry(void) { + xEventGroupClearBits(audioGroup, PLAY); + xEventGroupClearBits(audioGroup, STREAM); + device->updateBrightness(config.brightness); + xSemaphoreTake(wbSemaphore, portMAX_DELAY); + device->updateColors(current_colors); + xSemaphoreGive(wbSemaphore); + }; virtual void run(void) {}; void exit(void) {}; }; @@ -56,6 +63,7 @@ class Tts : public StateMachine void entry(void) override { publishDebug("Enter Tts"); current_colors = COLORS_TTS; + StateMachine::entry(); } void react(IdleEvent const &) override { @@ -91,12 +99,7 @@ class Updating : public StateMachine void entry(void) override { Serial.println("Enter Updating"); current_colors = COLORS_OTA; - xEventGroupClearBits(audioGroup, PLAY); - xEventGroupClearBits(audioGroup, STREAM); - device->updateBrightness(config.brightness); - xSemaphoreTake(wbSemaphore, portMAX_DELAY); - device->updateColors(current_colors); - xSemaphoreGive(wbSemaphore); + StateMachine::entry(); } }; @@ -105,13 +108,7 @@ class Listening : public StateMachine void entry(void) override { publishDebug("Enter Listening"); current_colors = COLORS_HOTWORD; - xEventGroupClearBits(audioGroup, PLAY); - xEventGroupClearBits(audioGroup, STREAM); - device->updateBrightness(config.hotword_brightness); - xSemaphoreTake(wbSemaphore, portMAX_DELAY); - device->updateColors(current_colors); - xSemaphoreGive(wbSemaphore); - initHeader(device->readSize, device->width, device->rate); + StateMachine::entry(); xEventGroupSetBits(audioGroup, STREAM); } @@ -150,13 +147,7 @@ class Idle : public StateMachine publishDebug("Enter Idle"); hotwordDetected = false; current_colors = COLORS_IDLE; - xEventGroupClearBits(audioGroup, PLAY); - xEventGroupClearBits(audioGroup, STREAM); - device->updateBrightness(config.brightness); - xSemaphoreTake(wbSemaphore, portMAX_DELAY); - device->updateColors(current_colors); - xSemaphoreGive(wbSemaphore); - initHeader(device->readSize, device->width, device->rate); + StateMachine::entry(); xEventGroupSetBits(audioGroup, STREAM); } @@ -208,10 +199,7 @@ class Error : public StateMachine void entry(void) override { publishDebug("Enter Error"); current_colors = COLORS_ERROR; - device->updateBrightness(config.brightness); - xSemaphoreTake(wbSemaphore, portMAX_DELAY); - device->updateColors(current_colors); - xSemaphoreGive(wbSemaphore); + StateMachine::entry(); } void react(IdleEvent const &) override { @@ -626,6 +614,10 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties DeserializationError err = deserializeJson(doc, payloadstr.c_str()); if (!err) { JsonObject root = doc.as(); + if (root.containsKey("animation")) { + config.animation = (uint16_t)(root["animation"]); + saveNeeded = true; + } if (root.containsKey("brightness")) { if (config.brightness != (int)root["brightness"]) { config.brightness = (int)(root["brightness"]); @@ -636,40 +628,40 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties config.hotword_brightness = (int)(root["hotword_brightness"]); } if (root.containsKey("hotword")) { - hotword_colors[0] = root["hotword"][0]; - hotword_colors[1] = root["hotword"][1]; - hotword_colors[2] = root["hotword"][2]; - hotword_colors[3] = root["hotword"][3]; + ColorMap[COLORS_HOTWORD][0] = root["hotword"][0]; + ColorMap[COLORS_HOTWORD][1] = root["hotword"][1]; + ColorMap[COLORS_HOTWORD][2] = root["hotword"][2]; + ColorMap[COLORS_HOTWORD][3] = root["hotword"][3]; } if (root.containsKey("tts")) { - tts_colors[0] = root["tts"][0]; - tts_colors[1] = root["tts"][1]; - tts_colors[2] = root["tts"][2]; - tts_colors[3] = root["tts"][3]; + ColorMap[COLORS_TTS][0] = root["tts"][0]; + ColorMap[COLORS_TTS][1] = root["tts"][1]; + ColorMap[COLORS_TTS][2] = root["tts"][2]; + ColorMap[COLORS_TTS][3] = root["tts"][3]; } if (root.containsKey("idle")) { - idle_colors[0] = root["idle"][0]; - idle_colors[1] = root["idle"][1]; - idle_colors[2] = root["idle"][2]; - idle_colors[3] = root["idle"][3]; + ColorMap[COLORS_IDLE][0] = root["idle"][0]; + ColorMap[COLORS_IDLE][1] = root["idle"][1]; + ColorMap[COLORS_IDLE][2] = root["idle"][2]; + ColorMap[COLORS_IDLE][3] = root["idle"][3]; } if (root.containsKey("wifi_disconnect")) { - wifi_disc_colors[0] = root["wifi_disconnect"][0]; - wifi_disc_colors[1] = root["wifi_disconnect"][1]; - wifi_disc_colors[2] = root["wifi_disconnect"][2]; - wifi_disc_colors[3] = root["wifi_disconnect"][3]; + ColorMap[COLORS_WIFI_DISCONNECTED][0] = root["wifi_disconnect"][0]; + ColorMap[COLORS_WIFI_DISCONNECTED][1] = root["wifi_disconnect"][1]; + ColorMap[COLORS_WIFI_DISCONNECTED][2] = root["wifi_disconnect"][2]; + ColorMap[COLORS_WIFI_DISCONNECTED][3] = root["wifi_disconnect"][3]; } if (root.containsKey("wifi_connect")) { - wifi_conn_colors[0] = root["wifi_connect"][0]; - wifi_conn_colors[1] = root["wifi_connect"][1]; - wifi_conn_colors[2] = root["wifi_connect"][2]; - wifi_conn_colors[3] = root["wifi_connect"][3]; + ColorMap[COLORS_WIFI_CONNECTED][0] = root["wifi_connect"][0]; + ColorMap[COLORS_WIFI_CONNECTED][1] = root["wifi_connect"][1]; + ColorMap[COLORS_WIFI_CONNECTED][2] = root["wifi_connect"][2]; + ColorMap[COLORS_WIFI_CONNECTED][3] = root["wifi_connect"][3]; } if (root.containsKey("update")) { - ota_colors[0] = root["update"][0]; - ota_colors[1] = root["update"][1]; - ota_colors[2] = root["update"][2]; - ota_colors[3] = root["update"][3]; + ColorMap[COLORS_OTA][0] = root["update"][0]; + ColorMap[COLORS_OTA][1] = root["update"][1]; + ColorMap[COLORS_OTA][2] = root["update"][2]; + ColorMap[COLORS_OTA][3] = root["update"][3]; } if (saveNeeded) { saveConfiguration(configfile, config); @@ -757,11 +749,9 @@ void I2Stask(void *p) { while (played < message_size && timeout == false) { - // if (fsm::is_in_state()) { - xSemaphoreTake(wbSemaphore, portMAX_DELAY); - device->animate(current_colors); - xSemaphoreGive(wbSemaphore); - // } + xSemaphoreTake(wbSemaphore, portMAX_DELAY); + device->animate(current_colors, config.animation); + xSemaphoreGive(wbSemaphore); int bytes_to_write = device->writeSize; if (message_size - played < device->writeSize) { @@ -847,23 +837,6 @@ void I2Stask(void *p) { vTaskDelete(NULL); } -void initHeader(int readSize, int width, int rate) { - strncpy(header.riff_tag, "RIFF", 4); - strncpy(header.wave_tag, "WAVE", 4); - strncpy(header.fmt_tag, "fmt ", 4); - strncpy(header.data_tag, "data", 4); - - header.riff_length = (uint32_t)sizeof(header) + (readSize * width); - header.fmt_length = 16; - header.audio_format = 1; - header.num_channels = 1; - header.sample_rate = rate; - header.byte_rate = rate * width; - header.block_align = width; - header.bits_per_sample = width * 8; - header.data_length = readSize * width; -} - void WiFiEvent(WiFiEvent_t event) { switch (event) { diff --git a/PlatformIO/src/device.h b/PlatformIO/src/device.h index 788903a..a66fc35 100644 --- a/PlatformIO/src/device.h +++ b/PlatformIO/src/device.h @@ -1,5 +1,7 @@ #pragma once +#include + int hotword_colors[4] = {0, 255, 0, 0}; int idle_colors[4] = {0, 0, 255, 0}; int wifi_conn_colors[4] = {0, 0, 255, 0}; @@ -16,6 +18,17 @@ enum { COLORS_TTS = 5, COLORS_ERROR = 6 }; + +std::map ColorMap = { + {COLORS_IDLE, idle_colors }, + {COLORS_HOTWORD, hotword_colors }, + {COLORS_WIFI_CONNECTED, wifi_conn_colors }, + {COLORS_WIFI_DISCONNECTED, wifi_disc_colors }, + {COLORS_OTA, ota_colors }, + {COLORS_TTS, tts_colors }, + {COLORS_ERROR, error_colors } +}; + enum DeviceMode { MODE_UNUSED = -1, // indicates the no explicit mode change has been stored yet MODE_MIC = 0, @@ -30,6 +43,13 @@ enum AmpOut { AMP_OUT_BOTH = 2, }; +enum AnimationMode { + SOLID = 0, + RUNNING = 1, + PULSING = 2, + BLINKING = 3 +}; + class Device { protected: // for derived classes which switch between read and write mode, we store there which mode is active. Otherwise it should remain as MODE_UNUSED @@ -40,7 +60,11 @@ class Device { //If your device has leds, override these methods to set the colors and brightness virtual void updateColors(int colors) {}; //You can create some animation here - virtual void animate(int colors) {}; + virtual void animate(int colors, int mode) {}; + virtual void animateRunning(int colors) {}; + virtual void animatePulsing(int colors) {}; + virtual void animateBlinking(int colors) {}; + // virtual void updateBrightness(int brightness) {}; //It may be needed to switch between read and write, i.e. if the need the same PIN //Override both methods @@ -62,6 +86,11 @@ class Device { // how many different output configurations does this devices support (1 = single output channel, 2 = 2 output channels, i.e. speaker or headphone, 3 = speaker, headphone, speaker + headphone) virtual int numAmpOutConfigurations() { return 2; }; + + virtual bool animationSupported() { return false; }; + virtual bool runningSupported() { return false; }; + virtual bool pulsingSupported() { return false; }; + virtual bool blinkingSupported() { return false; }; // //You can override these in your device int readSize = 256; diff --git a/PlatformIO/src/devices/MatrixVoice.hpp b/PlatformIO/src/devices/MatrixVoice.hpp index f27965a..06de57b 100644 --- a/PlatformIO/src/devices/MatrixVoice.hpp +++ b/PlatformIO/src/devices/MatrixVoice.hpp @@ -44,7 +44,8 @@ class MatrixVoice : public Device public: MatrixVoice(); void init(); - void animate(int colors); + void animate(int colors, int mode); + void animateRunning(int colors); void updateColors(int colors); void updateBrightness(int brightness); void muteOutput(bool mute); @@ -53,6 +54,10 @@ class MatrixVoice : public Device bool readAudio(uint8_t *data, size_t size); void writeAudio(uint8_t *data, size_t size, size_t *bytes_written); void ampOutput(int output); + bool animationSupported() { return true; }; + bool runningSupported() { return true; }; + bool pulsingSupported() { return true; }; + bool blinkingSupported() { return true; }; int readSize = 512; int writeSize = 1024; int width = 2; @@ -105,57 +110,24 @@ void MatrixVoice::updateBrightness(int brightness) { MatrixVoice::brightness = brightness * 90 / 100 + 10; } -void MatrixVoice::animate(int colors) { +void MatrixVoice::animate(int colors, int mode) { + switch (mode) + { + case AnimationMode::RUNNING: + animateRunning(colors); + break; + default: + break; + } +} + +void MatrixVoice::animateRunning(int colors) { currentMillis = millis(); if (currentMillis - startMillis > 10) { - int r = 0; - int g = 0; - int b = 0; - int w = 0; - switch (colors) { - case COLORS_TTS: - r = tts_colors[0]; - g = tts_colors[1]; - b = tts_colors[2]; - w = tts_colors[3]; - break; - case COLORS_ERROR: - r = error_colors[0]; - g = error_colors[1]; - b = error_colors[2]; - w = error_colors[3]; - break; - case COLORS_HOTWORD: - r = hotword_colors[0]; - g = hotword_colors[1]; - b = hotword_colors[2]; - w = hotword_colors[3]; - break; - case COLORS_WIFI_CONNECTED: - r = wifi_conn_colors[0]; - g = wifi_conn_colors[1]; - b = wifi_conn_colors[2]; - w = wifi_conn_colors[3]; - break; - case COLORS_IDLE: - r = idle_colors[0]; - g = idle_colors[1]; - b = idle_colors[2]; - w = idle_colors[3]; - break; - case COLORS_WIFI_DISCONNECTED: - r = wifi_disc_colors[0]; - g = wifi_disc_colors[1]; - b = wifi_disc_colors[2]; - w = wifi_disc_colors[3]; - break; - case COLORS_OTA: - r = ota_colors[0]; - g = ota_colors[1]; - b = ota_colors[2]; - w = ota_colors[3]; - break; - } + int r = ColorMap[colors][0]; + int g = ColorMap[colors][1]; + int b = ColorMap[colors][2]; + int w = ColorMap[colors][3]; startMillis = millis(); for (int i = 0; i < image1d.leds.size(); i++) { int red = ((i + 1) * brightness / image1d.leds.size()) * r / 100; @@ -174,54 +146,10 @@ void MatrixVoice::animate(int colors) { } void MatrixVoice::updateColors(int colors) { - int r = 0; - int g = 0; - int b = 0; - int w = 0; - switch (colors) { - case COLORS_TTS: - r = tts_colors[0]; - g = tts_colors[1]; - b = tts_colors[2]; - w = tts_colors[3]; - break; - case COLORS_ERROR: - r = error_colors[0]; - g = error_colors[1]; - b = error_colors[2]; - w = error_colors[3]; - break; - case COLORS_HOTWORD: - r = hotword_colors[0]; - g = hotword_colors[1]; - b = hotword_colors[2]; - w = hotword_colors[3]; - break; - case COLORS_WIFI_CONNECTED: - r = wifi_conn_colors[0]; - g = wifi_conn_colors[1]; - b = wifi_conn_colors[2]; - w = wifi_conn_colors[3]; - break; - case COLORS_IDLE: - r = idle_colors[0]; - g = idle_colors[1]; - b = idle_colors[2]; - w = idle_colors[3]; - break; - case COLORS_WIFI_DISCONNECTED: - r = wifi_disc_colors[0]; - g = wifi_disc_colors[1]; - b = wifi_disc_colors[2]; - w = wifi_disc_colors[3]; - break; - case COLORS_OTA: - r = ota_colors[0]; - g = ota_colors[1]; - b = ota_colors[2]; - w = ota_colors[3]; - break; - } + int r = ColorMap[colors][0]; + int g = ColorMap[colors][1]; + int b = ColorMap[colors][2]; + int w = ColorMap[colors][3]; r = floor(MatrixVoice::brightness * r / 100); r = pgm_read_byte(&gamma8[r]); g = floor(MatrixVoice::brightness * g / 100); diff --git a/PlatformIO/src/index_html.h b/PlatformIO/src/index_html.h index 090da18..b2d6b37 100644 --- a/PlatformIO/src/index_html.h +++ b/PlatformIO/src/index_html.h @@ -102,6 +102,15 @@ input::-moz-focus-inner,input::-moz-focus-outer {border: 0;} 0 +
+ + +
From 2e9b82409810d3ead529aaf6b5a4c26181f80c3e Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Mon, 25 Oct 2021 19:59:21 +0200 Subject: [PATCH 09/15] implment blink & pulse (1) --- PlatformIO/src/General.hpp | 6 +-- PlatformIO/src/device.h | 6 +-- PlatformIO/src/devices/MatrixVoice.hpp | 75 +++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 7 deletions(-) diff --git a/PlatformIO/src/General.hpp b/PlatformIO/src/General.hpp index a28d803..4efc097 100644 --- a/PlatformIO/src/General.hpp +++ b/PlatformIO/src/General.hpp @@ -196,9 +196,9 @@ const std::map processor_values = { {"SITEID", []() -> String { return config.siteid.c_str(); } }, {"ANIMATIONSUPPORT", []() -> String { return device->animationSupported() ? "block" : "none"; } }, {"ANIM_SOLID", []() -> String { return (config.animation == SOLID) ? "selected" : ""; } }, - {"ANIM_RUNNING", []() -> String { return device->runningSupported() ? (config.animation == RUNNING) ? "selected" : "" : "hidden"; } }, - {"ANIM_PULSING", []() -> String { return device->pulsingSupported() ? (config.animation == PULSING) ? "selected" : "" : "hidden"; } }, - {"ANIM_BLINKING", []() -> String { return device->blinkingSupported() ? (config.animation == BLINKING) ? "selected" : "" : "hidden"; } }, + {"ANIM_RUNNING", []() -> String { return device->runningSupported() ? (config.animation == RUN) ? "selected" : "" : "hidden"; } }, + {"ANIM_PULSING", []() -> String { return device->pulsingSupported() ? (config.animation == PULSE) ? "selected" : "" : "hidden"; } }, + {"ANIM_BLINKING", []() -> String { return device->blinkingSupported() ? (config.animation == BLINK) ? "selected" : "" : "hidden"; } }, }; // this function supplies template variables to the template engine diff --git a/PlatformIO/src/device.h b/PlatformIO/src/device.h index a66fc35..0e96534 100644 --- a/PlatformIO/src/device.h +++ b/PlatformIO/src/device.h @@ -45,9 +45,9 @@ enum AmpOut { enum AnimationMode { SOLID = 0, - RUNNING = 1, - PULSING = 2, - BLINKING = 3 + RUN = 1, + PULSE = 2, + BLINK = 3 }; class Device { diff --git a/PlatformIO/src/devices/MatrixVoice.hpp b/PlatformIO/src/devices/MatrixVoice.hpp index 06de57b..7ac5a95 100644 --- a/PlatformIO/src/devices/MatrixVoice.hpp +++ b/PlatformIO/src/devices/MatrixVoice.hpp @@ -46,6 +46,8 @@ class MatrixVoice : public Device void init(); void animate(int colors, int mode); void animateRunning(int colors); + void animateBlinking(int colors); + void animatePulsing(int colors); void updateColors(int colors); void updateBrightness(int brightness); void muteOutput(bool mute); @@ -82,6 +84,7 @@ class MatrixVoice : public Device int count = 0; int position = 0; long currentMillis, startMillis; + bool ledsOn = true; }; MatrixVoice::MatrixVoice() @@ -113,9 +116,15 @@ void MatrixVoice::updateBrightness(int brightness) { void MatrixVoice::animate(int colors, int mode) { switch (mode) { - case AnimationMode::RUNNING: + case AnimationMode::RUN: animateRunning(colors); break; + case AnimationMode::BLINK: + animateBlinking(colors); + break; + case AnimationMode::PULSE: + animatePulsing(colors); + break; default: break; } @@ -145,6 +154,70 @@ void MatrixVoice::animateRunning(int colors) { } } +void MatrixVoice::animateBlinking(int colors) { + currentMillis = millis(); + if (currentMillis - startMillis > 500) { + int r = ColorMap[colors][0]; + int g = ColorMap[colors][1]; + int b = ColorMap[colors][2]; + int w = ColorMap[colors][3]; + r = floor(MatrixVoice::brightness * r / 100); + r = pgm_read_byte(&gamma8[r]); + g = floor(MatrixVoice::brightness * g / 100); + g = pgm_read_byte(&gamma8[g]); + b = floor(MatrixVoice::brightness * b / 100); + b = pgm_read_byte(&gamma8[b]); + w = floor(MatrixVoice::brightness * w / 100); + w = pgm_read_byte(&gamma8[w]); + if (!ledsOn) { + r = 0; + g = 0; + b = 0; + w = 0; + } + startMillis = millis(); + ledsOn = !ledsOn; + for (matrix_hal::LedValue &led : image1d.leds) { + led.red = r; + led.green = g; + led.blue = b; + led.white = w; + } + everloop.Write(&image1d); + } +} + +void MatrixVoice::animatePulsing(int colors) { + //This is one is crap for now + currentMillis = millis(); + if (currentMillis - startMillis > 10) { + int r = ColorMap[colors][0]; + int g = ColorMap[colors][1]; + int b = ColorMap[colors][2]; + int w = ColorMap[colors][3]; + position = position < 15 ? 15 : position; + position = position > 255 ? 15 : position; + b = position; + r = floor(b * r / 100); + r = pgm_read_byte(&gamma8[r]); + g = floor(b * g / 100); + g = pgm_read_byte(&gamma8[g]); + b = floor(b * b / 100); + b = pgm_read_byte(&gamma8[b]); + w = floor(b * w / 100); + w = pgm_read_byte(&gamma8[w]); + startMillis = millis(); + for (matrix_hal::LedValue &led : image1d.leds) { + led.red = r; + led.green = g; + led.blue = b; + led.white = w; + } + position++; + everloop.Write(&image1d); + } +} + void MatrixVoice::updateColors(int colors) { int r = ColorMap[colors][0]; int g = ColorMap[colors][1]; From 57fd91a913f76d21b87e2b7ed383e2aabb4750bf Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Tue, 26 Oct 2021 23:00:18 +0200 Subject: [PATCH 10/15] Update read me and some refactoring --- PlatformIO/src/Satellite.cpp | 11 ++++++----- PlatformIO/src/StateMachine.hpp | 6 ++++++ PlatformIO/src/devices/TAudio.hpp | 18 +----------------- README.md | 14 +++++++++----- 4 files changed, 22 insertions(+), 27 deletions(-) diff --git a/PlatformIO/src/Satellite.cpp b/PlatformIO/src/Satellite.cpp index a0c407a..55c40b6 100644 --- a/PlatformIO/src/Satellite.cpp +++ b/PlatformIO/src/Satellite.cpp @@ -1,13 +1,12 @@ /* ************************************************************************* * - Matrix Voice Audio Streamer + ESP32 Rhasspy Satellite - This program is written to be a streaming audio server running on the Matrix - Voice. This is typically used for Rhasspy. + This program is written to be a streaming audio microphone for Rhasspy. See https://rhasspy.readthedocs.io/en/latest/ for more information Author: Paul Romkes - Date: Januari 2021 - Version: 7.0 + Date: October 2021 + Version: 7.8 Changelog: ========== @@ -101,6 +100,8 @@ - Added ESP32_POE_ISO and TAUDIO - Added animation function, work in progress - Added Speaking state for animation preparation (works for matrixvoice) + v7.8 + - Added animations during audio playback, every device has those animation defaulted to not supported * ************************************************************************ */ diff --git a/PlatformIO/src/StateMachine.hpp b/PlatformIO/src/StateMachine.hpp index cd4488b..bda5ad8 100644 --- a/PlatformIO/src/StateMachine.hpp +++ b/PlatformIO/src/StateMachine.hpp @@ -663,6 +663,12 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties ColorMap[COLORS_OTA][2] = root["update"][2]; ColorMap[COLORS_OTA][3] = root["update"][3]; } + if (root.containsKey("error")) { + ColorMap[COLORS_ERROR][0] = root["error"][0]; + ColorMap[COLORS_ERROR][1] = root["error"][1]; + ColorMap[COLORS_ERROR][2] = root["error"][2]; + ColorMap[COLORS_ERROR][3] = root["error"][3]; + } if (saveNeeded) { saveConfiguration(configfile, config); } diff --git a/PlatformIO/src/devices/TAudio.hpp b/PlatformIO/src/devices/TAudio.hpp index 88928b4..8b3d7a4 100644 --- a/PlatformIO/src/devices/TAudio.hpp +++ b/PlatformIO/src/devices/TAudio.hpp @@ -134,23 +134,7 @@ void TAudio::InitI2S() { } void TAudio::updateColors(int colors) { - switch (colors) { - case COLORS_HOTWORD: - strip.ClearTo(RgbColor(RgbwColor(hotword_colors[0],hotword_colors[1],hotword_colors[2],hotword_colors[3])).Dim(brightness)); - break; - case COLORS_WIFI_CONNECTED: - strip.ClearTo(RgbColor(RgbwColor(wifi_conn_colors[0],wifi_conn_colors[1],wifi_conn_colors[2],wifi_conn_colors[3])).Dim(brightness)); - break; - case COLORS_IDLE: - strip.ClearTo(RgbColor(RgbwColor(idle_colors[0],idle_colors[1],idle_colors[2],idle_colors[3])).Dim(brightness)); - break; - case COLORS_WIFI_DISCONNECTED: - strip.ClearTo(RgbColor(RgbwColor(wifi_disc_colors[0],wifi_disc_colors[1],wifi_disc_colors[2],wifi_disc_colors[3])).Dim(brightness)); - break; - case COLORS_OTA: - strip.ClearTo(RgbColor(RgbwColor(ota_colors[0],ota_colors[1],ota_colors[2],ota_colors[3])).Dim(brightness)); - break; - } + strip.ClearTo(RgbColor(RgbwColor(ColorMap[colors][0],ColorMap[colors][1],ColorMap[colors][2],ColorMap[colors][3])).Dim(brightness)); strip.Show(); } diff --git a/README.md b/README.md index fc470ac..777efd3 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Support for Snips is dropped. - Supports multiple devices, and you are welcomed to add more devices. Read futher below as to how. - For the Matrix Voice, during first flash a Raspberri Pi is needed, afer that OTA can be used - LED Support +- Various colors per state - OTA Updating - Dynamic brightness and colors for idle, hotword and disconnected - Mute / unmute microphones via MQTT @@ -18,8 +19,9 @@ Support for Snips is dropped. - Adjust gain via MQTT (if supported by device) - Reboot device by sending hashed password - Configuration possible in browser -- Audio playback, recommended not higher than 441000 samplerate (see Known Issues) +- Audio playback, recommended not higher than 16000 samplerate (see Known Issues) - Hardware button to start session (if supported by device) +- Animations when audio is played ## Getting started [Matrix Voice](matrixvoice.md) @@ -40,7 +42,7 @@ The ESP32 Satellite is subscribed to various topics. The topic SITEID/led, where SITEID is the name you have given the device in settings.ini, is used for commands concerning the leds on the device. When publishing to this topic, the led colors and brightness can be altered without coding and will be saved to a config file -The message can contain 7 keys: +The message can contain 10 keys: - brightness: integer value between 0 and 100 (%) - hotword_brightness: integer value between 0 and 100 (%) @@ -49,6 +51,9 @@ The message can contain 7 keys: - update: array of 4 codes: [red,green,blue,white], ranging 0-255 - wifi_disconnect: array of 4 codes: [red,green,blue,white], ranging 0-255 - wifi_connect: array of 4 codes: [red,green,blue,white], ranging 0-255 +- tts: array of 4 codes: [red,green,blue,white], ranging 0-255 +- error: array of 4 codes: [red,green,blue,white], ranging 0-255 +- animation: integer value of 0..3 (SOLID, RUNNING, PULSE, BLINK). Only if device supports it. Example: {"brightness":20,"idle":[240,210,17,0],"hotword":[173,17,240,0]} @@ -64,10 +69,9 @@ Restart the device by publishing {"passwordhash":"yourpasswordhash"} to SITEID/r ## Known issues -- Uploading sometimes fails or an error is thrown when the uploading is done. Lower the uploadspeed to fix it - Audio playback with sample rate higher than 44100 can lead to jitter due to network. Recommended is to use a samplerate of 16000 or 22050 -- Audio playback with matrix voice is not good, code needs to resample to 44100. WIP -- Update colors do not work yet +- Audio playback with matrix voice is not good, code needs to resample to 44100. 16000 Mono or Stereo should be fine +- Some settings (like the colors if update via MQTT) do not survive a reboot yet # Adding devices From effafaf04bf03e1f8798428a71503b018b432756 Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Thu, 28 Oct 2021 21:09:05 +0200 Subject: [PATCH 11/15] implment animtions for M5 and Matrix Voice --- PlatformIO/src/devices/M5AtomEcho.hpp | 74 +++++++++++++++++------ PlatformIO/src/devices/MatrixVoice.hpp | 81 ++++++++------------------ 2 files changed, 80 insertions(+), 75 deletions(-) diff --git a/PlatformIO/src/devices/M5AtomEcho.hpp b/PlatformIO/src/devices/M5AtomEcho.hpp index 3078f56..e71eccf 100644 --- a/PlatformIO/src/devices/M5AtomEcho.hpp +++ b/PlatformIO/src/devices/M5AtomEcho.hpp @@ -17,6 +17,9 @@ class M5AtomEcho : public Device public: M5AtomEcho(); void init(); + void animate(int colors, int mode); + void animateBlinking(int colors); + void animatePulsing(int colors); void updateColors(int colors); void updateBrightness(int brightness); void setReadMode(); @@ -25,9 +28,17 @@ class M5AtomEcho : public Device bool readAudio(uint8_t *data, size_t size); bool isHotwordDetected(); int numAmpOutConfigurations() { return 1; }; + bool animationSupported() { return true; }; + bool runningSupported() { return false; }; + bool pulsingSupported() { return true; }; + bool blinkingSupported() { return true; }; private: void InitI2SSpeakerOrMic(int mode); + long currentMillis, startMillis; + bool ledsOn = true; + bool directionDown = false; + int brightness, pulse; }; M5AtomEcho::M5AtomEcho() @@ -37,6 +48,8 @@ M5AtomEcho::M5AtomEcho() void M5AtomEcho::init() { M5.begin(true,true,true); + currentMillis = millis(); + startMillis = millis(); }; bool M5AtomEcho::isHotwordDetected() { @@ -46,27 +59,54 @@ bool M5AtomEcho::isHotwordDetected() { void M5AtomEcho::updateColors(int colors) { - switch (colors) { - case COLORS_HOTWORD: - M5.dis.drawpix(0, CRGB(hotword_colors[1],hotword_colors[0],hotword_colors[2])); - break; - case COLORS_WIFI_CONNECTED: - M5.dis.drawpix(0, CRGB(wifi_conn_colors[0],wifi_conn_colors[1],wifi_conn_colors[2])); - break; - case COLORS_IDLE: - M5.dis.drawpix(0, CRGB(idle_colors[0],idle_colors[1],idle_colors[2])); - break; - case COLORS_WIFI_DISCONNECTED: - M5.dis.drawpix(0, CRGB(wifi_disc_colors[1],wifi_disc_colors[0],wifi_disc_colors[2])); - break; - case COLORS_OTA: - M5.dis.drawpix(0, CRGB(ota_colors[3],ota_colors[3],ota_colors[3])); - break; - } + //Red and Green seem to be switched, we also need to map the white + float alpha = 0.6 * (1.0 - (1.0 - ColorMap[colors][3]) * (1.0 - ColorMap[colors][3])); + int r = (1.0 - alpha) * ColorMap[colors][1] + alpha; + int g = (1.0 - alpha) * ColorMap[colors][0] + alpha; + int b = (1.0 - alpha) * ColorMap[colors][2] + alpha; + M5.dis.drawpix(0, CRGB(r, g, b)); }; void M5AtomEcho::updateBrightness(int brightness) { M5.dis.setBrightness(brightness); + M5AtomEcho::pulse = brightness; + M5AtomEcho::brightness = 0; +} + +void M5AtomEcho::animate(int colors, int mode) { + switch (mode) + { + case AnimationMode::BLINK: + animateBlinking(colors); + break; + case AnimationMode::PULSE: + animatePulsing(colors); + break; + default: + break; + } +} + +void M5AtomEcho::animatePulsing(int colors) { + currentMillis = millis(); + if (currentMillis - startMillis > 5) { + if (M5AtomEcho::pulse > M5AtomEcho::brightness) { directionDown = true; } + M5AtomEcho::pulse = directionDown ? M5AtomEcho::pulse - 5 : M5AtomEcho::pulse + 5; + if (M5AtomEcho::pulse < 5) { directionDown = false; } + startMillis = millis(); + M5.dis.setBrightness(M5AtomEcho::pulse); + updateColors(colors); + } +} + +void M5AtomEcho::animateBlinking(int colors) { + currentMillis = millis(); + if (currentMillis - startMillis > 300) { + M5.dis.setBrightness(ledsOn ? M5AtomEcho::brightness : 0); + ledsOn = !ledsOn; + startMillis = millis(); + updateColors(colors); + } } void M5AtomEcho::InitI2SSpeakerOrMic(int mode) diff --git a/PlatformIO/src/devices/MatrixVoice.hpp b/PlatformIO/src/devices/MatrixVoice.hpp index 7ac5a95..4450eb1 100644 --- a/PlatformIO/src/devices/MatrixVoice.hpp +++ b/PlatformIO/src/devices/MatrixVoice.hpp @@ -73,11 +73,12 @@ class MatrixVoice : public Device void playBytes(int16_t* input, uint32_t length); void interleave(const int16_t * in_L, const int16_t * in_R, int16_t * out, const size_t num_samples); bool FIFOFlush(); + void updateColors(int colors, bool usePulse); uint16_t GetFIFOStatus(); uint32_t PCM_sampling_frequency = 16000; int fifoSize = 4096; int sampleRate, bitDepth, numChannels; - int brightness = 15; + int brightness, pulse = 15; float sample_time = 1.0 / 16000; uint32_t spiLength = 1024; int sleep = int(spiLength * sample_time * 1000); @@ -85,6 +86,7 @@ class MatrixVoice : public Device int position = 0; long currentMillis, startMillis; bool ledsOn = true; + bool directionDown = false; }; MatrixVoice::MatrixVoice() @@ -111,6 +113,7 @@ void MatrixVoice::updateBrightness(int brightness) { // all values below 10 is read as 0 in gamma8, we map 0 to 10 if (brightness > 100) { brightness = 100; } MatrixVoice::brightness = brightness * 90 / 100 + 10; + MatrixVoice::pulse = brightness * 90 / 100 + 10; } void MatrixVoice::animate(int colors, int mode) { @@ -156,80 +159,38 @@ void MatrixVoice::animateRunning(int colors) { void MatrixVoice::animateBlinking(int colors) { currentMillis = millis(); - if (currentMillis - startMillis > 500) { - int r = ColorMap[colors][0]; - int g = ColorMap[colors][1]; - int b = ColorMap[colors][2]; - int w = ColorMap[colors][3]; - r = floor(MatrixVoice::brightness * r / 100); - r = pgm_read_byte(&gamma8[r]); - g = floor(MatrixVoice::brightness * g / 100); - g = pgm_read_byte(&gamma8[g]); - b = floor(MatrixVoice::brightness * b / 100); - b = pgm_read_byte(&gamma8[b]); - w = floor(MatrixVoice::brightness * w / 100); - w = pgm_read_byte(&gamma8[w]); - if (!ledsOn) { - r = 0; - g = 0; - b = 0; - w = 0; - } - startMillis = millis(); + if (currentMillis - startMillis > 300) { + MatrixVoice::pulse = ledsOn ? MatrixVoice::brightness : 0; ledsOn = !ledsOn; - for (matrix_hal::LedValue &led : image1d.leds) { - led.red = r; - led.green = g; - led.blue = b; - led.white = w; - } - everloop.Write(&image1d); + startMillis = millis(); + updateColors(colors, true); } } void MatrixVoice::animatePulsing(int colors) { - //This is one is crap for now currentMillis = millis(); - if (currentMillis - startMillis > 10) { - int r = ColorMap[colors][0]; - int g = ColorMap[colors][1]; - int b = ColorMap[colors][2]; - int w = ColorMap[colors][3]; - position = position < 15 ? 15 : position; - position = position > 255 ? 15 : position; - b = position; - r = floor(b * r / 100); - r = pgm_read_byte(&gamma8[r]); - g = floor(b * g / 100); - g = pgm_read_byte(&gamma8[g]); - b = floor(b * b / 100); - b = pgm_read_byte(&gamma8[b]); - w = floor(b * w / 100); - w = pgm_read_byte(&gamma8[w]); + if (currentMillis - startMillis > 5) { + if (MatrixVoice::pulse > MatrixVoice::brightness) { directionDown = true; } + MatrixVoice::pulse = directionDown ? MatrixVoice::pulse - 5 : MatrixVoice::pulse + 5; + if (MatrixVoice::pulse < 5) { directionDown = false; } startMillis = millis(); - for (matrix_hal::LedValue &led : image1d.leds) { - led.red = r; - led.green = g; - led.blue = b; - led.white = w; - } - position++; - everloop.Write(&image1d); + updateColors(colors, true); } } -void MatrixVoice::updateColors(int colors) { +void MatrixVoice::updateColors(int colors, bool usePulse) { int r = ColorMap[colors][0]; int g = ColorMap[colors][1]; int b = ColorMap[colors][2]; int w = ColorMap[colors][3]; - r = floor(MatrixVoice::brightness * r / 100); + int brightness = usePulse ? MatrixVoice::pulse : MatrixVoice::brightness; + r = floor(brightness * r / 100); r = pgm_read_byte(&gamma8[r]); - g = floor(MatrixVoice::brightness * g / 100); + g = floor(brightness * g / 100); g = pgm_read_byte(&gamma8[g]); - b = floor(MatrixVoice::brightness * b / 100); + b = floor(brightness * b / 100); b = pgm_read_byte(&gamma8[b]); - w = floor(MatrixVoice::brightness * w / 100); + w = floor(brightness * w / 100); w = pgm_read_byte(&gamma8[w]); for (matrix_hal::LedValue &led : image1d.leds) { led.red = r; @@ -240,6 +201,10 @@ void MatrixVoice::updateColors(int colors) { everloop.Write(&image1d); } +void MatrixVoice::updateColors(int colors) { + updateColors(colors, false); +} + void MatrixVoice::muteOutput(bool mute) { int16_t muteValue = mute ? 1 : 0; wb.SpiWrite(matrix_hal::kConfBaseAddress+10,(const uint8_t *)(&muteValue), sizeof(uint16_t)); From 6731539199fa4ccb4241e5c59f54f76732f96c7b Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Thu, 28 Oct 2021 21:35:58 +0200 Subject: [PATCH 12/15] only reboot when needed --- PlatformIO/src/General.hpp | 22 +++++++++++++--------- PlatformIO/src/StateMachine.hpp | 7 +++++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/PlatformIO/src/General.hpp b/PlatformIO/src/General.hpp index 4efc097..4f124d6 100644 --- a/PlatformIO/src/General.hpp +++ b/PlatformIO/src/General.hpp @@ -68,7 +68,8 @@ bool mqttInitialized = false; int retryCount = 0; int I2SMode = -1; bool mqttConnected = false; -bool DEBUG = true; +bool DEBUG = false; +bool configChanged = false; std::string audioFrameTopic = std::string("hermes/audioServer/") + config.siteid + std::string("/audioFrame"); std::string playBytesTopic = std::string("hermes/audioServer/") + config.siteid + std::string("/playBytes/#"); @@ -236,6 +237,7 @@ template bool processParam(AsyncWebParameter *p, const char* p_name void handleFSf ( AsyncWebServerRequest* request, const String& route ) { AsyncWebServerResponse *response ; bool saveNeeded = false; + bool rebootNeeded = false; if ( route.indexOf ( "index.html" ) >= 0 ) // Index page is in PROGMEM { @@ -247,11 +249,11 @@ void handleFSf ( AsyncWebServerRequest* request, const String& route ) { AsyncWebParameter* p = request->getParam(i); Serial.printf("Parameter %s, value %s\r\n", p->name().c_str(), p->value().c_str()); - saveNeeded |= processParam(p, "siteid", config.siteid); - saveNeeded |= processParam(p, "mqtt_host", config.mqtt_host); - saveNeeded |= processParam(p, "mqtt_pass", config.mqtt_pass); - saveNeeded |= processParam(p, "mqtt_user", config.mqtt_user); - saveNeeded |= processParam(p, "mqtt_port", config.mqtt_port); + rebootNeeded |= processParam(p, "siteid", config.siteid); + rebootNeeded |= processParam(p, "mqtt_host", config.mqtt_host); + rebootNeeded |= processParam(p, "mqtt_pass", config.mqtt_pass); + rebootNeeded |= processParam(p, "mqtt_user", config.mqtt_user); + rebootNeeded |= processParam(p, "mqtt_port", config.mqtt_port); saveNeeded |= processParam(p, "mute_input", config.mute_input); saveNeeded |= processParam(p, "mute_output", config.mute_output); saveNeeded |= processParam(p, "amp_output", config.amp_output); @@ -277,14 +279,16 @@ void handleFSf ( AsyncWebServerRequest* request, const String& route ) { config.mute_output = false; saveNeeded = true; } - if (saveNeeded) { + if (saveNeeded || rebootNeeded) { Serial.println("Settings changed, saving configuration"); saveConfiguration(configfile, config); + loadConfiguration(configfile, config); + configChanged = true; } else { Serial.println("No settings changed"); } } - if (saveNeeded) { + if (rebootNeeded) { response = request->beginResponse_P ( 200, "text/html", "Rebooting...

Configuration saved, rebooting!

"); } else { response = request->beginResponse_P ( 200, "text/html", index_html, processor ); @@ -296,7 +300,7 @@ void handleFSf ( AsyncWebServerRequest* request, const String& route ) { request->send ( response ) ; - if (saveNeeded) { + if (rebootNeeded) { Serial.println("Rebooting!"); ESP.restart(); } diff --git a/PlatformIO/src/StateMachine.hpp b/PlatformIO/src/StateMachine.hpp index bda5ad8..c64e1d1 100644 --- a/PlatformIO/src/StateMachine.hpp +++ b/PlatformIO/src/StateMachine.hpp @@ -158,6 +158,13 @@ class Idle : public StateMachine std::string message = "{\"init\":{\"type\":\"action\",\"canBeEnqueued\": false},\"siteId\":\"" + std::string(config.siteid) + "\"}"; asyncClient.publish("hermes/dialogueManager/startSession", 0, false, message.c_str()); } + if (configChanged) { + configChanged = false; + device->updateBrightness(config.brightness); + xSemaphoreTake(wbSemaphore, portMAX_DELAY); + device->updateColors(current_colors); + xSemaphoreGive(wbSemaphore); + } } void react(ListeningEvent const &) override { From 676850f9aee637a3295699fe928e342453638f5a Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Thu, 28 Oct 2021 22:29:49 +0200 Subject: [PATCH 13/15] Fix Matrix Voice reboots --- PlatformIO/src/General.hpp | 1 - PlatformIO/src/StateMachine.hpp | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/PlatformIO/src/General.hpp b/PlatformIO/src/General.hpp index 4f124d6..8b0b99a 100644 --- a/PlatformIO/src/General.hpp +++ b/PlatformIO/src/General.hpp @@ -282,7 +282,6 @@ void handleFSf ( AsyncWebServerRequest* request, const String& route ) { if (saveNeeded || rebootNeeded) { Serial.println("Settings changed, saving configuration"); saveConfiguration(configfile, config); - loadConfiguration(configfile, config); configChanged = true; } else { Serial.println("No settings changed"); diff --git a/PlatformIO/src/StateMachine.hpp b/PlatformIO/src/StateMachine.hpp index c64e1d1..767ef27 100644 --- a/PlatformIO/src/StateMachine.hpp +++ b/PlatformIO/src/StateMachine.hpp @@ -160,10 +160,7 @@ class Idle : public StateMachine } if (configChanged) { configChanged = false; - device->updateBrightness(config.brightness); - xSemaphoreTake(wbSemaphore, portMAX_DELAY); - device->updateColors(current_colors); - xSemaphoreGive(wbSemaphore); + transit(); } } From d20d537b42b20b0c12529885e1c8c41eb58e281b Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Thu, 28 Oct 2021 22:31:50 +0200 Subject: [PATCH 14/15] Update version info --- PlatformIO/src/Satellite.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/PlatformIO/src/Satellite.cpp b/PlatformIO/src/Satellite.cpp index 55c40b6..ba08406 100644 --- a/PlatformIO/src/Satellite.cpp +++ b/PlatformIO/src/Satellite.cpp @@ -102,6 +102,7 @@ - Added Speaking state for animation preparation (works for matrixvoice) v7.8 - Added animations during audio playback, every device has those animation defaulted to not supported + - Implemented for M5 Atom echo and Matrix Voice * ************************************************************************ */ From 4b516dbb31f0d706d54e0f71e7a4afacfb6620fd Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Thu, 28 Oct 2021 22:38:32 +0200 Subject: [PATCH 15/15] Fix indentation --- PlatformIO/src/devices/M5AtomEcho.hpp | 58 ++-- PlatformIO/src/devices/MatrixVoice.hpp | 438 ++++++++++++------------- 2 files changed, 248 insertions(+), 248 deletions(-) diff --git a/PlatformIO/src/devices/M5AtomEcho.hpp b/PlatformIO/src/devices/M5AtomEcho.hpp index e71eccf..1d44494 100644 --- a/PlatformIO/src/devices/M5AtomEcho.hpp +++ b/PlatformIO/src/devices/M5AtomEcho.hpp @@ -17,8 +17,8 @@ class M5AtomEcho : public Device public: M5AtomEcho(); void init(); - void animate(int colors, int mode); - void animateBlinking(int colors); + void animate(int colors, int mode); + void animateBlinking(int colors); void animatePulsing(int colors); void updateColors(int colors); void updateBrightness(int brightness); @@ -35,8 +35,8 @@ class M5AtomEcho : public Device private: void InitI2SSpeakerOrMic(int mode); - long currentMillis, startMillis; - bool ledsOn = true; + long currentMillis, startMillis; + bool ledsOn = true; bool directionDown = false; int brightness, pulse; }; @@ -48,8 +48,8 @@ M5AtomEcho::M5AtomEcho() void M5AtomEcho::init() { M5.begin(true,true,true); - currentMillis = millis(); - startMillis = millis(); + currentMillis = millis(); + startMillis = millis(); }; bool M5AtomEcho::isHotwordDetected() { @@ -74,39 +74,39 @@ void M5AtomEcho::updateBrightness(int brightness) { } void M5AtomEcho::animate(int colors, int mode) { - switch (mode) - { - case AnimationMode::BLINK: - animateBlinking(colors); - break; - case AnimationMode::PULSE: - animatePulsing(colors); - break; - default: - break; - } + switch (mode) + { + case AnimationMode::BLINK: + animateBlinking(colors); + break; + case AnimationMode::PULSE: + animatePulsing(colors); + break; + default: + break; + } } void M5AtomEcho::animatePulsing(int colors) { - currentMillis = millis(); - if (currentMillis - startMillis > 5) { - if (M5AtomEcho::pulse > M5AtomEcho::brightness) { directionDown = true; } - M5AtomEcho::pulse = directionDown ? M5AtomEcho::pulse - 5 : M5AtomEcho::pulse + 5; - if (M5AtomEcho::pulse < 5) { directionDown = false; } + currentMillis = millis(); + if (currentMillis - startMillis > 5) { + if (M5AtomEcho::pulse > M5AtomEcho::brightness) { directionDown = true; } + M5AtomEcho::pulse = directionDown ? M5AtomEcho::pulse - 5 : M5AtomEcho::pulse + 5; + if (M5AtomEcho::pulse < 5) { directionDown = false; } startMillis = millis(); M5.dis.setBrightness(M5AtomEcho::pulse); - updateColors(colors); - } + updateColors(colors); + } } void M5AtomEcho::animateBlinking(int colors) { - currentMillis = millis(); - if (currentMillis - startMillis > 300) { + currentMillis = millis(); + if (currentMillis - startMillis > 300) { M5.dis.setBrightness(ledsOn ? M5AtomEcho::brightness : 0); - ledsOn = !ledsOn; + ledsOn = !ledsOn; startMillis = millis(); - updateColors(colors); - } + updateColors(colors); + } } void M5AtomEcho::InitI2SSpeakerOrMic(int mode) diff --git a/PlatformIO/src/devices/MatrixVoice.hpp b/PlatformIO/src/devices/MatrixVoice.hpp index 4450eb1..f92bff3 100644 --- a/PlatformIO/src/devices/MatrixVoice.hpp +++ b/PlatformIO/src/devices/MatrixVoice.hpp @@ -44,49 +44,49 @@ class MatrixVoice : public Device public: MatrixVoice(); void init(); - void animate(int colors, int mode); - void animateRunning(int colors); - void animateBlinking(int colors); - void animatePulsing(int colors); - void updateColors(int colors); - void updateBrightness(int brightness); + void animate(int colors, int mode); + void animateRunning(int colors); + void animateBlinking(int colors); + void animatePulsing(int colors); + void updateColors(int colors); + void updateBrightness(int brightness); void muteOutput(bool mute); void setVolume(uint16_t volume); void setWriteMode(int sampleRate, int bitDepth, int numChannels); - bool readAudio(uint8_t *data, size_t size); + bool readAudio(uint8_t *data, size_t size); void writeAudio(uint8_t *data, size_t size, size_t *bytes_written); void ampOutput(int output); bool animationSupported() { return true; }; bool runningSupported() { return true; }; bool pulsingSupported() { return true; }; bool blinkingSupported() { return true; }; - int readSize = 512; - int writeSize = 1024; - int width = 2; - int rate = 16000; + int readSize = 512; + int writeSize = 1024; + int width = 2; + int rate = 16000; private: matrix_hal::WishboneBus wb; matrix_hal::Everloop everloop; - matrix_hal::MicrophoneArray *mics; + matrix_hal::MicrophoneArray *mics; matrix_hal::EverloopImage image1d; void playBytes(int16_t* input, uint32_t length); - void interleave(const int16_t * in_L, const int16_t * in_R, int16_t * out, const size_t num_samples); - bool FIFOFlush(); - void updateColors(int colors, bool usePulse); - uint16_t GetFIFOStatus(); - uint32_t PCM_sampling_frequency = 16000; - int fifoSize = 4096; + void interleave(const int16_t * in_L, const int16_t * in_R, int16_t * out, const size_t num_samples); + bool FIFOFlush(); + void updateColors(int colors, bool usePulse); + uint16_t GetFIFOStatus(); + uint32_t PCM_sampling_frequency = 16000; + int fifoSize = 4096; int sampleRate, bitDepth, numChannels; - int brightness, pulse = 15; - float sample_time = 1.0 / 16000; - uint32_t spiLength = 1024; + int brightness, pulse = 15; + float sample_time = 1.0 / 16000; + uint32_t spiLength = 1024; int sleep = int(spiLength * sample_time * 1000); - int count = 0; - int position = 0; - long currentMillis, startMillis; - bool ledsOn = true; - bool directionDown = false; + int count = 0; + int position = 0; + long currentMillis, startMillis; + bool ledsOn = true; + bool directionDown = false; }; MatrixVoice::MatrixVoice() @@ -95,114 +95,114 @@ MatrixVoice::MatrixVoice() void MatrixVoice::init() { - Serial.println("Matrix Voice Initialized"); + Serial.println("Matrix Voice Initialized"); wb.Init(); everloop.Setup(&wb); - mics = new matrix_hal::MicrophoneArray(); + mics = new matrix_hal::MicrophoneArray(); mics->Setup(&wb); mics->SetSamplingRate(rate); matrix_hal::MicrophoneCore mic_core(*mics); mic_core.Setup(&wb); uint16_t PCM_constant = 492; wb.SpiWrite(matrix_hal::kConfBaseAddress + 9, (const uint8_t *)(&PCM_constant), sizeof(uint16_t)); - currentMillis = millis(); - startMillis = millis(); + currentMillis = millis(); + startMillis = millis(); }; void MatrixVoice::updateBrightness(int brightness) { - // all values below 10 is read as 0 in gamma8, we map 0 to 10 - if (brightness > 100) { brightness = 100; } - MatrixVoice::brightness = brightness * 90 / 100 + 10; - MatrixVoice::pulse = brightness * 90 / 100 + 10; + // all values below 10 is read as 0 in gamma8, we map 0 to 10 + if (brightness > 100) { brightness = 100; } + MatrixVoice::brightness = brightness * 90 / 100 + 10; + MatrixVoice::pulse = brightness * 90 / 100 + 10; } void MatrixVoice::animate(int colors, int mode) { - switch (mode) - { - case AnimationMode::RUN: - animateRunning(colors); - break; - case AnimationMode::BLINK: - animateBlinking(colors); - break; - case AnimationMode::PULSE: - animatePulsing(colors); - break; - default: - break; - } + switch (mode) + { + case AnimationMode::RUN: + animateRunning(colors); + break; + case AnimationMode::BLINK: + animateBlinking(colors); + break; + case AnimationMode::PULSE: + animatePulsing(colors); + break; + default: + break; + } } void MatrixVoice::animateRunning(int colors) { - currentMillis = millis(); - if (currentMillis - startMillis > 10) { - int r = ColorMap[colors][0]; - int g = ColorMap[colors][1]; - int b = ColorMap[colors][2]; - int w = ColorMap[colors][3]; - startMillis = millis(); - for (int i = 0; i < image1d.leds.size(); i++) { - int red = ((i + 1) * brightness / image1d.leds.size()) * r / 100; - int green = ((i + 1) * brightness / image1d.leds.size()) * g / 100; - int blue = ((i + 1) * brightness / image1d.leds.size()) * b / 100; - int white = ((i + 1) * brightness / image1d.leds.size()) * w / 100; - image1d.leds[(i + position) % image1d.leds.size()].red = pgm_read_byte(&gamma8[red]); - image1d.leds[(i + position) % image1d.leds.size()].green = pgm_read_byte(&gamma8[green]); - image1d.leds[(i + position) % image1d.leds.size()].blue = pgm_read_byte(&gamma8[blue]); - image1d.leds[(i + position) % image1d.leds.size()].white = pgm_read_byte(&gamma8[white]); - } - position++; - position %= image1d.leds.size(); - everloop.Write(&image1d); - } + currentMillis = millis(); + if (currentMillis - startMillis > 10) { + int r = ColorMap[colors][0]; + int g = ColorMap[colors][1]; + int b = ColorMap[colors][2]; + int w = ColorMap[colors][3]; + startMillis = millis(); + for (int i = 0; i < image1d.leds.size(); i++) { + int red = ((i + 1) * brightness / image1d.leds.size()) * r / 100; + int green = ((i + 1) * brightness / image1d.leds.size()) * g / 100; + int blue = ((i + 1) * brightness / image1d.leds.size()) * b / 100; + int white = ((i + 1) * brightness / image1d.leds.size()) * w / 100; + image1d.leds[(i + position) % image1d.leds.size()].red = pgm_read_byte(&gamma8[red]); + image1d.leds[(i + position) % image1d.leds.size()].green = pgm_read_byte(&gamma8[green]); + image1d.leds[(i + position) % image1d.leds.size()].blue = pgm_read_byte(&gamma8[blue]); + image1d.leds[(i + position) % image1d.leds.size()].white = pgm_read_byte(&gamma8[white]); + } + position++; + position %= image1d.leds.size(); + everloop.Write(&image1d); + } } void MatrixVoice::animateBlinking(int colors) { - currentMillis = millis(); - if (currentMillis - startMillis > 300) { - MatrixVoice::pulse = ledsOn ? MatrixVoice::brightness : 0; - ledsOn = !ledsOn; - startMillis = millis(); - updateColors(colors, true); - } + currentMillis = millis(); + if (currentMillis - startMillis > 300) { + MatrixVoice::pulse = ledsOn ? MatrixVoice::brightness : 0; + ledsOn = !ledsOn; + startMillis = millis(); + updateColors(colors, true); + } } void MatrixVoice::animatePulsing(int colors) { - currentMillis = millis(); - if (currentMillis - startMillis > 5) { - if (MatrixVoice::pulse > MatrixVoice::brightness) { directionDown = true; } - MatrixVoice::pulse = directionDown ? MatrixVoice::pulse - 5 : MatrixVoice::pulse + 5; - if (MatrixVoice::pulse < 5) { directionDown = false; } - startMillis = millis(); - updateColors(colors, true); - } + currentMillis = millis(); + if (currentMillis - startMillis > 5) { + if (MatrixVoice::pulse > MatrixVoice::brightness) { directionDown = true; } + MatrixVoice::pulse = directionDown ? MatrixVoice::pulse - 5 : MatrixVoice::pulse + 5; + if (MatrixVoice::pulse < 5) { directionDown = false; } + startMillis = millis(); + updateColors(colors, true); + } } void MatrixVoice::updateColors(int colors, bool usePulse) { - int r = ColorMap[colors][0]; - int g = ColorMap[colors][1]; - int b = ColorMap[colors][2]; - int w = ColorMap[colors][3]; - int brightness = usePulse ? MatrixVoice::pulse : MatrixVoice::brightness; - r = floor(brightness * r / 100); - r = pgm_read_byte(&gamma8[r]); - g = floor(brightness * g / 100); - g = pgm_read_byte(&gamma8[g]); - b = floor(brightness * b / 100); - b = pgm_read_byte(&gamma8[b]); - w = floor(brightness * w / 100); - w = pgm_read_byte(&gamma8[w]); - for (matrix_hal::LedValue &led : image1d.leds) { - led.red = r; - led.green = g; - led.blue = b; - led.white = w; - } - everloop.Write(&image1d); + int r = ColorMap[colors][0]; + int g = ColorMap[colors][1]; + int b = ColorMap[colors][2]; + int w = ColorMap[colors][3]; + int brightness = usePulse ? MatrixVoice::pulse : MatrixVoice::brightness; + r = floor(brightness * r / 100); + r = pgm_read_byte(&gamma8[r]); + g = floor(brightness * g / 100); + g = pgm_read_byte(&gamma8[g]); + b = floor(brightness * b / 100); + b = pgm_read_byte(&gamma8[b]); + w = floor(brightness * w / 100); + w = pgm_read_byte(&gamma8[w]); + for (matrix_hal::LedValue &led : image1d.leds) { + led.red = r; + led.green = g; + led.blue = b; + led.white = w; + } + everloop.Write(&image1d); } void MatrixVoice::updateColors(int colors) { - updateColors(colors, false); + updateColors(colors, false); } void MatrixVoice::muteOutput(bool mute) { @@ -211,8 +211,8 @@ void MatrixVoice::muteOutput(bool mute) { } void MatrixVoice::setVolume(uint16_t volume) { - uint16_t outputVolume = (100 - volume) * 25 / 100; //25 is minimum volume - wb.SpiWrite(matrix_hal::kConfBaseAddress+8,(const uint8_t *)(&outputVolume), sizeof(uint16_t)); + uint16_t outputVolume = (100 - volume) * 25 / 100; //25 is minimum volume + wb.SpiWrite(matrix_hal::kConfBaseAddress+8,(const uint8_t *)(&outputVolume), sizeof(uint16_t)); }; void MatrixVoice::ampOutput(int output) { @@ -220,126 +220,126 @@ void MatrixVoice::ampOutput(int output) { }; void MatrixVoice::setWriteMode(int sampleRate, int bitDepth, int numChannels) { - MatrixVoice::sampleRate = sampleRate; - MatrixVoice::bitDepth = bitDepth; - MatrixVoice::numChannels = numChannels; - FIFOFlush(); - speex_resampler_set_rate(resampler,sampleRate,16000); - speex_resampler_skip_zeros(resampler); - switch (sampleRate) - { - case 22050: - writeSize = 1411; - break; - case 44100: - writeSize = 2823; - break; - default: - writeSize = 1024; - break; - } - if (numChannels == 1) { - writeSize = writeSize / sizeof(uint16_t); - } - count = 0; - spiLength = 1024; + MatrixVoice::sampleRate = sampleRate; + MatrixVoice::bitDepth = bitDepth; + MatrixVoice::numChannels = numChannels; + FIFOFlush(); + speex_resampler_set_rate(resampler,sampleRate,16000); + speex_resampler_skip_zeros(resampler); + switch (sampleRate) + { + case 22050: + writeSize = 1411; + break; + case 44100: + writeSize = 2823; + break; + default: + writeSize = 1024; + break; + } + if (numChannels == 1) { + writeSize = writeSize / sizeof(uint16_t); + } + count = 0; + spiLength = 1024; }; bool MatrixVoice::readAudio(uint8_t *data, size_t size) { - mics->Read(); - uint16_t voicebuffer[readSize]; - for (uint32_t s = 0; s < readSize; s++) { - voicebuffer[s] = mics->Beam(s); - } + mics->Read(); + uint16_t voicebuffer[readSize]; + for (uint32_t s = 0; s < readSize; s++) { + voicebuffer[s] = mics->Beam(s); + } memcpy(data, voicebuffer, readSize * width); - return true; + return true; } void MatrixVoice::writeAudio(uint8_t *data, size_t inputLength, size_t *bytes_written) { - *bytes_written = inputLength; - uint32_t outputLength = (numChannels == 1) ? inputLength * sizeof(int16_t) : inputLength; - int16_t output[outputLength]; + *bytes_written = inputLength; + uint32_t outputLength = (numChannels == 1) ? inputLength * sizeof(int16_t) : inputLength; + int16_t output[outputLength]; if (numChannels == 1) { - uint32_t monoLength = inputLength / sizeof(int16_t); - int16_t mono[monoLength]; - int16_t stereo[inputLength]; - for (int i = 0; i < inputLength; i += 2) { - mono[i/2] = ((data[i] & 0xff) | (data[i + 1] << 8)); - } - interleave(mono, mono, stereo, monoLength); - for (int i = 0; i < inputLength; i++) { - output[i] = stereo[i]; - } - } else { - for (int i = 0; i < inputLength; i += 2) { - output[i/2] = ((data[i] & 0xff) | (data[i + 1] << 8)); - } - } - - //At this point, we always have a stereo audio message, at whatever rate. - //The length of the bytes is dependant on the input sampleRate. - //1024 with 16000Hz, 2822 for 441000. (44100/16000) * 1024 = 2822 - //We might need to resample of the inputRate is higher than 16000 - uint16_t spiSamples[spiLength]; - if (sampleRate > 16000) { - int16_t resampled[spiLength]; - //Serial.printf("before resample %d, %d\r\n", spiLength, outputLength); - speex_resampler_process_interleaved_int(resampler, output, &outputLength, resampled, &spiLength); - //Serial.printf("after resample %d, %d\r\n", spiLength, outputLength); - for (int i = 0; i < spiLength; i++) { - spiSamples[i] = resampled[i]; - } - } else { - for (int i = 0; i < spiLength; i++) { - spiSamples[i] = output[i]; - } - } - - - if (GetFIFOStatus() > fifoSize * 3 / 4) { - std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); - } - Serial.printf("Write %d, %d, %d, %d\r\n", spiLength, sizeof(spiSamples), sleep, count); - wb.SpiWrite(matrix_hal::kDACBaseAddress, (const uint8_t *)spiSamples, spiLength); - count++; + uint32_t monoLength = inputLength / sizeof(int16_t); + int16_t mono[monoLength]; + int16_t stereo[inputLength]; + for (int i = 0; i < inputLength; i += 2) { + mono[i/2] = ((data[i] & 0xff) | (data[i + 1] << 8)); + } + interleave(mono, mono, stereo, monoLength); + for (int i = 0; i < inputLength; i++) { + output[i] = stereo[i]; + } + } else { + for (int i = 0; i < inputLength; i += 2) { + output[i/2] = ((data[i] & 0xff) | (data[i + 1] << 8)); + } + } + + //At this point, we always have a stereo audio message, at whatever rate. + //The length of the bytes is dependant on the input sampleRate. + //1024 with 16000Hz, 2822 for 441000. (44100/16000) * 1024 = 2822 + //We might need to resample of the inputRate is higher than 16000 + uint16_t spiSamples[spiLength]; + if (sampleRate > 16000) { + int16_t resampled[spiLength]; + //Serial.printf("before resample %d, %d\r\n", spiLength, outputLength); + speex_resampler_process_interleaved_int(resampler, output, &outputLength, resampled, &spiLength); + //Serial.printf("after resample %d, %d\r\n", spiLength, outputLength); + for (int i = 0; i < spiLength; i++) { + spiSamples[i] = resampled[i]; + } + } else { + for (int i = 0; i < spiLength; i++) { + spiSamples[i] = output[i]; + } + } + + + if (GetFIFOStatus() > fifoSize * 3 / 4) { + std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); + } + Serial.printf("Write %d, %d, %d, %d\r\n", spiLength, sizeof(spiSamples), sleep, count); + wb.SpiWrite(matrix_hal::kDACBaseAddress, (const uint8_t *)spiSamples, spiLength); + count++; // if (numChannels == 1) { - // // int16_t mono[size / sizeof(int16_t)]; - // // int16_t stereo[size]; - // // for (int i = 0; i < size; i += 2) { - // // mono[i/2] = ((data[i] & 0xff) | (data[i + 1] << 8)); - // // } - // // interleave(mono, mono, stereo, size / sizeof(int16_t)); - // // if (fifo_status > fifoSize * 3 / 4) { - // // int sleep = int(size * sizeof(int16_t) * sample_time * 1000); - // // std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); - // // } - // // wb.SpiWrite(matrix_hal::kDACBaseAddress, (const uint8_t *)stereo, size * sizeof(int16_t)); - // } else { - // if (sampleRate == 44100) { - // uint32_t in_len = size / sizeof(int16_t); - // uint32_t out_len = 1024 / sizeof(int16_t); - // int16_t input[in_len]; - // int16_t output[out_len]; - // for (int i = 0; i < size; i += 2) { - // input[i/2] = ((data[i] & 0xff) | (data[i + 1] << 8)); - // } - // speex_resampler_process_interleaved_int(resampler, input, &in_len, output, &out_len); - // if (fifo_status > fifoSize * 3 / 4) { - // int sleep = int( ( out_len / sizeof(int16_t)) * sample_time * 1000); - // std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); - // } - // wb.SpiWrite(matrix_hal::kDACBaseAddress, (const uint8_t *)output, out_len * sizeof(int16_t)); - // } else { - // if (fifo_status > fifoSize * 3 / 4) { - // int sleep = int(size * sample_time * 1000); - // std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); - // } - // wb.SpiWrite(matrix_hal::kDACBaseAddress, (const uint8_t *)data, size); - // } - //} + // // int16_t mono[size / sizeof(int16_t)]; + // // int16_t stereo[size]; + // // for (int i = 0; i < size; i += 2) { + // // mono[i/2] = ((data[i] & 0xff) | (data[i + 1] << 8)); + // // } + // // interleave(mono, mono, stereo, size / sizeof(int16_t)); + // // if (fifo_status > fifoSize * 3 / 4) { + // // int sleep = int(size * sizeof(int16_t) * sample_time * 1000); + // // std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); + // // } + // // wb.SpiWrite(matrix_hal::kDACBaseAddress, (const uint8_t *)stereo, size * sizeof(int16_t)); + // } else { + // if (sampleRate == 44100) { + // uint32_t in_len = size / sizeof(int16_t); + // uint32_t out_len = 1024 / sizeof(int16_t); + // int16_t input[in_len]; + // int16_t output[out_len]; + // for (int i = 0; i < size; i += 2) { + // input[i/2] = ((data[i] & 0xff) | (data[i + 1] << 8)); + // } + // speex_resampler_process_interleaved_int(resampler, input, &in_len, output, &out_len); + // if (fifo_status > fifoSize * 3 / 4) { + // int sleep = int( ( out_len / sizeof(int16_t)) * sample_time * 1000); + // std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); + // } + // wb.SpiWrite(matrix_hal::kDACBaseAddress, (const uint8_t *)output, out_len * sizeof(int16_t)); + // } else { + // if (fifo_status > fifoSize * 3 / 4) { + // int sleep = int(size * sample_time * 1000); + // std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); + // } + // wb.SpiWrite(matrix_hal::kDACBaseAddress, (const uint8_t *)data, size); + // } + //} } // void MatrixVoice::writeAudio(uint8_t *data, size_t size, size_t *bytes_written) { @@ -415,7 +415,7 @@ void MatrixVoice::writeAudio(uint8_t *data, size_t inputLength, size_t *bytes_wr // if (MatrixVoice::numChannels == 2) { // speex_resampler_process_interleaved_int(resampler, input, &in_len, output, &out_len); - + // //play it! // playBytes(output, out_len); // } else { @@ -445,7 +445,7 @@ void MatrixVoice::interleave(const int16_t * in_L, const int16_t * in_R, int16_t bool MatrixVoice::FIFOFlush() { int16_t value = 1; wb.SpiWrite(matrix_hal::kConfBaseAddress + 12,(const uint8_t *)(&value), sizeof(uint16_t)); - value = 0; + value = 0; wb.SpiWrite(matrix_hal::kConfBaseAddress + 12,(const uint8_t *)(&value), sizeof(uint16_t)); return true; } @@ -453,8 +453,8 @@ bool MatrixVoice::FIFOFlush() { uint16_t MatrixVoice::GetFIFOStatus() { uint16_t write_pointer; uint16_t read_pointer; - wb.SpiRead(matrix_hal::kDACBaseAddress + 2050, (uint8_t *)(&read_pointer), sizeof(uint16_t)); - wb.SpiRead(matrix_hal::kDACBaseAddress + 2051, (uint8_t *)(&write_pointer), sizeof(uint16_t)); + wb.SpiRead(matrix_hal::kDACBaseAddress + 2050, (uint8_t *)(&read_pointer), sizeof(uint16_t)); + wb.SpiRead(matrix_hal::kDACBaseAddress + 2051, (uint8_t *)(&write_pointer), sizeof(uint16_t)); if (write_pointer > read_pointer) return write_pointer - read_pointer;