19static const char *
const TAG =
"esp8266.preferences";
21static uint32_t *s_flash_storage =
nullptr;
22static bool s_prevent_write =
false;
23static bool s_flash_dirty =
false;
25static constexpr uint32_t ESP_RTC_USER_MEM_START = 0x60001200;
26static constexpr uint32_t ESP_RTC_USER_MEM_SIZE_WORDS = 128;
27static constexpr uint32_t ESP_RTC_USER_MEM_SIZE_BYTES = ESP_RTC_USER_MEM_SIZE_WORDS * 4;
32static constexpr uint32_t RTC_EBOOT_REGION_WORDS = 32;
33static constexpr uint32_t RTC_NORMAL_REGION_WORDS = 96;
34static constexpr uint32_t PREF_TOTAL_WORDS = RTC_EBOOT_REGION_WORDS + RTC_NORMAL_REGION_WORDS;
37static constexpr uint32_t MAX_PREFERENCE_WORDS = 255;
39#define ESP_RTC_USER_MEM ((uint32_t *) ESP_RTC_USER_MEM_START)
41#ifdef USE_ESP8266_PREFERENCES_FLASH
42static constexpr uint32_t ESP8266_FLASH_STORAGE_SIZE = 128;
44static constexpr uint32_t ESP8266_FLASH_STORAGE_SIZE = 64;
47static inline bool esp_rtc_user_mem_read(uint32_t index, uint32_t *dest) {
48 if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) {
51 *dest = ESP_RTC_USER_MEM[index];
55static inline bool esp_rtc_user_mem_write(uint32_t index, uint32_t value) {
56 if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) {
59 if (index < 32 && s_prevent_write) {
63 auto *ptr = &ESP_RTC_USER_MEM[index];
70static uint32_t get_esp8266_flash_sector() {
76 return (data.uint - 0x40200000) / SPI_FLASH_SEC_SIZE;
78static uint32_t get_esp8266_flash_address() {
return get_esp8266_flash_sector() * SPI_FLASH_SEC_SIZE; }
80static inline size_t bytes_to_words(
size_t bytes) {
return (bytes + 3) / 4; }
84 while (first != last) {
85 crc ^= (*first++ * 2654435769UL) >> 1;
90static bool save_to_flash(
size_t offset,
const uint32_t *data,
size_t len) {
91 for (uint32_t i = 0; i <
len; i++) {
92 uint32_t j = offset + i;
93 if (j >= ESP8266_FLASH_STORAGE_SIZE)
96 uint32_t *ptr = &s_flash_storage[j];
104static bool load_from_flash(
size_t offset, uint32_t *data,
size_t len) {
105 for (
size_t i = 0; i <
len; i++) {
107 if (j >= ESP8266_FLASH_STORAGE_SIZE)
109 data[i] = s_flash_storage[j];
114static bool save_to_rtc(
size_t offset,
const uint32_t *data,
size_t len) {
115 for (uint32_t i = 0; i <
len; i++) {
116 if (!esp_rtc_user_mem_write(offset + i, data[i]))
122static bool load_from_rtc(
size_t offset, uint32_t *data,
size_t len) {
123 for (uint32_t i = 0; i <
len; i++) {
124 if (!esp_rtc_user_mem_read(offset + i, &data[i]))
132static constexpr size_t PREF_BUFFER_WORDS = 16;
134class ESP8266PreferenceBackend :
public ESPPreferenceBackend {
138 uint8_t length_words = 0;
139 bool in_flash =
false;
141 bool save(
const uint8_t *data,
size_t len)
override {
142 if (bytes_to_words(
len) != this->length_words)
145 const size_t buffer_size =
static_cast<size_t>(this->length_words) + 1;
146 uint32_t stack_buffer[PREF_BUFFER_WORDS];
147 std::unique_ptr<uint32_t[]> heap_buffer;
150 if (buffer_size <= PREF_BUFFER_WORDS) {
151 buffer = stack_buffer;
153 heap_buffer = make_unique<uint32_t[]>(buffer_size);
154 buffer = heap_buffer.get();
156 memset(buffer, 0, buffer_size *
sizeof(uint32_t));
158 memcpy(buffer, data,
len);
159 buffer[this->length_words] =
calculate_crc(buffer, buffer + this->length_words, this->type);
161 return this->in_flash ? save_to_flash(this->offset, buffer, buffer_size)
162 : save_to_rtc(this->offset, buffer, buffer_size);
165 bool load(uint8_t *data,
size_t len)
override {
166 if (bytes_to_words(
len) != this->length_words)
169 const size_t buffer_size =
static_cast<size_t>(this->length_words) + 1;
170 uint32_t stack_buffer[PREF_BUFFER_WORDS];
171 std::unique_ptr<uint32_t[]> heap_buffer;
174 if (buffer_size <= PREF_BUFFER_WORDS) {
175 buffer = stack_buffer;
177 heap_buffer = make_unique<uint32_t[]>(buffer_size);
178 buffer = heap_buffer.get();
181 bool ret = this->in_flash ? load_from_flash(this->offset, buffer, buffer_size)
182 : load_from_rtc(this->offset, buffer, buffer_size);
186 if (buffer[this->length_words] !=
calculate_crc(buffer, buffer + this->length_words, this->type))
189 memcpy(data, buffer,
len);
194class ESP8266Preferences :
public ESPPreferences {
200 s_flash_storage =
new uint32_t[ESP8266_FLASH_STORAGE_SIZE];
201 ESP_LOGVV(TAG,
"Loading preferences from flash");
205 spi_flash_read(get_esp8266_flash_address(), s_flash_storage, ESP8266_FLASH_STORAGE_SIZE * 4);
209 ESPPreferenceObject make_preference(
size_t length, uint32_t
type,
bool in_flash)
override {
211 if (length_words > MAX_PREFERENCE_WORDS) {
212 ESP_LOGE(TAG,
"Preference too large: %u words",
static_cast<unsigned int>(length_words));
216 const uint32_t total_words = length_words + 1;
220 if (this->current_flash_offset + total_words > ESP8266_FLASH_STORAGE_SIZE)
222 offset =
static_cast<uint16_t
>(this->current_flash_offset);
223 this->current_flash_offset += total_words;
225 uint32_t start = this->current_offset;
226 bool in_normal = start < RTC_NORMAL_REGION_WORDS;
229 if (in_normal && start + total_words > RTC_NORMAL_REGION_WORDS) {
231 this->current_offset = start = RTC_NORMAL_REGION_WORDS;
234 if (start + total_words > PREF_TOTAL_WORDS)
237 offset =
static_cast<uint16_t
>(in_normal ? start + RTC_EBOOT_REGION_WORDS : start - RTC_NORMAL_REGION_WORDS);
238 this->current_offset = start + total_words;
241 auto *pref =
new ESP8266PreferenceBackend();
242 pref->offset = offset;
244 pref->length_words =
static_cast<uint8_t
>(length_words);
245 pref->in_flash = in_flash;
249 ESPPreferenceObject make_preference(
size_t length, uint32_t
type)
override {
250#ifdef USE_ESP8266_PREFERENCES_FLASH
257 bool sync()
override {
263 ESP_LOGD(TAG,
"Saving");
264 SpiFlashOpResult erase_res, write_res = SPI_FLASH_RESULT_OK;
267 erase_res = spi_flash_erase_sector(get_esp8266_flash_sector());
268 if (erase_res == SPI_FLASH_RESULT_OK) {
269 write_res = spi_flash_write(get_esp8266_flash_address(), s_flash_storage, ESP8266_FLASH_STORAGE_SIZE * 4);
272 if (erase_res != SPI_FLASH_RESULT_OK) {
273 ESP_LOGE(TAG,
"Erasing failed");
276 if (write_res != SPI_FLASH_RESULT_OK) {
277 ESP_LOGE(TAG,
"Writing failed");
281 s_flash_dirty =
false;
285 bool reset()
override {
286 ESP_LOGD(TAG,
"Erasing storage");
287 SpiFlashOpResult erase_res;
290 erase_res = spi_flash_erase_sector(get_esp8266_flash_sector());
292 if (erase_res != SPI_FLASH_RESULT_OK) {
293 ESP_LOGE(TAG,
"Erasing failed");
298 s_prevent_write =
true;
304 auto *pref =
new ESP8266Preferences();
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