saving
This commit is contained in:
parent
cc9d55f160
commit
99b36997ba
2
memory.x
2
memory.x
|
|
@ -1,5 +1,5 @@
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
FLASH : ORIGIN = 0x00000000 + 156K, LENGTH = 1024K - 156K
|
FLASH : ORIGIN = 0x00000000 + 156K, LENGTH = 1024K - 156K
|
||||||
RAM : ORIGIN = 0x20000000 + 31K, LENGTH = 256K - 31K
|
RAM : ORIGIN = 0x20007b08, LENGTH = 256K - 31496
|
||||||
}
|
}
|
||||||
|
|
|
||||||
185
src/main.rs
185
src/main.rs
|
|
@ -5,6 +5,7 @@ mod fmt;
|
||||||
|
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::ops::{AddAssign, SubAssign};
|
use core::ops::{AddAssign, SubAssign};
|
||||||
|
use core::pin::Pin;
|
||||||
use defmt::{error, Format};
|
use defmt::{error, Format};
|
||||||
#[cfg(not(feature = "defmt"))]
|
#[cfg(not(feature = "defmt"))]
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
|
|
@ -30,7 +31,7 @@ use futures::pin_mut;
|
||||||
use nrf_softdevice::{raw, Flash, Softdevice};
|
use nrf_softdevice::{raw, Flash, Softdevice};
|
||||||
use nrf_softdevice::ble::advertisement_builder::{Flag, LegacyAdvertisementBuilder, LegacyAdvertisementPayload, ServiceList, ServiceUuid16};
|
use nrf_softdevice::ble::advertisement_builder::{Flag, LegacyAdvertisementBuilder, LegacyAdvertisementPayload, ServiceList, ServiceUuid16};
|
||||||
use nrf_softdevice::ble::{peripheral, gatt_server, Connection};
|
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)]
|
#[derive(Copy, Clone, Eq, PartialEq, Format)]
|
||||||
enum SysAction {
|
enum SysAction {
|
||||||
|
|
@ -46,6 +47,27 @@ enum TextLightType {
|
||||||
Hue
|
Hue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<u8> for TextLightType {
|
||||||
|
fn from(value: u8) -> Self {
|
||||||
|
match value {
|
||||||
|
0 => TextLightType::Hue,
|
||||||
|
1 => TextLightType::ConstantColor,
|
||||||
|
2 => TextLightType::White,
|
||||||
|
_ => TextLightType::Hue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<u8> for TextLightType {
|
||||||
|
fn into(self) -> u8 {
|
||||||
|
match self {
|
||||||
|
TextLightType::White => 2,
|
||||||
|
TextLightType::ConstantColor => 1,
|
||||||
|
TextLightType::Hue => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Format)]
|
#[derive(Copy, Clone, Eq, PartialEq, Format)]
|
||||||
enum HeartLightType {
|
enum HeartLightType {
|
||||||
ConstantColor,
|
ConstantColor,
|
||||||
|
|
@ -53,6 +75,27 @@ enum HeartLightType {
|
||||||
HeartBeat
|
HeartBeat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<u8> for HeartLightType {
|
||||||
|
fn from(value: u8) -> Self {
|
||||||
|
match value {
|
||||||
|
0 => HeartLightType::HeartBeat,
|
||||||
|
1 => HeartLightType::Hue,
|
||||||
|
2 => HeartLightType::ConstantColor,
|
||||||
|
_ => HeartLightType::HeartBeat,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<u8> for HeartLightType {
|
||||||
|
fn into(self) -> u8 {
|
||||||
|
match self {
|
||||||
|
HeartLightType::ConstantColor => 2,
|
||||||
|
HeartLightType::Hue => 1,
|
||||||
|
HeartLightType::HeartBeat => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Format)]
|
#[derive(Copy, Clone, Eq, PartialEq, Format)]
|
||||||
pub enum TouchAction {
|
pub enum TouchAction {
|
||||||
Tap,
|
Tap,
|
||||||
|
|
@ -68,13 +111,22 @@ pub enum AnimationSignal<T> {
|
||||||
SetColor(u16),
|
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<ThreadModeRawMutex, TouchAction> = Signal::new();
|
static TOUCH_SIGNAL: Signal<ThreadModeRawMutex, TouchAction> = Signal::new();
|
||||||
static RADAR_SIGNAL: Signal<ThreadModeRawMutex, u8> = Signal::new();
|
static RADAR_SIGNAL: Signal<ThreadModeRawMutex, u8> = Signal::new();
|
||||||
static TEXT_ANIMATION_SIGNAL: Signal<ThreadModeRawMutex, AnimationSignal<TextLightType>> = Signal::new();
|
static TEXT_ANIMATION_SIGNAL: Signal<ThreadModeRawMutex, AnimationSignal<TextLightType>> = Signal::new();
|
||||||
static HEART_ANIMATION_SIGNAL: Signal<ThreadModeRawMutex, AnimationSignal<HeartLightType>> = Signal::new();
|
static HEART_ANIMATION_SIGNAL: Signal<ThreadModeRawMutex, AnimationSignal<HeartLightType>> = Signal::new();
|
||||||
static SYS_ACTION: Mutex<ThreadModeRawMutex, SysAction> = Mutex::new(SysAction::PresentenceOn);
|
static SYS_ACTION: Mutex<ThreadModeRawMutex, SysAction> = Mutex::new(SysAction::PresentenceOn);
|
||||||
static TEXT_ANIMATION: Mutex<ThreadModeRawMutex, TextLightType> = Mutex::new(TextLightType::Hue);
|
static SAVE_SIGNAL: Signal<ThreadModeRawMutex, StoreType> = Signal::new();
|
||||||
static HEART_ANIMATION: Mutex<ThreadModeRawMutex, HeartLightType> = Mutex::new(HeartLightType::HeartBeat);
|
|
||||||
|
|
||||||
#[nrf_softdevice::gatt_service(uuid = "9e7312e0-2354-11eb-9f10-fbc30a62cf38")]
|
#[nrf_softdevice::gatt_service(uuid = "9e7312e0-2354-11eb-9f10-fbc30a62cf38")]
|
||||||
struct AnimationService {
|
struct AnimationService {
|
||||||
|
|
@ -105,6 +157,8 @@ bind_interrupts!(struct AdcIrqs {
|
||||||
SAADC => saadc::InterruptHandler;
|
SAADC => saadc::InterruptHandler;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const DATA_POINT: u32 = 0x000F9800 - 4096;
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
let mut config = embassy_nrf::config::Config::default();
|
let mut config = embassy_nrf::config::Config::default();
|
||||||
|
|
@ -153,13 +207,19 @@ async fn main(spawner: Spawner) {
|
||||||
channel_config.reference = Reference::INTERNAL;
|
channel_config.reference = Reference::INTERNAL;
|
||||||
let saadc = Saadc::new(p.SAADC, AdcIrqs, saadc::Config::default(), [channel_config]);
|
let saadc = Saadc::new(p.SAADC, AdcIrqs, saadc::Config::default(), [channel_config]);
|
||||||
saadc.calibrate().await;
|
saadc.calibrate().await;
|
||||||
let flash = Flash::take(sd);
|
let mut flash = Flash::take(sd);
|
||||||
pin_mut!(flash);
|
|
||||||
let mut data = [0u8; 16];
|
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);
|
error!("Error reading from flash: {}", e);
|
||||||
}
|
}
|
||||||
info!("Data in flash: {:?}", data);
|
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_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());
|
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(touch_button(p.P0_08.into())).expect("failed to spawn touch task");
|
||||||
spawner.spawn(actions_task()).expect("failed to spawn actions task");
|
spawner.spawn(actions_task()).expect("failed to spawn actions task");
|
||||||
spawner.spawn(softdevice_task(sd)).expect("failed to spawn softdevice 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) {
|
async fn gatt_task(server: Server, mut saadc: Saadc<'static, 1>, sd: &'static Softdevice) {
|
||||||
static ADV_DATA: LegacyAdvertisementPayload = LegacyAdvertisementBuilder::new()
|
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");
|
info!("Starting light task");
|
||||||
const MAX_DUTY: u16 = 32767;
|
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)]);
|
pwm.set_all_duties([DutyCycle::normal(0), DutyCycle::normal(0), DutyCycle::normal(0), DutyCycle::normal(0)]);
|
||||||
|
|
||||||
let mut intensity = 0i32;
|
let mut intensity = 0i32;
|
||||||
let mut hue: u16 = 0;
|
let mut hue: u16 = initial_color;
|
||||||
let mut animation_type = TextLightType::Hue;
|
let mut animation_type = light_type;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let action = {
|
let action = {
|
||||||
|
|
@ -519,12 +650,23 @@ async fn light_task(mut pwm: SimplePwm<'static>) {
|
||||||
TextLightType::Hue
|
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) => {
|
AnimationSignal::SetMode(val) => {
|
||||||
animation_type = 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) => {
|
AnimationSignal::SetColor(val) => {
|
||||||
hue = 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)
|
(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;
|
const MAX: u16 = 16384;
|
||||||
pwm.set_max_duty(MAX);
|
pwm.set_max_duty(MAX);
|
||||||
pwm.set_prescaler(Prescaler::Div1);
|
pwm.set_prescaler(Prescaler::Div1);
|
||||||
|
|
||||||
|
|
||||||
let mut intensity = 0i32;
|
let mut intensity = 0i32;
|
||||||
let mut animation_type = HeartLightType::HeartBeat;
|
let mut animation_type = light_type;
|
||||||
let mut hue: u16 = 0;
|
let mut hue: u16 = initial_color;
|
||||||
let mut heart_beat = 0;
|
let mut heart_beat = 0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -604,11 +746,11 @@ async fn heartbeat_task(mut pwm: SimplePwm<'static>) {
|
||||||
match action {
|
match action {
|
||||||
SysAction::PresentenceOn if intensity < 100 => {
|
SysAction::PresentenceOn if intensity < 100 => {
|
||||||
intensity.add_assign(1);
|
intensity.add_assign(1);
|
||||||
info!("Increase intensity to {}", intensity);
|
// info!("Increase intensity to {}", intensity);
|
||||||
},
|
},
|
||||||
SysAction::PresentenceOff if intensity > 0 => {
|
SysAction::PresentenceOff if intensity > 0 => {
|
||||||
intensity.sub_assign(1);
|
intensity.sub_assign(1);
|
||||||
info!("Decrease intensity to {}", intensity);
|
// info!("Decrease intensity to {}", intensity);
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
// ignore
|
// ignore
|
||||||
|
|
@ -629,12 +771,23 @@ async fn heartbeat_task(mut pwm: SimplePwm<'static>) {
|
||||||
HeartLightType::HeartBeat
|
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) => {
|
AnimationSignal::SetMode(val) => {
|
||||||
animation_type = 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) => {
|
AnimationSignal::SetColor(val) => {
|
||||||
hue = val;
|
hue = val;
|
||||||
|
SAVE_SIGNAL.signal(StoreType::HeartColor(hue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue