diff --git a/vscode/OmobiLEDdisplayBluetooth/.vscode/settings.json b/vscode/OmobiLEDdisplayBluetooth/.vscode/settings.json index 01232d4..b46ce26 100644 --- a/vscode/OmobiLEDdisplayBluetooth/.vscode/settings.json +++ b/vscode/OmobiLEDdisplayBluetooth/.vscode/settings.json @@ -7,6 +7,16 @@ "vector": "cpp", "unordered_map": "cpp", "random": "cpp", - "memory": "cpp" + "memory": "cpp", + "*.tcc": "cpp", + "tuple": "cpp", + "fstream": "cpp", + "iomanip": "cpp", + "istream": "cpp", + "limits": "cpp", + "ostream": "cpp", + "numeric": "cpp", + "streambuf": "cpp", + "utility": "cpp" } } \ No newline at end of file diff --git a/vscode/OmobiLEDdisplayBluetooth/include/EepromManager.h b/vscode/OmobiLEDdisplayBluetooth/include/EepromManager.h new file mode 100644 index 0000000..e38964e --- /dev/null +++ b/vscode/OmobiLEDdisplayBluetooth/include/EepromManager.h @@ -0,0 +1,83 @@ +#ifndef EEPROM_MANAGER +#define EEPROM_MANAGER + +#include +#include + +class EepromUnit; + +class EepromManager final +{ +public: + explicit EepromManager(); + + friend class EepromUnit; + + /*! + * \brief Function to register / allocate a part of the EEPROM + * + * This function ALWAYS has to be called in THE SAME ORDER for each unit. + * Otherwise THE DATA WILL BE LOST!! + */ + EepromUnit *registerEempromUnit(size_t size); + +private: + int currentAddressEnding; + + template + const T &writeToEeprom(int address, size_t size, const T &t) + { + Serial.println("Writing at: " + String(address) + " size: " + String(sizeof(T))); + + if (sizeof(T) > size) { + Serial.println("Error writing: Size should be: " + String(size)); + return t; + } + + const T &res = EEPROM.put(address, t); + Serial.println("Eeprom commit returned: " + String(EEPROM.commit())); + return res; + } + + template + T &readFromEeprom(int address, size_t size, T &t) + { + Serial.println("Reading at: " + String(address) + " size: " + String(sizeof(T))); + + if (sizeof(T) > size) { + Serial.println("Error reading: Size should be: " + String(size)); + return t; + } + + T &res = EEPROM.get(address, t); + + return res; + } +}; + +class EepromUnit final +{ +public: + friend class EepromManager; + + template + const T &write(const T &t) + { + return this->manager->writeToEeprom(this->address, this->size, t); + }; + + template + T &read(T &t) + { + return this->manager->readFromEeprom(this->address, this->size, t); + }; + +private: + explicit EepromUnit(EepromManager *manager, int address, size_t size); + + EepromManager *manager; + int address; + size_t size; +}; + +#endif // EEPROM_MANAGER \ No newline at end of file diff --git a/vscode/OmobiLEDdisplayBluetooth/include/LedDisplayController.h b/vscode/OmobiLEDdisplayBluetooth/include/LedDisplayController.h index dee716c..2a928a8 100644 --- a/vscode/OmobiLEDdisplayBluetooth/include/LedDisplayController.h +++ b/vscode/OmobiLEDdisplayBluetooth/include/LedDisplayController.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include "esp_task_wdt.h" class LedDisplayController @@ -52,6 +52,7 @@ public: void loop(); // modifiers for the internal text sets + bool registerEepromUnit(EepromManager* eepromManager); String getTextSetParameter(int index, DisplayTextSetParameter parameter); GetSetTextSetParameterExitCode setTextSetParameter(int index, DisplayTextSetParameter parameter, String value); @@ -105,8 +106,9 @@ private: sets_t text_sets; // storage control + EepromUnit* eepromUnit; GetSetTextSetParameterExitCode getSetTextSetParameter(int index, DisplayTextSetParameter parameter, String *value, bool set = false); - void storeTextSets(); + bool storeTextSets(); bool loadTextSets(); }; diff --git a/vscode/OmobiLEDdisplayBluetooth/include/OmobiLedDisplay.h b/vscode/OmobiLEDdisplayBluetooth/include/OmobiLedDisplay.h index 7c4f24b..347ce50 100644 --- a/vscode/OmobiLEDdisplayBluetooth/include/OmobiLedDisplay.h +++ b/vscode/OmobiLEDdisplayBluetooth/include/OmobiLedDisplay.h @@ -3,6 +3,8 @@ #include #include +#include "mbedtls/md.h" +#include "EepromManager.h" #include "BluetoothLeUartServer.h" #include "LedDisplayController.h" @@ -37,15 +39,32 @@ private: enum OmobiDisplayStatusCode { Success = 200, + Unauthorized = 401, InternalError = 500, DisplayControllerError = 501 }; unsigned long lastKeepAlive; const int maximumKeepAliveDelay = 10000; + bool sessionAuthorized; - LedDisplayController *ledDisplayController; - BluetoothLeUartServer *bleServer; + typedef struct { + char deviceName[50]; + char deviceCode[5]; + char valid[3]; + } DisplayProperties; + + DisplayProperties properties; + + EepromManager* eepromManager; + EepromUnit* eepromUnit; + LedDisplayController* ledDisplayController; + BluetoothLeUartServer* bleServer; + + bool loadProperties(); + bool storeProperties(); + + String sha256(String payload); }; #endif // OMOBI_LED_DISPLAY \ No newline at end of file diff --git a/vscode/OmobiLEDdisplayBluetooth/platformio.ini b/vscode/OmobiLEDdisplayBluetooth/platformio.ini index 6ff133a..b71343d 100644 --- a/vscode/OmobiLEDdisplayBluetooth/platformio.ini +++ b/vscode/OmobiLEDdisplayBluetooth/platformio.ini @@ -11,6 +11,8 @@ [env:esp32] platform = espressif32 board = esp32doit-devkit-v1 +board_upload.flash_size=4MB +board_upload.maximum_size=4194304 framework = arduino lib_deps = Wire diff --git a/vscode/OmobiLEDdisplayBluetooth/src/BluetoothLeUartServer.cpp b/vscode/OmobiLEDdisplayBluetooth/src/BluetoothLeUartServer.cpp index 0b1a5c3..6ed7215 100644 --- a/vscode/OmobiLEDdisplayBluetooth/src/BluetoothLeUartServer.cpp +++ b/vscode/OmobiLEDdisplayBluetooth/src/BluetoothLeUartServer.cpp @@ -58,6 +58,10 @@ void BluetoothLeUartServer::sendData(String data) void BluetoothLeUartServer::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param) { + // only allow one device + if(this->deviceConnected) + return this->bleServer->disconnect(param->connect.conn_id); + this->deviceConnected = true; this->deviceConnectionId = param->connect.conn_id; diff --git a/vscode/OmobiLEDdisplayBluetooth/src/EepromManager.cpp b/vscode/OmobiLEDdisplayBluetooth/src/EepromManager.cpp new file mode 100644 index 0000000..5deb52d --- /dev/null +++ b/vscode/OmobiLEDdisplayBluetooth/src/EepromManager.cpp @@ -0,0 +1,29 @@ +#include "EepromManager.h" + +EepromManager::EepromManager() +{ + this->currentAddressEnding = 4; + EEPROM.begin(4000); +} + +EepromUnit *EepromManager::registerEempromUnit(size_t size) +{ + Serial.println("Registering new EepromUnit with size: " + String(size) + " at " + String(this->currentAddressEnding)); + // create a new Unit at the current address ending + EepromUnit *newUnit = new EepromUnit(this, this->currentAddressEnding, size); + // move the new address ending + this->currentAddressEnding += size; + return newUnit; +} + + +// -------------- +// - EepromUnit - +// -------------- + +EepromUnit::EepromUnit(EepromManager *manager, int address, size_t size) +{ + this->manager = manager; + this->address = address; + this->size = size; +} \ No newline at end of file diff --git a/vscode/OmobiLEDdisplayBluetooth/src/LedDisplayController.cpp b/vscode/OmobiLEDdisplayBluetooth/src/LedDisplayController.cpp index 821dbf4..3495289 100644 --- a/vscode/OmobiLEDdisplayBluetooth/src/LedDisplayController.cpp +++ b/vscode/OmobiLEDdisplayBluetooth/src/LedDisplayController.cpp @@ -4,6 +4,7 @@ LedDisplayController *ledDisplayControllerGlobal = nullptr; LedDisplayController::LedDisplayController(Adafruit_NeoMatrix *matrix) { + this->eepromUnit = nullptr; ledDisplayControllerGlobal = this; this->loadTextSets(); @@ -47,6 +48,15 @@ void LedDisplayController::loop() } } +bool LedDisplayController::registerEepromUnit(EepromManager *eepromManager) +{ + if (this->eepromUnit != nullptr) + return false; + + this->eepromUnit = eepromManager->registerEempromUnit(sizeof(sets_t)); + return this->loadTextSets(); +} + String LedDisplayController::getTextSetParameter(int index, DisplayTextSetParameter parameter) { if (index < 0 || index > this->maximumTextSets || parameter < 0 || parameter >= DisplayTextSetParameterCount) @@ -194,13 +204,14 @@ LedDisplayController::GetSetTextSetParameterExitCode LedDisplayController::getSe switch (parameter) { case TextParameter: - if (set) { - if(*value == "") + if (set) + { + if (*value == "") // delete the index for (int i = index; i < this->maximumTextSets; i++) { - if(i > this->maximumTextSets - 1) - this->text_sets.sets[i] = this->text_sets.sets[i+1]; + if (i > this->maximumTextSets - 1) + this->text_sets.sets[i] = this->text_sets.sets[i + 1]; else this->text_sets.sets[i] = this->defaultTextSet; } @@ -294,32 +305,39 @@ LedDisplayController::GetSetTextSetParameterExitCode LedDisplayController::getSe } } -void LedDisplayController::storeTextSets() +bool LedDisplayController::storeTextSets() { - // write conf to EEPROM - EEPROM.begin(sizeof(sets_t)); - //for (size_t i = 0 ; i < DISP_STRUCT_SIZE ; i++) - //{ - // EEPROM.write(i, 0); - //} - strncpy(text_sets.valid, "OK", sizeof(text_sets.valid)); - EEPROM.put(0, text_sets); - EEPROM.commit(); - EEPROM.end(); + Serial.println("Storing Text Sets..."); + if (this->eepromUnit == nullptr) { + Serial.println("ERROR"); + return false; + } + + strncpy(this->text_sets.valid, "OK", sizeof(this->text_sets.valid)); + this->eepromUnit->write(this->text_sets); + Serial.println("OK"); + return true; } bool LedDisplayController::loadTextSets() { - bool rc = false; sets_t buf = {}; - // read conf from EEPROM - EEPROM.begin(sizeof(sets_t)); - EEPROM.get(0, buf); - EEPROM.end(); + + Serial.println("Loading Text Sets..."); + + if (this->eepromUnit != nullptr) + { + // read conf from EEPROM + this->eepromUnit->read(buf); + } + + Serial.println("Loaded text sets: valid: " + String(buf.valid)); + if (strcmp(buf.valid, "OK") == 0) { - rc = true; - memcpy(&text_sets, &buf, sizeof(text_sets)); + memcpy(&text_sets, &buf, sizeof(sets_t)); + Serial.println("OK"); + return true; } else { @@ -327,18 +345,14 @@ bool LedDisplayController::loadTextSets() sets_t defaultTextSets; - strncpy(defaultTextSets.valid, "OK", sizeof(defaultTextSets.valid)); - for (int i = 0; i < this->maximumTextSets; i++) { defaultTextSets.sets[i] = this->defaultTextSet; } - this->text_sets = defaultTextSets; - this->storeTextSets(); + memcpy(&text_sets, &defaultTextSets, sizeof(sets_t)); + return this->storeTextSets(); } - - return rc; } // ---------- diff --git a/vscode/OmobiLEDdisplayBluetooth/src/OmobiLedDisplay.cpp b/vscode/OmobiLEDdisplayBluetooth/src/OmobiLedDisplay.cpp index 64c6bb5..7367518 100644 --- a/vscode/OmobiLEDdisplayBluetooth/src/OmobiLedDisplay.cpp +++ b/vscode/OmobiLEDdisplayBluetooth/src/OmobiLedDisplay.cpp @@ -1,21 +1,29 @@ #include "OmobiLedDisplay.h" -#include "esp_gatts_api.h" OmobiLedDisplay::OmobiLedDisplay(String deviceName, Adafruit_NeoMatrix *ledDisplayMatrix) { this->lastKeepAlive = -1; + this->sessionAuthorized = false; + + // init eeprom manager + this->eepromManager = new EepromManager(); // init led display controller this->ledDisplayController = new LedDisplayController(ledDisplayMatrix); - this->ledDisplayController->setTextSetParameter(0, LedDisplayController::ActiveParameter, "true"); + this->ledDisplayController->registerEepromUnit(this->eepromManager); // init ble server - this->bleServer = new BluetoothLeUartServer(deviceName, "92fecb20-1406-426a-afa5-cd5c1f306462", "92fecb21-1406-426a-afa5-cd5c1f306462", "92fecb22-1406-426a-afa5-cd5c1f306462"); + this->bleServer = new BluetoothLeUartServer("Omobi Display", "92fecb20-1406-426a-afa5-cd5c1f306462", "92fecb21-1406-426a-afa5-cd5c1f306462", "92fecb22-1406-426a-afa5-cd5c1f306462"); this->bleServer->setCallbacks(this); + + this->eepromUnit = this->eepromManager->registerEempromUnit(sizeof(DisplayProperties)); + this->loadProperties(); } -void OmobiLedDisplay::loop() { - if(millis() - lastKeepAlive > this->maximumKeepAliveDelay) { +void OmobiLedDisplay::loop() +{ + if (millis() - lastKeepAlive > this->maximumKeepAliveDelay) + { this->lastKeepAlive = millis(); this->bleServer->disconnectCurrentDevice(); } @@ -23,13 +31,16 @@ void OmobiLedDisplay::loop() { void OmobiLedDisplay::onDeviceConnectedChanged(bool deviceConnected) { - if (deviceConnected) { + if (deviceConnected) + { Serial.println("Device connected"); this->lastKeepAlive = millis(); } - else { + else + { Serial.println("Device disconnected"); this->lastKeepAlive = -1; + this->sessionAuthorized = false; } } @@ -41,7 +52,7 @@ void OmobiLedDisplay::onDataReceived(String dataString) DeserializationError error = deserializeJson(requestDoc, dataString); // return on error - if(error != DeserializationError::Ok) + if (error != DeserializationError::Ok) return; // get reuqest data @@ -54,91 +65,180 @@ void OmobiLedDisplay::onDataReceived(String dataString) OmobiDisplayStatusCode replyStatus = InternalError; JsonObject replyData = replyDoc.createNestedObject("data"); - switch (requestHeader) - { - case KeepAliveCommand: - { - replyStatus = Success; - this->lastKeepAlive = millis(); - break; - } - - case GetAllTextSetsCommand: - { - // cycle through all text sets - for (int textSetIndex = 0; textSetIndex < LedDisplayController::maximumTextSets; textSetIndex++) + if (requestHeader != AuthorizeSessionCommand && !this->sessionAuthorized) + replyStatus = Unauthorized; + else + switch (requestHeader) { - if (this->ledDisplayController->getTextSetParameter(textSetIndex, LedDisplayController::TextParameter) == "") - continue; - - //Serial.println("Adding index " + String(textSetIndex) + " with text: " + this->ledDisplayController->getTextSetParameter(textSetIndex, LedDisplayController::TextParameter)); - - // if a set isn't empty, go through all parameters - for (int textSetParameter = 0; textSetParameter < LedDisplayController::DisplayTextSetParameterCount; textSetParameter++) + case AuthorizeSessionCommand: + { + if (this->sessionAuthorized) { - // send each parameter to the client - const size_t capacity = JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + 200; - DynamicJsonDocument doc(capacity); - - doc["header"] = GetTextSetParameterCommand; - - JsonObject data = doc.createNestedObject("data"); - data["index"] = textSetIndex; - data["parameter"] = textSetParameter; - data["value"] = this->ledDisplayController->getTextSetParameter( - textSetIndex, - LedDisplayController::DisplayTextSetParameter(textSetParameter)); - - String json; - serializeJson(doc, json); - this->bleServer->sendData(json); + replyStatus = Success; } + else if (requestData["secret"] == this->sha256(this->properties.deviceCode)) + { + replyStatus = Success; + this->sessionAuthorized = true; + } + else + { + replyStatus = Unauthorized; + this->sessionAuthorized = false; + break; + } + + // for future use: add some variables of the display } - replyStatus = Success; - replyData["maximumTextSets"] = LedDisplayController::maximumTextSets; - replyData["maximumTextLength"] = LedDisplayController::maximumTextLength; - - break; - } - case GetDisplayBrightnessCommand: - { - replyData["displayBrightness"] = this->ledDisplayController->getBrightness(); - replyStatus = Success; - break; - } - - case SetTextSetParameterCommand: - { - int index = requestData["index"]; - int parameter = requestData["parameter"]; - String value = requestData["value"]; - - LedDisplayController::GetSetTextSetParameterExitCode res = this->ledDisplayController->setTextSetParameter(index, LedDisplayController::DisplayTextSetParameter(parameter), value); - - if (res == LedDisplayController::Success) - replyStatus = Success; - else + case KeepAliveCommand: { - replyStatus = DisplayControllerError; - replyData["displayControllerError"] = res; + replyStatus = Success; + this->lastKeepAlive = millis(); + break; } - break; - } - case SetDisplayBrightnessCommand: - { - this->ledDisplayController->setBrightness(requestData["displayBrightness"]); - replyStatus = Success; - break; - } - default: - break; - } + case GetAllTextSetsCommand: + { + // cycle through all text sets + for (int textSetIndex = 0; textSetIndex < LedDisplayController::maximumTextSets; textSetIndex++) + { + if (this->ledDisplayController->getTextSetParameter(textSetIndex, LedDisplayController::TextParameter) == "") + continue; + + Serial.println("Adding index " + String(textSetIndex) + " with text: " + this->ledDisplayController->getTextSetParameter(textSetIndex, LedDisplayController::TextParameter)); + + // if a set isn't empty, go through all parameters + for (int textSetParameter = 0; textSetParameter < LedDisplayController::DisplayTextSetParameterCount; textSetParameter++) + { + // send each parameter to the client + const size_t capacity = JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + 200; + DynamicJsonDocument doc(capacity); + + doc["header"] = GetTextSetParameterCommand; + + JsonObject data = doc.createNestedObject("data"); + data["index"] = textSetIndex; + data["parameter"] = textSetParameter; + data["value"] = this->ledDisplayController->getTextSetParameter( + textSetIndex, + LedDisplayController::DisplayTextSetParameter(textSetParameter)); + + String json; + serializeJson(doc, json); + this->bleServer->sendData(json); + } + } + + replyStatus = Success; + replyData["maximumTextSets"] = LedDisplayController::maximumTextSets; + replyData["maximumTextLength"] = LedDisplayController::maximumTextLength; + + break; + } + case GetDisplayBrightnessCommand: + { + replyData["displayBrightness"] = this->ledDisplayController->getBrightness(); + replyStatus = Success; + break; + } + + case SetTextSetParameterCommand: + { + int index = requestData["index"]; + int parameter = requestData["parameter"]; + String value = requestData["value"]; + + LedDisplayController::GetSetTextSetParameterExitCode res = this->ledDisplayController->setTextSetParameter(index, LedDisplayController::DisplayTextSetParameter(parameter), value); + + if (res == LedDisplayController::Success) + replyStatus = Success; + else + { + replyStatus = DisplayControllerError; + replyData["displayControllerError"] = res; + } + + break; + } + case SetDisplayBrightnessCommand: + { + this->ledDisplayController->setBrightness(requestData["displayBrightness"]); + replyStatus = Success; + break; + } + default: + break; + } // reply to the client replyDoc["status"] = replyStatus; String json; serializeJson(replyDoc, json); this->bleServer->sendData(json); +} + +bool OmobiLedDisplay::loadProperties() +{ + if (this->eepromUnit == nullptr) + return false; + + DisplayProperties buf = {}; + + // read conf from EEPROM + this->eepromUnit->read(buf); + + if (strcmp(buf.valid, "OK") == 0) + { + memcpy(&this->properties, &buf, sizeof(DisplayProperties)); + } + else + { + // There was an error reading the properties -> rebuild with default values! + DisplayProperties defaultProperties{ + "Omobi Led Display", + "1234", + "OK"}; + + memcpy(&this->properties, &defaultProperties, sizeof(DisplayProperties)); + this->storeProperties(); + } + + return true; +} + +bool OmobiLedDisplay::storeProperties() +{ + if (this->eepromUnit == nullptr) + return false; + + strncpy(this->properties.valid, "OK", sizeof(this->properties.valid)); + this->eepromUnit->write(this->properties); + return true; +} + +String OmobiLedDisplay::sha256(String payload) +{ + byte shaResult[32]; + mbedtls_md_context_t ctx; + mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256; + const size_t payloadLength = strlen(payload.c_str()); + mbedtls_md_init(&ctx); + mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0); + mbedtls_md_starts(&ctx); + mbedtls_md_update(&ctx, (const unsigned char *)payload.c_str(), payloadLength); + mbedtls_md_finish(&ctx, shaResult); + mbedtls_md_free(&ctx); + + String resultString = ""; + + for (int i = 0; i < sizeof(shaResult); i++) + { + char str[3]; + + sprintf(str, "%02x", (int)shaResult[i]); + resultString += str; + } + + return resultString; } \ No newline at end of file