- 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",
"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"
}
}

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_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
#include <EEPROM.h>
#include <EepromManager.h>
#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();
};

View file

@ -3,6 +3,8 @@
#include <Arduino.h>
#include <ArduinoJson.h>
#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

View file

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

View file

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

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)
{
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;
}
// ----------

View file

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