Skip to content

Commit

Permalink
19-introduce-power-information (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
1-rafael-1 authored Jul 23, 2024
1 parent 2c5eaaf commit e2be835
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 82 deletions.
10 changes: 9 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,13 @@
"rust-analyzer.check.allTargets": false,
"rust-analyzer.checkOnSave": false,
"rust-analyzer.inlayHints.typeHints.enable": true,
"editor.formatOnSave": true
"rust-analyzer.inlayHints.closingBraceHints.enable": true,
"rust-analyzer.inlayHints.closingBraceHints.minLines": 0,
"editor.formatOnSave": true,
"rust-analyzer.inlayHints.implicitDrops.enable": true,
"rust-analyzer.restartServerOnConfigChange": true,
"rust-analyzer.typing.autoClosingAngleBrackets.enable": true,
"rust-analyzer.inlayHints.closureCaptureHints.enable": true,
"rust-analyzer.imports.preferNoStd": true,
"rust-analyzer.imports.prefix": "crate"
}
59 changes: 37 additions & 22 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
// we are in an environment with constrained resources, so we do not use the standard library and we define a different entry point.
//! # Main
//! This is the main entry point of the program.
//! we are in an environment with constrained resources, so we do not use the standard library and we define a different entry point.
#![no_std]
#![no_main]

use crate::task::buttons::{blue_button, green_button, yellow_button};
use crate::task::dfplayer::sound;
use crate::task::display::display;
use crate::task::power::{usb_power, vsys_voltage};
use crate::task::resources::*;
use crate::task::state::*;
use crate::task::time_updater::connect_and_update_rtc;
use crate::task::time_updater::time_updater;
use core::cell::RefCell;
use defmt::*;
use embassy_executor::Executor;
use embassy_executor::Spawner;
use embassy_executor::{Executor, Spawner};
use embassy_rp::multicore::{spawn_core1, Stack};
use embassy_rp::peripherals;
use embassy_rp::rtc::Rtc;
Expand All @@ -24,7 +26,7 @@ mod task;
// import the utility module (submodule of src)
mod utility;

