речь об эмулированной во флеш eeprom
вот как раз это и не получается эмулированный еепром. Если вы что-то эмулируете, то интерфейс должен быть такой же как у объекта который вы эмулируете. Т.е. должна быть возможноть обращаться к конкретным ячейкам по адресам, или в абстрактном случае по некоему ключу, которым может быть в частности некий виртуальный адрес.
если нужно обновлять переменную с моточасами раз в минуту или чаще?
вот с этого и нужно начинать. Вы сделали некую обертку, которая по сути занимается flash rollover. И, КМК, вам так её и нужно позиционировать, а не как эмулятор еепром. У вас получается запись данных в скользящее окно, т.е. при циклической записи достуны последние n значений. Задач на подобные хранилища дововольно много - всевозможные data series, данные с датчиков, метрики, результаты расчетов и пр. где нужна история за некоторый последний период. И КМК это будет более восстребованый сценарий использовать вашу либу чем как эмулятор еепром. Придется, правда, добавить методы доступа ко всем блокам данных, но это решаемо. Если интересно - посмотрите
мою реализацию подобного контейнера в памяти.
Замена еепром у вас не получилась - задачу выравнивания записи вы решили, а задачу произвольного поадресного доступа - нет.
structEE.begin(1019,2); //первый будет жить в 1018-1019 секторах
charEE.begin(1017, 2); //второй - в 1017-1016 секторах
тогда у вас в примере ошибка:
param.begin(1000, 2); //5 sectors 1000 -> 999
. Должно быть или 2 сектора или (1000, 5)
Но в целом я понял, под каждый объект ебум нужно выделять отдельные сектора, если выделать минимум 2, то это 8к.
Обычно структура пишется в 5 - 10 строчек под нужны конкретного приложения, а не на все случаи жизни... какие еще случаи?
приведу пример:
ардуино-стайл - создается некая струкрута для моего "скетча" и она же пишется через Ебум
struct AppParam {
uint32_t devID = 0x12345678; //default value for zero init
uint16_t minSpparamd = 10;
uint16_t maxSpparamd = 60;
uint16_t maxTemp = 85;
uint8_t lastOffSrc = 0x00;
uint8_t arr[10];
char ssid[20] = "Empty_SSID";
char pass[20] = "Empty_PASS";
};
Скетч работает, все рады. Теперь мне захотелось добавить функционал с энкодером, нужно писать в "конфиг" пины для энкодера, я меняю структуру, добавляю 2 члена
struct AppParam {
uint32_t devID = 0x12345678; //default value for zero init
uint16_t minSpparamd = 10;
uint16_t maxSpparamd = 60;
uint16_t maxTemp = 85;
uint8_t lastOffSrc = 0x00;
uint8_t arr[10];
char ssid[20] = "Empty_SSID";
char pass[20] = "Empty_PASS";
int encoder_clk;
int encoder_data;
};
в итоге старые данные в "еепром" становятся невалидными и мы получаем "чистый" конфиг. Несмертельно, но для конечного пользователя оч неприятно и это следует отметить в примерах/документации. Придется делать еще какой-то параллельный способ сохранять конфиг в независимый файл и переписывать данные туда-сюда.
Альтернативный вариант - заводить отдельную структуру и выделять под нее еще 8к. В случае мало-мальски приличного проекта а не "скетча",
подобное массовое заведение глобальных объектов станет проблемой в поддержке кода. Дабы не быть голословным приведу
пример на код из проекта лампы от vvip. Можно оценить количество переменных, хранимых в псевдо-еепром. Понятно что можно раскидать по структурам и уменьшить число объектов раз в 5, но тут и вылезет дилемма между постоянно обнуляемыми данными или количества секторов и объектов, которые нужно держать в памяти.
Мне сложно представить, какие вы задачи решаете, что сталкиваетесь с такими проблемами.
Это не проблемы это задачи возникающие в проектах, которые чуть сложнее чем "ардуино-скетч" на помигать светодиодом.
Количество и типы данных, которые нужно сохранять растет, их упорядочивание становится более насущной задачей чем иметь глобальный конфиг в виде струкруты. Возникает необходимость передавать структуры с конфигами/данными между объектами. В "конфиге" появятся объекты типа String или char* которые либа не переварит и нужно будет на местах создавать какие-то костыли в виде "завести где-то еще отдельно String, потом скопировать из нее в струкруту массив чаров и не забыть учесть максимальный размер." Т.е. для сериализации пригодны только структуры PoD, а другие объекты придется пристегивать как-то сбоку. При большом количестве строк это будет жутко раздувать память структуры местом "про запас".
Выделяете под свои хотелки, например 200 байт массивом. В этом блобе делаете, что душе угодно, хоть String храните. А EEBoom уже будет заниматься тиражированием и ротацией этого блоба по пулу выделенных секторов.
С блобом это понятно что можно накрутить что угодно. Но тут опять же но. Существующий у вас сейчас интерфейс для этого очень неудобен, если вообще пригоден.
Как создать блоб на 200 байт?
EEBoom<uint8_t*> blobcfg;
создаст сруктуру с указателем, а не с данными, которую затем не сможет сериализовать.
Как-то так?
struct MyBlob {
uint8_t raw[200];
}
EEBoom<MyBlob> blobcfg;
Ну допустим, но работать потом с raw как указателем на uint8_t ппц как неудобно. Над ним нужно будет сооружать каку-то надстройку, которая (например) будет в него побайтово переписывать ту же
AppParam
из кода выше. Плюс размер блоба опять же фиксированный, плюс блоб надо держать в памяти параллельно с основной структурой AppParam т.к. копи-конструктора нет. Что вообще сводит всю затею на нет.
Как по мне, то для блобов вам нужно сделать отдельный класс без шаблона, который будет:
а) иметь конструктор с указателем на массив байт и его размер
б) не содержит копии данных который пишет/читает, а делает это по указателю
в) будет способен писать/читать в сектор данные произвольной длинны, хотя бы в пределах сектора чтобы не думать загодя о проблеме роста и не писать лишнего. Тогда получится класс который делает то что умеет - выравнивание записи над сырыми данными без лишних затрат. Он может быть короткоживущим - создал, прочитал/записал, уничтожил или долгоживущим т.к. не жрет место под копию данных а содержит только указатель. И его можно будет использовать под хранение сериализованных объектов, напр через json или messagepack. Это будет реально полезно, т.к. приложений, хранящих данные в джейсонах на файловой системе навалом. И да, это тяжеловато, у литлФС большой перерасход на хранение мелких файлов, хотя выравнивание тоже есть.
Тут без примеров ваших затруднений - никак. Однако замечу, что шаблонной является только верхняя часть, а весь функционал реализован без шаблона.
Это не мои затруднения это требования языка.
Пример - я пишу некий базовый класс, который вычитывает и отдает (или принимает и сохраняет) "конфиг" для некоего объекта по ключу.
Т.е. напр кофиги для энкодера, дисплея, сенсоров и пр., которые лежат где-то на постоянном носителе, на ФС, флеше, в еепром и пр.
Абстрактно
class Configurator {
const &T getConfig(config_t module);
void setConfig(config_t module, const &T);
}
}
Где Т это некий объект с неизвестной структурой (конфиги то у нас разные).
В качестве Т может выступать, например
JsonVariant
из библиотеки
ArduinoJson
. В таком случае можно получать и скармливать конфигуратору произвольно струкрурированные объекты, которые затем сериализуются и пишутся куда-то там...
В случае с вашим
EEBoom
это не получится, т.к. в него еще нужен тип данных
EEBoom<Type>
, т.е. методы
getConfig();
,
setConfig()
неспособны работать с объектами ебум. Придется или конфигуратор тоже шаблонизировать или вообще отказаться от подобного подхода.
И это при том что внутри EEBoom для своей основной задачи сериализации/десериализации никак не использует тип струкруты которую читает/пишет, для него всё это массив байт. Единственное для чего ему нужен шаблонизированный тип, это создать копию хранимого объекта внутри себя.
Это указывает на изначально плохой дизайн.
Понятно что ваш проект ориентирован на простые задачки хранения чего-то понемножку для нетребовательного пользователя. Задел хороший, продолжайте совершенствоваться. Я просто указываю на архитектурные недостатки для, на первый взгляд, простых и очевидных решений.
Вы, наверное, в курсе про реализацию NVS в esp32, если внимательно присмотритесь, то поймёте почему там используется такой сложный формат записей. Но, в итоге, получается довольно мощный key:value сторадж с выравниванием записи, который не привязан к PoD структурам и который легко адаптируется к задачам сериализации/десериализации.