From be53432598e852df2c92180b6d58fdbac6c9600a Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Sat, 11 Mar 2023 14:16:55 +0100 Subject: [PATCH 01/31] Initialize modern C++ rewrite --- modules/control/Firmware/include/README | 39 +++++ .../BluetoothCommunicator.cpp | 8 - .../BluetoothCommunicator.h | 8 - .../lib/Communicator/Communicator.cpp | 62 -------- .../Firmware/lib/Communicator/Communicator.h | 21 --- .../lib/I2CCommunicator/I2CCommunicator.cpp | 8 - .../lib/I2CCommunicator/I2CCommunicator.h | 8 - .../lib/LightController/LightController.cpp | 137 ------------------ .../lib/LightController/LightController.h | 25 ---- modules/control/Firmware/lib/README | 46 ++++++ .../SerialCommunicator/SerialCommunicator.cpp | 8 - .../SerialCommunicator/SerialCommunicator.h | 8 - .../StreamCommunicator/StreamCommunicator.cpp | 27 ---- .../StreamCommunicator/StreamCommunicator.h | 18 --- .../WebsocketCommunicator.cpp | 61 -------- .../WebsocketCommunicator.h | 20 --- modules/control/Firmware/platformio.ini | 43 ++---- modules/control/Firmware/src/main.cpp | 108 +------------- modules/control/Firmware/test/README | 22 +-- 19 files changed, 115 insertions(+), 562 deletions(-) create mode 100644 modules/control/Firmware/include/README delete mode 100644 modules/control/Firmware/lib/BluetoothCommunicator/BluetoothCommunicator.cpp delete mode 100644 modules/control/Firmware/lib/BluetoothCommunicator/BluetoothCommunicator.h delete mode 100644 modules/control/Firmware/lib/Communicator/Communicator.cpp delete mode 100644 modules/control/Firmware/lib/Communicator/Communicator.h delete mode 100644 modules/control/Firmware/lib/I2CCommunicator/I2CCommunicator.cpp delete mode 100644 modules/control/Firmware/lib/I2CCommunicator/I2CCommunicator.h delete mode 100644 modules/control/Firmware/lib/LightController/LightController.cpp delete mode 100644 modules/control/Firmware/lib/LightController/LightController.h create mode 100644 modules/control/Firmware/lib/README delete mode 100644 modules/control/Firmware/lib/SerialCommunicator/SerialCommunicator.cpp delete mode 100644 modules/control/Firmware/lib/SerialCommunicator/SerialCommunicator.h delete mode 100644 modules/control/Firmware/lib/StreamCommunicator/StreamCommunicator.cpp delete mode 100644 modules/control/Firmware/lib/StreamCommunicator/StreamCommunicator.h delete mode 100644 modules/control/Firmware/lib/WebsocketCommunicator/WebsocketCommunicator.cpp delete mode 100644 modules/control/Firmware/lib/WebsocketCommunicator/WebsocketCommunicator.h diff --git a/modules/control/Firmware/include/README b/modules/control/Firmware/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/modules/control/Firmware/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/modules/control/Firmware/lib/BluetoothCommunicator/BluetoothCommunicator.cpp b/modules/control/Firmware/lib/BluetoothCommunicator/BluetoothCommunicator.cpp deleted file mode 100644 index e572651..0000000 --- a/modules/control/Firmware/lib/BluetoothCommunicator/BluetoothCommunicator.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include - -BluetoothCommunicator::BluetoothCommunicator(BluetoothSerial &p_out, int timeout, __SIZE_TYPE__ bufferSize) : StreamCommunicator(p_out, bufferSize) -{ - p_out.begin("Heliox"); - p_out.setTimeout(timeout); -} \ No newline at end of file diff --git a/modules/control/Firmware/lib/BluetoothCommunicator/BluetoothCommunicator.h b/modules/control/Firmware/lib/BluetoothCommunicator/BluetoothCommunicator.h deleted file mode 100644 index d8ec316..0000000 --- a/modules/control/Firmware/lib/BluetoothCommunicator/BluetoothCommunicator.h +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include "BluetoothSerial.h" - -class BluetoothCommunicator : public StreamCommunicator -{ -public: - BluetoothCommunicator(BluetoothSerial &p_out, int timeout, __SIZE_TYPE__ bufferSize); -}; diff --git a/modules/control/Firmware/lib/Communicator/Communicator.cpp b/modules/control/Firmware/lib/Communicator/Communicator.cpp deleted file mode 100644 index 3cd6a57..0000000 --- a/modules/control/Firmware/lib/Communicator/Communicator.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include -#include -#include - -Communicator::Communicator(__SIZE_TYPE__ bufferSize){ - this->messageBuffer = new char[bufferSize]; - this->bufferSize = bufferSize; -} - -void Communicator::sendMessage(int *values, __SIZE_TYPE__ numberOfValues) -{ - -} - -void Communicator::sendMessage(const char message[]) -{ - -} - -char *Communicator::receiveMessage() -{ - -} - -__SIZE_TYPE__ Communicator::calculateMessageOutSize(__SIZE_TYPE__ numberOfValues) -{ - return numberOfValues + (numberOfValues - 1) + 1; -} - -void Communicator::parseIDs(const int values[], __SIZE_TYPE__ numberOfValues, char *output) -{ - String out = ""; - __SIZE_TYPE__ outputSize = calculateMessageOutSize(numberOfValues); - __SIZE_TYPE__ outputCharPointer = 0; - - for (__SIZE_TYPE__ i = 0; i < numberOfValues; i++) - { - out += values[i]; - outputCharPointer++; - if (outputCharPointer < outputSize - 1) - { - out += ','; - outputCharPointer++; - } - } - strcpy(output, out.c_str()); -} - -char *Communicator::getBuffer() -{ - return messageBuffer; -} - -void Communicator::clearBuffer() -{ - memset(getBuffer(), '\0', getBufferSize()); -} - -int Communicator::getBufferSize() -{ - return this->bufferSize; -} \ No newline at end of file diff --git a/modules/control/Firmware/lib/Communicator/Communicator.h b/modules/control/Firmware/lib/Communicator/Communicator.h deleted file mode 100644 index c0680e6..0000000 --- a/modules/control/Firmware/lib/Communicator/Communicator.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _COMMUNICATOR_INCLUDED_ -#define _COMMUNICATOR_INCLUDED_ - -class Communicator -{ -protected: - char *messageBuffer; - __SIZE_TYPE__ bufferSize; - __SIZE_TYPE__ calculateMessageOutSize(__SIZE_TYPE__ numberOfValues); - void parseIDs(const int values[], __SIZE_TYPE__ numberOfValues, char *out); - -public: - Communicator(__SIZE_TYPE__ bufferSize); - virtual void sendMessage(int *values, __SIZE_TYPE__ numberOfValues); - virtual void sendMessage(const char message[]); - virtual char *receiveMessage(); - char *getBuffer(); - void clearBuffer(); - int getBufferSize(); -}; -#endif \ No newline at end of file diff --git a/modules/control/Firmware/lib/I2CCommunicator/I2CCommunicator.cpp b/modules/control/Firmware/lib/I2CCommunicator/I2CCommunicator.cpp deleted file mode 100644 index 46676a2..0000000 --- a/modules/control/Firmware/lib/I2CCommunicator/I2CCommunicator.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include - -I2CCommunicator::I2CCommunicator(TwoWire &w_out, int slaveAddr, int timeout, __SIZE_TYPE__ bufferSize) : StreamCommunicator(w_out, bufferSize) -{ - w_out.begin(slaveAddr); - w_out.setTimeout(timeout); -} \ No newline at end of file diff --git a/modules/control/Firmware/lib/I2CCommunicator/I2CCommunicator.h b/modules/control/Firmware/lib/I2CCommunicator/I2CCommunicator.h deleted file mode 100644 index 7523faa..0000000 --- a/modules/control/Firmware/lib/I2CCommunicator/I2CCommunicator.h +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include - -class I2CCommunicator : public StreamCommunicator -{ -public: - I2CCommunicator(TwoWire &w_out, int slaveAddr, int timeout, __SIZE_TYPE__ bufferSize); -}; diff --git a/modules/control/Firmware/lib/LightController/LightController.cpp b/modules/control/Firmware/lib/LightController/LightController.cpp deleted file mode 100644 index 9336386..0000000 --- a/modules/control/Firmware/lib/LightController/LightController.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include -#include -#include -#include -#include - -LightController::LightController(const int bjtPins[], __SIZE_TYPE__ bjtCount) -{ - this->bjtCount = bjtCount; - this->bjtPins = bjtPins; - this->bjtState = new int[bjtCount]; - EEPROM.begin(64); - initializePins(); - initializeState(); -} - -void LightController::initializeState() -{ - initializeStateDefaultValues(); - restoreState(); -} - -void LightController::initializePins() -{ - for (__SIZE_TYPE__ i = 0; i < bjtCount; i++) - { - pinMode(bjtPins[i], OUTPUT); - } -} - -void LightController::initializeStateDefaultValues() -{ - for (__SIZE_TYPE__ i = 0; i < bjtCount; i++) - { - bjtState[i] = 20; - } -} - -void LightController::restoreState() -{ - for (__SIZE_TYPE__ i = 0; i < bjtCount; i++) - { - bjtState[i] = EEPROM.readInt(i * sizeof(bjtState[i])); - commitPinState(i); - } -} - -void LightController::updateState(const char data[], int steps) -{ - for (__SIZE_TYPE__ i = 0; i < bjtCount; i++) - { - parseRelativeState(data, i, steps); - setAbsoluteState(data, i); - commitState(i); - } -} - -void LightController::parseRelativeState(const char data[], int index, int steps) -{ - char numChar[2]; - itoa(index, numChar, 10); - - if (data[0] == numChar[0]) - { - if (data[1] == 't') - { - if (bjtState[index] != 0) - { - bjtState[index] = 0; - } - else - { - bjtState[index] = 255; - } - } - else if (data[1] == 'i') - { - bjtState[index] = clampState(bjtState[index] + steps); - } - else if (data[1] == 'd') - { - bjtState[index] = clampState(bjtState[index] - steps); - } - } -} - -void LightController::setAbsoluteState(const char data[], int index) -{ - if (strcmp(data, "off") == 0) - { - bjtState[index] = 0; - } - - if (strcmp(data, "on") == 0) - { - bjtState[index] = 255; - } -} - -int LightController::clampState(int stateValue) -{ - int clampedState = stateValue; - if (stateValue > 255) - { - clampedState = 255; - } - else if (stateValue < 0) - { - clampedState = 0; - } - return clampedState; -} - -void LightController::commitState(int index) -{ - commitPinState(index); - EEPROM.writeInt(index * sizeof(bjtState[index]), bjtState[index]); - EEPROM.commit(); -} - -void LightController::commitPinState(int index) -{ - analogWrite(bjtPins[index], bjtState[index]); -} - -int LightController::getBjtCount() -{ - return bjtCount; -} -const int *LightController::getBjtPins() -{ - return bjtPins; -} -int *LightController::getBjtState() -{ - return bjtState; -} diff --git a/modules/control/Firmware/lib/LightController/LightController.h b/modules/control/Firmware/lib/LightController/LightController.h deleted file mode 100644 index 6fbded9..0000000 --- a/modules/control/Firmware/lib/LightController/LightController.h +++ /dev/null @@ -1,25 +0,0 @@ -class LightController -{ -protected: - __SIZE_TYPE__ bjtCount; - const int *bjtPins; - int *bjtState; - -private: - void setAbsoluteState(const char data[], int index); - void parseRelativeState(const char data[], int index, int steps); - int clampState(int stateValue); - void commitState(int index); - void commitPinState(int index); - void initializeStateDefaultValues(); - void restoreState(); - void initializeState(); - void initializePins(); - -public: - LightController(const int bjtPins[], __SIZE_TYPE__ bjtCount); - void updateState(const char data[], int steps); - int getBjtCount(); - const int *getBjtPins(); - int *getBjtState(); -}; diff --git a/modules/control/Firmware/lib/README b/modules/control/Firmware/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/modules/control/Firmware/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/modules/control/Firmware/lib/SerialCommunicator/SerialCommunicator.cpp b/modules/control/Firmware/lib/SerialCommunicator/SerialCommunicator.cpp deleted file mode 100644 index 0ddac36..0000000 --- a/modules/control/Firmware/lib/SerialCommunicator/SerialCommunicator.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include - -SerialCommunicator::SerialCommunicator(HardwareSerial &p_out, int baudRate, int timeout, __SIZE_TYPE__ bufferSize) : StreamCommunicator(p_out, bufferSize) -{ - p_out.begin(baudRate); - p_out.setTimeout(timeout); -} \ No newline at end of file diff --git a/modules/control/Firmware/lib/SerialCommunicator/SerialCommunicator.h b/modules/control/Firmware/lib/SerialCommunicator/SerialCommunicator.h deleted file mode 100644 index 9c2c050..0000000 --- a/modules/control/Firmware/lib/SerialCommunicator/SerialCommunicator.h +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include - -class SerialCommunicator : public StreamCommunicator -{ -public: - SerialCommunicator(HardwareSerial &p_out, int baudRate, int timeout, __SIZE_TYPE__ bufferSize); -}; diff --git a/modules/control/Firmware/lib/StreamCommunicator/StreamCommunicator.cpp b/modules/control/Firmware/lib/StreamCommunicator/StreamCommunicator.cpp deleted file mode 100644 index d0e7dcc..0000000 --- a/modules/control/Firmware/lib/StreamCommunicator/StreamCommunicator.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include - -StreamCommunicator::StreamCommunicator(Stream &s_out, __SIZE_TYPE__ bufferSize) : Communicator(bufferSize), stream(s_out) -{ -} - -void StreamCommunicator::sendMessage(int *values, __SIZE_TYPE__ numberOfValues) -{ - char message[calculateMessageOutSize(numberOfValues)]; - parseIDs(values, numberOfValues, message); - sendMessage(message); -} - -void StreamCommunicator::sendMessage(const char message[]) -{ - stream.println(message); -} - -char *StreamCommunicator::receiveMessage() -{ - if (stream.available()) - { - clearBuffer(); - stream.readBytesUntil('\n', getBuffer(), getBufferSize()); - } - return getBuffer(); -} diff --git a/modules/control/Firmware/lib/StreamCommunicator/StreamCommunicator.h b/modules/control/Firmware/lib/StreamCommunicator/StreamCommunicator.h deleted file mode 100644 index 6ed298f..0000000 --- a/modules/control/Firmware/lib/StreamCommunicator/StreamCommunicator.h +++ /dev/null @@ -1,18 +0,0 @@ -#include "Stream.h" -#include "Communicator.h" - -#ifndef _STREAM_COMMUNICATOR_INCLUDED_ -#define _STREAM_COMMUNICATOR_INCLUDED_ - -class StreamCommunicator: public Communicator -{ -protected: - Stream &stream; - -public: - StreamCommunicator(Stream &s_out, __SIZE_TYPE__ bufferSize); - void sendMessage(int *values, __SIZE_TYPE__ numberOfValues) override; - void sendMessage(const char message[]) override; - char *receiveMessage() override; -}; -#endif \ No newline at end of file diff --git a/modules/control/Firmware/lib/WebsocketCommunicator/WebsocketCommunicator.cpp b/modules/control/Firmware/lib/WebsocketCommunicator/WebsocketCommunicator.cpp deleted file mode 100644 index 2925fca..0000000 --- a/modules/control/Firmware/lib/WebsocketCommunicator/WebsocketCommunicator.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include - -WebsocketCommunicator::WebsocketCommunicator(AsyncWebSocket &socket, AsyncWebServer &server, __SIZE_TYPE__ bufferSize) : Communicator(bufferSize), socket(socket), server(server) -{ - AwsEventHandler onEvent = [this](AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, - void *arg, uint8_t *data, size_t len) - { - switch (type) - { - case WS_EVT_CONNECT: - Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str()); - break; - case WS_EVT_DISCONNECT: - Serial.printf("WebSocket client #%u disconnected\n", client->id()); - break; - case WS_EVT_DATA: - handleMessage(arg, data, len); - break; - case WS_EVT_PONG: - case WS_EVT_ERROR: - break; - } - }; - msgRead = false; - socket.onEvent(onEvent); - server.addHandler(&socket); -} - -void WebsocketCommunicator::sendMessage(int *values, __SIZE_TYPE__ numberOfValues) -{ - char message[calculateMessageOutSize(numberOfValues)]; - parseIDs(values, numberOfValues, message); - sendMessage(message); -} - -void WebsocketCommunicator::sendMessage(const char message[]) -{ - socket.textAll(message); -} - -char *WebsocketCommunicator::receiveMessage() -{ - msgRead = true; - return getBuffer(); -} - -void WebsocketCommunicator::clearBufferSafely() -{ - if (msgRead) - { - clearBuffer(); - } -} - -void WebsocketCommunicator::handleMessage(void *arg, uint8_t *data, size_t len) -{ - msgRead = false; - int effectiveLen = len < bufferSize ? len : bufferSize; - strncpy(messageBuffer, (char *)data, effectiveLen); -} \ No newline at end of file diff --git a/modules/control/Firmware/lib/WebsocketCommunicator/WebsocketCommunicator.h b/modules/control/Firmware/lib/WebsocketCommunicator/WebsocketCommunicator.h deleted file mode 100644 index b7b620d..0000000 --- a/modules/control/Firmware/lib/WebsocketCommunicator/WebsocketCommunicator.h +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include - -class WebsocketCommunicator : public Communicator -{ -private: - void handleMessage(void *arg, uint8_t *data, size_t len); - bool msgRead; - -protected: - AsyncWebSocket &socket; - AsyncWebServer &server; - -public: - WebsocketCommunicator(AsyncWebSocket &socket, AsyncWebServer &server, __SIZE_TYPE__ bufferSize); - void sendMessage(int *values, __SIZE_TYPE__ numberOfValues) override; - void sendMessage(const char message[]) override; - char *receiveMessage() override; - void clearBufferSafely(); -}; \ No newline at end of file diff --git a/modules/control/Firmware/platformio.ini b/modules/control/Firmware/platformio.ini index ca399d9..3fe2f4e 100644 --- a/modules/control/Firmware/platformio.ini +++ b/modules/control/Firmware/platformio.ini @@ -1,28 +1,15 @@ -; PlatformIO Project Configuration File -; -; Build options: build flags, source filter -; Upload options: custom upload port, speed and extra flags -; Library options: dependencies, extra library storages -; Advanced options: extra scripting -; -; Please visit documentation for the other options and examples -; https://docs.platformio.org/page/projectconf.html - -[platformio] -default_envs = Upload_UART - -[env] -platform = espressif32 -board = az-delivery-devkit-v4 -framework = arduino -lib_deps = - erropix/ESP32 AnalogWrite@^0.2 - ESP Async WebServer -board_build.partitions = huge_app.csv - -[env:Upload_UART] -upload_port = COM9 -monitor_port = COM9 - -[env:CI_Build] -build_flags = -D DEBUG=1 \ No newline at end of file +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino +board_build.partitions = huge_app.csv \ No newline at end of file diff --git a/modules/control/Firmware/src/main.cpp b/modules/control/Firmware/src/main.cpp index 0cdfaa9..58b344c 100644 --- a/modules/control/Firmware/src/main.cpp +++ b/modules/control/Firmware/src/main.cpp @@ -1,109 +1,9 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -const int STEPS = 5; -const int WIFI_TIMEOUT = 20; -const int bjtCount = 4; -const int bjtPin[bjtCount] = {SIG1A, SIG1B, SIG2A, SIG2B}; - -BluetoothSerial bt; -AsyncWebServer server(80); -AsyncWebSocket ws("/ws"); - -Communicator *computer; -Communicator *phone; -WebsocketCommunicator *websocket; -LightController *light; - -void websocketTask(void *parameter) -{ - while (true) - { - websocket->sendMessage(light->getBjtState(), light->getBjtCount()); - - vTaskDelay(100 / portTICK_PERIOD_MS); - } - vTaskDelete(NULL); +void setup() { + // put your setup code here, to run once: } -void registerWebSocketTask() -{ - xTaskCreate(websocketTask, "websocketTask", 10000, NULL, 1, NULL); -} - -void connectWifi(int timeout) -{ - int secondsPassed = 0; - WiFi.setHostname("Heliox"); - WiFi.begin(WIFI_SSID, WIFI_PW); - Serial.print("Connecting to WiFi"); - while (WiFi.status() != WL_CONNECTED) - { - if (secondsPassed < timeout) - { - Serial.print("."); - delay(1000); - secondsPassed++; - } - else - { - Serial.println(""); - Serial.println("WiFi timed out"); - return; - } - } - - Serial.println(""); - Serial.println(WiFi.localIP()); - digitalWrite(LEDB, HIGH); -} - -void setup() -{ - pinMode(LEDB, OUTPUT); - digitalWrite(LEDB, LOW); - computer = new SerialCommunicator(Serial, 9600, 5, 50); - phone = new BluetoothCommunicator(bt, 5, 50); - light = new LightController(bjtPin, bjtCount); - websocket = new WebsocketCommunicator(ws, server, 50); - - connectWifi(WIFI_TIMEOUT); - server.begin(); - registerWebSocketTask(); -} - -void computerCycle() -{ - light->updateState(computer->receiveMessage(), STEPS); - computer->sendMessage(light->getBjtState(), light->getBjtCount()); - computer->clearBuffer(); -} - -void phoneCycle() -{ - light->updateState(phone->receiveMessage(), STEPS); - phone->sendMessage(light->getBjtState(), light->getBjtCount()); - phone->clearBuffer(); -} - -void websocketCycle() -{ - light->updateState(websocket->receiveMessage(), STEPS); - websocket->clearBufferSafely(); -} - -void loop() -{ - computerCycle(); - phoneCycle(); - websocketCycle(); - ws.cleanupClients(); +void loop() { + // put your main code here, to run repeatedly: } \ No newline at end of file diff --git a/modules/control/Firmware/test/README b/modules/control/Firmware/test/README index e7d1588..9b1e87b 100644 --- a/modules/control/Firmware/test/README +++ b/modules/control/Firmware/test/README @@ -1,11 +1,11 @@ - -This directory is intended for PlatformIO Unit Testing and project tests. - -Unit Testing is a software testing method by which individual units of -source code, sets of one or more MCU program modules together with associated -control data, usage procedures, and operating procedures, are tested to -determine whether they are fit for use. Unit testing finds problems early -in the development cycle. - -More information about PlatformIO Unit Testing: -- https://docs.platformio.org/page/plus/unit-testing.html + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html From 2113f71722590b08bf04e6efcaa366b0d33963d9 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Sat, 11 Mar 2023 16:35:06 +0100 Subject: [PATCH 02/31] Move files from /include to /src --- .../Firmware/{include => src}/Credentials/CredentialManager.h | 2 +- modules/control/Firmware/src/Credentials/Credentials.h | 0 .../{include => src}/Credentials/Credentials.h.template | 0 modules/control/Firmware/{include => src}/PinMap/PinMap.h | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename modules/control/Firmware/{include => src}/Credentials/CredentialManager.h (71%) create mode 100644 modules/control/Firmware/src/Credentials/Credentials.h rename modules/control/Firmware/{include => src}/Credentials/Credentials.h.template (100%) rename modules/control/Firmware/{include => src}/PinMap/PinMap.h (100%) diff --git a/modules/control/Firmware/include/Credentials/CredentialManager.h b/modules/control/Firmware/src/Credentials/CredentialManager.h similarity index 71% rename from modules/control/Firmware/include/Credentials/CredentialManager.h rename to modules/control/Firmware/src/Credentials/CredentialManager.h index 3d78db0..fce49f9 100644 --- a/modules/control/Firmware/include/Credentials/CredentialManager.h +++ b/modules/control/Firmware/src/Credentials/CredentialManager.h @@ -1,5 +1,5 @@ #ifndef DEBUG -#include +#include "Credentials/Credentials.h" #endif #ifdef DEBUG #define WIFI_SSID "ssid" diff --git a/modules/control/Firmware/src/Credentials/Credentials.h b/modules/control/Firmware/src/Credentials/Credentials.h new file mode 100644 index 0000000..e69de29 diff --git a/modules/control/Firmware/include/Credentials/Credentials.h.template b/modules/control/Firmware/src/Credentials/Credentials.h.template similarity index 100% rename from modules/control/Firmware/include/Credentials/Credentials.h.template rename to modules/control/Firmware/src/Credentials/Credentials.h.template diff --git a/modules/control/Firmware/include/PinMap/PinMap.h b/modules/control/Firmware/src/PinMap/PinMap.h similarity index 100% rename from modules/control/Firmware/include/PinMap/PinMap.h rename to modules/control/Firmware/src/PinMap/PinMap.h From 1ae061c5d48914596502a130af8b91e568ce2562 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Sat, 11 Mar 2023 16:35:18 +0100 Subject: [PATCH 03/31] Update platformio.ini --- modules/control/Firmware/platformio.ini | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/control/Firmware/platformio.ini b/modules/control/Firmware/platformio.ini index 3fe2f4e..8c8e31f 100644 --- a/modules/control/Firmware/platformio.ini +++ b/modules/control/Firmware/platformio.ini @@ -12,4 +12,7 @@ platform = espressif32 board = esp32dev framework = arduino -board_build.partitions = huge_app.csv \ No newline at end of file +board_build.partitions = huge_app.csv +lib_deps = etlcpp/Embedded Template Library@^20.35.12 +monitor_speed=115200 +upload_port=/dev/ttyUSB1 From 7ff332ce1a0f8d6e1f2c91364a9628b2f8c52477 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Sat, 11 Mar 2023 16:35:44 +0100 Subject: [PATCH 04/31] Create FreeRTOS C++ wrappers --- modules/control/Firmware/src/FreeRTOS/Queue.h | 44 +++++++++++++++++++ .../control/Firmware/src/FreeRTOS/Task.cpp | 43 ++++++++++++++++++ modules/control/Firmware/src/FreeRTOS/Task.h | 32 ++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 modules/control/Firmware/src/FreeRTOS/Queue.h create mode 100644 modules/control/Firmware/src/FreeRTOS/Task.cpp create mode 100644 modules/control/Firmware/src/FreeRTOS/Task.h diff --git a/modules/control/Firmware/src/FreeRTOS/Queue.h b/modules/control/Firmware/src/FreeRTOS/Queue.h new file mode 100644 index 0000000..2b8df99 --- /dev/null +++ b/modules/control/Firmware/src/FreeRTOS/Queue.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include +namespace freertos { +template class Queue { +public: + Queue(UBaseType_t capacity) : queueCapacity{capacity} { + handle = xQueueCreate(capacity, sizeof(T)); + } + Queue(const Queue &other) = delete; + Queue(Queue &&other) + : handle{etl::move(other.handle)}, queueCapacity{ + etl::move(other.queueCapacity)} {} + ~Queue() { vQueueDelete(handle); } + Queue &operator=(const Queue &other) = delete; + Queue &operator=(const Queue &&other) { + handle = etl::move(other.handle); + queueCapacity = etl::move(other.queueCapacity); + } + + etl::optional peek(const TickType_t ticksToWait = portMAX_DELAY) { + T buf; + BaseType_t status = xQueuePeek(handle, &buf, ticksToWait); + return status == pdTRUE ? etl::make_optional(buf) : etl::nullopt; + } + size_t size() { return uxQueueMessagesWaiting(handle); } + size_t available() { return uxQueueSpacesAvailable(handle); } + size_t capacity() { return queueCapacity; } + bool push(const T &item, const TickType_t ticksToWait = portMAX_DELAY) { + return xQueueSend(handle, &item, ticksToWait) == pdTRUE; + } + etl::optional pop(const TickType_t ticksToWait = portMAX_DELAY) { + T buf; + BaseType_t status = xQueueReceive(handle, &buf, ticksToWait); + return status == pdTRUE ? etl::make_optional(buf) : etl::nullopt; + } + void clear() { xQueueReset(handle); } + bool isValid() { return handle != NULL; } + +private: + QueueHandle_t handle; + UBaseType_t queueCapacity; +}; +} // namespace freertos diff --git a/modules/control/Firmware/src/FreeRTOS/Task.cpp b/modules/control/Firmware/src/FreeRTOS/Task.cpp new file mode 100644 index 0000000..d823edf --- /dev/null +++ b/modules/control/Firmware/src/FreeRTOS/Task.cpp @@ -0,0 +1,43 @@ +#include "Task.h" + +freertos::Task::Task(TaskFunction_t taskFunction, const etl::string_view name, + const uint32_t stackDepth, void *const params, + UBaseType_t priority, const BaseType_t coreID) + : name{name}, stackDepth{stackDepth}, priority{priority}, coreID{coreID} { + status = xTaskCreatePinnedToCore(taskFunction, name.data(), stackDepth, + params, priority, &handle, coreID); +} + +freertos::Task::Task(Task &&other) + : name{etl::move(other.name)}, handle{etl::move(other.handle)}, + stackDepth{etl::move(other.stackDepth)}, + priority{etl::move(other.priority)}, coreID{etl::move(other.coreID)} {} + +freertos::Task::~Task() { + if (status == pdPASS) { + vTaskDelete(handle); + } +} + +auto freertos::Task::operator=(const Task &&other) -> Task & { + name = etl::move(other.name); + handle = etl::move(other.handle); + stackDepth = etl::move(other.stackDepth); + priority = etl::move(other.priority); + coreID = etl::move(other.coreID); + return *this; +} + +auto freertos::Task::getName() -> etl::string_view { return name; } + +auto freertos::Task::getStackDepth() -> uint32_t { return stackDepth; } + +auto freertos::Task::getPriority() -> UBaseType_t { return priority; } + +auto freertos::Task::setPriority(const UBaseType_t newPriority) -> void { + vTaskPrioritySet(handle, newPriority); +} + +auto freertos::Task::getCoreID() -> BaseType_t { return coreID; } + +auto freertos::Task::isValid() -> bool { return status == pdPASS; } \ No newline at end of file diff --git a/modules/control/Firmware/src/FreeRTOS/Task.h b/modules/control/Firmware/src/FreeRTOS/Task.h new file mode 100644 index 0000000..46f3d47 --- /dev/null +++ b/modules/control/Firmware/src/FreeRTOS/Task.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include + +namespace freertos { +class Task { +public: + Task(TaskFunction_t taskFunction, const etl::string_view name, + const uint32_t stackDepth, void *const params, UBaseType_t priority, + const BaseType_t coreID); + Task(const Task &other) = delete; + Task(Task &&other); + ~Task(); + Task &operator=(const Task &other) = delete; + Task &operator=(const Task &&other); + + etl::string_view getName(); + uint32_t getStackDepth(); + UBaseType_t getPriority(); + void setPriority(const UBaseType_t newPriority); + BaseType_t getCoreID(); + bool isValid(); + +private: + etl::string_view name; + TaskHandle_t handle; + uint32_t stackDepth; + UBaseType_t priority; + BaseType_t coreID; + BaseType_t status; +}; +} // namespace freertos From af9f122a98ef3312e6e57d2c9c660d84bde1a5f6 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Sat, 11 Mar 2023 16:52:25 +0100 Subject: [PATCH 05/31] Implement functional skeleton of message protocol handling --- .../Firmware/src/Messages/Composer.cpp | 15 ++ .../control/Firmware/src/Messages/Composer.h | 9 + .../control/Firmware/src/Messages/Parser.cpp | 242 ++++++++++++++++++ .../control/Firmware/src/Messages/Parser.h | 88 +++++++ .../control/Firmware/src/Messages/Protocol.h | 54 ++++ 5 files changed, 408 insertions(+) create mode 100644 modules/control/Firmware/src/Messages/Composer.cpp create mode 100644 modules/control/Firmware/src/Messages/Composer.h create mode 100644 modules/control/Firmware/src/Messages/Parser.cpp create mode 100644 modules/control/Firmware/src/Messages/Parser.h create mode 100644 modules/control/Firmware/src/Messages/Protocol.h diff --git a/modules/control/Firmware/src/Messages/Composer.cpp b/modules/control/Firmware/src/Messages/Composer.cpp new file mode 100644 index 0000000..87d2428 --- /dev/null +++ b/modules/control/Firmware/src/Messages/Composer.cpp @@ -0,0 +1,15 @@ +#include "Composer.h" + +bool message::composer::sendMessage(protocol::Message type, + etl::string_view content) { + if (content.length() > protocol::MAX_PAYLOAD_LEN) { + return false; + } + uint8_t length = + protocol::HEADER_LEN + static_cast(content.length()); + // Serial.write("HX"); + // Serial.write(length); + // Serial.write(protocol::to_underlying(type)); + // Serial.write(content.data()); + return true; +}; diff --git a/modules/control/Firmware/src/Messages/Composer.h b/modules/control/Firmware/src/Messages/Composer.h new file mode 100644 index 0000000..469d527 --- /dev/null +++ b/modules/control/Firmware/src/Messages/Composer.h @@ -0,0 +1,9 @@ +#pragma once +#include "Protocol.h" +#include + +namespace message { +namespace composer { +bool sendMessage(protocol::Message type, etl::string_view content); +} // namespace composer +} // namespace message diff --git a/modules/control/Firmware/src/Messages/Parser.cpp b/modules/control/Firmware/src/Messages/Parser.cpp new file mode 100644 index 0000000..d619148 --- /dev/null +++ b/modules/control/Firmware/src/Messages/Parser.cpp @@ -0,0 +1,242 @@ +#include "Parser.h" + +message::parser::StateVisitor::StateVisitor(etl::byte_stream_reader &sreader) + : stream{sreader} {} + +auto message::parser::StateVisitor::operator()(state::ModeSelection) -> State { + if (!stream.available()) { + return state::Invalid{}; + } + + auto mode = stream.read(); + if (!mode.has_value()) { + return state::Invalid{}; + } + + switch (mode.value()) { + case static_cast(protocol::Mode::Message): + return state::Message{}; + case static_cast(protocol::Mode::Settings): + return state::Settings{}; + case static_cast(protocol::Mode::LightControl): + return state::LightControl{}; + case static_cast(protocol::Mode::Command): + return state::Command{}; + default: + return state::Invalid{}; + } +} + +auto message::parser::StateVisitor::operator()(state::Message) -> State { + if (!stream.available()) { + return state::Invalid{}; + } + + auto mode = stream.read(); + if (!mode.has_value()) { + return state::Invalid{}; + } + + switch (mode.value()) { + case static_cast(protocol::Message::LightData): + return state::MessageLightData{}; + case static_cast(protocol::Message::Info): + return state::MessageInfo{}; + case static_cast(protocol::Message::Warning): + return state::MessageWarning{}; + case static_cast(protocol::Message::Error): + return state::MessageError{}; + case static_cast(protocol::Message::Success): + return state::MessageSuccess{}; + default: + return state::Invalid{}; + } +} +auto message::parser::StateVisitor::operator()(state::MessageLightData) + -> State { + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()(state::MessageInfo) -> State { + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()(state::MessageWarning) -> State { + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()(state::MessageError) -> State { + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()(state::MessageSuccess) -> State { + return state::Complete{}; +} + +auto message::parser::StateVisitor::operator()(state::Settings) -> State { + if (!stream.available()) { + return state::Invalid{}; + } + + auto mode = stream.read(); + if (!mode.has_value()) { + return state::Invalid{}; + } + + switch (mode.value()) { + case static_cast(protocol::Settings::SetBaud): + return state::SettingsSetBaud{}; + case static_cast(protocol::Settings::SetWifiPassword): + return state::SettingsWifiPassword{}; + case static_cast(protocol::Settings::SetWifiSSID): + return state::SettingsWifiSSID{}; + default: + return state::Invalid{}; + } +} +auto message::parser::StateVisitor::operator()(state::SettingsSetBaud) + -> State { + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()(state::SettingsWifiPassword) + -> State { + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()(state::SettingsWifiSSID) + -> State { + return state::Complete{}; +} + +auto message::parser::StateVisitor::operator()(state::LightControl) -> State { + if (!stream.available()) { + return state::Invalid{}; + } + + auto mode = stream.read(); + if (!mode.has_value()) { + return state::Invalid{}; + } + + switch (mode.value()) { + case static_cast(protocol::LightControl::On): + return state::LightControlOn{}; + case static_cast(protocol::LightControl::Off): + return state::LightControlOff{}; + case static_cast(protocol::LightControl::Toggle): + return state::LightControlToggle{}; + case static_cast(protocol::LightControl::Increase): + return state::LightControlIncrease{}; + case static_cast(protocol::LightControl::Decrease): + return state::LightControlDecrease{}; + default: + return state::Invalid{}; + } +} +auto message::parser::StateVisitor::operator()(state::LightControlOn) -> State { + if (stream.available_bytes() > 0) { + return state::Invalid{}; + } + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()(state::LightControlOff) + -> State { + if (stream.available_bytes() > 0) { + return state::Invalid{}; + } + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()(state::LightControlToggle) + -> State { + if (stream.available_bytes() > 1) { + return state::Invalid{}; + } + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()(state::LightControlIncrease) + -> State { + if (stream.available_bytes() > 1) { + return state::Invalid{}; + } + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()(state::LightControlDecrease) + -> State { + if (stream.available_bytes() > 1) { + return state::Invalid{}; + } + return state::Complete{}; +} + +auto message::parser::StateVisitor::operator()(state::Command) -> State { + if (!stream.available()) { + return state::Invalid{}; + } + + auto mode = stream.read(); + if (!mode.has_value()) { + return state::Invalid{}; + } + + switch (mode.value()) { + case static_cast(protocol::Command::RequestLightData): + return state::CommandRequestLightData{}; + case static_cast(protocol::Command::EnterConsoleFlashing): + return state::CommandEnterConsoleFlashing{}; + case static_cast(protocol::Command::ExitConsoleFlashing): + return state::CommandExitConsoleFlashing{}; + case static_cast(protocol::Command::PairBluetooth): + return state::CommandPairBluetooth{}; + case static_cast(protocol::Command::Help): + return state::CommandHelp{}; + case static_cast(protocol::Command::Version): + return state::CommandVersion{}; + default: + return state::Invalid{}; + } +} +auto message::parser::StateVisitor::operator()(state::CommandRequestLightData) + -> State { + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()( + state::CommandEnterConsoleFlashing) -> State { + if (stream.available_bytes() > 0) { + return state::Invalid{}; + } + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()( + state::CommandExitConsoleFlashing) -> State { + if (stream.available_bytes() > 0) { + return state::Invalid{}; + } + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()(state::CommandPairBluetooth) + -> State { + if (stream.available_bytes() > 0) { + return state::Invalid{}; + } + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()(state::CommandHelp) -> State { + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()(state::CommandVersion) -> State { + return state::Complete{}; +} + +auto message::parser::StateVisitor::operator()(state::Complete) -> State { + return state::Complete{}; +} +auto message::parser::StateVisitor::operator()(state::Invalid) -> State { + return state::Invalid{}; +} + +auto message::parser::parse(etl::byte_stream_reader &reader) -> void { + StateVisitor visitor{reader}; + State state{state::ModeSelection{}}; + while (!etl::holds_alternative(state) && + !etl::holds_alternative(state)) { + state = etl::visit(visitor, state); + } + if (etl::holds_alternative(state)) { + // TODO: error + } +} diff --git a/modules/control/Firmware/src/Messages/Parser.h b/modules/control/Firmware/src/Messages/Parser.h new file mode 100644 index 0000000..9ecaf66 --- /dev/null +++ b/modules/control/Firmware/src/Messages/Parser.h @@ -0,0 +1,88 @@ +#pragma once +#include "Protocol.h" +#include +#include + +namespace message { +namespace parser { +namespace state { +struct Invalid {}; +struct ModeSelection {}; +struct Message {}; +struct MessageLightData {}; +struct MessageInfo {}; +struct MessageWarning {}; +struct MessageError {}; +struct MessageSuccess {}; +struct Settings {}; +struct SettingsSetBaud {}; +struct SettingsWifiPassword {}; +struct SettingsWifiSSID {}; +struct LightControl {}; +struct LightControlOn {}; +struct LightControlOff {}; +struct LightControlToggle {}; +struct LightControlIncrease {}; +struct LightControlDecrease {}; +struct Command {}; +struct CommandRequestLightData {}; +struct CommandEnterConsoleFlashing {}; +struct CommandExitConsoleFlashing {}; +struct CommandPairBluetooth {}; +struct CommandHelp {}; +struct CommandVersion {}; +struct Complete {}; +} // namespace state + +using State = etl::variant< + state::Invalid, state::ModeSelection, state::Message, + state::MessageLightData, state::MessageInfo, state::MessageWarning, + state::MessageError, state::MessageSuccess, state::Settings, + state::SettingsSetBaud, state::SettingsWifiPassword, + state::SettingsWifiSSID, state::LightControl, state::LightControlOn, + state::LightControlOff, state::LightControlToggle, + state::LightControlIncrease, state::LightControlDecrease, state::Command, + state::CommandRequestLightData, state::CommandEnterConsoleFlashing, + state::CommandExitConsoleFlashing, state::CommandPairBluetooth, + state::CommandHelp, state::CommandVersion, state::Complete>; + +void parse(etl::byte_stream_reader &reader); + +class StateVisitor { +private: + etl::byte_stream_reader stream; + +public: + StateVisitor(etl::byte_stream_reader &); + State operator()(state::ModeSelection); + State operator()(state::Message); + State operator()(state::MessageLightData); + State operator()(state::MessageInfo); + State operator()(state::MessageWarning); + State operator()(state::MessageError); + State operator()(state::MessageSuccess); + + State operator()(state::Settings); + State operator()(state::SettingsSetBaud); + State operator()(state::SettingsWifiPassword); + State operator()(state::SettingsWifiSSID); + + State operator()(state::LightControl); + State operator()(state::LightControlOn); + State operator()(state::LightControlOff); + State operator()(state::LightControlToggle); + State operator()(state::LightControlIncrease); + State operator()(state::LightControlDecrease); + State operator()(state::Command); + State operator()(state::CommandRequestLightData); + State operator()(state::CommandEnterConsoleFlashing); + State operator()(state::CommandExitConsoleFlashing); + State operator()(state::CommandPairBluetooth); + State operator()(state::CommandHelp); + State operator()(state::CommandVersion); + + State operator()(state::Complete); + State operator()(state::Invalid); +}; +} // namespace parser +} // namespace message diff --git a/modules/control/Firmware/src/Messages/Protocol.h b/modules/control/Firmware/src/Messages/Protocol.h new file mode 100644 index 0000000..7afcf2b --- /dev/null +++ b/modules/control/Firmware/src/Messages/Protocol.h @@ -0,0 +1,54 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace message { +namespace protocol { +static const uint8_t HEADER_LEN = 5; +static const uint8_t MAX_TOTAL_LEN = 255; +static const uint8_t MAX_PAYLOAD_LEN = MAX_TOTAL_LEN - HEADER_LEN; + +enum class Mode : uint8_t { + Message = 0x0, + Settings = 0x1, + LightControl = 0x2, + Command = 0x3 +}; + +enum class Message : uint8_t { + LightData = 0x0, + Info = 0x1, + Warning = 0x2, + Error = 0x3, + Success = 0x5, +}; + +enum class Settings : uint8_t { + SetBaud = 0x0, + SetWifiPassword = 0x1, + SetWifiSSID = 0x2, +}; + +enum class LightControl : uint8_t { + On = 0x0, + Off = 0x1, + Toggle = 0x2, + Increase = 0x3, + Decrease = 0x4, +}; + +enum class Command : uint8_t { + RequestLightData = 0x0, + EnterConsoleFlashing = 0x1, + ExitConsoleFlashing = 0x2, + PairBluetooth = 0x3, + Help = 0x4, + Version = 0x5, +}; + +using Submode = etl::variant; +} // namespace protocol +} // namespace message From 4eee08aeed739606118e2753e53b769f10487f6e Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Sat, 11 Mar 2023 17:38:23 +0100 Subject: [PATCH 06/31] Add callback function calls to parser --- .../control/Firmware/src/Messages/Parser.cpp | 43 +++++++++++++++++++ .../control/Firmware/src/Messages/Parser.h | 28 ++++++++++-- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/modules/control/Firmware/src/Messages/Parser.cpp b/modules/control/Firmware/src/Messages/Parser.cpp index d619148..0f1a898 100644 --- a/modules/control/Firmware/src/Messages/Parser.cpp +++ b/modules/control/Firmware/src/Messages/Parser.cpp @@ -5,11 +5,13 @@ message::parser::StateVisitor::StateVisitor(etl::byte_stream_reader &sreader) auto message::parser::StateVisitor::operator()(state::ModeSelection) -> State { if (!stream.available()) { + invalidCallback("ModeSelection: Stream not available"); return state::Invalid{}; } auto mode = stream.read(); if (!mode.has_value()) { + invalidCallback("ModeSelection: Mode has no value"); return state::Invalid{}; } @@ -23,17 +25,20 @@ auto message::parser::StateVisitor::operator()(state::ModeSelection) -> State { case static_cast(protocol::Mode::Command): return state::Command{}; default: + invalidCallback("ModeSelection: Invalid index"); return state::Invalid{}; } } auto message::parser::StateVisitor::operator()(state::Message) -> State { if (!stream.available()) { + invalidCallback("Message: Stream not available"); return state::Invalid{}; } auto mode = stream.read(); if (!mode.has_value()) { + invalidCallback("Message: Mode has no value"); return state::Invalid{}; } @@ -49,33 +54,41 @@ auto message::parser::StateVisitor::operator()(state::Message) -> State { case static_cast(protocol::Message::Success): return state::MessageSuccess{}; default: + invalidCallback("Message: Invalid index"); return state::Invalid{}; } } auto message::parser::StateVisitor::operator()(state::MessageLightData) -> State { + messageLightDataCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::MessageInfo) -> State { + messageInfoCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::MessageWarning) -> State { + messageWarningCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::MessageError) -> State { + messageErrorCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::MessageSuccess) -> State { + messageSuccessCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::Settings) -> State { if (!stream.available()) { + invalidCallback("Settings: Stream not available"); return state::Invalid{}; } auto mode = stream.read(); if (!mode.has_value()) { + invalidCallback("Settings: Mode has no value"); return state::Invalid{}; } @@ -87,29 +100,35 @@ auto message::parser::StateVisitor::operator()(state::Settings) -> State { case static_cast(protocol::Settings::SetWifiSSID): return state::SettingsWifiSSID{}; default: + invalidCallback("Settings: Invalid index"); return state::Invalid{}; } } auto message::parser::StateVisitor::operator()(state::SettingsSetBaud) -> State { + settingsSetBaudCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::SettingsWifiPassword) -> State { + settingsWifiPasswordCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::SettingsWifiSSID) -> State { + settingsWifiSSIDCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::LightControl) -> State { if (!stream.available()) { + invalidCallback("LightControl: Stream not available"); return state::Invalid{}; } auto mode = stream.read(); if (!mode.has_value()) { + invalidCallback("LightControl: Mode has no value"); return state::Invalid{}; } @@ -125,51 +144,65 @@ auto message::parser::StateVisitor::operator()(state::LightControl) -> State { case static_cast(protocol::LightControl::Decrease): return state::LightControlDecrease{}; default: + invalidCallback("LightControl: Invalid index"); return state::Invalid{}; } } + auto message::parser::StateVisitor::operator()(state::LightControlOn) -> State { if (stream.available_bytes() > 0) { + invalidCallback("LightControlOn: Too many bytes left"); return state::Invalid{}; } + lightControlOnCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::LightControlOff) -> State { if (stream.available_bytes() > 0) { + invalidCallback("LightControlOff: Too many bytes left"); return state::Invalid{}; } + lightControlOffCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::LightControlToggle) -> State { if (stream.available_bytes() > 1) { + invalidCallback("LightControlToggle: Too many bytes left"); return state::Invalid{}; } + lightControlToggleCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::LightControlIncrease) -> State { if (stream.available_bytes() > 1) { + invalidCallback("LightControlIncrease: Too many bytes left"); return state::Invalid{}; } + lightControlIncreaseCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::LightControlDecrease) -> State { if (stream.available_bytes() > 1) { + invalidCallback("LightControlDecrease: Too many bytes left"); return state::Invalid{}; } + lightControlDecreaseCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::Command) -> State { if (!stream.available()) { + invalidCallback("Command: Stream not available"); return state::Invalid{}; } auto mode = stream.read(); if (!mode.has_value()) { + invalidCallback("Command: Mode has no value"); return state::Invalid{}; } @@ -187,38 +220,48 @@ auto message::parser::StateVisitor::operator()(state::Command) -> State { case static_cast(protocol::Command::Version): return state::CommandVersion{}; default: + invalidCallback("Command: Invalid index"); return state::Invalid{}; } } auto message::parser::StateVisitor::operator()(state::CommandRequestLightData) -> State { + commandRequestLightDataCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()( state::CommandEnterConsoleFlashing) -> State { if (stream.available_bytes() > 0) { + invalidCallback("CommandEnterConsoleFlashing: Too many bytes left"); return state::Invalid{}; } + commandEnterConsoleFlashingCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()( state::CommandExitConsoleFlashing) -> State { if (stream.available_bytes() > 0) { + invalidCallback("CommandExitConsoleFlashing: Too many bytes left"); return state::Invalid{}; } + commandExitConsoleFlashingCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::CommandPairBluetooth) -> State { if (stream.available_bytes() > 0) { + invalidCallback("CommandPairBluetooth: Too many bytes left"); return state::Invalid{}; } + commandPairBluetoothCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::CommandHelp) -> State { + commandHelpCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::CommandVersion) -> State { + commandVersionCallback(); return state::Complete{}; } diff --git a/modules/control/Firmware/src/Messages/Parser.h b/modules/control/Firmware/src/Messages/Parser.h index 9ecaf66..dbd93aa 100644 --- a/modules/control/Firmware/src/Messages/Parser.h +++ b/modules/control/Firmware/src/Messages/Parser.h @@ -1,6 +1,7 @@ #pragma once #include "Protocol.h" #include +#include #include namespace message { @@ -49,9 +50,6 @@ using State = etl::variant< void parse(etl::byte_stream_reader &reader); class StateVisitor { -private: - etl::byte_stream_reader stream; - public: StateVisitor(etl::byte_stream_reader &); State operator()(state::ModeSelection); @@ -83,6 +81,30 @@ public: State operator()(state::Complete); State operator()(state::Invalid); + +private: + etl::byte_stream_reader stream; + // TODO: callback function parameter types + etl::function invalidCallback; + etl::function messageLightDataCallback; + etl::function messageInfoCallback; + etl::function messageWarningCallback; + etl::function messageErrorCallback; + etl::function messageSuccessCallback; + etl::function settingsSetBaudCallback; + etl::function settingsWifiPasswordCallback; + etl::function settingsWifiSSIDCallback; + etl::function lightControlOnCallback; + etl::function lightControlOffCallback; + etl::function lightControlToggleCallback; + etl::function lightControlIncreaseCallback; + etl::function lightControlDecreaseCallback; + etl::function commandRequestLightDataCallback; + etl::function commandEnterConsoleFlashingCallback; + etl::function commandExitConsoleFlashingCallback; + etl::function commandPairBluetoothCallback; + etl::function commandHelpCallback; + etl::function commandVersionCallback; }; } // namespace parser } // namespace message From 10e937b0dd10ee6fcf3574c1a2dc206c971f33ea Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Mon, 13 Mar 2023 13:34:45 +0100 Subject: [PATCH 07/31] Fully implement callback logic --- .../control/Firmware/src/Messages/Parser.cpp | 228 ++++++++++++++++-- .../control/Firmware/src/Messages/Parser.h | 70 +++++- 2 files changed, 267 insertions(+), 31 deletions(-) diff --git a/modules/control/Firmware/src/Messages/Parser.cpp b/modules/control/Firmware/src/Messages/Parser.cpp index 0f1a898..a7476ea 100644 --- a/modules/control/Firmware/src/Messages/Parser.cpp +++ b/modules/control/Firmware/src/Messages/Parser.cpp @@ -1,7 +1,48 @@ #include "Parser.h" +#include -message::parser::StateVisitor::StateVisitor(etl::byte_stream_reader &sreader) - : stream{sreader} {} +message::parser::StateVisitor::StateVisitor( + etl::byte_stream_reader &&sreader, + etl::function invalidCallback, + etl::function> messageLightDataCallback, + etl::function> messageInfoCallback, + etl::function> messageWarningCallback, + etl::function> messageErrorCallback, + etl::function> messageSuccessCallback, + etl::function settingsSetBaudCallback, + etl::function> settingsWifiPasswordCallback, + etl::function> settingsWifiSSIDCallback, + etl::function lightControlOnCallback, + etl::function lightControlOffCallback, + etl::function lightControlToggleCallback, + etl::function lightControlIncreaseCallback, + etl::function lightControlDecreaseCallback, + etl::function commandRequestLightDataCallback, + etl::function commandEnterConsoleFlashingCallback, + etl::function commandExitConsoleFlashingCallback, + etl::function commandPairBluetoothCallback, + etl::function commandHelpCallback, + etl::function commandVersionCallback) + : stream{etl::move(sreader)}, invalidCallback{invalidCallback}, + messageLightDataCallback{messageLightDataCallback}, + messageInfoCallback{messageInfoCallback}, + messageWarningCallback{messageWarningCallback}, + messageErrorCallback{messageErrorCallback}, + messageSuccessCallback{messageSuccessCallback}, + settingsSetBaudCallback{settingsSetBaudCallback}, + settingsWifiPasswordCallback{settingsWifiPasswordCallback}, + settingsWifiSSIDCallback{settingsWifiSSIDCallback}, + lightControlOnCallback{lightControlOnCallback}, + lightControlOffCallback{lightControlOffCallback}, + lightControlToggleCallback{lightControlToggleCallback}, + lightControlIncreaseCallback{lightControlIncreaseCallback}, + lightControlDecreaseCallback{lightControlDecreaseCallback}, + commandRequestLightDataCallback{commandRequestLightDataCallback}, + commandEnterConsoleFlashingCallback{commandEnterConsoleFlashingCallback}, + commandExitConsoleFlashingCallback{commandExitConsoleFlashingCallback}, + commandPairBluetoothCallback{commandPairBluetoothCallback}, + commandHelpCallback{commandHelpCallback}, commandVersionCallback{ + commandVersionCallback} {} auto message::parser::StateVisitor::operator()(state::ModeSelection) -> State { if (!stream.available()) { @@ -58,25 +99,76 @@ auto message::parser::StateVisitor::operator()(state::Message) -> State { return state::Invalid{}; } } + auto message::parser::StateVisitor::operator()(state::MessageLightData) -> State { - messageLightDataCallback(); + if (!stream.available()) { + invalidCallback("MessageLightData: Stream not available"); + return state::Invalid{}; + } + + auto msg = stream.read(stream.available()); + if (!msg.has_value()) { + invalidCallback("MessageLightData: Message has no value"); + return state::Invalid{}; + } + messageLightDataCallback(msg.value()); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::MessageInfo) -> State { - messageInfoCallback(); + if (!stream.available()) { + invalidCallback("MessageInfo: Stream not available"); + return state::Invalid{}; + } + + auto msg = stream.read(stream.available()); + if (!msg.has_value()) { + invalidCallback("MessageInfo: Message has no value"); + return state::Invalid{}; + } + messageInfoCallback(msg.value()); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::MessageWarning) -> State { - messageWarningCallback(); + if (!stream.available()) { + invalidCallback("MessageWarning: Stream not available"); + return state::Invalid{}; + } + + auto msg = stream.read(stream.available()); + if (!msg.has_value()) { + invalidCallback("MessageWarning: Message has no value"); + return state::Invalid{}; + } + messageWarningCallback(msg.value()); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::MessageError) -> State { - messageErrorCallback(); + if (!stream.available()) { + invalidCallback("MessageError: Stream not available"); + return state::Invalid{}; + } + + auto msg = stream.read(stream.available()); + if (!msg.has_value()) { + invalidCallback("MessageError: Message has no value"); + return state::Invalid{}; + } + messageErrorCallback(msg.value()); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::MessageSuccess) -> State { - messageSuccessCallback(); + if (!stream.available()) { + invalidCallback("MessageSuccess: Stream not available"); + return state::Invalid{}; + } + + auto msg = stream.read(stream.available()); + if (!msg.has_value()) { + invalidCallback("MessageSuccess: Message has no value"); + return state::Invalid{}; + } + messageSuccessCallback(msg.value()); return state::Complete{}; } @@ -106,17 +198,51 @@ auto message::parser::StateVisitor::operator()(state::Settings) -> State { } auto message::parser::StateVisitor::operator()(state::SettingsSetBaud) -> State { - settingsSetBaudCallback(); + if (!stream.available()) { + invalidCallback("Settings: Stream not available"); + return state::Invalid{}; + } + + auto baud = stream.read(); + + if (!baud.has_value()) { + invalidCallback("SettingsSetBaud: Baud has no value"); + return state::Invalid{}; + } + + settingsSetBaudCallback(baud.value()); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::SettingsWifiPassword) -> State { - settingsWifiPasswordCallback(); + if (!stream.available()) { + invalidCallback("SettingsWifiPassword: Stream not available"); + return state::Invalid{}; + } + + auto passwd = stream.read(stream.available()); + if (!passwd.has_value()) { + invalidCallback("SettingsWifiPassword: Passwd has no value"); + return state::Invalid{}; + } + + settingsWifiPasswordCallback(passwd.value()); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::SettingsWifiSSID) -> State { - settingsWifiSSIDCallback(); + if (!stream.available()) { + invalidCallback("SettingsWifiSSID: Stream not available"); + return state::Invalid{}; + } + + auto ssid = stream.read(stream.available()); + if (!ssid.has_value()) { + invalidCallback("SettingsWifiSSID: SSID has no value"); + return state::Invalid{}; + } + + settingsWifiSSIDCallback(ssid.value()); return state::Complete{}; } @@ -171,8 +297,14 @@ auto message::parser::StateVisitor::operator()(state::LightControlToggle) if (stream.available_bytes() > 1) { invalidCallback("LightControlToggle: Too many bytes left"); return state::Invalid{}; + } else if (!stream.available()) { + invalidCallback("LightControlToggle: Stream not available"); + return state::Invalid{}; } - lightControlToggleCallback(); + + auto id = stream.read(); + + lightControlToggleCallback(id.value()); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::LightControlIncrease) @@ -180,8 +312,14 @@ auto message::parser::StateVisitor::operator()(state::LightControlIncrease) if (stream.available_bytes() > 1) { invalidCallback("LightControlIncrease: Too many bytes left"); return state::Invalid{}; + } else if (!stream.available()) { + invalidCallback("LightControlIncrease: Stream not available"); + return state::Invalid{}; } - lightControlIncreaseCallback(); + + auto id = stream.read(); + + lightControlIncreaseCallback(id.value()); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::LightControlDecrease) @@ -189,8 +327,14 @@ auto message::parser::StateVisitor::operator()(state::LightControlDecrease) if (stream.available_bytes() > 1) { invalidCallback("LightControlDecrease: Too many bytes left"); return state::Invalid{}; + } else if (!stream.available()) { + invalidCallback("LightControlDecrease: Stream not available"); + return state::Invalid{}; } - lightControlDecreaseCallback(); + + auto id = stream.read(); + + lightControlDecreaseCallback(id.value()); return state::Complete{}; } @@ -257,10 +401,20 @@ auto message::parser::StateVisitor::operator()(state::CommandPairBluetooth) return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::CommandHelp) -> State { + if (stream.available_bytes() > 0) { + invalidCallback("CommandHelp: Too many bytes left"); + return state::Invalid{}; + } + commandHelpCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::CommandVersion) -> State { + if (stream.available_bytes() > 0) { + invalidCallback("CommandVersion: Too many bytes left"); + return state::Invalid{}; + } + commandVersionCallback(); return state::Complete{}; } @@ -272,14 +426,52 @@ auto message::parser::StateVisitor::operator()(state::Invalid) -> State { return state::Invalid{}; } -auto message::parser::parse(etl::byte_stream_reader &reader) -> void { - StateVisitor visitor{reader}; +auto message::parser::parse( + etl::byte_stream_reader &&reader, + etl::function invalidCallback, + etl::function> messageLightDataCallback, + etl::function> messageInfoCallback, + etl::function> messageWarningCallback, + etl::function> messageErrorCallback, + etl::function> messageSuccessCallback, + etl::function settingsSetBaudCallback, + etl::function> settingsWifiPasswordCallback, + etl::function> settingsWifiSSIDCallback, + etl::function lightControlOnCallback, + etl::function lightControlOffCallback, + etl::function lightControlToggleCallback, + etl::function lightControlIncreaseCallback, + etl::function lightControlDecreaseCallback, + etl::function commandRequestLightDataCallback, + etl::function commandEnterConsoleFlashingCallback, + etl::function commandExitConsoleFlashingCallback, + etl::function commandPairBluetoothCallback, + etl::function commandHelpCallback, + etl::function commandVersionCallback) -> void { + StateVisitor visitor{etl::move(reader), + invalidCallback, + messageLightDataCallback, + messageInfoCallback, + messageWarningCallback, + messageErrorCallback, + messageSuccessCallback, + settingsSetBaudCallback, + settingsWifiPasswordCallback, + settingsWifiSSIDCallback, + lightControlOnCallback, + lightControlOffCallback, + lightControlToggleCallback, + lightControlIncreaseCallback, + lightControlDecreaseCallback, + commandRequestLightDataCallback, + commandEnterConsoleFlashingCallback, + commandExitConsoleFlashingCallback, + commandPairBluetoothCallback, + commandHelpCallback, + commandVersionCallback}; State state{state::ModeSelection{}}; while (!etl::holds_alternative(state) && !etl::holds_alternative(state)) { state = etl::visit(visitor, state); } - if (etl::holds_alternative(state)) { - // TODO: error - } } diff --git a/modules/control/Firmware/src/Messages/Parser.h b/modules/control/Firmware/src/Messages/Parser.h index dbd93aa..d9e8774 100644 --- a/modules/control/Firmware/src/Messages/Parser.h +++ b/modules/control/Firmware/src/Messages/Parser.h @@ -2,6 +2,8 @@ #include "Protocol.h" #include #include +#include +#include #include namespace message { @@ -47,11 +49,53 @@ using State = etl::variant< state::CommandExitConsoleFlashing, state::CommandPairBluetooth, state::CommandHelp, state::CommandVersion, state::Complete>; -void parse(etl::byte_stream_reader &reader); +void parse( + etl::byte_stream_reader &&reader, + etl::function invalidCallback, + etl::function> messageLightDataCallback, + etl::function> messageInfoCallback, + etl::function> messageWarningCallback, + etl::function> messageErrorCallback, + etl::function> messageSuccessCallback, + etl::function settingsSetBaudCallback, + etl::function> settingsWifiPasswordCallback, + etl::function> settingsWifiSSIDCallback, + etl::function lightControlOnCallback, + etl::function lightControlOffCallback, + etl::function lightControlToggleCallback, + etl::function lightControlIncreaseCallback, + etl::function lightControlDecreaseCallback, + etl::function commandRequestLightDataCallback, + etl::function commandEnterConsoleFlashingCallback, + etl::function commandExitConsoleFlashingCallback, + etl::function commandPairBluetoothCallback, + etl::function commandHelpCallback, + etl::function commandVersionCallback); class StateVisitor { public: - StateVisitor(etl::byte_stream_reader &); + StateVisitor( + etl::byte_stream_reader &&sreader, + etl::function invalidCallback, + etl::function> messageLightDataCallback, + etl::function> messageInfoCallback, + etl::function> messageWarningCallback, + etl::function> messageErrorCallback, + etl::function> messageSuccessCallback, + etl::function settingsSetBaudCallback, + etl::function> settingsWifiPasswordCallback, + etl::function> settingsWifiSSIDCallback, + etl::function lightControlOnCallback, + etl::function lightControlOffCallback, + etl::function lightControlToggleCallback, + etl::function lightControlIncreaseCallback, + etl::function lightControlDecreaseCallback, + etl::function commandRequestLightDataCallback, + etl::function commandEnterConsoleFlashingCallback, + etl::function commandExitConsoleFlashingCallback, + etl::function commandPairBluetoothCallback, + etl::function commandHelpCallback, + etl::function commandVersionCallback); State operator()(state::ModeSelection); State operator()(state::Message); State operator()(state::MessageLightData); @@ -86,19 +130,19 @@ private: etl::byte_stream_reader stream; // TODO: callback function parameter types etl::function invalidCallback; - etl::function messageLightDataCallback; - etl::function messageInfoCallback; - etl::function messageWarningCallback; - etl::function messageErrorCallback; - etl::function messageSuccessCallback; - etl::function settingsSetBaudCallback; - etl::function settingsWifiPasswordCallback; - etl::function settingsWifiSSIDCallback; + etl::function> messageLightDataCallback; + etl::function> messageInfoCallback; + etl::function> messageWarningCallback; + etl::function> messageErrorCallback; + etl::function> messageSuccessCallback; + etl::function settingsSetBaudCallback; + etl::function> settingsWifiPasswordCallback; + etl::function> settingsWifiSSIDCallback; etl::function lightControlOnCallback; etl::function lightControlOffCallback; - etl::function lightControlToggleCallback; - etl::function lightControlIncreaseCallback; - etl::function lightControlDecreaseCallback; + etl::function lightControlToggleCallback; + etl::function lightControlIncreaseCallback; + etl::function lightControlDecreaseCallback; etl::function commandRequestLightDataCallback; etl::function commandEnterConsoleFlashingCallback; etl::function commandExitConsoleFlashingCallback; From 1e40e1f8708549e8cae0e3e2b0bb845065c75d11 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Fri, 24 Mar 2023 14:08:42 +0100 Subject: [PATCH 08/31] Implement basic light controller logic --- .../Firmware/src/LightController/Controller.h | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 modules/control/Firmware/src/LightController/Controller.h diff --git a/modules/control/Firmware/src/LightController/Controller.h b/modules/control/Firmware/src/LightController/Controller.h new file mode 100644 index 0000000..0fe8d6f --- /dev/null +++ b/modules/control/Firmware/src/LightController/Controller.h @@ -0,0 +1,84 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace LightController { +enum class LightActionType { INCREASE, DECREASE, TOGGLE, ON, OFF }; +using LightAction = etl::pair>; +void init(const etl::span pins); + +template class Controller { +public: + Controller(size_t actionQueueSize, UBaseType_t priority, BaseType_t coreID) + : lightPins{PIN...}, lightActions{actionQueueSize}, + lightControllerTask{exec, "control lights", stackDepth, + this, priority, coreID} { + for (auto pin : lightPins) { + pinMode(pin, OUTPUT); + } + } + + freertos::Queue &getActionQueue() { return lightActions; } + constexpr size_t lightCount() { return lightPins.size(); } + const etl::span getPins() { return lightPins; } + +private: + static void exec(void *controllerPtr) { + Controller *controller{ + static_cast *>(controllerPtr)}; + etl::array lightStates; + const uint8_t step = 5; + + while (true) { + auto action{controller->getActionQueue().pop()}; + if (action.has_value()) { + LightActionType type{action.value().first}; + etl::optional index{action.value().second}; + + switch (type) { + case LightActionType::INCREASE: + if (index.has_value() && index.value() < lightStates.size() && + lightStates[index.value()] <= 255 - step) { + lightStates[index.value()] += step; + } + break; + case LightActionType::DECREASE: + if (index.has_value() && index.value() < lightStates.size() && + lightStates[index.value()] >= 0) { + lightStates[index.value()] -= step; + } + break; + case LightActionType::TOGGLE: + if (index.has_value() && index.value() < lightStates.size()) { + lightStates[index.value()] = + lightStates[index.value()] == 0 ? 255 : 0; + } + break; + case LightActionType::ON: + lightStates.fill(255); + break; + case LightActionType::OFF: + lightStates.fill(0); + break; + default: + break; + } + + for (size_t lightIndex = 0; lightIndex < lightStates.size(); + ++lightIndex) { + analogWrite(controller->getPins()[lightIndex], + lightStates[lightIndex]); + } + } + } + } + + static const uint32_t stackDepth = 2048; + etl::array lightPins; + freertos::Queue lightActions; + freertos::Task lightControllerTask; +}; +} // namespace LightController From 21b5796ee3287ec0ace682a5549eb2f0aca687f0 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Fri, 24 Mar 2023 14:09:00 +0100 Subject: [PATCH 09/31] Add sleep function --- modules/control/Firmware/src/FreeRTOS/Util.h | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 modules/control/Firmware/src/FreeRTOS/Util.h diff --git a/modules/control/Firmware/src/FreeRTOS/Util.h b/modules/control/Firmware/src/FreeRTOS/Util.h new file mode 100644 index 0000000..4feead0 --- /dev/null +++ b/modules/control/Firmware/src/FreeRTOS/Util.h @@ -0,0 +1,8 @@ +#pragma once +#include + +namespace freertos { +/// @brief Blocks the task for the specified amount of time +/// @param time in milliseconds +void sleep(int time) { vTaskDelay(time * portTICK_PERIOD_MS); } +} // namespace freertos From 215315bd3a26b5f1ec0969a830291a6c984ffb1b Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Fri, 24 Mar 2023 14:10:28 +0100 Subject: [PATCH 10/31] Remove unused function declaration --- modules/control/Firmware/src/LightController/Controller.h | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/control/Firmware/src/LightController/Controller.h b/modules/control/Firmware/src/LightController/Controller.h index 0fe8d6f..9b8b74f 100644 --- a/modules/control/Firmware/src/LightController/Controller.h +++ b/modules/control/Firmware/src/LightController/Controller.h @@ -8,7 +8,6 @@ namespace LightController { enum class LightActionType { INCREASE, DECREASE, TOGGLE, ON, OFF }; using LightAction = etl::pair>; -void init(const etl::span pins); template class Controller { public: From 5de0b22adabccb5f35400402c8cf35fa04aa934e Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Fri, 24 Mar 2023 16:42:13 +0100 Subject: [PATCH 11/31] Make pinmode for-loop typing more declarative --- modules/control/Firmware/src/LightController/Controller.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/control/Firmware/src/LightController/Controller.h b/modules/control/Firmware/src/LightController/Controller.h index 9b8b74f..e5aa7d6 100644 --- a/modules/control/Firmware/src/LightController/Controller.h +++ b/modules/control/Firmware/src/LightController/Controller.h @@ -15,7 +15,7 @@ public: : lightPins{PIN...}, lightActions{actionQueueSize}, lightControllerTask{exec, "control lights", stackDepth, this, priority, coreID} { - for (auto pin : lightPins) { + for (int pin : lightPins) { pinMode(pin, OUTPUT); } } From d1369fd8ecf72e5c2150bd4811b6eac0b179f905 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Fri, 24 Mar 2023 16:51:25 +0100 Subject: [PATCH 12/31] Enable -Wall build flag --- modules/control/Firmware/platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/control/Firmware/platformio.ini b/modules/control/Firmware/platformio.ini index 8c8e31f..e11936a 100644 --- a/modules/control/Firmware/platformio.ini +++ b/modules/control/Firmware/platformio.ini @@ -15,4 +15,4 @@ framework = arduino board_build.partitions = huge_app.csv lib_deps = etlcpp/Embedded Template Library@^20.35.12 monitor_speed=115200 -upload_port=/dev/ttyUSB1 +build_flags=-Wall \ No newline at end of file From 0a8802fb5128bd31554fe6adf21c71f0b596749e Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Fri, 24 Mar 2023 17:19:03 +0100 Subject: [PATCH 13/31] Clangtidy improvements (const & noexcept) --- modules/control/Firmware/src/FreeRTOS/Task.cpp | 14 +++++++------- modules/control/Firmware/src/FreeRTOS/Task.h | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/control/Firmware/src/FreeRTOS/Task.cpp b/modules/control/Firmware/src/FreeRTOS/Task.cpp index d823edf..acd1a89 100644 --- a/modules/control/Firmware/src/FreeRTOS/Task.cpp +++ b/modules/control/Firmware/src/FreeRTOS/Task.cpp @@ -8,7 +8,7 @@ freertos::Task::Task(TaskFunction_t taskFunction, const etl::string_view name, params, priority, &handle, coreID); } -freertos::Task::Task(Task &&other) +freertos::Task::Task(Task &&other) noexcept : name{etl::move(other.name)}, handle{etl::move(other.handle)}, stackDepth{etl::move(other.stackDepth)}, priority{etl::move(other.priority)}, coreID{etl::move(other.coreID)} {} @@ -19,7 +19,7 @@ freertos::Task::~Task() { } } -auto freertos::Task::operator=(const Task &&other) -> Task & { +auto freertos::Task::operator=(const Task &&other) noexcept -> Task & { name = etl::move(other.name); handle = etl::move(other.handle); stackDepth = etl::move(other.stackDepth); @@ -28,16 +28,16 @@ auto freertos::Task::operator=(const Task &&other) -> Task & { return *this; } -auto freertos::Task::getName() -> etl::string_view { return name; } +auto freertos::Task::getName() const -> etl::string_view { return name; } -auto freertos::Task::getStackDepth() -> uint32_t { return stackDepth; } +auto freertos::Task::getStackDepth() const -> uint32_t { return stackDepth; } -auto freertos::Task::getPriority() -> UBaseType_t { return priority; } +auto freertos::Task::getPriority() const -> UBaseType_t { return priority; } auto freertos::Task::setPriority(const UBaseType_t newPriority) -> void { vTaskPrioritySet(handle, newPriority); } -auto freertos::Task::getCoreID() -> BaseType_t { return coreID; } +auto freertos::Task::getCoreID() const -> BaseType_t { return coreID; } -auto freertos::Task::isValid() -> bool { return status == pdPASS; } \ No newline at end of file +auto freertos::Task::isValid() const -> bool { return status == pdPASS; } \ No newline at end of file diff --git a/modules/control/Firmware/src/FreeRTOS/Task.h b/modules/control/Firmware/src/FreeRTOS/Task.h index 46f3d47..fce288e 100644 --- a/modules/control/Firmware/src/FreeRTOS/Task.h +++ b/modules/control/Firmware/src/FreeRTOS/Task.h @@ -9,17 +9,17 @@ public: const uint32_t stackDepth, void *const params, UBaseType_t priority, const BaseType_t coreID); Task(const Task &other) = delete; - Task(Task &&other); + Task(Task &&other) noexcept; ~Task(); Task &operator=(const Task &other) = delete; - Task &operator=(const Task &&other); + Task &operator=(const Task &&other) noexcept; - etl::string_view getName(); - uint32_t getStackDepth(); - UBaseType_t getPriority(); + etl::string_view getName() const; + uint32_t getStackDepth() const; + UBaseType_t getPriority() const; void setPriority(const UBaseType_t newPriority); - BaseType_t getCoreID(); - bool isValid(); + BaseType_t getCoreID() const; + bool isValid() const; private: etl::string_view name; From c5f3220569de6bd2a9eec418aa404ecd4cb37f92 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Fri, 24 Mar 2023 17:23:09 +0100 Subject: [PATCH 14/31] Enable clangtidy checking --- modules/control/Firmware/platformio.ini | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/control/Firmware/platformio.ini b/modules/control/Firmware/platformio.ini index e11936a..a810fe6 100644 --- a/modules/control/Firmware/platformio.ini +++ b/modules/control/Firmware/platformio.ini @@ -15,4 +15,7 @@ framework = arduino board_build.partitions = huge_app.csv lib_deps = etlcpp/Embedded Template Library@^20.35.12 monitor_speed=115200 -build_flags=-Wall \ No newline at end of file +build_flags=-Wall +check_tool = clangtidy +check_flags = + clangtidy: --checks=-*,clang-diagnostic-*,-clang-diagnostic-unused-value,clang-analyzer-*,-*,bugprone-*,performance-*,readability-*,-readability-magic-numbers,-readability-braces-around-statements,-readability-inconsistent-declaration-parameter-name,-readability-named-parameter --fix From 428a0f437fd5c0bd0d48bb3048386a03d1aeb673 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Sat, 25 Mar 2023 18:07:57 +0100 Subject: [PATCH 15/31] Add sleepForever() function --- modules/control/Firmware/src/FreeRTOS/Util.cpp | 3 +++ modules/control/Firmware/src/FreeRTOS/Util.h | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 modules/control/Firmware/src/FreeRTOS/Util.cpp diff --git a/modules/control/Firmware/src/FreeRTOS/Util.cpp b/modules/control/Firmware/src/FreeRTOS/Util.cpp new file mode 100644 index 0000000..2f9babb --- /dev/null +++ b/modules/control/Firmware/src/FreeRTOS/Util.cpp @@ -0,0 +1,3 @@ +#include "Util.h" +void freertos::sleep(int time) { vTaskDelay(time * portTICK_PERIOD_MS); } +void freertos::sleepForever() { vTaskDelay(portMAX_DELAY); } \ No newline at end of file diff --git a/modules/control/Firmware/src/FreeRTOS/Util.h b/modules/control/Firmware/src/FreeRTOS/Util.h index 4feead0..c715747 100644 --- a/modules/control/Firmware/src/FreeRTOS/Util.h +++ b/modules/control/Firmware/src/FreeRTOS/Util.h @@ -4,5 +4,8 @@ namespace freertos { /// @brief Blocks the task for the specified amount of time /// @param time in milliseconds -void sleep(int time) { vTaskDelay(time * portTICK_PERIOD_MS); } +void sleep(int time); + +/// @brief Sleep for the maximum delay possible +void sleepForever(); } // namespace freertos From 96c39d4792492d8012b94ec7277639f5927a01d3 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Sat, 25 Mar 2023 18:09:11 +0100 Subject: [PATCH 16/31] Accept T as copy instead of referende xQueueSend() copies the value, so accepting a reference is misleading --- modules/control/Firmware/src/FreeRTOS/Queue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/control/Firmware/src/FreeRTOS/Queue.h b/modules/control/Firmware/src/FreeRTOS/Queue.h index 2b8df99..99f2fa0 100644 --- a/modules/control/Firmware/src/FreeRTOS/Queue.h +++ b/modules/control/Firmware/src/FreeRTOS/Queue.h @@ -26,7 +26,7 @@ public: size_t size() { return uxQueueMessagesWaiting(handle); } size_t available() { return uxQueueSpacesAvailable(handle); } size_t capacity() { return queueCapacity; } - bool push(const T &item, const TickType_t ticksToWait = portMAX_DELAY) { + bool push(const T item, const TickType_t ticksToWait = portMAX_DELAY) { return xQueueSend(handle, &item, ticksToWait) == pdTRUE; } etl::optional pop(const TickType_t ticksToWait = portMAX_DELAY) { From 48c2cdf97b07dc7d74e192c9ce0784fe080c8228 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Sat, 25 Mar 2023 18:09:28 +0100 Subject: [PATCH 17/31] Implement freertos mutex wrapper --- .../control/Firmware/src/FreeRTOS/Mutex.cpp | 20 +++++++++++++++++++ modules/control/Firmware/src/FreeRTOS/Mutex.h | 19 ++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 modules/control/Firmware/src/FreeRTOS/Mutex.cpp create mode 100644 modules/control/Firmware/src/FreeRTOS/Mutex.h diff --git a/modules/control/Firmware/src/FreeRTOS/Mutex.cpp b/modules/control/Firmware/src/FreeRTOS/Mutex.cpp new file mode 100644 index 0000000..7f8683a --- /dev/null +++ b/modules/control/Firmware/src/FreeRTOS/Mutex.cpp @@ -0,0 +1,20 @@ +#include "Mutex.h" +#include + +freertos::Mutex::Mutex() : handle{xSemaphoreCreateMutex()} {} +freertos::Mutex::Mutex(Mutex &&other) noexcept { + handle = etl::move(other.handle); +} +freertos::Mutex::~Mutex() { vSemaphoreDelete(handle); } +auto freertos::Mutex::operator=(const Mutex &&other) noexcept -> Mutex & { + handle = etl::move(other.handle); + return *this; +} +auto freertos::Mutex::lock(TickType_t timeout) -> bool{ + BaseType_t success = xSemaphoreTake(handle, timeout); + return success == pdTRUE ? true : false; +} +auto freertos::Mutex::unlock() -> bool { + BaseType_t success = xSemaphoreGive(handle); + return success == pdTRUE ? true : false; +} \ No newline at end of file diff --git a/modules/control/Firmware/src/FreeRTOS/Mutex.h b/modules/control/Firmware/src/FreeRTOS/Mutex.h new file mode 100644 index 0000000..77ec4b8 --- /dev/null +++ b/modules/control/Firmware/src/FreeRTOS/Mutex.h @@ -0,0 +1,19 @@ +#pragma once +#include + +namespace freertos { +class Mutex { +public: + Mutex(); + Mutex(const Mutex &other) = delete; + Mutex(Mutex &&other) noexcept; + ~Mutex(); + Mutex &operator=(const Mutex &other) = delete; + Mutex &operator=(const Mutex &&other) noexcept; + bool lock(TickType_t timeout = portMAX_DELAY); + bool unlock(); + +private: + SemaphoreHandle_t handle; +}; +} // namespace freertos From d1f9e3d88c0df5590c18c721bdde4ec024524d37 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Sat, 25 Mar 2023 18:09:47 +0100 Subject: [PATCH 18/31] Implement custom barebones circular buffer --- .../Firmware/src/Util/CircularBuffer.h | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 modules/control/Firmware/src/Util/CircularBuffer.h diff --git a/modules/control/Firmware/src/Util/CircularBuffer.h b/modules/control/Firmware/src/Util/CircularBuffer.h new file mode 100644 index 0000000..c65f85e --- /dev/null +++ b/modules/control/Firmware/src/Util/CircularBuffer.h @@ -0,0 +1,21 @@ +#pragma once +#include +#include + +namespace util { +template class CircularBuffer { +public: + T &peek() { return buffer[index]; } + + /// @brief Warning: Does not deconstruct entry! If necessary use + /// etl::circular_buffer instead! + T &pop() { + return buffer[index]; + index = (index + 1) % buffer.size(); + } + +private: + etl::array buffer; + size_t index; +}; +} // namespace util From e2a64ac2311a5b210a6d5486d3e034b1e373c23a Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Sat, 25 Mar 2023 18:10:25 +0100 Subject: [PATCH 19/31] Implement serial interaction --- .../src/Communication/Serial/SerialReceiver.h | 61 +++++++++++++++++++ .../src/Communication/Serial/SerialSender.cpp | 24 ++++++++ .../src/Communication/Serial/SerialSender.h | 27 ++++++++ .../control/Firmware/src/Messages/Message.h | 12 ++++ 4 files changed, 124 insertions(+) create mode 100644 modules/control/Firmware/src/Communication/Serial/SerialReceiver.h create mode 100644 modules/control/Firmware/src/Communication/Serial/SerialSender.cpp create mode 100644 modules/control/Firmware/src/Communication/Serial/SerialSender.h create mode 100644 modules/control/Firmware/src/Messages/Message.h diff --git a/modules/control/Firmware/src/Communication/Serial/SerialReceiver.h b/modules/control/Firmware/src/Communication/Serial/SerialReceiver.h new file mode 100644 index 0000000..ae5b224 --- /dev/null +++ b/modules/control/Firmware/src/Communication/Serial/SerialReceiver.h @@ -0,0 +1,61 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace comm { +namespace serial { +template class SerialReceiver { +public: + SerialReceiver(HardwareSerial &port, + freertos::Queue &messageQueue, + UBaseType_t priority, BaseType_t coreID) + : port{port}, messageQueue{messageQueue}, + serialReceiverTask{ + exec, "receive serial data", stackDepth, this, priority, coreID} {} + +private: + static void exec(void *receiverPtr) { + freertos::sleep(20); // wait until Serial port has been initialized + SerialReceiver *receiver = + static_cast *>(receiverPtr); + + while (true) { + receiver->magicNumberBuf.clear(); + receiver->magicNumberBuf.resize(receiver->magicNumber.length()); + size_t availableBytes = receiver->port.available(); + if (availableBytes >= receiver->magicNumber.length() + 1) { + receiver->port.readBytes(receiver->magicNumberBuf.data(), + receiver->magicNumber.length()); + receiver->port.println(receiver->magicNumberBuf == + receiver->magicNumber); + + uint8_t size; + receiver->port.readBytes(&size, sizeof(size)); + if (receiver->port.available() >= size) { + message::Message &msg = receiver->buffer.pop(); + msg.second.lock(); + msg.first.clear(); + msg.first.resize(size); + receiver->port.readBytes(msg.first.data(), size); + msg.second.unlock(); + receiver->messageQueue.push(&msg); + } + } + } + } + + HardwareSerial &port; + freertos::Queue &messageQueue; + etl::string<4> magicNumberBuf; + const etl::string<2> magicNumber{"HX"}; + util::CircularBuffer buffer; + freertos::Task serialReceiverTask; + static const uint32_t stackDepth = 2048; +}; +} // namespace serial +} // namespace comm diff --git a/modules/control/Firmware/src/Communication/Serial/SerialSender.cpp b/modules/control/Firmware/src/Communication/Serial/SerialSender.cpp new file mode 100644 index 0000000..b687f31 --- /dev/null +++ b/modules/control/Firmware/src/Communication/Serial/SerialSender.cpp @@ -0,0 +1,24 @@ +#include + +comm::serial::SerialSender::SerialSender(HardwareSerial &port, + UBaseType_t queueCapacity, + UBaseType_t priority, + BaseType_t coreID) + : port{port}, messages{queueCapacity}, SerialSenderTask{ + exec, "send serial data", + stackDepth, this, + priority, coreID} {} + +void comm::serial::SerialSender::exec(void *senderPtr) { + SerialSender *sender = static_cast(senderPtr); + freertos::sleep(20); // wait until Serial port has been initialized + while (true) { + if (!sender->port.availableForWrite()) { + continue; + } + auto message = sender->messages.pop(); + if (message.has_value()) { + sender->port.write(message.value().data(), message.value().size()); + } + } +} \ No newline at end of file diff --git a/modules/control/Firmware/src/Communication/Serial/SerialSender.h b/modules/control/Firmware/src/Communication/Serial/SerialSender.h new file mode 100644 index 0000000..1ed80c0 --- /dev/null +++ b/modules/control/Firmware/src/Communication/Serial/SerialSender.h @@ -0,0 +1,27 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace comm { +namespace serial { +class SerialSender { +public: + SerialSender(HardwareSerial &port, UBaseType_t queueCapacity, + UBaseType_t priority, BaseType_t coreID); + freertos::Queue> &getMessageQueue() { return messages; } + +private: + static void exec(void *senderPtr); + HardwareSerial &port; + freertos::Queue> messages; + freertos::Task SerialSenderTask; + static const uint32_t stackDepth = 2048; +}; +} // namespace serial +} // namespace comm diff --git a/modules/control/Firmware/src/Messages/Message.h b/modules/control/Firmware/src/Messages/Message.h new file mode 100644 index 0000000..7987868 --- /dev/null +++ b/modules/control/Firmware/src/Messages/Message.h @@ -0,0 +1,12 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace message { +using Message = + etl::pair, + freertos::Mutex>; +} // namespace message From 908a6becba0ee226460fb27bbe9b793867a81dad Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Thu, 30 Mar 2023 17:48:53 +0200 Subject: [PATCH 20/31] Implement BinarySemaphore freertos wrapper --- .../Firmware/src/FreeRTOS/BinarySemaphore.cpp | 26 +++++++++++++++++++ .../Firmware/src/FreeRTOS/BinarySemaphore.h | 20 ++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 modules/control/Firmware/src/FreeRTOS/BinarySemaphore.cpp create mode 100644 modules/control/Firmware/src/FreeRTOS/BinarySemaphore.h diff --git a/modules/control/Firmware/src/FreeRTOS/BinarySemaphore.cpp b/modules/control/Firmware/src/FreeRTOS/BinarySemaphore.cpp new file mode 100644 index 0000000..96464ae --- /dev/null +++ b/modules/control/Firmware/src/FreeRTOS/BinarySemaphore.cpp @@ -0,0 +1,26 @@ +#include +#include + +freertos::BinarySemaphore::BinarySemaphore() + : handle{xSemaphoreCreateBinary()} {} +freertos::BinarySemaphore::BinarySemaphore(BinarySemaphore &&other) noexcept { + handle = etl::move(other.handle); +} +freertos::BinarySemaphore::~BinarySemaphore() { vSemaphoreDelete(handle); } +auto freertos::BinarySemaphore::operator=( + const BinarySemaphore &&other) noexcept -> BinarySemaphore & { + handle = etl::move(other.handle); + return *this; +} +auto freertos::BinarySemaphore::take(TickType_t timeout) -> bool { + BaseType_t success = xSemaphoreTake(handle, timeout); + return success == pdTRUE ? true : false; +} +auto freertos::BinarySemaphore::give() -> bool { + BaseType_t success = xSemaphoreGive(handle); + return success == pdTRUE ? true : false; +} + +auto freertos::BinarySemaphore::getCount() -> size_t { + return uxSemaphoreGetCount(handle); +} \ No newline at end of file diff --git a/modules/control/Firmware/src/FreeRTOS/BinarySemaphore.h b/modules/control/Firmware/src/FreeRTOS/BinarySemaphore.h new file mode 100644 index 0000000..08fb38d --- /dev/null +++ b/modules/control/Firmware/src/FreeRTOS/BinarySemaphore.h @@ -0,0 +1,20 @@ +#pragma once +#include + +namespace freertos { +class BinarySemaphore { +public: + BinarySemaphore(); + BinarySemaphore(const BinarySemaphore &other) = delete; + BinarySemaphore(BinarySemaphore &&other) noexcept; + ~BinarySemaphore(); + BinarySemaphore &operator=(const BinarySemaphore &other) = delete; + BinarySemaphore &operator=(const BinarySemaphore &&other) noexcept; + bool take(TickType_t timeout = portMAX_DELAY); + bool give(); + size_t getCount(); + +private: + SemaphoreHandle_t handle; +}; +} // namespace freertos From 908f0a5e9c43265e21d1b402e872152dfe431c68 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Thu, 30 Mar 2023 17:49:40 +0200 Subject: [PATCH 21/31] Fix circular buffer index Index was previously not being increased as statement could not be reached --- modules/control/Firmware/src/Util/CircularBuffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/control/Firmware/src/Util/CircularBuffer.h b/modules/control/Firmware/src/Util/CircularBuffer.h index c65f85e..c879dc6 100644 --- a/modules/control/Firmware/src/Util/CircularBuffer.h +++ b/modules/control/Firmware/src/Util/CircularBuffer.h @@ -10,8 +10,8 @@ public: /// @brief Warning: Does not deconstruct entry! If necessary use /// etl::circular_buffer instead! T &pop() { - return buffer[index]; index = (index + 1) % buffer.size(); + return buffer[index]; } private: From 5fe004ce30da3f5375927a6e1bdc2b121cb9b863 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Thu, 30 Mar 2023 17:50:04 +0200 Subject: [PATCH 22/31] Add serial debugger --- modules/debugger/debugger.py | 10 +++++++ modules/debugger/poetry.lock | 48 +++++++++++++++++++++++++++++++++ modules/debugger/pyproject.toml | 18 +++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 modules/debugger/debugger.py create mode 100644 modules/debugger/poetry.lock create mode 100644 modules/debugger/pyproject.toml diff --git a/modules/debugger/debugger.py b/modules/debugger/debugger.py new file mode 100644 index 0000000..ea9a5f4 --- /dev/null +++ b/modules/debugger/debugger.py @@ -0,0 +1,10 @@ +import serial +import time +ser = serial.Serial(port='/dev/ttyUSB0', baudrate=115200) +ser.write(b'HX\x02\x02\x00') +time.sleep(1) +ser.write(b'HX\x02\x02\x01') +print(f"Listening for messages on {ser.name}:") +while True: + print(ser.readline()) +ser.close() diff --git a/modules/debugger/poetry.lock b/modules/debugger/poetry.lock new file mode 100644 index 0000000..3a0dd7f --- /dev/null +++ b/modules/debugger/poetry.lock @@ -0,0 +1,48 @@ +# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. + +[[package]] +name = "autopep8" +version = "2.0.2" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "autopep8-2.0.2-py2.py3-none-any.whl", hash = "sha256:86e9303b5e5c8160872b2f5ef611161b2893e9bfe8ccc7e2f76385947d57a2f1"}, + {file = "autopep8-2.0.2.tar.gz", hash = "sha256:f9849cdd62108cb739dbcdbfb7fdcc9a30d1b63c4cc3e1c1f893b5360941b61c"}, +] + +[package.dependencies] +pycodestyle = ">=2.10.0" + +[[package]] +name = "pycodestyle" +version = "2.10.0" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pycodestyle-2.10.0-py2.py3-none-any.whl", hash = "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"}, + {file = "pycodestyle-2.10.0.tar.gz", hash = "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053"}, +] + +[[package]] +name = "pyserial" +version = "3.5" +description = "Python Serial Port Extension" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0"}, + {file = "pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb"}, +] + +[package.extras] +cp2110 = ["hidapi"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.11" +content-hash = "e62c345d7dc7473afcf2d0b5bdf8471dec4b4834efc156b4cef53daeb59c89ff" diff --git a/modules/debugger/pyproject.toml b/modules/debugger/pyproject.toml new file mode 100644 index 0000000..405fd46 --- /dev/null +++ b/modules/debugger/pyproject.toml @@ -0,0 +1,18 @@ +[tool.poetry] +name = "debugger" +version = "0.1.0" +description = "" +authors = ["GHOSCHT <31184695+GHOSCHT@users.noreply.github.com>"] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.11" +pyserial = "^3.5" + + +[tool.poetry.group.dev.dependencies] +autopep8 = "^2.0.2" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" From 05adf10e452d94b41d28dde26af4b56dafe1f790 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Thu, 30 Mar 2023 17:53:27 +0200 Subject: [PATCH 23/31] Make receiver work Switch from mutex to bin-semaphore (thread overarching) & Proper failure handling --- .../src/Communication/Serial/SerialReceiver.h | 43 +++++++++++++------ .../control/Firmware/src/Messages/Message.h | 5 ++- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/modules/control/Firmware/src/Communication/Serial/SerialReceiver.h b/modules/control/Firmware/src/Communication/Serial/SerialReceiver.h index ae5b224..457e59f 100644 --- a/modules/control/Firmware/src/Communication/Serial/SerialReceiver.h +++ b/modules/control/Firmware/src/Communication/Serial/SerialReceiver.h @@ -16,7 +16,12 @@ public: UBaseType_t priority, BaseType_t coreID) : port{port}, messageQueue{messageQueue}, serialReceiverTask{ - exec, "receive serial data", stackDepth, this, priority, coreID} {} + exec, "receive serial data", stackDepth, this, priority, coreID} { + for (size_t i = 0; i < bufferSize; ++i) { + message::Message &msg = buffer.pop(); + msg.second.give(); + } + } private: static void exec(void *receiverPtr) { @@ -28,22 +33,32 @@ private: receiver->magicNumberBuf.clear(); receiver->magicNumberBuf.resize(receiver->magicNumber.length()); size_t availableBytes = receiver->port.available(); - if (availableBytes >= receiver->magicNumber.length() + 1) { + if (receiver->port.available() > 0 && receiver->port.peek() != 'H') { + receiver->port.read(); + receiver->port.println("flush"); + } else if (availableBytes >= receiver->magicNumber.length() + 1) { receiver->port.readBytes(receiver->magicNumberBuf.data(), receiver->magicNumber.length()); - receiver->port.println(receiver->magicNumberBuf == - receiver->magicNumber); + receiver->port.println("Received Magic Number:"); + receiver->port.println(receiver->magicNumberBuf == receiver->magicNumber + ? "correct" + : "incorrect"); - uint8_t size; - receiver->port.readBytes(&size, sizeof(size)); - if (receiver->port.available() >= size) { - message::Message &msg = receiver->buffer.pop(); - msg.second.lock(); - msg.first.clear(); - msg.first.resize(size); - receiver->port.readBytes(msg.first.data(), size); - msg.second.unlock(); - receiver->messageQueue.push(&msg); + if (receiver->magicNumberBuf == receiver->magicNumber) { + uint8_t size; + receiver->port.readBytes(&size, sizeof(size)); + if (receiver->port.available() >= size) { + message::Message &msg = receiver->buffer.pop(); + receiver->port.printf("Current semaphore count: %u\n", + msg.second.getCount()); + msg.second.take(); + msg.first.clear(); + msg.first.resize(size); + receiver->port.readBytes(msg.first.data(), size); + // msg.second.unlock(); + receiver->port.println("Push message to queue"); + receiver->messageQueue.push(&msg); + } } } } diff --git a/modules/control/Firmware/src/Messages/Message.h b/modules/control/Firmware/src/Messages/Message.h index 7987868..af77ffc 100644 --- a/modules/control/Firmware/src/Messages/Message.h +++ b/modules/control/Firmware/src/Messages/Message.h @@ -1,12 +1,13 @@ #pragma once #include -#include +#include #include #include #include namespace message { +/// @brief first: data, second: data ready to reuse using Message = etl::pair, - freertos::Mutex>; + freertos::BinarySemaphore>; } // namespace message From a35ae616df3c1e8617d45400e4da7c472a2aa6c5 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Thu, 30 Mar 2023 17:53:56 +0200 Subject: [PATCH 24/31] Switch parser to self contained task class --- .../control/Firmware/src/Messages/Parser.cpp | 248 +++++++----------- .../control/Firmware/src/Messages/Parser.h | 89 ++----- 2 files changed, 123 insertions(+), 214 deletions(-) diff --git a/modules/control/Firmware/src/Messages/Parser.cpp b/modules/control/Firmware/src/Messages/Parser.cpp index a7476ea..55c663d 100644 --- a/modules/control/Firmware/src/Messages/Parser.cpp +++ b/modules/control/Firmware/src/Messages/Parser.cpp @@ -1,58 +1,23 @@ #include "Parser.h" +#include +#include #include message::parser::StateVisitor::StateVisitor( - etl::byte_stream_reader &&sreader, - etl::function invalidCallback, - etl::function> messageLightDataCallback, - etl::function> messageInfoCallback, - etl::function> messageWarningCallback, - etl::function> messageErrorCallback, - etl::function> messageSuccessCallback, - etl::function settingsSetBaudCallback, - etl::function> settingsWifiPasswordCallback, - etl::function> settingsWifiSSIDCallback, - etl::function lightControlOnCallback, - etl::function lightControlOffCallback, - etl::function lightControlToggleCallback, - etl::function lightControlIncreaseCallback, - etl::function lightControlDecreaseCallback, - etl::function commandRequestLightDataCallback, - etl::function commandEnterConsoleFlashingCallback, - etl::function commandExitConsoleFlashingCallback, - etl::function commandPairBluetoothCallback, - etl::function commandHelpCallback, - etl::function commandVersionCallback) - : stream{etl::move(sreader)}, invalidCallback{invalidCallback}, - messageLightDataCallback{messageLightDataCallback}, - messageInfoCallback{messageInfoCallback}, - messageWarningCallback{messageWarningCallback}, - messageErrorCallback{messageErrorCallback}, - messageSuccessCallback{messageSuccessCallback}, - settingsSetBaudCallback{settingsSetBaudCallback}, - settingsWifiPasswordCallback{settingsWifiPasswordCallback}, - settingsWifiSSIDCallback{settingsWifiSSIDCallback}, - lightControlOnCallback{lightControlOnCallback}, - lightControlOffCallback{lightControlOffCallback}, - lightControlToggleCallback{lightControlToggleCallback}, - lightControlIncreaseCallback{lightControlIncreaseCallback}, - lightControlDecreaseCallback{lightControlDecreaseCallback}, - commandRequestLightDataCallback{commandRequestLightDataCallback}, - commandEnterConsoleFlashingCallback{commandEnterConsoleFlashingCallback}, - commandExitConsoleFlashingCallback{commandExitConsoleFlashingCallback}, - commandPairBluetoothCallback{commandPairBluetoothCallback}, - commandHelpCallback{commandHelpCallback}, commandVersionCallback{ - commandVersionCallback} {} + message::Message *message, + freertos::Queue &lightActionQueue) + : stream{message->first.begin(), message->first.size(), etl::endian::big}, + message{message}, lightActionQueue{lightActionQueue} {} auto message::parser::StateVisitor::operator()(state::ModeSelection) -> State { if (!stream.available()) { - invalidCallback("ModeSelection: Stream not available"); + // invalidCallback("ModeSelection: Stream not available"); return state::Invalid{}; } auto mode = stream.read(); if (!mode.has_value()) { - invalidCallback("ModeSelection: Mode has no value"); + // invalidCallback("ModeSelection: Mode has no value"); return state::Invalid{}; } @@ -66,20 +31,20 @@ auto message::parser::StateVisitor::operator()(state::ModeSelection) -> State { case static_cast(protocol::Mode::Command): return state::Command{}; default: - invalidCallback("ModeSelection: Invalid index"); + // invalidCallback("ModeSelection: Invalid index"); return state::Invalid{}; } } auto message::parser::StateVisitor::operator()(state::Message) -> State { if (!stream.available()) { - invalidCallback("Message: Stream not available"); + // invalidCallback("Message: Stream not available"); return state::Invalid{}; } auto mode = stream.read(); if (!mode.has_value()) { - invalidCallback("Message: Mode has no value"); + // invalidCallback("Message: Mode has no value"); return state::Invalid{}; } @@ -95,7 +60,7 @@ auto message::parser::StateVisitor::operator()(state::Message) -> State { case static_cast(protocol::Message::Success): return state::MessageSuccess{}; default: - invalidCallback("Message: Invalid index"); + // invalidCallback("Message: Invalid index"); return state::Invalid{}; } } @@ -103,84 +68,84 @@ auto message::parser::StateVisitor::operator()(state::Message) -> State { auto message::parser::StateVisitor::operator()(state::MessageLightData) -> State { if (!stream.available()) { - invalidCallback("MessageLightData: Stream not available"); + // invalidCallback("MessageLightData: Stream not available"); return state::Invalid{}; } auto msg = stream.read(stream.available()); if (!msg.has_value()) { - invalidCallback("MessageLightData: Message has no value"); + // invalidCallback("MessageLightData: Message has no value"); return state::Invalid{}; } - messageLightDataCallback(msg.value()); + return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::MessageInfo) -> State { if (!stream.available()) { - invalidCallback("MessageInfo: Stream not available"); + // invalidCallback("MessageInfo: Stream not available"); return state::Invalid{}; } auto msg = stream.read(stream.available()); if (!msg.has_value()) { - invalidCallback("MessageInfo: Message has no value"); + // invalidCallback("MessageInfo: Message has no value"); return state::Invalid{}; } - messageInfoCallback(msg.value()); + return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::MessageWarning) -> State { if (!stream.available()) { - invalidCallback("MessageWarning: Stream not available"); + // invalidCallback("MessageWarning: Stream not available"); return state::Invalid{}; } auto msg = stream.read(stream.available()); if (!msg.has_value()) { - invalidCallback("MessageWarning: Message has no value"); + // invalidCallback("MessageWarning: Message has no value"); return state::Invalid{}; } - messageWarningCallback(msg.value()); + return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::MessageError) -> State { if (!stream.available()) { - invalidCallback("MessageError: Stream not available"); + // invalidCallback("MessageError: Stream not available"); return state::Invalid{}; } auto msg = stream.read(stream.available()); if (!msg.has_value()) { - invalidCallback("MessageError: Message has no value"); + // invalidCallback("MessageError: Message has no value"); return state::Invalid{}; } - messageErrorCallback(msg.value()); + return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::MessageSuccess) -> State { if (!stream.available()) { - invalidCallback("MessageSuccess: Stream not available"); + // invalidCallback("MessageSuccess: Stream not available"); return state::Invalid{}; } auto msg = stream.read(stream.available()); if (!msg.has_value()) { - invalidCallback("MessageSuccess: Message has no value"); + // invalidCallback("MessageSuccess: Message has no value"); return state::Invalid{}; } - messageSuccessCallback(msg.value()); + return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::Settings) -> State { if (!stream.available()) { - invalidCallback("Settings: Stream not available"); + // invalidCallback("Settings: Stream not available"); return state::Invalid{}; } auto mode = stream.read(); if (!mode.has_value()) { - invalidCallback("Settings: Mode has no value"); + // invalidCallback("Settings: Mode has no value"); return state::Invalid{}; } @@ -192,69 +157,66 @@ auto message::parser::StateVisitor::operator()(state::Settings) -> State { case static_cast(protocol::Settings::SetWifiSSID): return state::SettingsWifiSSID{}; default: - invalidCallback("Settings: Invalid index"); + // invalidCallback("Settings: Invalid index"); return state::Invalid{}; } } auto message::parser::StateVisitor::operator()(state::SettingsSetBaud) -> State { if (!stream.available()) { - invalidCallback("Settings: Stream not available"); + // invalidCallback("Settings: Stream not available"); return state::Invalid{}; } auto baud = stream.read(); if (!baud.has_value()) { - invalidCallback("SettingsSetBaud: Baud has no value"); + // invalidCallback("SettingsSetBaud: Baud has no value"); return state::Invalid{}; } - settingsSetBaudCallback(baud.value()); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::SettingsWifiPassword) -> State { if (!stream.available()) { - invalidCallback("SettingsWifiPassword: Stream not available"); + // invalidCallback("SettingsWifiPassword: Stream not available"); return state::Invalid{}; } auto passwd = stream.read(stream.available()); if (!passwd.has_value()) { - invalidCallback("SettingsWifiPassword: Passwd has no value"); + // invalidCallback("SettingsWifiPassword: Passwd has no value"); return state::Invalid{}; } - settingsWifiPasswordCallback(passwd.value()); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::SettingsWifiSSID) -> State { if (!stream.available()) { - invalidCallback("SettingsWifiSSID: Stream not available"); + // invalidCallback("SettingsWifiSSID: Stream not available"); return state::Invalid{}; } auto ssid = stream.read(stream.available()); if (!ssid.has_value()) { - invalidCallback("SettingsWifiSSID: SSID has no value"); + // invalidCallback("SettingsWifiSSID: SSID has no value"); return state::Invalid{}; } - settingsWifiSSIDCallback(ssid.value()); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::LightControl) -> State { if (!stream.available()) { - invalidCallback("LightControl: Stream not available"); + // invalidCallback("LightControl: Stream not available"); return state::Invalid{}; } auto mode = stream.read(); if (!mode.has_value()) { - invalidCallback("LightControl: Mode has no value"); + // invalidCallback("LightControl: Mode has no value"); return state::Invalid{}; } @@ -270,83 +232,92 @@ auto message::parser::StateVisitor::operator()(state::LightControl) -> State { case static_cast(protocol::LightControl::Decrease): return state::LightControlDecrease{}; default: - invalidCallback("LightControl: Invalid index"); + // invalidCallback("LightControl: Invalid index"); return state::Invalid{}; } } auto message::parser::StateVisitor::operator()(state::LightControlOn) -> State { if (stream.available_bytes() > 0) { - invalidCallback("LightControlOn: Too many bytes left"); + // invalidCallback("LightControlOn: Too many bytes left"); return state::Invalid{}; } - lightControlOnCallback(); + + message->second.give(); + Serial.println("ON"); + lightActionQueue.push({LightController::LightActionType::ON, etl::nullopt}); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::LightControlOff) -> State { if (stream.available_bytes() > 0) { - invalidCallback("LightControlOff: Too many bytes left"); + // invalidCallback("LightControlOff: Too many bytes left"); return state::Invalid{}; } - lightControlOffCallback(); + + message->second.give(); + Serial.println("OFF"); + lightActionQueue.push({LightController::LightActionType::OFF, etl::nullopt}); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::LightControlToggle) -> State { if (stream.available_bytes() > 1) { - invalidCallback("LightControlToggle: Too many bytes left"); + // invalidCallback("LightControlToggle: Too many bytes left"); return state::Invalid{}; } else if (!stream.available()) { - invalidCallback("LightControlToggle: Stream not available"); + // invalidCallback("LightControlToggle: Stream not available"); return state::Invalid{}; } auto id = stream.read(); - - lightControlToggleCallback(id.value()); + message->second.give(); + Serial.println("Toggle"); + lightActionQueue.push({LightController::LightActionType::TOGGLE, id}); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::LightControlIncrease) -> State { if (stream.available_bytes() > 1) { - invalidCallback("LightControlIncrease: Too many bytes left"); + // invalidCallback("LightControlIncrease: Too many bytes left"); return state::Invalid{}; } else if (!stream.available()) { - invalidCallback("LightControlIncrease: Stream not available"); + // invalidCallback("LightControlIncrease: Stream not available"); return state::Invalid{}; } auto id = stream.read(); - - lightControlIncreaseCallback(id.value()); + message->second.give(); + Serial.println("Increase"); + lightActionQueue.push({LightController::LightActionType::INCREASE, id}); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::LightControlDecrease) -> State { if (stream.available_bytes() > 1) { - invalidCallback("LightControlDecrease: Too many bytes left"); + // invalidCallback("LightControlDecrease: Too many bytes left"); return state::Invalid{}; } else if (!stream.available()) { - invalidCallback("LightControlDecrease: Stream not available"); + // invalidCallback("LightControlDecrease: Stream not available"); return state::Invalid{}; } auto id = stream.read(); - - lightControlDecreaseCallback(id.value()); + message->second.give(); + Serial.println("Decrease"); + lightActionQueue.push({LightController::LightActionType::DECREASE, id}); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::Command) -> State { if (!stream.available()) { - invalidCallback("Command: Stream not available"); + // invalidCallback("Command: Stream not available"); return state::Invalid{}; } auto mode = stream.read(); if (!mode.has_value()) { - invalidCallback("Command: Mode has no value"); + // invalidCallback("Command: Mode has no value"); return state::Invalid{}; } @@ -364,58 +335,56 @@ auto message::parser::StateVisitor::operator()(state::Command) -> State { case static_cast(protocol::Command::Version): return state::CommandVersion{}; default: - invalidCallback("Command: Invalid index"); + // invalidCallback("Command: Invalid index"); return state::Invalid{}; } } auto message::parser::StateVisitor::operator()(state::CommandRequestLightData) -> State { - commandRequestLightDataCallback(); + return state::Complete{}; } auto message::parser::StateVisitor::operator()( state::CommandEnterConsoleFlashing) -> State { if (stream.available_bytes() > 0) { - invalidCallback("CommandEnterConsoleFlashing: Too many bytes left"); + // invalidCallback("CommandEnterConsoleFlashing: Too many bytes left"); return state::Invalid{}; } - commandEnterConsoleFlashingCallback(); + return state::Complete{}; } auto message::parser::StateVisitor::operator()( state::CommandExitConsoleFlashing) -> State { if (stream.available_bytes() > 0) { - invalidCallback("CommandExitConsoleFlashing: Too many bytes left"); + // invalidCallback("CommandExitConsoleFlashing: Too many bytes left"); return state::Invalid{}; } - commandExitConsoleFlashingCallback(); + return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::CommandPairBluetooth) -> State { if (stream.available_bytes() > 0) { - invalidCallback("CommandPairBluetooth: Too many bytes left"); + // invalidCallback("CommandPairBluetooth: Too many bytes left"); return state::Invalid{}; } - commandPairBluetoothCallback(); + return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::CommandHelp) -> State { if (stream.available_bytes() > 0) { - invalidCallback("CommandHelp: Too many bytes left"); + // invalidCallback("CommandHelp: Too many bytes left"); return state::Invalid{}; } - commandHelpCallback(); return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::CommandVersion) -> State { if (stream.available_bytes() > 0) { - invalidCallback("CommandVersion: Too many bytes left"); + // invalidCallback("CommandVersion: Too many bytes left"); return state::Invalid{}; } - commandVersionCallback(); return state::Complete{}; } @@ -423,55 +392,36 @@ auto message::parser::StateVisitor::operator()(state::Complete) -> State { return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::Invalid) -> State { + message->second.give(); return state::Invalid{}; } auto message::parser::parse( - etl::byte_stream_reader &&reader, - etl::function invalidCallback, - etl::function> messageLightDataCallback, - etl::function> messageInfoCallback, - etl::function> messageWarningCallback, - etl::function> messageErrorCallback, - etl::function> messageSuccessCallback, - etl::function settingsSetBaudCallback, - etl::function> settingsWifiPasswordCallback, - etl::function> settingsWifiSSIDCallback, - etl::function lightControlOnCallback, - etl::function lightControlOffCallback, - etl::function lightControlToggleCallback, - etl::function lightControlIncreaseCallback, - etl::function lightControlDecreaseCallback, - etl::function commandRequestLightDataCallback, - etl::function commandEnterConsoleFlashingCallback, - etl::function commandExitConsoleFlashingCallback, - etl::function commandPairBluetoothCallback, - etl::function commandHelpCallback, - etl::function commandVersionCallback) -> void { - StateVisitor visitor{etl::move(reader), - invalidCallback, - messageLightDataCallback, - messageInfoCallback, - messageWarningCallback, - messageErrorCallback, - messageSuccessCallback, - settingsSetBaudCallback, - settingsWifiPasswordCallback, - settingsWifiSSIDCallback, - lightControlOnCallback, - lightControlOffCallback, - lightControlToggleCallback, - lightControlIncreaseCallback, - lightControlDecreaseCallback, - commandRequestLightDataCallback, - commandEnterConsoleFlashingCallback, - commandExitConsoleFlashingCallback, - commandPairBluetoothCallback, - commandHelpCallback, - commandVersionCallback}; + message::Message *message, + freertos::Queue &lightActionQueue) -> void { + StateVisitor visitor{message, lightActionQueue}; State state{state::ModeSelection{}}; while (!etl::holds_alternative(state) && !etl::holds_alternative(state)) { state = etl::visit(visitor, state); } } + +message::parser::Parser::Parser( + freertos::Queue &lightActionQueue, + UBaseType_t queueCapacity, UBaseType_t priority, BaseType_t coreID) + : parserTask{exec, "parse received data", stackDepth, this, priority, + coreID}, + messages{queueCapacity}, lightActionQueue{lightActionQueue} {} + +void message::parser::Parser::exec(void *parserPtr) { + Parser *parser = static_cast(parserPtr); + + while (true) { + auto message = parser->messages.pop(); + Serial.println("starting parse"); + if (message.has_value()) { + parse(message.value(), parser->lightActionQueue); + } + } +} \ No newline at end of file diff --git a/modules/control/Firmware/src/Messages/Parser.h b/modules/control/Firmware/src/Messages/Parser.h index d9e8774..845a956 100644 --- a/modules/control/Firmware/src/Messages/Parser.h +++ b/modules/control/Firmware/src/Messages/Parser.h @@ -1,5 +1,9 @@ #pragma once #include "Protocol.h" +#include +#include +#include +#include #include #include #include @@ -49,53 +53,13 @@ using State = etl::variant< state::CommandExitConsoleFlashing, state::CommandPairBluetooth, state::CommandHelp, state::CommandVersion, state::Complete>; -void parse( - etl::byte_stream_reader &&reader, - etl::function invalidCallback, - etl::function> messageLightDataCallback, - etl::function> messageInfoCallback, - etl::function> messageWarningCallback, - etl::function> messageErrorCallback, - etl::function> messageSuccessCallback, - etl::function settingsSetBaudCallback, - etl::function> settingsWifiPasswordCallback, - etl::function> settingsWifiSSIDCallback, - etl::function lightControlOnCallback, - etl::function lightControlOffCallback, - etl::function lightControlToggleCallback, - etl::function lightControlIncreaseCallback, - etl::function lightControlDecreaseCallback, - etl::function commandRequestLightDataCallback, - etl::function commandEnterConsoleFlashingCallback, - etl::function commandExitConsoleFlashingCallback, - etl::function commandPairBluetoothCallback, - etl::function commandHelpCallback, - etl::function commandVersionCallback); +void parse(message::Message *message, + freertos::Queue &lightActionQueue); class StateVisitor { public: - StateVisitor( - etl::byte_stream_reader &&sreader, - etl::function invalidCallback, - etl::function> messageLightDataCallback, - etl::function> messageInfoCallback, - etl::function> messageWarningCallback, - etl::function> messageErrorCallback, - etl::function> messageSuccessCallback, - etl::function settingsSetBaudCallback, - etl::function> settingsWifiPasswordCallback, - etl::function> settingsWifiSSIDCallback, - etl::function lightControlOnCallback, - etl::function lightControlOffCallback, - etl::function lightControlToggleCallback, - etl::function lightControlIncreaseCallback, - etl::function lightControlDecreaseCallback, - etl::function commandRequestLightDataCallback, - etl::function commandEnterConsoleFlashingCallback, - etl::function commandExitConsoleFlashingCallback, - etl::function commandPairBluetoothCallback, - etl::function commandHelpCallback, - etl::function commandVersionCallback); + StateVisitor(message::Message *message, + freertos::Queue &lightActionQueue); State operator()(state::ModeSelection); State operator()(state::Message); State operator()(state::MessageLightData); @@ -128,27 +92,22 @@ public: private: etl::byte_stream_reader stream; - // TODO: callback function parameter types - etl::function invalidCallback; - etl::function> messageLightDataCallback; - etl::function> messageInfoCallback; - etl::function> messageWarningCallback; - etl::function> messageErrorCallback; - etl::function> messageSuccessCallback; - etl::function settingsSetBaudCallback; - etl::function> settingsWifiPasswordCallback; - etl::function> settingsWifiSSIDCallback; - etl::function lightControlOnCallback; - etl::function lightControlOffCallback; - etl::function lightControlToggleCallback; - etl::function lightControlIncreaseCallback; - etl::function lightControlDecreaseCallback; - etl::function commandRequestLightDataCallback; - etl::function commandEnterConsoleFlashingCallback; - etl::function commandExitConsoleFlashingCallback; - etl::function commandPairBluetoothCallback; - etl::function commandHelpCallback; - etl::function commandVersionCallback; + message::Message *message; + freertos::Queue &lightActionQueue; +}; + +class Parser { +public: + Parser(freertos::Queue &lightActionQueue, + UBaseType_t queueCapacity, UBaseType_t priority, BaseType_t coreID); + freertos::Queue &getMessageQueue() { return messages; } + +private: + static void exec(void *parserPtr); + freertos::Task parserTask; + static const uint32_t stackDepth = 2048; + freertos::Queue messages; + freertos::Queue &lightActionQueue; }; } // namespace parser } // namespace message From 08ca59099d9096e57ae51f77eb48bd5e1739d9d1 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Thu, 30 Mar 2023 19:51:41 +0200 Subject: [PATCH 25/31] Add reset command --- modules/control/Firmware/src/Messages/Parser.cpp | 11 +++++++++++ modules/control/Firmware/src/Messages/Parser.h | 5 ++++- modules/control/Firmware/src/Messages/Protocol.h | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/control/Firmware/src/Messages/Parser.cpp b/modules/control/Firmware/src/Messages/Parser.cpp index 55c663d..ab2cc09 100644 --- a/modules/control/Firmware/src/Messages/Parser.cpp +++ b/modules/control/Firmware/src/Messages/Parser.cpp @@ -334,6 +334,8 @@ auto message::parser::StateVisitor::operator()(state::Command) -> State { return state::CommandHelp{}; case static_cast(protocol::Command::Version): return state::CommandVersion{}; + case static_cast(protocol::Command::Reset): + return state::CommandReset{}; default: // invalidCallback("Command: Invalid index"); return state::Invalid{}; @@ -388,6 +390,15 @@ auto message::parser::StateVisitor::operator()(state::CommandVersion) -> State { return state::Complete{}; } +auto message::parser::StateVisitor::operator()(state::CommandReset) -> State { + if (stream.available_bytes() > 0) { + // invalidCallback("CommandVersion: Too many bytes left"); + return state::Invalid{}; + } + + return state::Invalid{}; +} + auto message::parser::StateVisitor::operator()(state::Complete) -> State { return state::Complete{}; } diff --git a/modules/control/Firmware/src/Messages/Parser.h b/modules/control/Firmware/src/Messages/Parser.h index 845a956..dac6ef5 100644 --- a/modules/control/Firmware/src/Messages/Parser.h +++ b/modules/control/Firmware/src/Messages/Parser.h @@ -38,6 +38,7 @@ struct CommandExitConsoleFlashing {}; struct CommandPairBluetooth {}; struct CommandHelp {}; struct CommandVersion {}; +struct CommandReset {}; struct Complete {}; } // namespace state @@ -51,7 +52,8 @@ using State = etl::variant< state::LightControlIncrease, state::LightControlDecrease, state::Command, state::CommandRequestLightData, state::CommandEnterConsoleFlashing, state::CommandExitConsoleFlashing, state::CommandPairBluetooth, - state::CommandHelp, state::CommandVersion, state::Complete>; + state::CommandHelp, state::CommandVersion, state::CommandReset, + state::Complete>; void parse(message::Message *message, freertos::Queue &lightActionQueue); @@ -86,6 +88,7 @@ public: State operator()(state::CommandPairBluetooth); State operator()(state::CommandHelp); State operator()(state::CommandVersion); + State operator()(state::CommandReset); State operator()(state::Complete); State operator()(state::Invalid); diff --git a/modules/control/Firmware/src/Messages/Protocol.h b/modules/control/Firmware/src/Messages/Protocol.h index 7afcf2b..0587849 100644 --- a/modules/control/Firmware/src/Messages/Protocol.h +++ b/modules/control/Firmware/src/Messages/Protocol.h @@ -47,6 +47,7 @@ enum class Command : uint8_t { PairBluetooth = 0x3, Help = 0x4, Version = 0x5, + Reset = 0x6, }; using Submode = etl::variant; From f458187095e07980bb812e0bb9d2bc8604e80ad0 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Thu, 30 Mar 2023 19:53:33 +0200 Subject: [PATCH 26/31] Add reset functionality --- modules/control/Firmware/src/Messages/Parser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/control/Firmware/src/Messages/Parser.cpp b/modules/control/Firmware/src/Messages/Parser.cpp index ab2cc09..e2bf9fb 100644 --- a/modules/control/Firmware/src/Messages/Parser.cpp +++ b/modules/control/Firmware/src/Messages/Parser.cpp @@ -395,8 +395,8 @@ auto message::parser::StateVisitor::operator()(state::CommandReset) -> State { // invalidCallback("CommandVersion: Too many bytes left"); return state::Invalid{}; } - - return state::Invalid{}; + ESP.restart(); + return state::Complete{}; } auto message::parser::StateVisitor::operator()(state::Complete) -> State { From bca8a347ff5940d66eeea29f3f6ec07f6424b75d Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Thu, 30 Mar 2023 19:55:46 +0200 Subject: [PATCH 27/31] Make Invalid default for not implemented functionality --- .../control/Firmware/src/Messages/Parser.cpp | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/modules/control/Firmware/src/Messages/Parser.cpp b/modules/control/Firmware/src/Messages/Parser.cpp index e2bf9fb..2f6711b 100644 --- a/modules/control/Firmware/src/Messages/Parser.cpp +++ b/modules/control/Firmware/src/Messages/Parser.cpp @@ -78,7 +78,7 @@ auto message::parser::StateVisitor::operator()(state::MessageLightData) return state::Invalid{}; } - return state::Complete{}; + return state::Invalid{}; } auto message::parser::StateVisitor::operator()(state::MessageInfo) -> State { if (!stream.available()) { @@ -92,7 +92,7 @@ auto message::parser::StateVisitor::operator()(state::MessageInfo) -> State { return state::Invalid{}; } - return state::Complete{}; + return state::Invalid{}; } auto message::parser::StateVisitor::operator()(state::MessageWarning) -> State { if (!stream.available()) { @@ -106,7 +106,7 @@ auto message::parser::StateVisitor::operator()(state::MessageWarning) -> State { return state::Invalid{}; } - return state::Complete{}; + return state::Invalid{}; } auto message::parser::StateVisitor::operator()(state::MessageError) -> State { if (!stream.available()) { @@ -120,7 +120,7 @@ auto message::parser::StateVisitor::operator()(state::MessageError) -> State { return state::Invalid{}; } - return state::Complete{}; + return state::Invalid{}; } auto message::parser::StateVisitor::operator()(state::MessageSuccess) -> State { if (!stream.available()) { @@ -134,7 +134,7 @@ auto message::parser::StateVisitor::operator()(state::MessageSuccess) -> State { return state::Invalid{}; } - return state::Complete{}; + return state::Invalid{}; } auto message::parser::StateVisitor::operator()(state::Settings) -> State { @@ -175,7 +175,7 @@ auto message::parser::StateVisitor::operator()(state::SettingsSetBaud) return state::Invalid{}; } - return state::Complete{}; + return state::Invalid{}; } auto message::parser::StateVisitor::operator()(state::SettingsWifiPassword) -> State { @@ -190,7 +190,7 @@ auto message::parser::StateVisitor::operator()(state::SettingsWifiPassword) return state::Invalid{}; } - return state::Complete{}; + return state::Invalid{}; } auto message::parser::StateVisitor::operator()(state::SettingsWifiSSID) -> State { @@ -205,7 +205,7 @@ auto message::parser::StateVisitor::operator()(state::SettingsWifiSSID) return state::Invalid{}; } - return state::Complete{}; + return state::Invalid{}; } auto message::parser::StateVisitor::operator()(state::LightControl) -> State { @@ -343,8 +343,7 @@ auto message::parser::StateVisitor::operator()(state::Command) -> State { } auto message::parser::StateVisitor::operator()(state::CommandRequestLightData) -> State { - - return state::Complete{}; + return state::Invalid{}; } auto message::parser::StateVisitor::operator()( state::CommandEnterConsoleFlashing) -> State { @@ -352,8 +351,7 @@ auto message::parser::StateVisitor::operator()( // invalidCallback("CommandEnterConsoleFlashing: Too many bytes left"); return state::Invalid{}; } - - return state::Complete{}; + return state::Invalid{}; } auto message::parser::StateVisitor::operator()( state::CommandExitConsoleFlashing) -> State { @@ -361,8 +359,7 @@ auto message::parser::StateVisitor::operator()( // invalidCallback("CommandExitConsoleFlashing: Too many bytes left"); return state::Invalid{}; } - - return state::Complete{}; + return state::Invalid{}; } auto message::parser::StateVisitor::operator()(state::CommandPairBluetooth) -> State { @@ -370,24 +367,21 @@ auto message::parser::StateVisitor::operator()(state::CommandPairBluetooth) // invalidCallback("CommandPairBluetooth: Too many bytes left"); return state::Invalid{}; } - - return state::Complete{}; + return state::Invalid{}; } auto message::parser::StateVisitor::operator()(state::CommandHelp) -> State { if (stream.available_bytes() > 0) { // invalidCallback("CommandHelp: Too many bytes left"); return state::Invalid{}; } - - return state::Complete{}; + return state::Invalid{}; } auto message::parser::StateVisitor::operator()(state::CommandVersion) -> State { if (stream.available_bytes() > 0) { // invalidCallback("CommandVersion: Too many bytes left"); return state::Invalid{}; } - - return state::Complete{}; + return state::Invalid{}; } auto message::parser::StateVisitor::operator()(state::CommandReset) -> State { From 2c6396fbf58fea8c04b08106bad67c5230425375 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Thu, 30 Mar 2023 20:08:25 +0200 Subject: [PATCH 28/31] Remove unnecessary ternary operator --- modules/control/Firmware/src/FreeRTOS/BinarySemaphore.cpp | 4 ++-- modules/control/Firmware/src/FreeRTOS/Mutex.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/control/Firmware/src/FreeRTOS/BinarySemaphore.cpp b/modules/control/Firmware/src/FreeRTOS/BinarySemaphore.cpp index 96464ae..a0fb7d6 100644 --- a/modules/control/Firmware/src/FreeRTOS/BinarySemaphore.cpp +++ b/modules/control/Firmware/src/FreeRTOS/BinarySemaphore.cpp @@ -14,11 +14,11 @@ auto freertos::BinarySemaphore::operator=( } auto freertos::BinarySemaphore::take(TickType_t timeout) -> bool { BaseType_t success = xSemaphoreTake(handle, timeout); - return success == pdTRUE ? true : false; + return success == pdTRUE; } auto freertos::BinarySemaphore::give() -> bool { BaseType_t success = xSemaphoreGive(handle); - return success == pdTRUE ? true : false; + return success == pdTRUE; } auto freertos::BinarySemaphore::getCount() -> size_t { diff --git a/modules/control/Firmware/src/FreeRTOS/Mutex.cpp b/modules/control/Firmware/src/FreeRTOS/Mutex.cpp index 7f8683a..b1786a1 100644 --- a/modules/control/Firmware/src/FreeRTOS/Mutex.cpp +++ b/modules/control/Firmware/src/FreeRTOS/Mutex.cpp @@ -12,9 +12,9 @@ auto freertos::Mutex::operator=(const Mutex &&other) noexcept -> Mutex & { } auto freertos::Mutex::lock(TickType_t timeout) -> bool{ BaseType_t success = xSemaphoreTake(handle, timeout); - return success == pdTRUE ? true : false; + return success == pdTRUE; } auto freertos::Mutex::unlock() -> bool { BaseType_t success = xSemaphoreGive(handle); - return success == pdTRUE ? true : false; + return success == pdTRUE; } \ No newline at end of file From 0af156182006f97caeae90cd2897bf162a47c9bd Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Thu, 30 Mar 2023 20:36:16 +0200 Subject: [PATCH 29/31] Define composable command interface --- modules/control/Firmware/src/Messages/Composer.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/modules/control/Firmware/src/Messages/Composer.h b/modules/control/Firmware/src/Messages/Composer.h index 469d527..dcbd61f 100644 --- a/modules/control/Firmware/src/Messages/Composer.h +++ b/modules/control/Firmware/src/Messages/Composer.h @@ -1,9 +1,21 @@ #pragma once #include "Protocol.h" +#include +#include #include namespace message { namespace composer { +enum class ComposableType { + MESSAGE_LIGHTDATA, + MESSAGE_INFO, + MESSAGE_WARNING, + MESSAGE_ERROR, + MESSAGE_SUCCESS, + COMMAND_HELP, + COMMAND_VERSION, +}; +using Composable = etl::pair>>; bool sendMessage(protocol::Message type, etl::string_view content); } // namespace composer } // namespace message From 851cbb6e52f494dcde7238c8a9917fed9768f053 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Fri, 31 Mar 2023 12:29:07 +0200 Subject: [PATCH 30/31] Implement light fading --- .../Firmware/src/LightController/Controller.h | 91 ++++++++++++++----- 1 file changed, 67 insertions(+), 24 deletions(-) diff --git a/modules/control/Firmware/src/LightController/Controller.h b/modules/control/Firmware/src/LightController/Controller.h index e5aa7d6..a9c1a3d 100644 --- a/modules/control/Firmware/src/LightController/Controller.h +++ b/modules/control/Firmware/src/LightController/Controller.h @@ -1,7 +1,9 @@ #pragma once #include +#include #include #include +#include #include #include @@ -11,13 +13,18 @@ using LightAction = etl::pair>; template class Controller { public: - Controller(size_t actionQueueSize, UBaseType_t priority, BaseType_t coreID) + Controller(size_t actionQueueSize, UBaseType_t controllerPriority, + BaseType_t controllerCoreID, UBaseType_t faderPriority, + BaseType_t faderCoreID) : lightPins{PIN...}, lightActions{actionQueueSize}, - lightControllerTask{exec, "control lights", stackDepth, - this, priority, coreID} { + lightControllerTask{exec, "control lights", stackDepth, + this, controllerPriority, controllerCoreID}, + lightFaderTask{execFade, "fade lights", stackDepth, + this, faderPriority, faderCoreID} { for (int pin : lightPins) { pinMode(pin, OUTPUT); } + lightStatesCurrent.second.unlock(); } freertos::Queue &getActionQueue() { return lightActions; } @@ -26,10 +33,7 @@ public: private: static void exec(void *controllerPtr) { - Controller *controller{ - static_cast *>(controllerPtr)}; - etl::array lightStates; - const uint8_t step = 5; + Controller *controller{static_cast(controllerPtr)}; while (true) { auto action{controller->getActionQueue().pop()}; @@ -39,45 +43,84 @@ private: switch (type) { case LightActionType::INCREASE: - if (index.has_value() && index.value() < lightStates.size() && - lightStates[index.value()] <= 255 - step) { - lightStates[index.value()] += step; + if (index.has_value() && + index.value() < controller->lightStatesFinal.size() && + controller->lightStatesFinal[index.value()] <= + 255 - controller->step) { + controller->lightStatesFinal[index.value()] += controller->step; } break; case LightActionType::DECREASE: - if (index.has_value() && index.value() < lightStates.size() && - lightStates[index.value()] >= 0) { - lightStates[index.value()] -= step; + if (index.has_value() && + index.value() < controller->lightStatesFinal.size() && + controller->lightStatesFinal[index.value()] >= 0) { + controller->lightStatesFinal[index.value()] -= controller->step; } break; case LightActionType::TOGGLE: - if (index.has_value() && index.value() < lightStates.size()) { - lightStates[index.value()] = - lightStates[index.value()] == 0 ? 255 : 0; + if (index.has_value() && + index.value() < controller->lightStatesFinal.size()) { + controller->lightStatesCurrent.second.lock(); + controller->lightStatesFinal[index.value()] = + controller->lightStatesFinal[index.value()] == 0 ? 255 : 0; + controller->lightStatesCurrent.first[index.value()] = + controller->lightStatesFinal[index.value()]; + controller->lightStatesCurrent.second.unlock(); } break; case LightActionType::ON: - lightStates.fill(255); + controller->lightStatesCurrent.second.lock(); + controller->lightStatesFinal.fill(255); + controller->lightStatesCurrent.first.fill(255); + controller->lightStatesCurrent.second.unlock(); break; case LightActionType::OFF: - lightStates.fill(0); + controller->lightStatesCurrent.second.lock(); + controller->lightStatesFinal.fill(0); + controller->lightStatesCurrent.first.fill(0); + controller->lightStatesCurrent.second.unlock(); break; default: break; } - - for (size_t lightIndex = 0; lightIndex < lightStates.size(); - ++lightIndex) { - analogWrite(controller->getPins()[lightIndex], - lightStates[lightIndex]); - } } } } + static void execFade(void *controllerPtr) { + Controller *controller{static_cast(controllerPtr)}; + while (true) { + freertos::sleep(controller->fadeRate); + controller->lightStatesCurrent.second.lock(); + for (size_t i = 0; i < controller->lightStatesFinal.size(); i++) { + if (controller->lightStatesCurrent.first[i] < + controller->lightStatesFinal[i]) { + controller->lightStatesCurrent.first[i] += 1; + } else if (controller->lightStatesCurrent.first[i] > + controller->lightStatesFinal[i]) { + controller->lightStatesCurrent.first[i] -= 1; + } + } + + for (size_t lightIndex = 0; + lightIndex < controller->lightStatesCurrent.first.size(); + ++lightIndex) { + analogWrite(controller->getPins()[lightIndex], + controller->lightStatesCurrent.first[lightIndex]); + } + controller->lightStatesCurrent.second.unlock(); + } + } + static const uint32_t stackDepth = 2048; + const uint8_t step = 5; + const size_t fadeRate = 50; etl::array lightPins; freertos::Queue lightActions; freertos::Task lightControllerTask; + freertos::Task lightFaderTask; + etl::array lightStatesFinal; + etl::pair, freertos::Mutex> + lightStatesCurrent; }; } // namespace LightController From 1e9a83136108b4fd49494020eb794865a330550c Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Sat, 1 Apr 2023 11:02:29 +0200 Subject: [PATCH 31/31] Invalid state now frees up allocated memory Semaphore is reset so receiver can reuse said entry --- modules/control/Firmware/src/Messages/Parser.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/control/Firmware/src/Messages/Parser.cpp b/modules/control/Firmware/src/Messages/Parser.cpp index 2f6711b..500b448 100644 --- a/modules/control/Firmware/src/Messages/Parser.cpp +++ b/modules/control/Firmware/src/Messages/Parser.cpp @@ -398,7 +398,7 @@ auto message::parser::StateVisitor::operator()(state::Complete) -> State { } auto message::parser::StateVisitor::operator()(state::Invalid) -> State { message->second.give(); - return state::Invalid{}; + return state::Complete{}; } auto message::parser::parse( @@ -406,8 +406,7 @@ auto message::parser::parse( freertos::Queue &lightActionQueue) -> void { StateVisitor visitor{message, lightActionQueue}; State state{state::ModeSelection{}}; - while (!etl::holds_alternative(state) && - !etl::holds_alternative(state)) { + while (!etl::holds_alternative(state)) { state = etl::visit(visitor, state); } }