Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve-state-communication #39

Merged
merged 6 commits into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ embassy-net = { git = "https://github.com/embassy-rs/embassy", features = [
"dhcpv4",
"medium-ethernet",
"dns",
"dhcpv4-hostname",
] }
embassy-net-wiznet = { git = "https://github.com/embassy-rs/embassy", features = [
"defmt",
Expand Down
43 changes: 42 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use crate::task::alarm_settings::manage_alarm_settings;
use crate::task::buttons::{blue_button, green_button, yellow_button};
use crate::task::dfplayer::sound;
use crate::task::display::display;
use crate::task::orchestrate::orchestrate;
use crate::task::power::{usb_power, vsys_voltage};
use crate::task::resources::*;
use crate::task::state::*;
use crate::task::time_updater::time_updater;
use core::cell::RefCell;
use defmt::*;
Expand Down Expand Up @@ -133,3 +133,44 @@ async fn main(spawner: Spawner) {
spawner.spawn(sound(spawner, r.dfplayer)).unwrap();
}
}

/// This struct is used to configure which tasks are enabled
/// This is useful for troubleshooting, as we can disable tasks to reduce the binary size
/// and clutter in the output.
/// Also, we can disable tasks that are not needed for the current development stage and also test tasks in isolation.
/// For a production build we will need all tasks enabled
pub struct TaskConfig {
pub btn_green: bool,
pub btn_blue: bool,
pub btn_yellow: bool,
pub time_updater: bool,
pub neopixel: bool,
pub display: bool,
pub dfplayer: bool,
pub usb_power: bool,
pub vsys_voltage: bool,
pub alarm_settings: bool,
}

impl Default for TaskConfig {
fn default() -> Self {
TaskConfig {
btn_green: true,
btn_blue: true,
btn_yellow: true,
time_updater: true,
neopixel: true,
display: true,
dfplayer: true,
usb_power: true,
vsys_voltage: true,
alarm_settings: true,
}
}
}

impl TaskConfig {
pub fn new() -> Self {
TaskConfig::default()
}
}
6 changes: 2 additions & 4 deletions src/task/alarm_settings.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! # persisted_alarm_time
//! This module contains the functionality to persist the alarm settings in the flash memory.
//!
//! The alarm settings are stored in the flash memory as three separate key/value pairs.
use crate::task::resources::FlashResources;
use crate::task::state::{AlarmSettings, Commands, Events, EVENT_CHANNEL, FLASH_CHANNEL};
Expand All @@ -26,7 +26,6 @@ pub struct PersistedAlarmSettings<'a> {
}

impl<'a> PersistedAlarmSettings<'a> {
/// # new
/// This function creates a new instance of the PersistedAlarmTime struct.
/// It takes a FlashResources struct as an argument and returns a PersistedAlarmTime struct.
pub fn new(r: FlashResources) -> Self {
Expand All @@ -39,7 +38,6 @@ impl<'a> PersistedAlarmSettings<'a> {
}

/// this function reads the alarm time from the flash memory.
/// It returns a tuple of two u8 values representing the hour and minute of the alarm time.
pub async fn read_alarm_settings_from_flash(&mut self) -> AlarmSettings {
let keys: [u8; 3] = [0, 1, 2];
let mut values = [0u8; 3];
Expand Down Expand Up @@ -119,8 +117,8 @@ impl<'a> PersistedAlarmSettings<'a> {
}
}

/// # alarm_settings
/// This function reads the alarm settings from the flash memory and sends it to the event channel.
///
/// It is done only once at the start of the program.
/// After that, it waits for commands to update the alarm settings.
#[embassy_executor::task]
Expand Down
1 change: 1 addition & 0 deletions src/task/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod buttons;
pub mod dfplayer;
pub mod display;
pub mod neopixel;
pub mod orchestrate;
pub mod power;
pub mod resources;
pub mod state;
Expand Down
108 changes: 108 additions & 0 deletions src/task/orchestrate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//! Task to orchestrate the state transitions of the system.
use crate::task::state::*;
use core::cell::RefCell;
use defmt::*;
use embassy_executor::Spawner;
use embassy_rp::peripherals::RTC;
use embassy_rp::rtc::Rtc;

/// This task is responsible for the state transitions of the system. It acts as the main task of the system.
/// It receives events from the other tasks and reacts to them by changing the state of the system.
#[embassy_executor::task]
pub async fn orchestrate(_spawner: Spawner, rtc_ref: &'static RefCell<Rtc<'static, RTC>>) {
// initialize the state manager and put it into the mutex
{
let state_manager = StateManager::new();
*(STATE_MANAGER_MUTEX.lock().await) = Some(state_manager);
}

let event_receiver = EVENT_CHANNEL.receiver();
let flash_sender = FLASH_CHANNEL.sender();

info!("Orchestrate task started");

// // just testing: set the alarm time to 7:30 and enable the alarm
// state_manager.alarm_settings.enabled = true;
// state_manager.alarm_settings.time = (7, 30);
// flash_sender
// .send(Commands::AlarmSettingsWriteToFlash(
// state_manager.alarm_settings.clone(),
// ))
// .await;

loop {
// receive the events, halting the task until an event is received
let event = event_receiver.receive().await;

'_mutex_guard: {
// Lock the mutex to get a mutable reference to the state manager
let mut state_manager_guard = STATE_MANAGER_MUTEX.lock().await;
// Get a mutable reference to the state manager. We can unwrap here because we know that the state manager is initialized
let state_manager = state_manager_guard.as_mut().unwrap();

// react to the events
match event {
Events::BlueBtn(presses) => {
info!("Blue button pressed, presses: {}", presses);
}
Events::GreenBtn(presses) => {
state_manager.handle_green_button_press();
}
Events::YellowBtn(presses) => {
info!("Yellow button pressed, presses: {}", presses);
}
Events::Vbus(usb) => {
info!("Vbus event, usb: {}", usb);
state_manager.power_state.usb_power = usb;
}
Events::Vsys(voltage) => {
info!("Vsys event, voltage: {}", voltage);
state_manager.power_state.vsys = voltage;
state_manager.power_state.set_battery_level();
}
Events::AlarmSettingsReadFromFlash(alarm_settings) => {
info!("Alarm time read from flash: {:?}", alarm_settings);
state_manager.alarm_settings = alarm_settings;
}
}
}

// at this point we have altered the state of the system, we can now trigger actions based on the state
// for now we will just log the state, in another task :-)
if let Ok(dt) = rtc_ref.borrow_mut().now() {
info!(
"orhestrate loop: {}-{:02}-{:02} {}:{:02}:{:02}",
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
);
}

match _spawner.spawn(info_task(_spawner)) {
Ok(_) => info!("info_task spawned"),
Err(_) => info!("info_task spawn failed"),
}
// ToDo: send the state to the display task. This will be straightforward, as we will design the display task to
// receive the state and update the display accordingly.

// ToDo: send the state to the sound task. This will be straightforward, as there is only one sound to play, the alarm sound.

// ToDo: send the state to the neopixel task. This will need a little thinking, as the neopixel hs different effects to display
}
}

/// Task to log the state of the system.
///
/// This task is responsible for logging the state of the system. It is triggered by the orchestrate task.
/// This is just a simple way to prove the Mutex is working as expected.
#[embassy_executor::task]
pub async fn info_task(_spawner: Spawner) {
info!("Info task started");
let mut state_manager_guard = STATE_MANAGER_MUTEX.lock().await;
match state_manager_guard.as_mut() {
Some(state_manager) => {
info!("{:?}", state_manager);
}
None => {
info!("Info task started, but the state manager is not initialized yet");
}
}
}
Loading
Loading