ESPHome 2026.8.0-dev
Loading...
Searching...
No Matches
preferences.cpp
Go to the documentation of this file.
1#ifdef USE_RP2
2
3#include <Arduino.h>
4
5#include <hardware/flash.h>
6#include <hardware/sync.h>
7
8#include "preferences.h"
9
10#include <cstring>
11
13#include "esphome/core/log.h"
14
15namespace esphome::rp2 {
16
17static const char *const TAG = "preferences";
18
19static constexpr uint32_t RP2040_FLASH_STORAGE_SIZE = 512;
20
21static bool s_prevent_write = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
22static uint8_t
23 s_flash_storage[RP2040_FLASH_STORAGE_SIZE]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
24static bool s_flash_dirty = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
25
26// No preference can exceed the total flash storage, so stack buffer covers all cases.
27static constexpr size_t PREF_MAX_BUFFER_SIZE = RP2040_FLASH_STORAGE_SIZE;
28
29// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
30extern "C" uint8_t _EEPROM_start;
31
32template<class It> uint8_t calculate_crc(It first, It last, uint32_t type) {
33 std::array<uint8_t, 4> type_array = decode_value(type);
34 uint8_t crc = type_array[0] ^ type_array[1] ^ type_array[2] ^ type_array[3];
35 while (first != last) {
36 crc ^= (*first++);
37 }
38 return crc;
39}
40
41bool RP2PreferenceBackend::save(const uint8_t *data, size_t len) {
42 if (len >= PREF_MAX_BUFFER_SIZE)
43 return false;
44 const size_t buffer_size = len + 1;
45 uint8_t buffer[PREF_MAX_BUFFER_SIZE];
46 memcpy(buffer, data, len);
47 buffer[len] = calculate_crc(buffer, buffer + len, this->type);
48
49 for (size_t i = 0; i < buffer_size; i++) {
50 uint32_t j = this->offset + i;
51 if (j >= RP2040_FLASH_STORAGE_SIZE)
52 return false;
53 uint8_t v = buffer[i];
54 uint8_t *ptr = &s_flash_storage[j];
55 if (*ptr != v)
56 s_flash_dirty = true;
57 *ptr = v;
58 }
59 return true;
60}
61
62bool RP2PreferenceBackend::load(uint8_t *data, size_t len) {
63 if (len >= PREF_MAX_BUFFER_SIZE)
64 return false;
65 const size_t buffer_size = len + 1;
66 uint8_t buffer[PREF_MAX_BUFFER_SIZE];
67
68 for (size_t i = 0; i < buffer_size; i++) {
69 uint32_t j = this->offset + i;
70 if (j >= RP2040_FLASH_STORAGE_SIZE)
71 return false;
72 buffer[i] = s_flash_storage[j];
73 }
74
75 uint8_t crc = calculate_crc(buffer, buffer + len, this->type);
76 if (buffer[len] != crc) {
77 return false;
78 }
79
80 memcpy(data, buffer, len);
81 return true;
82}
83
85
87 ESP_LOGVV(TAG, "Loading preferences from flash");
88 memcpy(s_flash_storage, this->eeprom_sector_, RP2040_FLASH_STORAGE_SIZE);
89}
90
92 uint32_t start = this->current_flash_offset;
93 uint32_t end = start + length + 1;
94 if (end > RP2040_FLASH_STORAGE_SIZE) {
95 return {};
96 }
97 auto *pref = new RP2PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory)
98 pref->offset = start;
99 pref->type = type;
101 return ESPPreferenceObject(pref);
102}
103
105 if (!s_flash_dirty)
106 return true;
107 if (s_prevent_write)
108 return false;
109
110 ESP_LOGD(TAG, "Saving");
111
112 {
114 ::rp2040.idleOtherCore();
115 flash_range_erase((intptr_t) this->eeprom_sector_ - (intptr_t) XIP_BASE, 4096);
116 flash_range_program((intptr_t) this->eeprom_sector_ - (intptr_t) XIP_BASE, s_flash_storage,
117 RP2040_FLASH_STORAGE_SIZE);
118 ::rp2040.resumeOtherCore();
119 }
120
121 s_flash_dirty = false;
122 return true;
123}
124
126 ESP_LOGD(TAG, "Erasing storage");
127 {
129 ::rp2040.idleOtherCore();
130 flash_range_erase((intptr_t) this->eeprom_sector_ - (intptr_t) XIP_BASE, 4096);
131 ::rp2040.resumeOtherCore();
132 }
133 s_prevent_write = true;
134 return true;
135}
136
137static RP2Preferences s_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
138
139RP2Preferences *get_preferences() { return &s_preferences; }
140
142 s_preferences.setup();
143 global_preferences = &s_preferences;
144}
145void preferences_prevent_write(bool prevent) { s_prevent_write = prevent; }
146
147} // namespace esphome::rp2
148
149namespace esphome {
150ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
151} // namespace esphome
152
153#endif // USE_RP2
Helper class to disable interrupts.
Definition helpers.h:1966
bool load(uint8_t *data, size_t len)
bool save(const uint8_t *data, size_t len)
ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)
Definition preferences.h:13
uint16_t type
void preferences_prevent_write(bool prevent)
uint8_t calculate_crc(It first, It last, uint32_t type)
void setup_preferences()
RP2Preferences * get_preferences()
uint8_t _EEPROM_start
Preferences ESPPreferences
Definition preferences.h:42
const void size_t len
Definition hal.h:64
ESPPreferences * global_preferences
constexpr std::array< uint8_t, sizeof(T)> decode_value(T val)
Decode a value into its constituent bytes (from most to least significant).
Definition helpers.h:902
static void uint32_t
uint8_t end[39]
Definition sun_gtil2.cpp:17
uint16_t length
Definition tt21100.cpp:0
SemaphoreHandle_t lock