- created a class used to manage the EEPROM space allocation

- added authorization via a shared secret
This commit is contained in:
Dorian Zedler 2020-10-15 22:54:20 +02:00
parent 7cca98106d
commit 2c44f8b873
Signed by: dorian
GPG key ID: D3B255CB8BC7CD37
9 changed files with 377 additions and 114 deletions

View file

@ -7,6 +7,16 @@
"vector": "cpp", "vector": "cpp",
"unordered_map": "cpp", "unordered_map": "cpp",
"random": "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"
} }
} }

View file

@ -0,0 +1,83 @@
#ifndef EEPROM_MANAGER
#define EEPROM_MANAGER
#include <Arduino.h>
#include <EEPROM.h>
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 <typename T>
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 <typename T>
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 <typename T>
const T &write(const T &t)
{
return this->manager->writeToEeprom(this->address, this->size, t);
};
template <typename T>
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

View file

@ -5,7 +5,7 @@
#include <Adafruit_GFX.h> #include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h> #include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h> #include <Adafruit_NeoPixel.h>
#include <EEPROM.h> #include <EepromManager.h>
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
class LedDisplayController class LedDisplayController
@ -52,6 +52,7 @@ public:
void loop(); void loop();
// modifiers for the internal text sets // modifiers for the internal text sets
bool registerEepromUnit(EepromManager* eepromManager);
String getTextSetParameter(int index, DisplayTextSetParameter parameter); String getTextSetParameter(int index, DisplayTextSetParameter parameter);
GetSetTextSetParameterExitCode setTextSetParameter(int index, DisplayTextSetParameter parameter, String value); GetSetTextSetParameterExitCode setTextSetParameter(int index, DisplayTextSetParameter parameter, String value);
@ -105,8 +106,9 @@ private:
sets_t text_sets; sets_t text_sets;
// storage control // storage control
EepromUnit* eepromUnit;
GetSetTextSetParameterExitCode getSetTextSetParameter(int index, DisplayTextSetParameter parameter, String *value, bool set = false); GetSetTextSetParameterExitCode getSetTextSetParameter(int index, DisplayTextSetParameter parameter, String *value, bool set = false);
void storeTextSets(); bool storeTextSets();
bool loadTextSets(); bool loadTextSets();
}; };

View file

@ -3,6 +3,8 @@
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include "mbedtls/md.h"
#include "EepromManager.h"
#include "BluetoothLeUartServer.h" #include "BluetoothLeUartServer.h"
#include "LedDisplayController.h" #include "LedDisplayController.h"
@ -37,15 +39,32 @@ private:
enum OmobiDisplayStatusCode enum OmobiDisplayStatusCode
{ {
Success = 200, Success = 200,
Unauthorized = 401,
InternalError = 500, InternalError = 500,
DisplayControllerError = 501 DisplayControllerError = 501
}; };
unsigned long lastKeepAlive; unsigned long lastKeepAlive;
const int maximumKeepAliveDelay = 10000; const int maximumKeepAliveDelay = 10000;
bool sessionAuthorized;
LedDisplayController *ledDisplayController; typedef struct {
BluetoothLeUartServer *bleServer; 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 #endif // OMOBI_LED_DISPLAY

View file

@ -11,6 +11,8 @@
[env:esp32] [env:esp32]
platform = espressif32 platform = espressif32
board = esp32doit-devkit-v1 board = esp32doit-devkit-v1
board_upload.flash_size=4MB
board_upload.maximum_size=4194304
framework = arduino framework = arduino
lib_deps = lib_deps =
Wire Wire

View file

@ -58,6 +58,10 @@ void BluetoothLeUartServer::sendData(String data)
void BluetoothLeUartServer::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param) 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->deviceConnected = true;
this->deviceConnectionId = param->connect.conn_id; this->deviceConnectionId = param->connect.conn_id;

View file

@ -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;
}

View file

