1use crate::hardware::{flash::Flash, metadata::ApplicationMetadata, platform};
26use core::fmt::Write;
27use embassy_futures::block_on;
28use embedded_io::Write as EioWrite;
29use heapless::{String, Vec};
30use miniconf::{
31 postcard, Leaf, Path, Tree, TreeDeserializeOwned, TreeKey, TreeSerialize,
32};
33use sequential_storage::{
34 cache::NoCache,
35 map::{fetch_item, store_item, SerializationError},
36};
37use serial_settings::{BestEffortInterface, Platform, Settings};
38use smoltcp_nal::smoltcp::wire::EthernetAddress;
39use stm32h7xx_hal::flash::LockedFlashBank;
40
41#[derive(Clone, Debug, Tree)]
43pub struct NetSettings {
44 pub broker: Leaf<String<255>>,
46
47 pub id: Leaf<String<23>>,
49
50 pub ip: Leaf<String<15>>,
53 #[tree(skip)]
54 pub mac: EthernetAddress,
56}
57
58impl NetSettings {
59 pub fn new(mac: EthernetAddress) -> Self {
60 let mut id = String::new();
61 write!(&mut id, "{mac}").unwrap();
62
63 Self {
64 broker: String::try_from("mqtt").unwrap().into(),
65 ip: String::try_from("0.0.0.0").unwrap().into(),
66 id: id.into(),
67 mac,
68 }
69 }
70}
71
72pub trait AppSettings {
73 fn new(net: NetSettings) -> Self;
75
76 fn net(&self) -> &NetSettings;
78}
79
80#[derive(
81 Default, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq,
82)]
83pub struct SettingsKey(Vec<u8, 128>);
84
85impl sequential_storage::map::Key for SettingsKey {
86 fn serialize_into(
87 &self,
88 buffer: &mut [u8],
89 ) -> Result<usize, SerializationError> {
90 Ok(::postcard::to_slice(self, buffer)
91 .map_err(|_| SerializationError::BufferTooSmall)?
92 .len())
93 }
94
95 fn deserialize_from(
96 buffer: &[u8],
97 ) -> Result<(Self, usize), SerializationError> {
98 let original_length = buffer.len();
99 let (result, remainder) = ::postcard::take_from_bytes(buffer)
100 .map_err(|_| SerializationError::BufferTooSmall)?;
101 Ok((result, original_length - remainder.len()))
102 }
103}
104
105pub struct SerialSettingsPlatform<C> {
106 pub interface: BestEffortInterface<crate::hardware::SerialPort>,
108
109 pub _settings_marker: core::marker::PhantomData<C>,
110
111 pub storage: Flash,
113
114 pub metadata: &'static ApplicationMetadata,
116}
117
118impl<C> SerialSettingsPlatform<C>
119where
120 C: TreeDeserializeOwned + TreeSerialize + TreeKey,
121{
122 pub fn load(structure: &mut C, storage: &mut Flash) {
123 let mut buffer = [0u8; 512];
125 for path in C::nodes::<Path<String<128>, '/'>, 8>() {
126 let (path, _node) = path.unwrap();
127
128 let value: &[u8] = match block_on(fetch_item(
130 storage,
131 storage.range(),
132 &mut NoCache::new(),
133 &mut buffer,
134 &SettingsKey(path.clone().into_inner().into_bytes()),
135 )) {
136 Err(e) => {
137 log::warn!(
138 "Failed to fetch `{}` from flash: {e:?}",
139 path.as_str()
140 );
141 continue;
142 }
143 Ok(Some(value)) => value,
144 Ok(None) => continue,
145 };
146
147 if value.is_empty() {
150 continue;
151 }
152
153 log::info!("Loading initial `{}` from flash", path.as_str());
154
155 let flavor = ::postcard::de_flavors::Slice::new(value);
156 if let Err(e) = postcard::set_by_key(structure, &path, flavor) {
157 log::warn!(
158 "Failed to deserialize `{}` from flash: {e:?}",
159 path.as_str()
160 );
161 }
162 }
163 }
164}
165
166impl<C> Platform for SerialSettingsPlatform<C>
167where
168 C: Settings,
169{
170 type Interface = BestEffortInterface<crate::hardware::SerialPort>;
171 type Settings = C;
172 type Error = sequential_storage::Error<
173 <LockedFlashBank as embedded_storage::nor_flash::ErrorType>::Error,
174 >;
175
176 fn fetch<'a>(
177 &mut self,
178 buf: &'a mut [u8],
179 key: &[u8],
180 ) -> Result<Option<&'a [u8]>, Self::Error> {
181 let range = self.storage.range();
182 block_on(fetch_item(
183 &mut self.storage,
184 range,
185 &mut NoCache::new(),
186 buf,
187 &SettingsKey(Vec::try_from(key).unwrap()),
188 ))
189 .map(|v| v.filter(|v: &&[u8]| !v.is_empty()))
190 }
191
192 fn store(
193 &mut self,
194 buf: &mut [u8],
195 key: &[u8],
196 value: &[u8],
197 ) -> Result<(), Self::Error> {
198 let range = self.storage.range();
199 block_on(store_item(
200 &mut self.storage,
201 range,
202 &mut NoCache::new(),
203 buf,
204 &SettingsKey(Vec::try_from(key).unwrap()),
205 &value,
206 ))
207 }
208
209 fn clear(&mut self, buf: &mut [u8], key: &[u8]) -> Result<(), Self::Error> {
210 self.store(buf, key, b"")
211 }
212
213 fn cmd(&mut self, cmd: &str) {
214 match cmd {
215 "reboot" => cortex_m::peripheral::SCB::sys_reset(),
216 "dfu" => platform::start_dfu_reboot(),
217 "service" => {
218 writeln!(
219 &mut self.interface,
220 "{:<20}: {} [{}]",
221 "Version",
222 self.metadata.firmware_version,
223 self.metadata.profile,
224 )
225 .unwrap();
226 writeln!(
227 &mut self.interface,
228 "{:<20}: {}",
229 "Hardware Revision", self.metadata.hardware_version
230 )
231 .unwrap();
232 writeln!(
233 &mut self.interface,
234 "{:<20}: {}",
235 "Rustc Version", self.metadata.rust_version
236 )
237 .unwrap();
238 writeln!(
239 &mut self.interface,
240 "{:<20}: {}",
241 "Features", self.metadata.features
242 )
243 .unwrap();
244 writeln!(
245 &mut self.interface,
246 "{:<20}: {}",
247 "Panic Info", self.metadata.panic_info
248 )
249 .unwrap();
250 }
251 _ => {
252 writeln!(
253 self.interface_mut(),
254 "Invalid platform command: `{cmd}` not in [`dfu`, `reboot`, `service`]"
255 )
256 .ok();
257 }
258 }
259 }
260
261 fn interface_mut(&mut self) -> &mut Self::Interface {
262 &mut self.interface
263 }
264}