Skip to content

Commit

Permalink
Merge pull request #79 from Romkabouter/development
Browse files Browse the repository at this point in the history
New version with animations
  • Loading branch information
Romkabouter authored Oct 28, 2021
2 parents 017a3c4 + 4b516db commit 5d5a5cb
Show file tree
Hide file tree
Showing 9 changed files with 650 additions and 429 deletions.
74 changes: 58 additions & 16 deletions PlatformIO/src/General.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -68,6 +69,7 @@ int retryCount = 0;
int I2SMode = -1;
bool mqttConnected = false;
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/#");
Expand All @@ -79,6 +81,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);
Expand All @@ -88,28 +91,39 @@ 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;

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 { };
struct UpdateConfigurationEvent : tinyfsm::Event { };

void onMqttConnect(bool sessionPresent);
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason);
Expand Down Expand Up @@ -181,6 +195,11 @@ const std::map<const std::string, String (*)()> 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 == 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
Expand Down Expand Up @@ -218,6 +237,7 @@ template <typename T> 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
{
Expand All @@ -229,17 +249,18 @@ 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);
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);

Expand All @@ -258,14 +279,15 @@ 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);
configChanged = true;
} else {
Serial.println("No settings changed");
}
}
if (saveNeeded) {
if (rebootNeeded) {
response = request->beginResponse_P ( 200, "text/html", "<html><head><title>Rebooting...</title><script>setTimeout(function(){window.location.href = '/';},4000);</script></head><body><h1>Configuration saved, rebooting!</h1></body></html>");
} else {
response = request->beginResponse_P ( 200, "text/html", index_html, processor );
Expand All @@ -277,7 +299,7 @@ void handleFSf ( AsyncWebServerRequest* request, const String& route ) {

request->send ( response ) ;

if (saveNeeded) {
if (rebootNeeded) {
Serial.println("Rebooting!");
ESP.restart();
}
Expand All @@ -289,7 +311,25 @@ 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) {
asyncClient.publish(debugTopic.c_str(), 0, false, message);
}
Expand Down Expand Up @@ -323,6 +363,7 @@ void loadConfiguration(const char *filename, Config &config) {
config.volume = doc.getMember("volume").as<int>();
device->setVolume(config.volume);
config.gain = doc.getMember("gain").as<int>();
config.animation = doc.getMember("animation").as<int>();
device->setGain(config.gain);
audioFrameTopic = std::string("hermes/audioServer/") + config.siteid + std::string("/audioFrame");
playBytesTopic = std::string("hermes/audioServer/") + config.siteid + std::string("/playBytes/#");
Expand All @@ -345,7 +386,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;
Expand All @@ -359,6 +400,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"));
}
Expand Down
16 changes: 10 additions & 6 deletions PlatformIO/src/Satellite.cpp
Original file line number Diff line number Diff line change
@@ -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:
==========
Expand Down Expand Up @@ -101,6 +100,9 @@
- 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
- Implemented for M5 Atom echo and Matrix Voice
* ************************************************************************ */

Expand Down Expand Up @@ -183,6 +185,8 @@ void setup() {
device->setGain(config.gain);
device->setVolume(config.volume);

initHeader(device->readSize, device->width, device->rate);

// ---------------------------------------------------------------------------
// ArduinoOTA
// ---------------------------------------------------------------------------
Expand All @@ -191,7 +195,7 @@ void setup() {
ArduinoOTA
.onStart([]() {
Serial.println("Uploading...");
send_event(OtaEvent());
send_event(UpdateEvent());
})
.onEnd([]() {
Serial.println("\nEnd");
Expand Down
Loading

0 comments on commit 5d5a5cb

Please sign in to comment.