diff --git a/Cargo.lock b/Cargo.lock index c25bfea..d074b02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -757,6 +757,7 @@ dependencies = [ "embassy-nrf", "embassy-sync 0.7.2", "embassy-time", + "futures", "nrf-softdevice", "panic-halt", "panic-probe", diff --git a/Cargo.toml b/Cargo.toml index fff6d74..ca28661 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ embassy-time = { version = "0.5.0", features = ["tick-hz-32_768", "defmt", "defm panic-halt = "1.0.0" panic-probe = { version = "1.0.0", features = ["print-defmt"], optional = true } nrf-softdevice = { version = "0.1.0", features = ["s140", "ble-peripheral", "nrf52840", "nrf-softdevice-s140", "critical-section-impl", "defmt", "nrf52840-pac", "critical-section", "ble-gatt-server"] } +futures = { version = "0.3", default-features = false, features = [] } [[bin]] name = "odesa" diff --git a/src/main.rs b/src/main.rs index 952059b..6e0597b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ mod fmt; use core::mem; use core::ops::{AddAssign, SubAssign}; -use defmt::Format; +use defmt::{error, Format}; #[cfg(not(feature = "defmt"))] use panic_halt as _; #[cfg(feature = "defmt")] @@ -16,18 +16,20 @@ use defmt::info; use embassy_executor::Spawner; use embassy_futures::join::{join3, join4}; use embassy_nrf::gpio::{AnyPin, Input, OutputDrive, Pull}; -use embassy_nrf::interrupt::Priority; +use embassy_nrf::interrupt::{InterruptExt, Priority}; use embassy_nrf::pwm::{DutyCycle, Prescaler, SimplePwm}; -use embassy_nrf::{bind_interrupts, saadc, Peri}; +use embassy_nrf::{bind_interrupts, interrupt, saadc, Peri}; use embassy_nrf::peripherals::SAADC; use embassy_nrf::saadc::{ChannelConfig, Gain, Reference, Saadc}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::mutex::Mutex; use embassy_sync::signal::Signal; use embassy_time::{with_timeout, Duration, Timer}; +use futures::future::{select, Either}; +use futures::pin_mut; use nrf_softdevice::{raw, Softdevice}; use nrf_softdevice::ble::advertisement_builder::{Flag, LegacyAdvertisementBuilder, LegacyAdvertisementPayload, ServiceList, ServiceUuid16}; -use nrf_softdevice::ble::{peripheral, gatt_server}; +use nrf_softdevice::ble::{peripheral, gatt_server, Connection}; #[derive(Copy, Clone, Eq, PartialEq, Format)] enum SysAction { @@ -145,9 +147,11 @@ async fn main(spawner: Spawner) { let server = Server::new(sd).expect("failed to enable softdevice"); let mut channel_config = ChannelConfig::single_ended(p.P0_31.reborrow()); + interrupt::SAADC.set_priority(Priority::P3); channel_config.gain = Gain::GAIN1_6; channel_config.reference = Reference::INTERNAL; let saadc = Saadc::new(p.SAADC, AdcIrqs, saadc::Config::default(), [channel_config]); + saadc.calibrate().await; 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()); @@ -155,13 +159,13 @@ 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(battery_task(saadc)).expect("failed to spawn softdevice task"); - join4(gatt_task(server, 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), light_task(pwm_text)).await; } -async fn gatt_task(server: Server, sd: &'static Softdevice) { +async fn gatt_task(server: Server, mut saadc: Saadc<'static, 1>, sd: &'static Softdevice) { static ADV_DATA: LegacyAdvertisementPayload = LegacyAdvertisementBuilder::new() .flags(&[Flag::GeneralDiscovery, Flag::LE_Only]) .services_16(ServiceList::Complete, &[ServiceUuid16::BATTERY]) @@ -185,11 +189,9 @@ async fn gatt_task(server: Server, sd: &'static Softdevice) { info!("advertising done!"); - // Run the GATT server on the connection. This returns when the connection gets disconnected. - // - // Event enums (ServerEvent's) are generated by nrf_softdevice::gatt_server - // proc macro when applied to the Server struct above - let e = gatt_server::run(&conn, &server, |e| match e { + + let battery_fut = battery_task(&mut saadc, &server, &conn); + let gatt_fut = gatt_server::run(&conn, &server, |e| match e { ServerEvent::Bas(e) => match e { BatteryServiceEvent::BatteryLevelCccdWrite { notifications } => { info!("battery notifications: {}", notifications) @@ -265,10 +267,19 @@ async fn gatt_task(server: Server, sd: &'static Softdevice) { info!("text color indications: {}, notifications: {}", indications, notifications) } }, - }) - .await; + }); - info!("gatt_server run exited with error: {:?}", e); + pin_mut!(battery_fut); + pin_mut!(gatt_fut); + + let _ = match select(battery_fut, gatt_fut).await { + Either::Left((_, _)) => { + info!("Battery encountered an error and stopped!") + } + Either::Right((e, _)) => { + info!("GATT run exited with error: {:?}", e); + } + }; } } @@ -330,8 +341,7 @@ async fn touch_button(pin: Peri<'static, AnyPin>) { } } -#[embassy_executor::task] -async fn battery_task(mut saadc: Saadc<'static, 1>) { +async fn battery_task<'a>(saadc: &'a mut Saadc<'_, 1>, server: &'a Server, connection: &'a Connection) { const REFERENCE: f32 = 3.6; let mut measurements = [0i16; 4]; let mut index = 0; @@ -348,7 +358,10 @@ async fn battery_task(mut saadc: Saadc<'static, 1>) { let total = (measurements.iter().sum::() / measurements.len() as i16) as u16; let percent = battery_mv_to_percent(total); info!("Battery: {=i16}, {=u16}, {}%", voltage, total, percent); - Timer::after_millis(2000).await; + if let Err(e) = server.bas.battery_level_notify(connection, &percent) { + error!("Error notifying battery level: {:?}", e); + } + Timer::after_millis(60000).await; } } } @@ -403,7 +416,7 @@ async fn actions_task() { #[embassy_executor::task] async fn radar_task() { - const MAX_TIME: usize = 24; + const MAX_TIME: usize = 480; let mut time = MAX_TIME; loop { let action = {