18static const char *
const TAG =
"esp8266.preferences";
20static constexpr uint32_t ESP_RTC_USER_MEM_START = 0x60001200;
21static constexpr uint32_t ESP_RTC_USER_MEM_SIZE_WORDS = 128;
22static constexpr uint32_t ESP_RTC_USER_MEM_SIZE_BYTES = ESP_RTC_USER_MEM_SIZE_WORDS * 4;
27static constexpr uint32_t RTC_EBOOT_REGION_WORDS = 32;
28static constexpr uint32_t RTC_NORMAL_REGION_WORDS = 96;
29static constexpr uint32_t PREF_TOTAL_WORDS = RTC_EBOOT_REGION_WORDS + RTC_NORMAL_REGION_WORDS;
32static constexpr uint32_t MAX_PREFERENCE_WORDS = 255;
34#define ESP_RTC_USER_MEM ((uint32_t *) ESP_RTC_USER_MEM_START)
40#ifdef USE_ESP8266_PREFERENCES_FLASH
41static constexpr uint32_t ESP8266_FLASH_STORAGE_SIZE = 128;
43static constexpr uint32_t ESP8266_FLASH_STORAGE_SIZE = 64;
47 s_flash_storage[ESP8266_FLASH_STORAGE_SIZE];
48static bool s_prevent_write =
false;
49static bool s_flash_dirty =
false;
51static inline bool esp_rtc_user_mem_read(uint32_t index, uint32_t *dest) {
52 if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) {
55 *dest = ESP_RTC_USER_MEM[index];
59static inline bool esp_rtc_user_mem_write(uint32_t index, uint32_t value) {
60 if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) {
63 if (index < 32 && s_prevent_write) {
67 auto *ptr = &ESP_RTC_USER_MEM[index];
74static uint32_t get_esp8266_flash_sector() {
80 return (data.uint - 0x40200000) / SPI_FLASH_SEC_SIZE;
82static uint32_t get_esp8266_flash_address() {
return get_esp8266_flash_sector() * SPI_FLASH_SEC_SIZE; }
84static inline size_t bytes_to_words(
size_t bytes) {
return (bytes + 3) / 4; }
88 while (first != last) {
89 crc ^= (*first++ * 2654435769UL) >> 1;
94static bool save_to_flash(
size_t offset,
const uint32_t *data,
size_t len) {
95 for (uint32_t i = 0; i <
len; i++) {
96 uint32_t j = offset + i;
97 if (j >= ESP8266_FLASH_STORAGE_SIZE)
100 uint32_t *ptr = &s_flash_storage[j];
102 s_flash_dirty =
true;
108static bool load_from_flash(
size_t offset, uint32_t *data,
size_t len) {
109 for (
size_t i = 0; i <
len; i++) {
111 if (j >= ESP8266_FLASH_STORAGE_SIZE)
113 data[i] = s_flash_storage[j];
118static bool save_to_rtc(
size_t offset,
const uint32_t *data,
size_t len) {
119 for (uint32_t i = 0; i <
len; i++) {
120 if (!esp_rtc_user_mem_write(offset + i, data[i]))
126static bool load_from_rtc(
size_t offset, uint32_t *data,
size_t len) {
127 for (uint32_t i = 0; i <
len; i++) {
128 if (!esp_rtc_user_mem_read(offset + i, &data[i]))
137static constexpr size_t PREF_MAX_BUFFER_WORDS =
138 ESP8266_FLASH_STORAGE_SIZE > RTC_NORMAL_REGION_WORDS ? ESP8266_FLASH_STORAGE_SIZE : RTC_NORMAL_REGION_WORDS;
140class ESP8266PreferenceBackend :
public ESPPreferenceBackend {
144 uint8_t length_words = 0;
145 bool in_flash =
false;
147 bool save(
const uint8_t *data,
size_t len)
override {
148 if (bytes_to_words(
len) != this->length_words)
150 const size_t buffer_size =
static_cast<size_t>(this->length_words) + 1;
151 if (buffer_size > PREF_MAX_BUFFER_WORDS)
153 uint32_t buffer[PREF_MAX_BUFFER_WORDS];
154 memset(buffer, 0, buffer_size *
sizeof(uint32_t));
155 memcpy(buffer, data,
len);
156 buffer[this->length_words] =
calculate_crc(buffer, buffer + this->length_words, this->type);
157 return this->in_flash ? save_to_flash(this->offset, buffer, buffer_size)
158 : save_to_rtc(this->offset, buffer, buffer_size);
161 bool load(uint8_t *data,
size_t len)
override {
162 if (bytes_to_words(
len) != this->length_words)
164 const size_t buffer_size =
static_cast<size_t>(this->length_words) + 1;
165 if (buffer_size > PREF_MAX_BUFFER_WORDS)
167 uint32_t buffer[PREF_MAX_BUFFER_WORDS];
168 bool ret = this->in_flash ? load_from_flash(this->offset, buffer, buffer_size)
169 : load_from_rtc(this->offset, buffer, buffer_size);
172 if (buffer[this->length_words] !=
calculate_crc(buffer, buffer + this->length_words, this->type))
174 memcpy(data, buffer,
len);
179class ESP8266Preferences :
public ESPPreferences {
185 ESP_LOGVV(TAG,
"Loading preferences from flash");
189 spi_flash_read(get_esp8266_flash_address(), s_flash_storage, ESP8266_FLASH_STORAGE_SIZE * 4);
193 ESPPreferenceObject make_preference(
size_t length, uint32_t
type,
bool in_flash)
override {
195 if (length_words > MAX_PREFERENCE_WORDS) {
196 ESP_LOGE(TAG,
"Preference too large: %u words",
static_cast<unsigned int>(length_words));
200 const uint32_t total_words = length_words + 1;
204 if (this->current_flash_offset + total_words > ESP8266_FLASH_STORAGE_SIZE)
206 offset =
static_cast<uint16_t
>(this->current_flash_offset);
207 this->current_flash_offset += total_words;
209 uint32_t start = this->current_offset;
210 bool in_normal = start < RTC_NORMAL_REGION_WORDS;
213 if (in_normal && start + total_words > RTC_NORMAL_REGION_WORDS) {
215 this->current_offset = start = RTC_NORMAL_REGION_WORDS;
218 if (start + total_words > PREF_TOTAL_WORDS)
221 offset =
static_cast<uint16_t
>(in_normal ? start + RTC_EBOOT_REGION_WORDS : start - RTC_NORMAL_REGION_WORDS);
222 this->current_offset = start + total_words;
225 auto *pref =
new ESP8266PreferenceBackend();
226 pref->offset = offset;
228 pref->length_words =
static_cast<uint8_t
>(length_words);
229 pref->in_flash = in_flash;
233 ESPPreferenceObject make_preference(
size_t length, uint32_t
type)
override {
234#ifdef USE_ESP8266_PREFERENCES_FLASH
241 bool sync()
override {
247 ESP_LOGD(TAG,
"Saving");
248 SpiFlashOpResult erase_res, write_res = SPI_FLASH_RESULT_OK;
251 erase_res = spi_flash_erase_sector(get_esp8266_flash_sector());
252 if (erase_res == SPI_FLASH_RESULT_OK) {
253 write_res = spi_flash_write(get_esp8266_flash_address(), s_flash_storage, ESP8266_FLASH_STORAGE_SIZE * 4);
256 if (erase_res != SPI_FLASH_RESULT_OK) {
257 ESP_LOGE(TAG,
"Erasing failed");
260 if (write_res != SPI_FLASH_RESULT_OK) {
261 ESP_LOGE(TAG,
"Writing failed");
265 s_flash_dirty =
false;
269 bool reset()
override {
270 ESP_LOGD(TAG,
"Erasing storage");
271 SpiFlashOpResult erase_res;
274 erase_res = spi_flash_erase_sector(get_esp8266_flash_sector());
276 if (erase_res != SPI_FLASH_RESULT_OK) {
277 ESP_LOGE(TAG,
"Erasing failed");
282 s_prevent_write =
true;
287static ESP8266Preferences s_preferences;
290 s_preferences.setup();
void preferences_prevent_write(bool prevent)
uint32_t calculate_crc(It first, It last, uint32_t type)
Providing packet encoding functions for exchanging data with a remote host.
ESPPreferences * global_preferences