Найти тему
Arduino и прочие питомцы

Параметры в EEPROM для ESP8266

В большинстве скетчей, которые уже не только мигают светодиодом (и даже в той самой "погодной станции", например :)) требуется хранить некоторое количество различных параметров. Имитация EEPROM для этого прекрасно подходит, если, конечно, общий размер ваших параметров не превышает 4 КБ.

Учитывая, что библиотека EEPROM.h хранит в оперативной памяти буфер, чтобы записывать во флеш-память данные только при вызове метода commit(), грех не использовать эту уже выделенную из "кучи" область памяти для параметров, чтобы занимать как можно меньше ОЗУ.

Итак, вырисовывается класс, который при инициализации проверяет содержимое EEPROM на целостность параметров с помощью, скажем, контрольной суммы, и в случае несовпадения сбрасывает значения параметров на умалчиваемые величины.

Но как же нам упростить себе задачу именования параметров, не увеличивая размер кода? Да очень просто. Создадим тип структуры с именованными полями нужных типов (это и будут наши параметры), а сам класс с помощью шаблонов "научим" работать с этой структурой, возвращая указатель на нее как результат переопределения оператора -> (т.е. как будто наш класс и есть эта структура).

template<typename T>
class Parameters {
public:
typedef std::function<void(T*)> clearcb_t;

Parameters() : _onclear(nullptr) {}

void begin();
bool check() const;
operator bool() const {
return check();
}
T* operator ->() {
return ptr();
}
bool import(const uint8_t *data);
bool store();
void onClear(clearcb_t cb) {
_onclear = cb;
}
void clear();

protected:
static const uint16_t SIGN = 0xA3C5;

T *ptr() {
return (T*)&EEPROM.getDataPtr()[4];
}

clearcb_t _onclear;
};

Как же этим пользоваться? В первую очередь, создаем тип структуры (можно упакованной) с полями нужных нам типов. Затем создаем экземпляр класса Parameters с именем типа этой структуры в треугольных скобках (как параметр шаблона). Определяем с помощью лямбда или обычной функции и метода onClear() код для присвоения начальных значений нашим параметрам (если вас не устраивают все 0), и после вызова метода begin() нашими параметрами можно пользоваться с помощью оператора -> и имен полей структуры, как будто мы работаем именно с ней. Если вы поменяли значение какого-нибудь параметра или сбросили их в начальные значения с помощью метода clear(), то не забудьте вызвать метод store(), предварительно можно проверить необходимость обновления EEPROM с помощью if().

struct __attribute__((__packed__)) config_t {
char wifi_ssid[32];
char wifi_pswd[32];
char ntp_server[32];
int8_t ntp_tz;
};

Parameters<config_t> config;

void setup() {
Serial.begin(115200);
Serial.println();

config.onClear([](config_t *cfg) {
strcpy_P(cfg->ntp_server, PSTR("pool.ntp.org"));
cfg->ntp_tz = 3;
});
config.begin();
Serial.printf_P(PSTR("WiFi SSID: \"%s\"\r\n"), config->wifi_ssid);
Serial.printf_P(PSTR("WiFi password: \"%s\"\r\n"), config->wifi_pswd);
Serial.printf_P(PSTR("NTP server: \"%s\"\r\n"), config->ntp_server);
Serial.printf_P(PSTR("NTP tize zone: \"%i\"\r\n"), config->ntp_tz);
strcpy_P(config->wifi_pswd, PSTR("12345678"));
config->ntp_tz = 0;
if (! config) {
if (config.store()) {
Serial.println(F("Parameters stored"));
} else {
Serial.println(F("Error storing parameters!"));
}
}

Serial.flush();
ESP.deepSleep(0);
}

void loop() {}

Полный код примера доступен по ссылке https://disk.yandex.ru/d/aLkwUmaH25-Zrg