From 235029fb518bce09c878b04b59f1e9ab60def569 Mon Sep 17 00:00:00 2001 From: Paul Romkes Date: Mon, 14 Dec 2020 22:32:28 +0100 Subject: [PATCH] Basic functions working --- PlatformIO/src/Application.cpp | 49 -- PlatformIO/src/Application.h | 28 - PlatformIO/src/MatrixVoiceAudioServer.cpp | 166 ++---- PlatformIO/src/StateMachine.hpp | 509 ++++++++++++++++++ .../src/state_machine/EverloopState.cpp | 121 ----- PlatformIO/src/state_machine/EverloopState.h | 23 - PlatformIO/src/state_machine/States.h | 12 - PlatformIO/src/tinyfsm.hpp | 244 +++++++++ Streamer.code-workspace | 61 +++ 9 files changed, 853 insertions(+), 360 deletions(-) delete mode 100644 PlatformIO/src/Application.cpp delete mode 100644 PlatformIO/src/Application.h create mode 100644 PlatformIO/src/StateMachine.hpp delete mode 100644 PlatformIO/src/state_machine/EverloopState.cpp delete mode 100644 PlatformIO/src/state_machine/EverloopState.h delete mode 100644 PlatformIO/src/state_machine/States.h create mode 100644 PlatformIO/src/tinyfsm.hpp diff --git a/PlatformIO/src/Application.cpp b/PlatformIO/src/Application.cpp deleted file mode 100644 index ebc7ba7..0000000 --- a/PlatformIO/src/Application.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include -#include "Application.h" -#include "state_machine/EverloopState.h" -#include "wishbone_bus.h" - -//#include "state_machine/DetectWakeWordState.h" -// #include "state_machine/RecogniseCommandState.h" -// #include "IndicatorLight.h" -// #include "Speaker.h" -// #include "IntentProcessor.h" - -//Application::Application(I2SSampler *sample_provider, IntentProcessor *intent_processor, Speaker *speaker, IndicatorLight *indicator_light) -Application::Application() -{ - // // detect wake word state - waits for the wake word to be detected - // m_detect_wake_word_state = new DetectWakeWordState(sample_provider); - // // command recongiser - streams audio to the server for recognition - // m_recognise_command_state = new RecogniseCommandState(sample_provider, indicator_light, speaker, intent_processor); - // // start off in the detecting wakeword state - // m_current_state = m_detect_wake_word_state; - // m_current_state->enterState(); - matrix_hal::WishboneBus wb; - wb.Init(); - everloopState = new EverloopState(&wb); - currentState = everloopState; - //currentState->enterState(); -} - -void Application::run() -{ - bool state_done = currentState->run(); - currentState->enterState(); - if (state_done) - { - currentState->exitState(); - // switch to the next state - if (currentState == everloopState) - { - //Swith state - currentState = everloopState; - } - else - { - currentState = everloopState; - } - currentState->enterState(); - } - vTaskDelay(10); -} diff --git a/PlatformIO/src/Application.h b/PlatformIO/src/Application.h deleted file mode 100644 index b0f4a7f..0000000 --- a/PlatformIO/src/Application.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef _application_h_ -#define _application_h_ - -#include "state_machine/States.h" - -//class I2SSampler; -//class I2SOutput; -class State; -//class IndicatorLight; -//class Speaker; -//class IntentProcessor; - -class Application -{ -private: - State *everloopState; -// State *m_detect_wake_word_state; - // State *m_recognise_command_state; - State *currentState; - -public: -// Application(I2SSampler *sample_provider, IntentProcessor *intent_processor, Speaker *speaker, IndicatorLight *indicator_light); - Application(); - ~Application(); - void run(); -}; - -#endif \ No newline at end of file diff --git a/PlatformIO/src/MatrixVoiceAudioServer.cpp b/PlatformIO/src/MatrixVoiceAudioServer.cpp index ac66212..470e06d 100644 --- a/PlatformIO/src/MatrixVoiceAudioServer.cpp +++ b/PlatformIO/src/MatrixVoiceAudioServer.cpp @@ -69,142 +69,54 @@ - Added configuration webserver - Improved stability for MQTT stream v7.0: - - Complete rewrite + - Complete rewrite using StateMachine * ************************************************************************ */ #include #include #include -// extern "C" { -// #include "freertos/FreeRTOS.h" -// #include "freertos/event_groups.h" -// #include "freertos/timers.h" -// } -#include "Application.h" +#include +#include "M5Atom.h" -bool isUpdateInProgess = false; -bool wifi_connected = false; -TimerHandle_t wifiReconnectTimer; -TaskHandle_t applicationTaskHandle; -int retryCount = 0; - -void connectToWifi() { - Serial.println("Connecting to Wi-Fi..."); - WiFi.mode(WIFI_STA); - WiFi.begin(WIFI_SSID, WIFI_PASS); - retryCount = 0; - while (WiFi.waitForConnectResult() != WL_CONNECTED) { - retryCount++; - if (retryCount > 2) { - Serial.println("Connection Failed! Rebooting..."); - ESP.restart(); - } - } -} - -void WiFiEvent(WiFiEvent_t event) { - switch (event) { - case SYSTEM_EVENT_STA_START: - WiFi.setHostname(HOSTNAME); - break; - case SYSTEM_EVENT_STA_GOT_IP: - wifi_connected = true; - //xTaskNotify(applicationTaskHandle, 1, eSetBits); - Serial.println("Connected to Wifi with IP: " + WiFi.localIP().toString()); - xTimerStop(wifiReconnectTimer, 0); // Stop the reconnect timer - break; - case SYSTEM_EVENT_STA_DISCONNECTED: - wifi_connected = false; - Serial.println("Disconnected from Wifi!"); - xTimerStart(wifiReconnectTimer, 0); // Start the reconnect timer - break; - default: - break; - } -} - -void applicationTask(void *param) -{ - Application *application = static_cast(param); - - // const TickType_t xMaxBlockTime = pdMS_TO_TICKS(100); -// uint32_t ulNotificationValue = 1; - while (true) - { - //uint32_t ulNotificationValue = ulTaskNotifyTake(pdTRUE, xMaxBlockTime); - -// if (ulNotificationValue > 0) -// { - application->run(); -// ulNotificationValue = 0; -// } - } -} - -/* ************************************************************************ * - SETUP - * ************************************************************************ */ void setup() { - Serial.begin(115200); - Serial.println("Booting"); - //Application *application = new Application(); - - wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(2000), pdTRUE, (void *)0, reinterpret_cast(connectToWifi)); - - WiFi.onEvent(WiFiEvent); - WiFi.mode(WIFI_STA); - WiFi.begin(WIFI_SSID, WIFI_PASS); - while (WiFi.waitForConnectResult() != WL_CONNECTED) { - retryCount++; - if (retryCount > 2) { - Serial.println("Connection Failed! Rebooting..."); - ESP.restart(); - } else { - Serial.println("Connection Failed! Retry..."); - } - } - - // xTaskCreatePinnedToCore(applicationTask, "Application Task", 8192, application, 1, &applicationTaskHandle, 0); - //xTaskNotify(applicationTaskHandle, 1, eIncrement); - - // --------------------------------------------------------------------------- - // ArduinoOTA - // --------------------------------------------------------------------------- - ArduinoOTA.setPasswordHash(OTA_PASS_HASH); - - ArduinoOTA - .onStart([]() { - // vTaskSuspend(applicationTaskHandle); - isUpdateInProgess = true; - Serial.println("Uploading..."); - xTimerStop(wifiReconnectTimer, 0); - }) - .onEnd([]() { - isUpdateInProgess = false; - Serial.println("\nEnd"); - }) - .onProgress([](unsigned int progress, unsigned int total) { - Serial.printf("Progress: %u%%\r", (progress / (total / 100))); - }) - .onError([](ota_error_t error) { - Serial.printf("Error[%u]: ", error); - if (error == OTA_AUTH_ERROR) - Serial.println("Auth Failed"); - else if (error == OTA_BEGIN_ERROR) - Serial.println("Begin Failed"); - else if (error == OTA_CONNECT_ERROR) - Serial.println("Connect Failed"); - else if (error == OTA_RECEIVE_ERROR) - Serial.println("Receive Failed"); - else if (error == OTA_END_ERROR) - Serial.println("End Failed"); - }); - ArduinoOTA.begin(); + Serial.begin(115200); + Serial.println("Booting"); + M5.begin(true,true,true); + // --------------------------------------------------------------------------- + // ArduinoOTA + // --------------------------------------------------------------------------- + ArduinoOTA.setPasswordHash(OTA_PASS_HASH); + + ArduinoOTA + .onStart([]() { + Serial.println("Uploading..."); + }) + .onEnd([]() { + Serial.println("\nEnd"); + }) + .onProgress([](unsigned int progress, unsigned int total) { + Serial.printf("Progress: %u%%\r", (progress / (total / 100))); + }) + .onError([](ota_error_t error) { + Serial.printf("Error[%u]: ", error); + if (error == OTA_AUTH_ERROR) + Serial.println("Auth Failed"); + else if (error == OTA_BEGIN_ERROR) + Serial.println("Begin Failed"); + else if (error == OTA_CONNECT_ERROR) + Serial.println("Connect Failed"); + else if (error == OTA_RECEIVE_ERROR) + Serial.println("Receive Failed"); + else if (error == OTA_END_ERROR) + Serial.println("End Failed"); + }); + + fsm_list::start(); } -/* ************************************************************************ * - MAIN LOOP - * ************************************************************************ */ void loop() { + if (WiFi.isConnected()) { ArduinoOTA.handle(); + } + fsm_list::run(); } diff --git a/PlatformIO/src/StateMachine.hpp b/PlatformIO/src/StateMachine.hpp new file mode 100644 index 0000000..82c020a --- /dev/null +++ b/PlatformIO/src/StateMachine.hpp @@ -0,0 +1,509 @@ +#include +#include "M5Atom.h" +#include +#include +#include +#include "RingBuf.h" + +#define CONFIG_I2S_BCK_PIN 19 +#define CONFIG_I2S_LRCK_PIN 33 +#define CONFIG_I2S_DATA_PIN 22 +#define CONFIG_I2S_DATA_IN_PIN 23 + +#define SPEAKER_I2S_NUMBER I2S_NUM_0 + +#define MODE_MIC 0 +#define MODE_SPK 1 +#define READ_SIZE 256 +#define WRITE_SIZE 256 +#define WIDTH 2 +#define RATE 16000 + +const int PLAY = BIT0; +const int STREAM = BIT1; + +uint8_t micdata[READ_SIZE * WIDTH]; +struct wavfile_header { + char riff_tag[4]; // 4 + int riff_length; // 4 + char wave_tag[4]; // 4 + char fmt_tag[4]; // 4 + int fmt_length; // 4 + short audio_format; // 2 + short num_channels; // 2 + int sample_rate; // 4 + int byte_rate; // 4 + short block_align; // 2 + short bits_per_sample; // 2 + char data_tag[4]; // 4 + int data_length; // 4 +}; +struct wavfile_header header; +std::string finishedMsg = ""; +int retryCount = 0; +int I2SMode = -1; +std::string audioFrameTopic = std::string("hermes/audioServer/") + SITEID + std::string("/audioFrame"); +std::string playBytesTopic = std::string("hermes/audioServer/") + SITEID + std::string("/playBytes/#"); +std::string hotwordTopic = "hermes/hotword/#"; +std::string debugTopic = SITEID + std::string("/debug"); +AsyncMqttClient asyncClient; +WiFiClient net; +PubSubClient audioServer(net); +RingBuf audioData; +long message_size = 0; +static EventGroupHandle_t audioGroup; + +struct WifiDisconnected; +struct MQTTDisconnected; +struct HotwordDetected; +struct StreamAudio; +struct PlayAudio; + +struct WifiDisconnectEvent : tinyfsm::Event { }; +struct WifiConnectEvent : tinyfsm::Event { }; +struct MQTTDisconnectedEvent : tinyfsm::Event { }; +struct MQTTConnectedEvent : tinyfsm::Event { }; +struct StreamAudioEvent : tinyfsm::Event { }; +struct PlayAudioEvent : tinyfsm::Event { }; +struct HotwordDetectedEvent : tinyfsm::Event { }; + +void onMqttConnect(bool sessionPresent); +void onMqttDisconnect(AsyncMqttClientDisconnectReason reason); +void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total); +void publishDebug(const char* message); +void InitI2SSpeakerOrMic(int mode); +void WiFiEvent(WiFiEvent_t event); +void initHeader(); +void I2Stask(void *p); + +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(PlayAudioEvent const &) {}; + virtual void react(HotwordDetectedEvent const &) {}; + + virtual void entry(void) {}; + virtual void run(void) {}; + void exit(void) {}; +}; + +class HotwordDetected : public StateMachine +{ + void entry(void) override { + Serial.println("Enter HotwordDetected"); + M5.dis.drawpix(0, CRGB(0xFF0000)); + xEventGroupClearBits(audioGroup, PLAY); + xEventGroupSetBits(audioGroup, STREAM); + initHeader(); + } + + void react(StreamAudioEvent const &) override { + transit(); + }; + + void react(WifiDisconnectEvent const &) override { + transit(); + }; + + void react(PlayAudioEvent const &) override { + transit(); + }; +}; + +class PlayAudio : public StateMachine +{ + void entry(void) override { + Serial.println("Enter PlayAudio"); + //Set the taskbits to play the audiobuffer + xEventGroupClearBits(audioGroup, STREAM); + xEventGroupSetBits(audioGroup, PLAY); + } + + void react(WifiDisconnectEvent const &) override { + transit(); + }; + + void react(StreamAudioEvent const &) override { + transit(); + }; +}; + +class StreamAudio : public StateMachine +{ + void entry(void) override { + Serial.println("Enter StreamAudio"); + M5.dis.drawpix(0, CRGB(0x0000FF)); + xEventGroupClearBits(audioGroup, PLAY); + xEventGroupSetBits(audioGroup, STREAM); + initHeader(); + } + + void react(WifiDisconnectEvent const &) override { + transit(); + } + + void react(MQTTDisconnectedEvent const &) override { + transit(); + } + + void react(HotwordDetectedEvent const &) override { + transit(); + } + + void react(PlayAudioEvent const &) override { + transit(); + }; + +}; + +class MQTTConnected : public StateMachine { + void entry(void) override { + Serial.println("Enter MQTTConnected"); + Serial.printf("Connected as %s\n",SITEID); + asyncClient.subscribe(playBytesTopic.c_str(), 0); + asyncClient.subscribe(hotwordTopic.c_str(), 0); + transit(); + } + + void react(MQTTDisconnectedEvent const &) override { + transit(); + } + + void react(WifiDisconnectEvent const &) override { + transit(); + } +}; + +class MQTTDisconnected : public StateMachine { + void entry(void) override { + Serial.println("Enter MQTTDisconnected"); + if (audioServer.connected()) { + audioServer.disconnect(); + } + if (asyncClient.connected()) { + asyncClient.disconnect(); + } + asyncClient.setClientId(SITEID); + asyncClient.setServer(MQTT_HOST, MQTT_PORT); + asyncClient.setCredentials(MQTT_USER, MQTT_PASS); + asyncClient.connect(); + asyncClient.onDisconnect(onMqttDisconnect); + asyncClient.onMessage(onMqttMessage); + audioServer.setServer(MQTT_HOST, MQTT_PORT); + audioServer.connect("MatrixVoiceAudio", MQTT_USER, MQTT_PASS); + } + + void run(void) override { + if (audioServer.connected() && asyncClient.connected()) { + transit(); + } + } + + void react(MQTTConnectedEvent const &) override { + transit(); + } + + void react(WifiDisconnectEvent const &) override { + transit(); + } +}; + +class WifiConnected : public StateMachine +{ + void entry(void) override { + Serial.println("Enter WifiConnected"); + Serial.println("Connected to Wifi with IP: " + WiFi.localIP().toString()); + M5.dis.drawpix(0, CRGB(0x0000FF)); + ArduinoOTA.begin(); + transit(); + } + + void react(WifiDisconnectEvent const &) override { + Serial.println("DisconnectEvent"); + transit(); + }; +}; + +class WifiDisconnected : public StateMachine +{ + void entry(void) override { + if (!audioGroup) { + audioGroup = xEventGroupCreate(); + } + xEventGroupClearBits(audioGroup, STREAM); + xEventGroupClearBits(audioGroup, PLAY); + xTaskCreatePinnedToCore(I2Stask, "I2Stask", 30000, NULL, 1, NULL, 1); + Serial.println("Enter WifiDisconnected"); + Serial.printf("Total heap: %d\n", ESP.getHeapSize()); + Serial.printf("Free heap: %d\n", ESP.getFreeHeap()); + M5.dis.drawpix(0, CRGB(0x00FF00)); + WiFi.onEvent(WiFiEvent); + + WiFi.mode(WIFI_STA); + WiFi.begin(WIFI_SSID, WIFI_PASS); + while (WiFi.waitForConnectResult() != WL_CONNECTED) { + retryCount++; + if (retryCount > 2) { + Serial.println("Connection Failed! Rebooting..."); + ESP.restart(); + } else { + Serial.println("Connection Failed! Retry..."); + } + } + } + + void react(WifiConnectEvent const &) override { + transit(); + }; +}; + +FSM_INITIAL_STATE(StateMachine, WifiDisconnected) + +using fsm_list = tinyfsm::FsmList; + +template +void send_event(E const & event) +{ + fsm_list::template dispatch(event); +} + +std::vector explode( const std::string &delimiter, const std::string &str) +{ + std::vector arr; + + int strleng = str.length(); + int delleng = delimiter.length(); + if (delleng==0) + return arr;//no change + + int i=0; + int k=0; + while( i topicparts = explode("/", topicstr); + finishedMsg = "{\"id\":\"" + topicparts[4] + "\",\"siteId\":\"" + SITEID + "\",\"sessionId\":null}"; + for (int i = 0; i < len; i++) { + while (!audioData.push((uint8_t)payload[i])) { + delay(1); + } + if (audioData.isFull()) { + send_event(PlayAudioEvent()); + } + } + } + } else { + // len + index < total ==> partial message + if (topicstr.find("playBytes") != std::string::npos) { + if (index == 0) { + message_size = total; + for (int i = 0; i < len; i++) { + audioData.push((uint8_t)payload[i]); + if (audioData.isFull()) { + send_event(PlayAudioEvent()); + } + } + } else { + for (int i = 0; i < len; i++) { + while (!audioData.push((uint8_t)payload[i])) { + delay(1); + } + if (audioData.isFull()) { + send_event(PlayAudioEvent()); + } + } + } + } + } +} + +void publishDebug(const char* message) { + if (true) { + audioServer.publish(debugTopic.c_str(), message); + } +} + +void I2Stask(void *p) { + while (1) { + if (xEventGroupGetBitsFromISR(audioGroup) == PLAY) { + if (I2SMode != MODE_SPK) { + InitI2SSpeakerOrMic(MODE_SPK); + I2SMode = MODE_SPK; + } + size_t bytes_written; + boolean timeout = false; + int played = 0; + long now = millis(); + long lastBytesPlayed = millis(); + uint8_t WaveData[44]; + for (int k = 0; k < 44; k++) { + audioData.pop(WaveData[k]); + played++; + } + while (played < message_size && timeout == false) { + int bytes_to_read = WRITE_SIZE; + if (message_size - played < WRITE_SIZE) { + bytes_to_read = message_size - played; + } + uint8_t data[bytes_to_read]; + while (audioData.size() < bytes_to_read && played < message_size && timeout == false) { + vTaskDelay(1); + now = millis(); + if (now - lastBytesPlayed > 1000) { + //force exit + Serial.printf("Exit timeout, audioData.size : %d, bytes_to_read: %d, played: %d, message_size: %d\n",(int)audioData.size(), (int)bytes_to_read, (int)played, (int)message_size); + timeout = true; + } + } + lastBytesPlayed = millis(); + + if (!timeout) { + for (int i = 0; i < bytes_to_read; i++) { + audioData.pop(data[i]); + } + played = played + bytes_to_read; + i2s_write(SPEAKER_I2S_NUMBER, data, sizeof(data), &bytes_written, portMAX_DELAY); + //if (bytes_written < bytes_to_read) { + Serial.printf("Bytes written %d\n",bytes_written); + //} + } + } + audioData.clear(); + xEventGroupClearBits(audioGroup, PLAY); + send_event(StreamAudioEvent()); + } else if (xEventGroupGetBitsFromISR(audioGroup) == STREAM) { + if (I2SMode != MODE_MIC) { + InitI2SSpeakerOrMic(MODE_MIC); + I2SMode = MODE_MIC; + } + size_t byte_read; + if (audioServer.connected()) { + i2s_read(SPEAKER_I2S_NUMBER, (char *)(micdata), READ_SIZE * WIDTH, &byte_read, (100 / portTICK_RATE_MS)); + uint8_t payload[sizeof(header) + (READ_SIZE * WIDTH)]; + memcpy(payload, &header, sizeof(header)); + memcpy(&payload[sizeof(header)], micdata,sizeof(micdata)); + audioServer.publish(audioFrameTopic.c_str(),(uint8_t *)payload, sizeof(payload)); + } else { + xEventGroupClearBits(audioGroup, STREAM); + send_event(MQTTDisconnectedEvent()); + } + } + } + vTaskDelete(NULL); +} + +void initHeader() { + 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) + (READ_SIZE * 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 = READ_SIZE * WIDTH; +} + +void InitI2SSpeakerOrMic(int mode) +{ + esp_err_t err = ESP_OK; + + i2s_driver_uninstall(SPEAKER_I2S_NUMBER); + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER), + .sample_rate = RATE, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // is fixed at 12bit, stereo, MSB + .channel_format = I2S_CHANNEL_FMT_ALL_RIGHT, + .communication_format = I2S_COMM_FORMAT_I2S, + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + .dma_buf_count = 6, + .dma_buf_len = 60, + }; + if (mode == MODE_MIC) + { + i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); + } + else + { + i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX); + i2s_config.use_apll = false; + i2s_config.tx_desc_auto_clear = true; + } + + err += i2s_driver_install(SPEAKER_I2S_NUMBER, &i2s_config, 0, NULL); + i2s_pin_config_t tx_pin_config; + + tx_pin_config.bck_io_num = CONFIG_I2S_BCK_PIN; + tx_pin_config.ws_io_num = CONFIG_I2S_LRCK_PIN; + tx_pin_config.data_out_num = CONFIG_I2S_DATA_PIN; + tx_pin_config.data_in_num = CONFIG_I2S_DATA_IN_PIN; + + err += i2s_set_pin(SPEAKER_I2S_NUMBER, &tx_pin_config); + err += i2s_set_clk(SPEAKER_I2S_NUMBER, RATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO); + + return; +} + +void WiFiEvent(WiFiEvent_t event) { + switch (event) { + case SYSTEM_EVENT_STA_START: + WiFi.setHostname(HOSTNAME); + break; + case SYSTEM_EVENT_STA_GOT_IP: + send_event(WifiConnectEvent()); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + send_event(WifiDisconnectEvent()); + break; + default: + break; + } +} diff --git a/PlatformIO/src/state_machine/EverloopState.cpp b/PlatformIO/src/state_machine/EverloopState.cpp deleted file mode 100644 index c619cd7..0000000 --- a/PlatformIO/src/state_machine/EverloopState.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include -//#include -#include "EverloopState.h" -#include "wishbone_bus.h" -#include "everloop.h" -#include "everloop_image.h" - -static matrix_hal::Everloop everloop; -static matrix_hal::EverloopImage image1d; - -// This is used to be able to change brightness, while keeping the colors appear -// the same Called gamma correction, check this -// https://learn.adafruit.com/led-tricks-gamma-correction/the-issue -const uint8_t PROGMEM gamma8[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, - 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, - 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, - 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, - 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, - 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, - 31, 32, 32, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, 41, 42, - 43, 44, 45, 46, 47, 48, 49, 50, 50, 51, 52, 54, 55, 56, 57, - 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, 69, 70, 72, 73, 74, - 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, 90, 92, 93, 95, - 96, 98, 99, 101, 102, 104, 105, 107, 109, 110, 112, 114, 115, 117, 119, - 120, 122, 124, 126, 127, 129, 131, 133, 135, 137, 138, 140, 142, 144, 146, - 148, 150, 152, 154, 156, 158, 160, 162, 164, 167, 169, 171, 173, 175, 177, - 180, 182, 184, 186, 189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213, - 215, 218, 220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252, - 255}; - -EverloopState::EverloopState(matrix_hal::WishboneBus *wb) -{ - everloop.Setup(wb); -} - -void EverloopState::enterState() -{ - writeLeds(); - // int r = colors[0], g = colors[1], b = colors[2], w = colors[3], br = brightness * 90 / 100 + 10; - // r = floor(br * r / 100); - // r = pgm_read_byte(&gamma8[r]); - // g = floor(br * g / 100); - // g = pgm_read_byte(&gamma8[g]); - // b = floor(br * b / 100); - // b = pgm_read_byte(&gamma8[b]); - // w = floor(br * 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); -} - -bool EverloopState::run() -{ - //if (WiFi.isConnected()) { - // colors[0] = 0; - // colors[2] = 255; - //} else { - // colors[0] = 0; - // colors[1] = 255; - //} - //update everloop and return - - // int r = colors[0], g = colors[1], b = colors[2], w = colors[3], br = brightness * 90 / 100 + 10; - // r = floor(br * r / 100); - // r = pgm_read_byte(&gamma8[r]); - // g = floor(br * g / 100); - // g = pgm_read_byte(&gamma8[g]); - // b = floor(br * b / 100); - // b = pgm_read_byte(&gamma8[b]); - // w = floor(br * 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); - //writeLeds(); - if (ran == true) { - return false; - } - brightness = 100; - ran = true; - writeLeds(); - return true; - - // nothing detected stay in the current state - //return false; -} -void EverloopState::exitState() -{ - -} - -void EverloopState::writeLeds() { - int r = colors[0], g = colors[1], b = colors[2], w = colors[3], br = brightness * 90 / 100 + 10; - r = floor(br * r / 100); - r = pgm_read_byte(&gamma8[r]); - g = floor(br * g / 100); - g = pgm_read_byte(&gamma8[g]); - b = floor(br * b / 100); - b = pgm_read_byte(&gamma8[b]); - w = floor(br * 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); -} \ No newline at end of file diff --git a/PlatformIO/src/state_machine/EverloopState.h b/PlatformIO/src/state_machine/EverloopState.h deleted file mode 100644 index 77406fd..0000000 --- a/PlatformIO/src/state_machine/EverloopState.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _everloop_state_h_ -#define _everloop_state_h_ - -#include "States.h" -#include "wishbone_bus.h" - - -class EverloopState : public State -{ -public: - int colors[4] = {0, 0, 255, 0}; - int brightness = 15; - bool ran = false; - -public: - EverloopState(matrix_hal::WishboneBus *wb); - void enterState(); - bool run(); - void exitState(); - void writeLeds(); -}; - -#endif diff --git a/PlatformIO/src/state_machine/States.h b/PlatformIO/src/state_machine/States.h deleted file mode 100644 index f019eed..0000000 --- a/PlatformIO/src/state_machine/States.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _states_h_ -#define _states_h_ - -class State -{ -public: - virtual void enterState() = 0; - virtual bool run() = 0; - virtual void exitState() = 0; -}; - -#endif \ No newline at end of file diff --git a/PlatformIO/src/tinyfsm.hpp b/PlatformIO/src/tinyfsm.hpp new file mode 100644 index 0000000..868e951 --- /dev/null +++ b/PlatformIO/src/tinyfsm.hpp @@ -0,0 +1,244 @@ +/* + * TinyFSM - Tiny Finite State Machine Processor + * + * Copyright (c) 2012-2018 Axel Burri + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* --------------------------------------------------------------------- + * Version: 0.3.2 + * + * API documentation: see "../doc/50-API.md" + * + * The official TinyFSM website is located at: + * https://digint.ch/tinyfsm/ + * + * Author: + * Axel Burri + * --------------------------------------------------------------------- + */ + +#ifndef TINYFSM_HPP_INCLUDED +#define TINYFSM_HPP_INCLUDED + +#ifndef TINYFSM_NOSTDLIB +#include +#endif + +// #include +// #define DBG(str) do { std::cerr << str << std::endl; } while( false ) +// DBG("*** dbg_example *** " << __PRETTY_FUNCTION__); + +namespace tinyfsm +{ + + // -------------------------------------------------------------------------- + + struct Event { }; + + // -------------------------------------------------------------------------- + +#ifdef TINYFSM_NOSTDLIB + // remove dependency on standard library (silent fail!). + // useful in conjunction with -nostdlib option, e.g. if your compiler + // does not provide a standard library. + // NOTE: this silently disables all static_assert() calls below! + template + struct is_same_fsm { static constexpr bool value = true; }; +#else + // check if both fsm and state class share same fsmtype + template + struct is_same_fsm : std::is_same< typename F::fsmtype, typename S::fsmtype > { }; +#endif + + template + struct _state_instance + { + using value_type = S; + using type = _state_instance; + static S value; + }; + + template + typename _state_instance::value_type _state_instance::value; + + // -------------------------------------------------------------------------- + + template + class Fsm + { + public: + + using fsmtype = Fsm; + using state_ptr_t = F *; + + static state_ptr_t current_state_ptr; + + // public, leaving ability to access state instance (e.g. on reset) + template + static constexpr S & state(void) { + static_assert(is_same_fsm::value, "accessing state of different state machine"); + return _state_instance::value; + } + + template + static constexpr bool is_in_state(void) { + static_assert(is_same_fsm::value, "accessing state of different state machine"); + return current_state_ptr == &_state_instance::value; + } + + /// state machine functions + public: + + // explicitely specialized in FSM_INITIAL_STATE macro + static void set_initial_state(); + + static void reset() { }; + + static void enter() { + current_state_ptr->entry(); + } + + static void run() { + current_state_ptr->run(); + } + + static void start() { + set_initial_state(); + enter(); + } + + template + static void dispatch(E const & event) { + current_state_ptr->react(event); + } + + + /// state transition functions + protected: + + template + void transit(void) { + static_assert(is_same_fsm::value, "transit to different state machine"); + current_state_ptr->exit(); + current_state_ptr = &_state_instance::value; + current_state_ptr->entry(); + } + + template + void transit(ActionFunction action_function) { + static_assert(is_same_fsm::value, "transit to different state machine"); + current_state_ptr->exit(); + // NOTE: we get into deep trouble if the action_function sends a new event. + // TODO: implement a mechanism to check for reentrancy + action_function(); + current_state_ptr = &_state_instance::value; + current_state_ptr->entry(); + } + + template + void transit(ActionFunction action_function, ConditionFunction condition_function) { + if(condition_function()) { + transit(action_function); + } + } + }; + + template + typename Fsm::state_ptr_t Fsm::current_state_ptr; + + // -------------------------------------------------------------------------- + + template + struct FsmList; + + template<> struct FsmList<> { + static void set_initial_state() { } + static void reset() { } + static void enter() { } + static void run() { } + template + static void dispatch(E const &) { } + }; + + template + struct FsmList + { + using fsmtype = Fsm; + + static void set_initial_state() { + fsmtype::set_initial_state(); + FsmList::set_initial_state(); + } + + static void reset() { + F::reset(); + FsmList::reset(); + } + + static void enter() { + fsmtype::enter(); + FsmList::enter(); + } + + static void run() { + fsmtype::run(); + FsmList::run(); + } + + static void start() { + set_initial_state(); + enter(); + } + + template + static void dispatch(E const & event) { + fsmtype::template dispatch(event); + FsmList::template dispatch(event); + } + }; + + // -------------------------------------------------------------------------- + + template struct StateList; + template<> struct StateList<> { + static void reset() { } + }; + template + struct StateList + { + static void reset() { + _state_instance::value = S(); + StateList::reset(); + } + }; + + // -------------------------------------------------------------------------- +} /* namespace tinyfsm */ + + +#define FSM_INITIAL_STATE(_FSM, _STATE) \ +namespace tinyfsm { \ + template<> void Fsm< _FSM >::set_initial_state(void) { \ + current_state_ptr = &_state_instance< _STATE >::value; \ + } \ +} + +#endif /* TINYFSM_HPP_INCLUDED */ diff --git a/Streamer.code-workspace b/Streamer.code-workspace index 6d5f54f..6540b83 100644 --- a/Streamer.code-workspace +++ b/Streamer.code-workspace @@ -12,6 +12,67 @@ "terminal.integrated.env.osx": { "PATH": "/Users/paulromkes/.platformio/penv/bin:/Users/paulromkes/.platformio/penv:/Users/paulromkes/.yarn/bin:/Users/paulromkes/.config/yarn/global/node_modules/.bin:/opt/local/bin:/opt/local/sbin:/Library/Frameworks/Python.framework/Versions/3.6/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Frameworks/Mono.framework/Versions/Current/Commands:/Applications/Wireshark.app/Contents/MacOS:/Users/paulromkes/Library/Android/sdk/platform-tools/:/Users/paulromkes/esp/xtensa-esp32-elf/bin", "PLATFORMIO_CALLER": "vscode" + }, + "files.associations": { + "*.tcc": "cpp", + "istream": "cpp", + "ostream": "cpp", + "typeinfo": "cpp", + "array": "cpp", + "atomic": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "condition_variable": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "list": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "optional": "cpp", + "ratio": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "limits": "cpp", + "memory": "cpp", + "mutex": "cpp", + "new": "cpp", + "numeric": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "regex": "cpp", + "utility": "cpp", + "valarray": "cpp", + "iterator": "cpp", + "map": "cpp", + "memory_resource": "cpp", + "random": "cpp", + "set": "cpp", + "string": "cpp" } } } \ No newline at end of file