use anyhow::{anyhow, Result};
use esp_idf_svc::log::EspLogger;
use log::info;
use std::sync::{Arc, Mutex};
use esp32_simple::{
gps::{Reading, Sensor},
infra::{Poller, Switch},
thread,
};
mod common;
use common::{
hw::Context,
logic::{trace_func, Core, DeviceNearby, State, Trigger},
};
struct StateMachine<'a> {
core: Core<'a>,
location: Arc<Mutex<Option<Reading>>>,
max_speed_mps: f32,
}
impl<'a> StateMachine<'a> {
fn new(core: Core<'a>, location: Arc<Mutex<Option<Reading>>>) -> Self {
Self {
core,
location,
max_speed_mps: 0.0,
}
}
fn handle_button_pressed(
core: &mut Core<'_>,
max_speed_mps: &mut f32,
) -> Result<()> {
trace_func!();
if core.state.is_off() {
*max_speed_mps = 0.0;
core.state = State::on();
} else {
core.state = State::off();
}
core.advertiser.toggle()
}
fn run(&mut self) -> Result<()> {
let max_speed_mps = &mut self.max_speed_mps;
let location = &self.location;
self.core.run(|core, triggers| {
if core.handle_common_triggers(
triggers,
|c| Self::handle_button_pressed(c, max_speed_mps),
|c| {
trace_func!();
if c.state.is_on()
&& !matches!(c.state, State::On(Some(DeviceNearby::Active)))
{
c.state = State::On(Some(DeviceNearby::Active));
}
Ok(())
},
)? {
Ok(())
} else if triggers.contains(&Trigger::GpsDataAvailable) {
let mut data = location
.lock()
.map_err(|e| anyhow!("Mutex lock error: {:?}", e))?;
if let Some(reading) = data.take() {
info!("GPS Reading: {}", reading);
if let Some(speed) = reading.speed_mps() {
if speed > *max_speed_mps {
*max_speed_mps = speed;
}
}
let payload = (*max_speed_mps > 0.0).then(|| {
let bytes = max_speed_mps.to_le_bytes().to_vec();
let kmph = *max_speed_mps * 3.6;
info!(
"Advertising {} bytes: {:?} (max_speed: {kmph:.2} km/h)",
bytes.len(),
bytes
);
bytes
});
core.advertiser.set_payload(payload)?;
}
Ok(())
} else {
Err(anyhow!("Unknown triggers: {:?}", triggers))
}
})
}
}
fn main() -> ! {
thread::main(|| {
EspLogger::initialize_default();
let context = Context::try_default()?;
let location = Arc::new(Mutex::new(None::<Reading>));
let (
dispatcher,
advertiser,
led,
led_timer,
gps_notifier,
button_state,
uart_driver,
_,
_,
) = context.into_parts();
let mut gps = Sensor::new(
gps_notifier,
&Trigger::GpsDataAvailable,
button_state,
uart_driver,
Arc::clone(&location),
);
thread::spawn(move || gps.poll());
let core = Core::new(State::on(), dispatcher, advertiser, led, led_timer)?;
let mut sm = StateMachine::new(core, location);
sm.run()
})
}