From 99b36997ba8cdcfb04afcc0085ea35b75ac1d3a3 Mon Sep 17 00:00:00 2001 From: Vitalii Popov Date: Wed, 11 Feb 2026 23:12:53 +0200 Subject: [PATCH] saving --- memory.x | 2 +- src/main.rs | 185 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 170 insertions(+), 17 deletions(-) diff --git a/memory.x b/memory.x index 346a27d..bd42310 100644 --- a/memory.x +++ b/memory.x @@ -1,5 +1,5 @@ MEMORY { FLASH : ORIGIN = 0x00000000 + 156K, LENGTH = 1024K - 156K - RAM : ORIGIN = 0x20000000 + 31K, LENGTH = 256K - 31K + RAM : ORIGIN = 0x20007b08, LENGTH = 256K - 31496 } diff --git a/src/main.rs b/src/main.rs index 1eb56da..25b15b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ mod fmt; use core::mem; use core::ops::{AddAssign, SubAssign}; +use core::pin::Pin; use defmt::{error, Format}; #[cfg(not(feature = "defmt"))] use panic_halt as _; @@ -30,7 +31,7 @@ use futures::pin_mut; use nrf_softdevice::{raw, Flash, Softdevice}; use nrf_softdevice::ble::advertisement_builder::{Flag, LegacyAdvertisementBuilder, LegacyAdvertisementPayload, ServiceList, ServiceUuid16}; use nrf_softdevice::ble::{peripheral, gatt_server, Connection}; -use embedded_storage_async::nor_flash::ReadNorFlash; +use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash}; #[derive(Copy, Clone, Eq, PartialEq, Format)] enum SysAction { @@ -46,6 +47,27 @@ enum TextLightType { Hue } +impl From for TextLightType { + fn from(value: u8) -> Self { + match value { + 0 => TextLightType::Hue, + 1 => TextLightType::ConstantColor, + 2 => TextLightType::White, + _ => TextLightType::Hue, + } + } +} + +impl Into for TextLightType { + fn into(self) -> u8 { + match self { + TextLightType::White => 2, + TextLightType::ConstantColor => 1, + TextLightType::Hue => 0, + } + } +} + #[derive(Copy, Clone, Eq, PartialEq, Format)] enum HeartLightType { ConstantColor, @@ -53,6 +75,27 @@ enum HeartLightType { HeartBeat } +impl From for HeartLightType { + fn from(value: u8) -> Self { + match value { + 0 => HeartLightType::HeartBeat, + 1 => HeartLightType::Hue, + 2 => HeartLightType::ConstantColor, + _ => HeartLightType::HeartBeat, + } + } +} + +impl Into for HeartLightType { + fn into(self) -> u8 { + match self { + HeartLightType::ConstantColor => 2, + HeartLightType::Hue => 1, + HeartLightType::HeartBeat => 0, + } + } +} + #[derive(Copy, Clone, Eq, PartialEq, Format)] pub enum TouchAction { Tap, @@ -68,13 +111,22 @@ pub enum AnimationSignal { SetColor(u16), } +#[derive(Copy, Clone, Eq, PartialEq, Format)] +pub enum StoreType { + TextLight(TextLightType), + HeartLight(HeartLightType), + TextColor(u16), + HeartColor(u16), + Text(TextLightType, u16), + Heart(HeartLightType, u16), +} + static TOUCH_SIGNAL: Signal = Signal::new(); static RADAR_SIGNAL: Signal = Signal::new(); static TEXT_ANIMATION_SIGNAL: Signal> = Signal::new(); static HEART_ANIMATION_SIGNAL: Signal> = Signal::new(); static SYS_ACTION: Mutex = Mutex::new(SysAction::PresentenceOn); -static TEXT_ANIMATION: Mutex = Mutex::new(TextLightType::Hue); -static HEART_ANIMATION: Mutex = Mutex::new(HeartLightType::HeartBeat); +static SAVE_SIGNAL: Signal = Signal::new(); #[nrf_softdevice::gatt_service(uuid = "9e7312e0-2354-11eb-9f10-fbc30a62cf38")] struct AnimationService { @@ -105,6 +157,8 @@ bind_interrupts!(struct AdcIrqs { SAADC => saadc::InterruptHandler; }); +const DATA_POINT: u32 = 0x000F9800 - 4096; + #[embassy_executor::main] async fn main(spawner: Spawner) { let mut config = embassy_nrf::config::Config::default(); @@ -153,13 +207,19 @@ async fn main(spawner: Spawner) { channel_config.reference = Reference::INTERNAL; let saadc = Saadc::new(p.SAADC, AdcIrqs, saadc::Config::default(), [channel_config]); saadc.calibrate().await; - let flash = Flash::take(sd); - pin_mut!(flash); + let mut flash = Flash::take(sd); let mut data = [0u8; 16]; - if let Err(e) = flash.as_mut().read(0x000F9800 - 4096, &mut data).await { + if let Err(e) = flash.read(DATA_POINT, &mut data).await { error!("Error reading from flash: {}", e); } info!("Data in flash: {:?}", data); + let initial_data = if data[0] == 0xFF && data[1] == 0xFF { + (TextLightType::Hue, 0u16, HeartLightType::HeartBeat, 0u16) + } else { + let text_color = u16::from_le_bytes([data[4], data[5]]); + let heart_color = u16::from_le_bytes([data[6], data[7]]); + (TextLightType::from(data[2]), text_color, HeartLightType::from(data[3]), heart_color) + }; let pwm_text = SimplePwm::new_3ch(p.PWM0, p.P1_00, p.P1_04, p.P1_06, &Default::default()); let pwm_heart = SimplePwm::new_3ch(p.PWM2, p.P0_17, p.P0_20, p.P0_22, &Default::default()); @@ -167,11 +227,82 @@ async fn main(spawner: Spawner) { spawner.spawn(touch_button(p.P0_08.into())).expect("failed to spawn touch task"); spawner.spawn(actions_task()).expect("failed to spawn actions task"); spawner.spawn(softdevice_task(sd)).expect("failed to spawn softdevice task"); - // spawner.spawn(battery_task(saadc)).expect("failed to spawn softdevice task"); + spawner.spawn(storage_task(flash, initial_data.0, initial_data.1, initial_data.2, initial_data.3)).expect("failed to spawn softdevice task"); - join4(gatt_task(server, saadc, sd), moving_radar(p.P0_06.into()), heartbeat_task(pwm_heart), light_task(pwm_text)).await; + join4(gatt_task(server, saadc, sd), moving_radar(p.P0_06.into()), heartbeat_task(pwm_heart, initial_data.2, initial_data.3), light_task(pwm_text, initial_data.0, initial_data.1)).await; } +#[embassy_executor::task] +async fn storage_task(flash: Flash, initial_text_type: TextLightType, initial_text_color: u16, initial_heart_type: HeartLightType, initial_heart_color: u16) { + pin_mut!(flash); + let mut text_type = initial_text_type; + let mut text_color = initial_text_color; + let mut heart_type = initial_heart_type; + let mut heart_color = initial_heart_color; + let mut changes = false; + loop { + match with_timeout(Duration::from_secs(60), SAVE_SIGNAL.wait()).await { + Ok(sig) => { + info!("Received signal with {:?}", sig); + match sig { + StoreType::TextLight(val) => { + if text_type != val { + text_type = val; + changes = true; + } + } + StoreType::HeartLight(val) => { + if heart_type != val { + heart_type = val; + changes = true; + } + } + StoreType::TextColor(val) => { + if text_color != val { + text_color = val % 1536; + changes = true; + } + } + StoreType::HeartColor(val) => { + if heart_color != val { + heart_color = val % 1536; + changes = true; + } + } + StoreType::Text(t, c) => { + if text_color != c || text_type != t { + text_color = c; + text_type = t; + changes = true; + } + } + StoreType::Heart(t, c) => { + if heart_color != c || heart_type != t { + heart_color = c; + heart_type = t; + changes = true; + } + } + } + }, + Err(e) => { + info!("Error fetching from storage: {}", e); + if changes { + changes = false; + let t_color: [u8; 2] = text_color.to_le_bytes(); + let h_color: [u8; 2] = heart_color.to_le_bytes(); + let data = [0b01010101u8, 0b10101010u8, text_type.into(), heart_type.into(), t_color[0], t_color[1], h_color[0], h_color[1], 0xFF, 0xFF, 0xFF, 0xFF]; + info!("Saving data: {:?}", data); + if let Err(e) = flash.as_mut().write(DATA_POINT, &data).await { + error!("Error writing to flash: {}", e); + } + } else { + info!("Storage tick"); + } + } + } + } +} async fn gatt_task(server: Server, mut saadc: Saadc<'static, 1>, sd: &'static Softdevice) { static ADV_DATA: LegacyAdvertisementPayload = LegacyAdvertisementBuilder::new() @@ -471,7 +602,7 @@ async fn moving_radar(pin: Peri<'static, AnyPin>) { } } -async fn light_task(mut pwm: SimplePwm<'static>) { +async fn light_task(mut pwm: SimplePwm<'static>, light_type: TextLightType, initial_color: u16) { info!("Starting light task"); const MAX_DUTY: u16 = 32767; @@ -483,8 +614,8 @@ async fn light_task(mut pwm: SimplePwm<'static>) { pwm.set_all_duties([DutyCycle::normal(0), DutyCycle::normal(0), DutyCycle::normal(0), DutyCycle::normal(0)]); let mut intensity = 0i32; - let mut hue: u16 = 0; - let mut animation_type = TextLightType::Hue; + let mut hue: u16 = initial_color; + let mut animation_type = light_type; loop { let action = { @@ -519,12 +650,23 @@ async fn light_task(mut pwm: SimplePwm<'static>) { TextLightType::Hue } }; + if animation_type == TextLightType::ConstantColor { + SAVE_SIGNAL.signal(StoreType::Text(TextLightType::ConstantColor, hue)); + } else { + SAVE_SIGNAL.signal(StoreType::TextLight(animation_type)); + } } AnimationSignal::SetMode(val) => { animation_type = val; + if animation_type == TextLightType::ConstantColor { + SAVE_SIGNAL.signal(StoreType::Text(TextLightType::ConstantColor, hue)); + } else { + SAVE_SIGNAL.signal(StoreType::TextLight(animation_type)); + } } AnimationSignal::SetColor(val) => { hue = val; + SAVE_SIGNAL.signal(StoreType::TextColor(hue)); } } } @@ -585,15 +727,15 @@ fn hue_to_rgb(hue: u16, max: u16) -> (u16, u16, u16) { (r as u16, g as u16, b as u16) } -async fn heartbeat_task(mut pwm: SimplePwm<'static>) { +async fn heartbeat_task(mut pwm: SimplePwm<'static>, light_type: HeartLightType, initial_color: u16) { const MAX: u16 = 16384; pwm.set_max_duty(MAX); pwm.set_prescaler(Prescaler::Div1); let mut intensity = 0i32; - let mut animation_type = HeartLightType::HeartBeat; - let mut hue: u16 = 0; + let mut animation_type = light_type; + let mut hue: u16 = initial_color; let mut heart_beat = 0; loop { @@ -604,11 +746,11 @@ async fn heartbeat_task(mut pwm: SimplePwm<'static>) { match action { SysAction::PresentenceOn if intensity < 100 => { intensity.add_assign(1); - info!("Increase intensity to {}", intensity); + // info!("Increase intensity to {}", intensity); }, SysAction::PresentenceOff if intensity > 0 => { intensity.sub_assign(1); - info!("Decrease intensity to {}", intensity); + // info!("Decrease intensity to {}", intensity); }, _ => { // ignore @@ -629,12 +771,23 @@ async fn heartbeat_task(mut pwm: SimplePwm<'static>) { HeartLightType::HeartBeat } }; + if animation_type == HeartLightType::ConstantColor { + SAVE_SIGNAL.signal(StoreType::Heart(HeartLightType::ConstantColor, hue)); + } else { + SAVE_SIGNAL.signal(StoreType::HeartLight(animation_type)); + } } AnimationSignal::SetMode(val) => { animation_type = val; + if animation_type == HeartLightType::ConstantColor { + SAVE_SIGNAL.signal(StoreType::Heart(HeartLightType::ConstantColor, hue)); + } else { + SAVE_SIGNAL.signal(StoreType::HeartLight(animation_type)); + } } AnimationSignal::SetColor(val) => { hue = val; + SAVE_SIGNAL.signal(StoreType::HeartColor(hue)); } } }