ESPHome 2026.2.1
Loading...
Searching...
No Matches
preferences.cpp
Go to the documentation of this file.
1#ifdef USE_RP2040
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"
15
16namespace esphome {
17namespace rp2040 {
18
19static const char *const TAG = "rp2040.preferences";
20
21static constexpr uint32_t RP2040_FLASH_STORAGE_SIZE = 512;
22
23static bool s_prevent_write = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
24static uint8_t
25 s_flash_storage[RP2040_FLASH_STORAGE_SIZE]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
26static bool s_flash_dirty = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
27
28// No preference can exceed the total flash storage, so stack buffer covers all cases.
29static constexpr size_t PREF_MAX_BUFFER_SIZE = RP2040_FLASH_STORAGE_SIZE;
30
31extern "C" uint8_t _EEPROM_start;
32
33template<class It> uint8_t calculate_crc(It first, It last, uint32_t type) {
34 std::array<uint8_t, 4> type_array = decode_value(type);
35 uint8_t crc = type_array[0] ^ type_array[1] ^ type_array[2] ^ type_array[3];
36 while (first != last) {
37 crc ^= (*first++);
38 }
39 return crc;
40}
41
42class RP2040PreferenceBackend : public ESPPreferenceBackend {
43 public:
44 size_t offset = 0;
45 uint32_t type = 0;
46
47 bool save(const uint8_t *data, size_t len) override {
48 const size_t buffer_size = len + 1;
49 if (buffer_size > PREF_MAX_BUFFER_SIZE)
50 return false;
51 uint8_t buffer[PREF_MAX_BUFFER_SIZE];
52 memcpy(buffer, data, len);
53 buffer[len] = calculate_crc(buffer, buffer + len, this->type);
54
55 for (size_t i = 0; i < buffer_size; i++) {
56 uint32_t j = this->offset + i;
57 if (j >= RP2040_FLASH_STORAGE_SIZE)
58 return false;
59 uint8_t v = buffer[i];
60 uint8_t *ptr = &s_flash_storage[j];
61 if (*ptr != v)
62 s_flash_dirty = true;
63 *ptr = v;
64 }
65 return true;
66 }
67 bool load(uint8_t *data, size_t len) override {
68 const size_t buffer_size = len + 1;
69 if (buffer_size > PREF_MAX_BUFFER_SIZE)
70 return false;
71 uint8_t buffer[PREF_MAX_BUFFER_SIZE];
72
73 for (size_t i = 0; i < buffer_size; i++) {
74 uint32_t j = this->offset + i;
75 if (j >= RP2040_FLASH_STORAGE_SIZE)
76 return false;
77 buffer[i] = s_flash_storage[j];
78 }
79
80 uint8_t crc = calculate_crc(buffer, buffer + len, this->type);
81 if (buffer[len] != crc) {
82 return false;
83 }
84
85 memcpy(data, buffer, len);
86 return true;
87 }
88};
89
90class RP2040Preferences : public ESPPreferences {
91 public:
92 uint32_t current_flash_offset = 0;
93
94 RP2040Preferences() : eeprom_sector_(&_EEPROM_start) {}
95 void setup() {
96 ESP_LOGVV(TAG, "Loading preferences from flash");
97 memcpy(s_flash_storage, this->eeprom_sector_, RP2040_FLASH_STORAGE_SIZE);
98 }
99
100 ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override {
101 return make_preference(length, type);
102 }
103
104 ESPPreferenceObject make_preference(size_t length, uint32_t type) override {
105 uint32_t start = this->current_flash_offset;
106 uint32_t end = start + length + 1;
107 if (end > RP2040_FLASH_STORAGE_SIZE) {
108 return {};
109 }
110 auto *pref = new RP2040PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory)
111 pref->offset = start;
112 pref->type = type;
113 current_flash_offset = end;
114 return {pref};
115 }
116
117 bool sync() override {
118 if (!s_flash_dirty)
119 return true;
120 if (s_prevent_write)
121 return false;
122
123 ESP_LOGD(TAG, "Saving");
124
125 {
126 InterruptLock lock;
127 ::rp2040.idleOtherCore();
128 flash_range_erase((intptr_t) eeprom_sector_ - (intptr_t) XIP_BASE, 4096);
129 flash_range_program((intptr_t) eeprom_sector_ - (intptr_t) XIP_BASE, s_flash_storage, RP2040_FLASH_STORAGE_SIZE);
130 ::rp2040.resumeOtherCore();
131 }
132
133 s_flash_dirty = false;
134 return true;
135 }
136
137 bool reset() override {
138 ESP_LOGD(TAG, "Erasing storage");
139 {
140 InterruptLock lock;
141 ::rp2040.idleOtherCore();
142 flash_range_erase((intptr_t) eeprom_sector_ - (intptr_t) XIP_BASE, 4096);
143 ::rp2040.resumeOtherCore();
144 }
145 s_prevent_write = true;
146 return true;
147 }
148
149 protected:
150 uint8_t *eeprom_sector_;
151};
152
153static RP2040Preferences s_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
154
156 s_preferences.setup();
157 global_preferences = &s_preferences;
158}
159void preferences_prevent_write(bool prevent) { s_prevent_write = prevent; }
160
161} // namespace rp2040
162
163ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
164
165} // namespace esphome
166
167#endif // USE_RP2040
uint16_t type
void preferences_prevent_write(bool prevent)
uint8_t _EEPROM_start
uint8_t calculate_crc(It first, It last, uint32_t type)
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:692
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:557
uint8_t end[39]
Definition sun_gtil2.cpp:17
uint16_t length
Definition tt21100.cpp:0