ESPHome  2024.3.1
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 #include <vector>
12 
13 #include "esphome/core/helpers.h"
14 #include "esphome/core/log.h"
16 
17 namespace esphome {
18 namespace rp2040 {
19 
20 static const char *const TAG = "rp2040.preferences";
21 
22 static bool s_prevent_write = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
23 static uint8_t *s_flash_storage = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
24 static bool s_flash_dirty = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
25 
26 static const uint32_t RP2040_FLASH_STORAGE_SIZE = 512;
27 
28 extern "C" uint8_t _EEPROM_start;
29 
30 template<class It> uint8_t calculate_crc(It first, It last, uint32_t type) {
31  std::array<uint8_t, 4> type_array = decode_value(type);
32  uint8_t crc = type_array[0] ^ type_array[1] ^ type_array[2] ^ type_array[3];
33  while (first != last) {
34  crc ^= (*first++);
35  }
36  return crc;
37 }
38 
39 class RP2040PreferenceBackend : public ESPPreferenceBackend {
40  public:
41  size_t offset = 0;
42  uint32_t type = 0;
43 
44  bool save(const uint8_t *data, size_t len) override {
45  std::vector<uint8_t> buffer;
46  buffer.resize(len + 1);
47  memcpy(buffer.data(), data, len);
48  buffer[buffer.size() - 1] = calculate_crc(buffer.begin(), buffer.end() - 1, type);
49 
50  for (uint32_t i = 0; i < len + 1; i++) {
51  uint32_t j = offset + i;
52  if (j >= RP2040_FLASH_STORAGE_SIZE)
53  return false;
54  uint8_t v = buffer[i];
55  uint8_t *ptr = &s_flash_storage[j];
56  if (*ptr != v)
57  s_flash_dirty = true;
58  *ptr = v;
59  }
60  return true;
61  }
62  bool load(uint8_t *data, size_t len) override {
63  std::vector<uint8_t> buffer;
64  buffer.resize(len + 1);
65 
66  for (size_t i = 0; i < len + 1; i++) {
67  uint32_t j = offset + i;
68  if (j >= RP2040_FLASH_STORAGE_SIZE)
69  return false;
70  buffer[i] = s_flash_storage[j];
71  }
72 
73  uint8_t crc = calculate_crc(buffer.begin(), buffer.end() - 1, type);
74  if (buffer[buffer.size() - 1] != crc) {
75  return false;
76  }
77 
78  memcpy(data, buffer.data(), len);
79  return true;
80  }
81 };
82 
83 class RP2040Preferences : public ESPPreferences {
84  public:
85  uint32_t current_flash_offset = 0;
86 
87  RP2040Preferences() : eeprom_sector_(&_EEPROM_start) {}
88  void setup() {
89  s_flash_storage = new uint8_t[RP2040_FLASH_STORAGE_SIZE]; // NOLINT
90  ESP_LOGVV(TAG, "Loading preferences from flash...");
91  memcpy(s_flash_storage, this->eeprom_sector_, RP2040_FLASH_STORAGE_SIZE);
92  }
93 
94  ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override {
95  return make_preference(length, type);
96  }
97 
98  ESPPreferenceObject make_preference(size_t length, uint32_t type) override {
99  uint32_t start = this->current_flash_offset;
100  uint32_t end = start + length + 1;
101  if (end > RP2040_FLASH_STORAGE_SIZE) {
102  return {};
103  }
104  auto *pref = new RP2040PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory)
105  pref->offset = start;
106  pref->type = type;
107  current_flash_offset = end;
108  return {pref};
109  }
110 
111  bool sync() override {
112  if (!s_flash_dirty)
113  return true;
114  if (s_prevent_write)
115  return false;
116 
117  ESP_LOGD(TAG, "Saving preferences to flash...");
118 
119  {
120  InterruptLock lock;
121  ::rp2040.idleOtherCore();
122  flash_range_erase((intptr_t) eeprom_sector_ - (intptr_t) XIP_BASE, 4096);
123  flash_range_program((intptr_t) eeprom_sector_ - (intptr_t) XIP_BASE, s_flash_storage, RP2040_FLASH_STORAGE_SIZE);
124  ::rp2040.resumeOtherCore();
125  }
126 
127  s_flash_dirty = false;
128  return true;
129  }
130 
131  bool reset() override {
132  ESP_LOGD(TAG, "Cleaning up preferences in flash...");
133  {
134  InterruptLock lock;
135  ::rp2040.idleOtherCore();
136  flash_range_erase((intptr_t) eeprom_sector_ - (intptr_t) XIP_BASE, 4096);
137  ::rp2040.resumeOtherCore();
138  }
139  s_prevent_write = true;
140  return true;
141  }
142 
143  protected:
144  uint8_t *eeprom_sector_;
145 };
146 
148  auto *prefs = new RP2040Preferences(); // NOLINT(cppcoreguidelines-owning-memory)
149  prefs->setup();
150  global_preferences = prefs;
151 }
152 void preferences_prevent_write(bool prevent) { s_prevent_write = prevent; }
153 
154 } // namespace rp2040
155 
156 ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
157 
158 } // namespace esphome
159 
160 #endif // USE_RP2040
void setup()
uint8_t _EEPROM_start
Definition: preferences.cpp:28
void setup_preferences()
ESPPreferences * global_preferences
uint8_t calculate_crc(It first, It last, uint32_t type)
Definition: preferences.cpp:30
void preferences_prevent_write(bool prevent)
constexpr14 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:212
uint8_t type
uint16_t reset
Definition: ina226.h:39
Helper class to disable interrupts.
Definition: helpers.h:587
std::string size_t len
Definition: helpers.h:292
uint16_t length
Definition: tt21100.cpp:12
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7