// Entry point
/// Entry point
#[embassy_executor::main]
async fn main(spawner: Spawner) {
info!("Program start");
Expand All @@ -37,41 +39,54 @@ async fn main(spawner: Spawner) {
// configure, which tasks to spawn. For a production build we need all tasks, for troubleshooting we can disable some
// the tasks are all spawned in main.rs, so we can disable them here
// clutter in the output aside, the binary size is conveniently reduced by disabling tasks
let task_config = TaskConfig::new();
// let mut task_config = TaskConfig::new();
// task_config.spawn_connect_and_update_rtc = false;
// task_config.spawn_btn_green = false;
// task_config.spawn_btn_blue = false;
// task_config.spawn_btn_yellow = false;
// task_config.spawn_neopixel = false;
// task_config.spawn_display = false;
// task_config.spawn_dfplayer = false;
let mut task_config = TaskConfig::new();
task_config.time_updater = true;
task_config.btn_green = true;
task_config.btn_blue = true;
task_config.btn_yellow = true;
task_config.neopixel = false;
task_config.display = false;
task_config.dfplayer = false;
task_config.usb_power = true;
task_config.vsys_voltage = true;

// RTC
// Initialize the RTC in a static cell, we will need it in multiple places
static RTC: StaticCell<RefCell<Rtc<'static, peripherals::RTC>>> = StaticCell::new();
let rtc_instance: Rtc<'static, peripherals::RTC> = Rtc::new(r.rtc.rtc_inst);
let rtc_ref = RTC.init(RefCell::new(rtc_instance));

// Orchestrate
// there is no main loop, the tasks are spawned and run in parallel
// orchestrating the tasks is done here:
spawner.spawn(orchestrate(spawner, rtc_ref)).unwrap();

// Power
if task_config.usb_power {
spawner.spawn(usb_power(spawner, r.vbus_power)).unwrap();
};

if task_config.vsys_voltage {
spawner
.spawn(vsys_voltage(spawner, r.vsys_resources))
.unwrap();
}

// Buttons
if task_config.spawn_btn_green {
if task_config.btn_green {
spawner.spawn(green_button(spawner, r.btn_green)).unwrap();
};
if task_config.spawn_btn_blue {
if task_config.btn_blue {
spawner.spawn(blue_button(spawner, r.btn_blue)).unwrap();
};
if task_config.spawn_btn_yellow {
if task_config.btn_yellow {
spawner.spawn(yellow_button(spawner, r.btn_yellow)).unwrap();
};

// update the RTC
if task_config.spawn_connect_and_update_rtc {
if task_config.time_updater {
spawner
.spawn(connect_and_update_rtc(spawner, r.wifi, rtc_ref))
.spawn(time_updater(spawner, r.wifi, rtc_ref))
.unwrap();
}

Expand All @@ -90,7 +105,7 @@ async fn main(spawner: Spawner) {
move || {
let executor1 = EXECUTOR1.init(Executor::new());
executor1.run(|spawner| {
if task_config.spawn_neopixel {
if task_config.neopixel {
spawner
.spawn(task::neopixel::analog_clock(spawner, r.neopixel))
.unwrap();
Expand All @@ -100,12 +115,12 @@ async fn main(spawner: Spawner) {
);

// Display
if task_config.spawn_display {
if task_config.display {
spawner.spawn(display(spawner, r.display)).unwrap();
}

// DFPlayer
if task_config.spawn_dfplayer {
if task_config.dfplayer {
spawner.spawn(sound(spawner, r.dfplayer)).unwrap();
}
}
9 changes: 8 additions & 1 deletion src/task/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,14 @@ pub async fn display(_spawner: Spawner, r: DisplayResources) {
let interface = I2CDisplayInterface::new(i2c);
let mut display = Ssd1306::new(interface, DisplaySize128x64, DisplayRotation::Rotate0)
.into_buffered_graphics_mode();
display.init().await.unwrap();
match display.init().await {
Ok(_) => {}
Err(e) => {
error!("Failed to initialize display: {}", defmt::Debug2Format(&e));
return;
}
}
display.set_brightness(Brightness::DIMMEST).await.unwrap();

// Load BMP images from media
let bmps = Bmps::new();
Expand Down
2 changes: 2 additions & 0 deletions src/task/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! Tasks that make up the application as well as the resources they use.
pub mod buttons;
pub mod dfplayer;
pub mod display;
pub mod neopixel;
pub mod power;
pub mod resources;
pub mod state;
pub mod time_updater;
55 changes: 55 additions & 0 deletions src/task/power.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//! # Power
//! Determine the power state of the system: battery or power supply.
//! Detremine the supply voltage of the system.
use crate::task::resources::{Irqs, UsbPowerResources};
use crate::task::state::VBUS_CHANNEL;
use crate::VsysResources;
use defmt::*;
use embassy_executor::Spawner;
use embassy_rp::adc::{Adc, Channel, Config};
use embassy_rp::gpio::{Input, Pull};
use embassy_time::{Duration, Timer};

/// determine the power source of the system, specifically if the USB power supply is connected
/// the USB power supply is connected, if the pin is high
/// Note: We are using a voltage divider to detect the USB power supply through a GPIO pin. Due to the intricacies of the Pico W,
/// the VBUS pin is not available for direct use (it is run through the wifi module, and there is no safe way to use wifi and the
/// vbus concurrently).
#[embassy_executor::task]
pub async fn usb_power(_spawner: Spawner, r: UsbPowerResources) {
info!("usb_power task started");
let mut vbus_in = Input::new(r.vbus_pin, Pull::None);
let sender = VBUS_CHANNEL.sender();
loop {
info!("usb_power task loop");
sender.send(vbus_in.is_high().into()).await;
vbus_in.wait_for_any_edge().await;
info!("usb_power edge detected");
}
}

/// measure the voltage of the Vsys rail
/// this is either the battery voltage or the usb power supply voltage, if the usb power supply is connected.
/// Note: We are using a voltage divider to measure the Vsys voltage through a GPIO pin. Due to the intricacies of the Pico W,
/// the VSYS pin is not available for direct use (it is run through the wifi module, and there is no safe way to use wifi and the
/// vsys concurrently).
#[embassy_executor::task]
pub async fn vsys_voltage(_spawner: Spawner, r: VsysResources) {
info!("vsys_voltage task started");
let mut adc = Adc::new(r.adc, Irqs, Config::default());
let vsys_in = r.pin_27;
let mut channel = Channel::new_pin(vsys_in, Pull::None);
let refresh_after_secs = 600; // 10 minutes
loop {
// read the adc value
let adc_value = adc.read(&mut channel).await.unwrap();
let voltage = (adc_value as f32) * 3.3 * 3.0 / 4096.0;

info!(
"vsys_voltage: adc_value: {}, voltage: {}",
adc_value, voltage
);
Timer::after(Duration::from_secs(refresh_after_secs)).await;
}
}
72 changes: 27 additions & 45 deletions src/task/resources.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/// this module is used to define the resources that will be used in the tasks
///
/// the resources are defined in the main.rs file, and assigned to the tasks in the main.rs file
//! # Resources
//! this module is used to define the resources that will be used in the tasks
//!
//! the resources are defined in the main.rs file, and assigned to the tasks in the main.rs file
use assign_resources::assign_resources;
use embassy_rp::adc::InterruptHandler as AdcInterruptHandler;
use embassy_rp::i2c::InterruptHandler as I2cInterruptHandler;
use embassy_rp::peripherals::I2C0;
use embassy_rp::peripherals::PIO0;
use embassy_rp::peripherals::UART1;
use embassy_rp::peripherals::{I2C0, PIO0};
use embassy_rp::pio::InterruptHandler;
use embassy_rp::uart::BufferedInterruptHandler;
use embassy_rp::{bind_interrupts, peripherals};
Expand All @@ -22,14 +23,6 @@ assign_resources! {
btn_yellow: YellowButtonResources {
button_pin: PIN_22,
},
wifi: WifiResources {
pwr_pin: PIN_23,
cs_pin: PIN_25,
pio_sm: PIO0,
dio_pin: PIN_24,
clk_pin: PIN_29,
dma_ch: DMA_CH0,
},
rtc: RtcResources {
rtc_inst: RTC,
},
Expand All @@ -52,42 +45,31 @@ assign_resources! {
tx_dma_ch: DMA_CH3,
power_pin: PIN_8, // not a part of the dfplayer, using a mosfet to control power to the dfplayer because it draws too much current when idle
},
vbus_power: UsbPowerResources {
// we cannot use the VBUS power pin 24, because on the Pico W the vbus pin is run through the wifi module and is not available
// instead we wire a voltage divider between VBUS and a GPIO pin
vbus_pin: PIN_28,
},
wifi: WifiResources {
pwr_pin: PIN_23,
cs_pin: PIN_25,
pio_sm: PIO0,
dio_pin: PIN_24,
clk_pin: PIN_29,
dma_ch: DMA_CH0,
},
vsys_resources: VsysResources {
// we cannot use the VSYS power pin 29, because on the Pico W the vsys pin is run through the wifi module and is not available
// instead we wire a voltage divider between VSYS and a GPIO pin
adc: ADC,
pin_27: PIN_27,
},
}

// bind the interrupts, on a global scope, until i find a better way
// bind the interrupts, on a global scope for convenience
bind_interrupts!(pub struct Irqs {
PIO0_IRQ_0 => InterruptHandler<PIO0>;
I2C0_IRQ => I2cInterruptHandler<I2C0>;
// UART0_IRQ => UartInterruptHandler<UART0>;
UART1_IRQ => BufferedInterruptHandler<UART1>;
ADC_IRQ_FIFO => AdcInterruptHandler;
});

pub struct TaskConfig {
pub spawn_btn_green: bool,
pub spawn_btn_blue: bool,
pub spawn_btn_yellow: bool,
pub spawn_connect_and_update_rtc: bool,
pub spawn_neopixel: bool,
pub spawn_display: bool,
pub spawn_dfplayer: bool,
}

impl Default for TaskConfig {
fn default() -> Self {
TaskConfig {
spawn_btn_green: true,
spawn_btn_blue: true,
spawn_btn_yellow: true,
spawn_connect_and_update_rtc: true,
spawn_neopixel: true,
spawn_display: true,
spawn_dfplayer: true,
}
}
}

impl TaskConfig {
pub fn new() -> Self {
TaskConfig::default()
}
}
Loading

0 comments on commit e2be835

Please sign in to comment.