embedded_wg_display/storage/
mod.rs1use crate::util::hasher::Hasher;
7use alloc::format;
8use common::models::WifiCredentials;
9use common::models::{SystemConfiguration, WidgetInstallationData};
10use defmt::info;
11use esp_bootloader_esp_idf::partitions;
12use esp_hal::peripherals::FLASH;
13use esp_hal::peripherals::SHA;
14use esp_nvs::{Key, Nvs, error::Error as NvsError};
15use esp_storage::{FlashStorage, FlashStorageError};
16
17pub struct Storage<'d> {
18 nvs: Nvs<FlashStorage<'d>>,
19 hasher: Hasher<'d>,
20 config_updated: bool,
21}
22
23#[derive(Debug, defmt::Format)]
25pub enum StorageError {
26 Flash(esp_storage::FlashStorageError),
28 Partition(partitions::Error),
30 PartitionNotFound,
32 Nvs(NvsError),
34}
35
36impl From<FlashStorageError> for StorageError {
37 fn from(e: FlashStorageError) -> Self {
38 StorageError::Flash(e)
39 }
40}
41
42impl From<partitions::Error> for StorageError {
43 fn from(e: partitions::Error) -> Self {
44 StorageError::Partition(e)
45 }
46}
47
48impl From<NvsError> for StorageError {
49 fn from(e: NvsError) -> Self {
50 StorageError::Nvs(e)
51 }
52}
53
54impl<'d> Storage<'d> {
55 fn wasm_key_from_name(&mut self, name: &str) -> Key {
56 let digest = self.hasher.hash(name);
58 let mut key_bytes = [b'0'; 15];
59 const HEX: &[u8; 16] = b"0123456789abcdef";
60
61 for i in 0..7 {
62 key_bytes[2 * i] = HEX[(digest[i] >> 4) as usize];
63 key_bytes[2 * i + 1] = HEX[(digest[i] & 0x0f) as usize];
64 }
65 key_bytes[14] = HEX[(digest[7] >> 4) as usize];
66
67 Key::from_array(&key_bytes)
68 }
69
70 pub fn new(flash: FLASH<'d>, sha_peripherals: SHA<'d>) -> Result<Self, StorageError> {
73 let mut flash_storage = FlashStorage::new(flash).multicore_auto_park();
74
75 let mut partition_table_buffer =
78 alloc::boxed::Box::new([0u8; partitions::PARTITION_TABLE_MAX_LEN]);
79 let partition_table =
80 partitions::read_partition_table(&mut flash_storage, &mut *partition_table_buffer)?;
81
82 defmt::info!("Partition table:");
84 for partition in partition_table.iter() {
85 defmt::info!(
86 " {}: offset=0x{:x}, size=0x{:x}",
87 partition.label_as_str(),
88 partition.offset(),
89 partition.len()
90 );
91 }
92
93 let storage = partition_table
95 .iter()
96 .find(|p| p.label_as_str() == "storage")
97 .ok_or(StorageError::PartitionNotFound)?;
98
99 let nvs = Nvs::new(
100 storage.offset() as usize,
101 storage.len() as usize,
102 flash_storage,
103 )?;
104
105 Ok(Self {
106 nvs,
107 hasher: Hasher::new(sha_peripherals),
108 config_updated: false,
109 })
110 }
111
112 pub fn save_system_config(
116 &mut self,
117 system_config: &SystemConfiguration,
118 ) -> Result<(), StorageError> {
119 if let Ok(current_config) = self.get_system_config()
121 && current_config == *system_config
122 {
123 info!("System config unchanged, not saving to flash");
124 return Ok(());
125 }
126
127 let value = serde_json::to_string(system_config)
128 .map_err(|_| StorageError::Nvs(NvsError::FlashError))?;
129 self.config_set("system_config", &value)?;
130 self.config_updated = true;
131 Ok(())
132 }
133
134 pub fn get_system_config(&mut self) -> Result<SystemConfiguration, StorageError> {
136 let value: alloc::string::String = self.config_get("system_config")?;
137 let config: SystemConfiguration =
138 serde_json::from_str(&value).map_err(|_| StorageError::Nvs(NvsError::FlashError))?;
139 Ok(config)
140 }
141
142 pub fn get_system_config_change(&mut self) -> Option<SystemConfiguration> {
145 if self.config_updated {
146 self.config_updated = false;
147 match self.get_system_config() {
148 Ok(config) => Some(config),
149 Err(err) => {
150 info!("Error getting updated config: {:?}", err);
151 None
152 }
153 }
154 } else {
155 None
156 }
157 }
158
159 pub fn save_compiled_widget(
161 &mut self,
162 widget_metadata: WidgetInstallationData,
163 data: &[u8],
164 ) -> Result<(), StorageError> {
165 self.wasm_write(&widget_metadata.name, data)?;
166 let mut config = self.get_system_config()?;
167 config.widgets.push(widget_metadata);
168 self.save_system_config(&config)?;
169 Ok(())
170 }
171
172 pub fn deinstall_widget(&mut self, name: &str) -> Result<(), StorageError> {
174 self.wasm_delete(name)?; let mut config = self.get_system_config()?;
177 config.widgets.retain(|w| w.name != name);
178 self.save_system_config(&config)?;
179 Ok(())
180 }
181
182 pub fn config_set(&mut self, key: &str, value: &str) -> Result<(), StorageError> {
184 info!("Setting config for key '{}'", key);
185 let ns = Key::from_str("config");
186 let k = Key::from_str(key);
187 self.nvs.set(&ns, &k, value)?;
188 Ok(())
189 }
190
191 pub fn config_get(&mut self, key: &str) -> Result<alloc::string::String, StorageError> {
193 info!("Getting config for key '{}'", key);
194 let ns = Key::from_str("config");
195 let k = Key::from_str(key);
196 Ok(self.nvs.get(&ns, &k)?)
197 }
198
199 pub fn set_wifi_credentials_and_mode(
200 &mut self,
201 credentials: WifiCredentials,
202 wifi_mode: &str,
203 ) -> Result<(), StorageError> {
204 self.config_set("ssid", &credentials.ssid)?;
205 self.config_set("pw", &credentials.password)?;
206 self.config_set("wifi_mode", wifi_mode)?;
207 Ok(())
208 }
209
210 pub fn get_wifi_credentials(&mut self) -> Result<WifiCredentials, StorageError> {
211 let ssid = self.config_get("ssid")?;
212 let password = self.config_get("pw")?;
213
214 Ok(WifiCredentials { ssid, password })
215 }
216
217 pub fn wasm_write(&mut self, name: &str, data: &[u8]) -> Result<(), StorageError> {
220 let ns = Key::from_str("wasm");
221 let max_chunk_size = 500 * 1000; let mut part = 0;
223 for chunk in data.chunks(max_chunk_size).enumerate() {
224 let key = self.wasm_key_from_name(&format!("{}-{}", name, part));
225 info!(
226 "Writing WASM binary part with name: '{}' and key: {:?}",
227 name, key
228 );
229 self.nvs.set(&ns, &key, chunk.1)?;
230 part += 1;
231 }
232
233 let hased_name = self.wasm_key_from_name(&format!("{}-parts", name));
235 self.nvs.set(&ns, &hased_name, part)?;
236 Ok(())
237 }
238
239 pub fn wasm_read(&mut self, name: &str) -> Result<alloc::vec::Vec<u8>, StorageError> {
241 let ns = Key::from_str("wasm");
242 let parts_key = self.wasm_key_from_name(&format!("{}-parts", name));
243 let num_parts = self.nvs.get(&ns, &parts_key)?;
244
245 let mut data = alloc::vec::Vec::new();
246
247 for part in 0..num_parts {
248 let key = self.wasm_key_from_name(&format!("{}-{}", name, part));
249
250 info!(
251 "Reading WASM binary part with name: '{}' and key: {:?}",
252 name, key
253 );
254
255 let mut part_data = self.nvs.get(&ns, &key)?;
256 data.append(&mut part_data);
257 }
258 Ok(data)
259 }
260
261 pub fn wasm_delete(&mut self, name: &str) -> Result<(), StorageError> {
263 let key = self.wasm_key_from_name(name);
264 let ns = Key::from_str("wasm");
265 info!(
266 "Deleting WASM binary with name: '{}' and key: {:?}",
267 name, key
268 );
269 self.nvs.delete(&ns, &key)?;
270 Ok(())
271 }
272
273 #[allow(dead_code)]
275 pub fn list_widgets(&mut self) -> Result<alloc::vec::Vec<alloc::string::String>, StorageError> {
276 let config = self.get_system_config()?;
277 Ok(config.widgets.iter().map(|w| w.name.clone()).collect())
278 }
279}