@ -4,6 +4,7 @@ LedDisplayController *ledDisplayControllerGlobal = nullptr;
LedDisplayController::LedDisplayController(Adafruit_NeoMatrix *matrix) LedDisplayController::LedDisplayController(Adafruit_NeoMatrix *matrix)
{ {
this->eepromUnit = nullptr;
ledDisplayControllerGlobal = this; ledDisplayControllerGlobal = this;
this->loadTextSets(); 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) String LedDisplayController::getTextSetParameter(int index, DisplayTextSetParameter parameter)
{ {
if (index < 0 || index > this->maximumTextSets || parameter < 0 || parameter >= DisplayTextSetParameterCount) if (index < 0 || index > this->maximumTextSets || parameter < 0 || parameter >= DisplayTextSetParameterCount)
@ -194,13 +204,14 @@ LedDisplayController::GetSetTextSetParameterExitCode LedDisplayController::getSe
switch (parameter) switch (parameter)
{ {
case TextParameter: case TextParameter:
if (set) { if (set)
if(*value == "") {
if (*value == "")
// delete the index // delete the index
for (int i = index; i < this->maximumTextSets; i++) for (int i = index; i < this->maximumTextSets; i++)
{ {
if(i > this->maximumTextSets - 1) if (i > this->maximumTextSets - 1)
this->text_sets.sets[i] = this->text_sets.sets[i+1]; this->text_sets.sets[i] = this->text_sets.sets[i + 1];
else else
this->text_sets.sets[i] = this->defaultTextSet; 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 Serial.println("Storing Text Sets...");
EEPROM.begin(sizeof(sets_t)); if (this->eepromUnit == nullptr) {
//for (size_t i = 0 ; i < DISP_STRUCT_SIZE ; i++) Serial.println("ERROR");
//{ return false;
// EEPROM.write(i, 0); }
//}
strncpy(text_sets.valid, "OK", sizeof(text_sets.valid)); strncpy(this->text_sets.valid, "OK", sizeof(this->text_sets.valid));
EEPROM.put(0, text_sets); this->eepromUnit->write(this->text_sets);
EEPROM.commit(); Serial.println("OK");
EEPROM.end(); return true;
} }
bool LedDisplayController::loadTextSets() bool LedDisplayController::loadTextSets()
{ {
bool rc = false;
sets_t buf = {}; sets_t buf = {};
// read conf from EEPROM
EEPROM.begin(sizeof(sets_t)); Serial.println("Loading Text Sets...");
EEPROM.get(0, buf);
EEPROM.end(); 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) if (strcmp(buf.valid, "OK") == 0)
{ {
rc = true; memcpy(&text_sets, &buf, sizeof(sets_t));
memcpy(&text_sets, &buf, sizeof(text_sets)); Serial.println("OK");
return true;
} }
else else
{ {
@ -327,18 +345,14 @@ bool LedDisplayController::loadTextSets()
sets_t defaultTextSets; sets_t defaultTextSets;
strncpy(defaultTextSets.valid, "OK", sizeof(defaultTextSets.valid));
for (int i = 0; i < this->maximumTextSets; i++) for (int i = 0; i < this->maximumTextSets; i++)
{ {
defaultTextSets.sets[i] = this->defaultTextSet; defaultTextSets.sets[i] = this->defaultTextSet;
} }
this->text_sets = defaultTextSets; memcpy(&text_sets, &defaultTextSets, sizeof(sets_t));
this->storeTextSets(); return this->storeTextSets();
} }
return rc;
} }
// ---------- // ----------

View file

@ -1,21 +1,29 @@
#include "OmobiLedDisplay.h" #include "OmobiLedDisplay.h"
#include "esp_gatts_api.h"
OmobiLedDisplay::OmobiLedDisplay(String deviceName, Adafruit_NeoMatrix *ledDisplayMatrix) OmobiLedDisplay::OmobiLedDisplay(String deviceName, Adafruit_NeoMatrix *ledDisplayMatrix)
{ {
this->lastKeepAlive = -1; this->lastKeepAlive = -1;
this->sessionAuthorized = false;
// init eeprom manager
this->eepromManager = new EepromManager();
// init led display controller // init led display controller
this->ledDisplayController = new LedDisplayController(ledDisplayMatrix); this->ledDisplayController = new LedDisplayController(ledDisplayMatrix);
this->ledDisplayController->setTextSetParameter(0, LedDisplayController::ActiveParameter, "true"); this->ledDisplayController->registerEepromUnit(this->eepromManager);
// init ble server // 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->bleServer->setCallbacks(this);
this->eepromUnit = this->eepromManager->registerEempromUnit(sizeof(DisplayProperties));
this->loadProperties();
} }
void OmobiLedDisplay::loop() { void OmobiLedDisplay::loop()
if(millis() - lastKeepAlive > this->maximumKeepAliveDelay) { {
if (millis() - lastKeepAlive > this->maximumKeepAliveDelay)
{
this->lastKeepAlive = millis(); this->lastKeepAlive = millis();
this->bleServer->disconnectCurrentDevice(); this->bleServer->disconnectCurrentDevice();
} }
@ -23,13 +31,16 @@ void OmobiLedDisplay::loop() {
void OmobiLedDisplay::onDeviceConnectedChanged(bool deviceConnected) void OmobiLedDisplay::onDeviceConnectedChanged(bool deviceConnected)
{ {
if (deviceConnected) { if (deviceConnected)
{
Serial.println("Device connected"); Serial.println("Device connected");
this->lastKeepAlive = millis(); this->lastKeepAlive = millis();
} }
else { else
{
Serial.println("Device disconnected"); Serial.println("Device disconnected");
this->lastKeepAlive = -1; this->lastKeepAlive = -1;
this->sessionAuthorized = false;
} }
} }
@ -41,7 +52,7 @@ void OmobiLedDisplay::onDataReceived(String dataString)
DeserializationError error = deserializeJson(requestDoc, dataString); DeserializationError error = deserializeJson(requestDoc, dataString);
// return on error // return on error
if(error != DeserializationError::Ok) if (error != DeserializationError::Ok)
return; return;
// get reuqest data // get reuqest data
@ -54,87 +65,111 @@ void OmobiLedDisplay::onDataReceived(String dataString)
OmobiDisplayStatusCode replyStatus = InternalError; OmobiDisplayStatusCode replyStatus = InternalError;
JsonObject replyData = replyDoc.createNestedObject("data"); JsonObject replyData = replyDoc.createNestedObject("data");
switch (requestHeader) if (requestHeader != AuthorizeSessionCommand && !this->sessionAuthorized)
{ replyStatus = Unauthorized;
case KeepAliveCommand: else
{ switch (requestHeader)
replyStatus = Success;
this->lastKeepAlive = millis();
break;
}
case GetAllTextSetsCommand:
{
// cycle through all text sets
for (int textSetIndex = 0; textSetIndex < LedDisplayController::maximumTextSets; textSetIndex++)
{ {
if (this->ledDisplayController->getTextSetParameter(textSetIndex, LedDisplayController::TextParameter) == "") case AuthorizeSessionCommand:
continue; {
if (this->sessionAuthorized)
//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 replyStatus = Success;
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);
} }
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; case KeepAliveCommand:
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; replyStatus = Success;
replyData["displayControllerError"] = res; this->lastKeepAlive = millis();
break;
} }
break; case GetAllTextSetsCommand:
} {
case SetDisplayBrightnessCommand: // cycle through all text sets
{ for (int textSetIndex = 0; textSetIndex < LedDisplayController::maximumTextSets; textSetIndex++)
this->ledDisplayController->setBrightness(requestData["displayBrightness"]); {
replyStatus = Success; if (this->ledDisplayController->getTextSetParameter(textSetIndex, LedDisplayController::TextParameter) == "")
break; continue;
}
default: Serial.println("Adding index " + String(textSetIndex) + " with text: " + this->ledDisplayController->getTextSetParameter(textSetIndex, LedDisplayController::TextParameter));
break;
} // 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 // reply to the client
replyDoc["status"] = replyStatus; replyDoc["status"] = replyStatus;
@ -142,3 +177,68 @@ void OmobiLedDisplay::onDataReceived(String dataString)
serializeJson(replyDoc, json); serializeJson(replyDoc, json);
this->bleServer->sendData(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;
}