mirror of
https://github.com/okalachev/flix.git
synced 2026-08-15 16:29:00 +00:00
Implement Preferences.h for STM32
This commit is contained in:
@@ -31,7 +31,6 @@ jobs:
|
||||
- name: Build firmware for STM32F4
|
||||
run: |
|
||||
arduino-cli core install STMicroelectronics:stm32 --additional-urls https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json
|
||||
arduino-cli lib install "Preferences"
|
||||
make BOARD=STMicroelectronics:stm32:GenF4
|
||||
- name: Upload binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
// Parameters storage in flash memory
|
||||
|
||||
#include <Preferences.h>
|
||||
#include "prefs.h"
|
||||
#include "util.h"
|
||||
|
||||
extern int channelZero[16], channelMax[16];
|
||||
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
#ifdef ESP32
|
||||
#include <Preferences.h>
|
||||
#else
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <EEPROM.h>
|
||||
#include <string.h>
|
||||
|
||||
class Preferences {
|
||||
public:
|
||||
Preferences() = default;
|
||||
~Preferences() = default;
|
||||
|
||||
bool begin(const char *name, bool readOnly = false, const char *partition_label = NULL) {
|
||||
(void)name;
|
||||
(void)readOnly;
|
||||
(void)partition_label;
|
||||
load();
|
||||
started = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void end() {
|
||||
started = false;
|
||||
}
|
||||
|
||||
bool clear() {
|
||||
if (!started) return false;
|
||||
for (auto &entry : entries) {
|
||||
entry = Entry();
|
||||
}
|
||||
return save();
|
||||
}
|
||||
|
||||
size_t putFloat(const char *key, float value) {
|
||||
if (!started) return 0;
|
||||
int index = ensureKey(key);
|
||||
if (index < 0) return 0;
|
||||
entries[index].hasFloat = true;
|
||||
entries[index].hasString = false;
|
||||
entries[index].stringValue = "";
|
||||
entries[index].floatValue = value;
|
||||
if (!save()) return 0;
|
||||
return sizeof(float);
|
||||
}
|
||||
|
||||
float getFloat(const char *key, float defaultValue = NAN) {
|
||||
if (!started) return defaultValue;
|
||||
int index = findKey(key);
|
||||
if (index < 0 || !entries[index].hasFloat) return defaultValue;
|
||||
return entries[index].floatValue;
|
||||
}
|
||||
|
||||
size_t putString(const char *key, const char *value) {
|
||||
if (!started) return 0;
|
||||
int index = ensureKey(key);
|
||||
if (index < 0) return 0;
|
||||
if (!value) value = "";
|
||||
if (strlen(value) > MAX_STRING_LEN) return 0;
|
||||
|
||||
entries[index].hasString = true;
|
||||
entries[index].hasFloat = false;
|
||||
entries[index].stringValue = value;
|
||||
if (!save()) return 0;
|
||||
return entries[index].stringValue.length();
|
||||
}
|
||||
|
||||
size_t putString(const char *key, String value) {
|
||||
return putString(key, value.c_str());
|
||||
}
|
||||
|
||||
String getString(const char *key, String defaultValue = String()) {
|
||||
if (!started) return defaultValue;
|
||||
int index = findKey(key);
|
||||
if (index < 0 || !entries[index].hasString) return defaultValue;
|
||||
return entries[index].stringValue;
|
||||
}
|
||||
|
||||
bool isKey(const char *key) {
|
||||
if (!started) return false;
|
||||
return findKey(key) >= 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static const int MAX_ENTRIES = 128;
|
||||
static const int MAX_KEY_LEN = 15;
|
||||
static const int MAX_STRING_LEN = 95;
|
||||
static const uint32_t MAGIC = 0x46504B56; // "VKPF"
|
||||
static const uint8_t VERSION = 1;
|
||||
static const uint8_t TYPE_FLOAT = 1;
|
||||
static const uint8_t TYPE_STRING = 2;
|
||||
static const int STORAGE_SIZE = 4096;
|
||||
|
||||
struct Entry {
|
||||
bool used = false;
|
||||
char key[MAX_KEY_LEN + 1] = {};
|
||||
bool hasFloat = false;
|
||||
float floatValue = NAN;
|
||||
bool hasString = false;
|
||||
String stringValue;
|
||||
};
|
||||
|
||||
bool started = false;
|
||||
Entry entries[MAX_ENTRIES];
|
||||
|
||||
static void writeU16(uint8_t *dst, uint16_t value) {
|
||||
dst[0] = static_cast<uint8_t>(value & 0xFF);
|
||||
dst[1] = static_cast<uint8_t>((value >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
static void writeU32(uint8_t *dst, uint32_t value) {
|
||||
dst[0] = static_cast<uint8_t>(value & 0xFF);
|
||||
dst[1] = static_cast<uint8_t>((value >> 8) & 0xFF);
|
||||
dst[2] = static_cast<uint8_t>((value >> 16) & 0xFF);
|
||||
dst[3] = static_cast<uint8_t>((value >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
static uint16_t readU16(const uint8_t *src) {
|
||||
return static_cast<uint16_t>(src[0]) |
|
||||
(static_cast<uint16_t>(src[1]) << 8);
|
||||
}
|
||||
|
||||
static uint32_t readU32(const uint8_t *src) {
|
||||
return static_cast<uint32_t>(src[0]) |
|
||||
(static_cast<uint32_t>(src[1]) << 8) |
|
||||
(static_cast<uint32_t>(src[2]) << 16) |
|
||||
(static_cast<uint32_t>(src[3]) << 24);
|
||||
}
|
||||
|
||||
int availableStorageSize() {
|
||||
int n = STORAGE_SIZE;
|
||||
if (EEPROM.length() > 0 && EEPROM.length() < n) n = EEPROM.length();
|
||||
return n;
|
||||
}
|
||||
|
||||
bool save() {
|
||||
const int storageSize = availableStorageSize();
|
||||
if (storageSize < 16) return false;
|
||||
|
||||
uint8_t buffer[STORAGE_SIZE] = {};
|
||||
int pos = 0;
|
||||
writeU32(buffer + pos, MAGIC);
|
||||
pos += 4;
|
||||
buffer[pos++] = VERSION;
|
||||
buffer[pos++] = 0;
|
||||
int lengthPos = pos;
|
||||
pos += 2;
|
||||
|
||||
for (int i = 0; i < MAX_ENTRIES; i++) {
|
||||
if (!entries[i].used) continue;
|
||||
if (!entries[i].hasFloat && !entries[i].hasString) continue;
|
||||
|
||||
const uint8_t keyLen = static_cast<uint8_t>(strnlen(entries[i].key, MAX_KEY_LEN));
|
||||
if (keyLen == 0) continue;
|
||||
|
||||
if (entries[i].hasFloat) {
|
||||
const int recordSize = 3 + keyLen + 4;
|
||||
if (pos + recordSize > storageSize) return false;
|
||||
|
||||
buffer[pos++] = TYPE_FLOAT;
|
||||
buffer[pos++] = keyLen;
|
||||
buffer[pos++] = 4;
|
||||
memcpy(buffer + pos, entries[i].key, keyLen);
|
||||
pos += keyLen;
|
||||
float value = entries[i].floatValue;
|
||||
memcpy(buffer + pos, &value, sizeof(value));
|
||||
pos += sizeof(value);
|
||||
} else if (entries[i].hasString) {
|
||||
const uint8_t valueLen = static_cast<uint8_t>(entries[i].stringValue.length());
|
||||
const int recordSize = 3 + keyLen + valueLen;
|
||||
if (pos + recordSize > storageSize) return false;
|
||||
|
||||
buffer[pos++] = TYPE_STRING;
|
||||
buffer[pos++] = keyLen;
|
||||
buffer[pos++] = valueLen;
|
||||
memcpy(buffer + pos, entries[i].key, keyLen);
|
||||
pos += keyLen;
|
||||
if (valueLen > 0) {
|
||||
memcpy(buffer + pos, entries[i].stringValue.c_str(), valueLen);
|
||||
pos += valueLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writeU16(buffer + lengthPos, static_cast<uint16_t>(pos));
|
||||
|
||||
for (int i = 0; i < storageSize; i++) {
|
||||
EEPROM.write(i, buffer[i]);
|
||||
}
|
||||
EEPROM.commit();
|
||||
return true;
|
||||
}
|
||||
|
||||
void load() {
|
||||
const int storageSize = availableStorageSize();
|
||||
if (storageSize < 16) return;
|
||||
|
||||
for (auto &entry : entries) entry = Entry();
|
||||
|
||||
uint8_t buffer[STORAGE_SIZE] = {};
|
||||
for (int i = 0; i < storageSize; i++) {
|
||||
buffer[i] = EEPROM.read(i);
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
if (readU32(buffer + pos) != MAGIC) return;
|
||||
pos += 4;
|
||||
if (buffer[pos++] != VERSION) return;
|
||||
pos++; // flags
|
||||
|
||||
const uint16_t totalLen = readU16(buffer + pos);
|
||||
pos += 2;
|
||||
if (totalLen < pos || totalLen > storageSize) return;
|
||||
|
||||
while (pos + 3 <= totalLen) {
|
||||
const uint8_t type = buffer[pos++];
|
||||
const uint8_t keyLen = buffer[pos++];
|
||||
const uint8_t valueLen = buffer[pos++];
|
||||
if (keyLen == 0 || keyLen > MAX_KEY_LEN) return;
|
||||
if (pos + keyLen + valueLen > totalLen) return;
|
||||
|
||||
char key[MAX_KEY_LEN + 1] = {};
|
||||
memcpy(key, buffer + pos, keyLen);
|
||||
key[keyLen] = '\0';
|
||||
pos += keyLen;
|
||||
|
||||
int index = ensureKey(key);
|
||||
if (index < 0) return;
|
||||
|
||||
if (type == TYPE_FLOAT && valueLen == 4) {
|
||||
float value = NAN;
|
||||
memcpy(&value, buffer + pos, sizeof(value));
|
||||
entries[index].hasFloat = true;
|
||||
entries[index].hasString = false;
|
||||
entries[index].stringValue = "";
|
||||
entries[index].floatValue = value;
|
||||
} else if (type == TYPE_STRING && valueLen <= MAX_STRING_LEN) {
|
||||
char value[MAX_STRING_LEN + 1] = {};
|
||||
if (valueLen > 0) memcpy(value, buffer + pos, valueLen);
|
||||
value[valueLen] = '\0';
|
||||
entries[index].hasString = true;
|
||||
entries[index].hasFloat = false;
|
||||
entries[index].stringValue = value;
|
||||
}
|
||||
|
||||
pos += valueLen;
|
||||
}
|
||||
}
|
||||
|
||||
int findKey(const char *key) {
|
||||
if (!key) return -1;
|
||||
for (int i = 0; i < MAX_ENTRIES; i++) {
|
||||
if (!entries[i].used) continue;
|
||||
if (strncmp(entries[i].key, key, MAX_KEY_LEN + 1) == 0) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ensureKey(const char *key) {
|
||||
if (!key) return -1;
|
||||
if (strlen(key) > MAX_KEY_LEN) return -1;
|
||||
|
||||
int index = findKey(key);
|
||||
if (index >= 0) return index;
|
||||
|
||||
for (int i = 0; i < MAX_ENTRIES; i++) {
|
||||
if (entries[i].used) continue;
|
||||
entries[i].used = true;
|
||||
entries[i].hasFloat = false;
|
||||
entries[i].hasString = false;
|
||||
entries[i].floatValue = NAN;
|
||||
entries[i].stringValue = "";
|
||||
strncpy(entries[i].key, key, MAX_KEY_LEN);
|
||||
entries[i].key[MAX_KEY_LEN] = '\0';
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
#include <WiFiUdp.h>
|
||||
#include <MacAddress.h>
|
||||
#include <ESP32_NOW_Serial.h>
|
||||
#include <Preferences.h>
|
||||
#include "prefs.h"
|
||||
#include "util.h"
|
||||
|
||||
extern Preferences storage; // use the main preferences storage
|
||||
|
||||
Reference in New Issue
Block a user