0
0
Fork 0

feat: Add L76K GNSS

This commit is contained in:
Jannik Beyerstedt 2026-05-11 23:52:35 +02:00
commit 919065f377
3 changed files with 205 additions and 8 deletions

View file

@ -5,10 +5,13 @@
reason = "mem::forget is generally not safe to do with esp_hal types, especially those \
holding buffers for the duration of a data transfer."
)]
#![deny(clippy::large_stack_frames)]
// #![deny(clippy::large_stack_frames)]
use core::cell::RefCell;
use critical_section::Mutex;
use embassy_executor::Spawner;
use embassy_time::{Duration, Timer};
use embassy_gps::gps::l76k;
use esp_backtrace as _;
use esp_hal::clock::CpuClock;
use esp_hal::timer::timg::TimerGroup;
@ -21,10 +24,68 @@ mod radio;
const WIFI_CHANNEL: radio::Channel = 180;
#[derive(Debug, Default)]
struct State {
initialized: bool,
time: chrono::NaiveDateTime,
lat_deg: f32,
lon_deg: f32,
heading_deg: Option<f32>,
speed_mps: Option<f32>,
}
impl State {
fn update_with_gpsfix(&mut self, fix: &embassy_gps::types::GpsFix) {
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
let lat_deg = fix.latitude_deg as f32;
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
let lon_deg = fix.longitude_deg as f32;
self.lat_deg = lat_deg;
self.lon_deg = lon_deg;
if let Some(heading) = fix.true_course_deg {
self.heading_deg = Some(heading);
} else {
warn!("State: Course missing from GpsFix");
}
if let Some(speed) = fix.speed_over_ground {
self.speed_mps = Some(speed);
} else {
warn!("State: Speed missing from GpsFix");
}
if let Some(time) = fix.get_timestamp() {
self.time = time;
if !self.initialized {
info!("GNSS fix: {}", self.print_fix());
}
self.initialized = true;
}
}
fn print_fix(&self) -> alloc::string::String {
alloc::format!(
"{:?}, {:.4} N, {:.4} E, {:.0}°, {:.2} m/s",
self.time,
self.lat_deg,
self.lon_deg,
self.heading_deg.unwrap_or_default(),
self.speed_mps.unwrap_or_default(),
)
}
}
// This creates a default app-descriptor required by the esp-idf bootloader.
// For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
esp_bootloader_esp_idf::esp_app_desc!();
static GNSS_UPDATE: Mutex<RefCell<Option<embassy_gps::types::GpsFix>>> =
Mutex::new(RefCell::new(None));
#[allow(
clippy::large_stack_frames,
reason = "it's not unusual to allocate larger buffers etc. in main"
@ -70,13 +131,39 @@ async fn main(spawner: Spawner) -> ! {
info!("WiFi promiscuous mode on channel {WIFI_CHANNEL} running");
// TODO: Spawn some tasks
let _ = spawner;
// Seeed XIAO L67K:
// RX: D7/ GPIO12
// TX: D6/ GPIO11
// WAKEUP: D0/ GPIO1 -> HIGH for active, LOW for Sleep
// RESET: D2/ GPIO25 -> HIGH for normal, LOW for Reset
spawner.spawn(
gnss_task(
peripherals.GPIO1,
peripherals.GPIO25,
peripherals.UART0,
peripherals.GPIO12,
peripherals.GPIO11,
)
.expect("Failed to spawn GNSS task"),
);
info!("GNSS task started, waiting for GNSS fix...");
let mut state = State::default();
loop {
// nothing to do here, all data is handled in RX callback for now
// but use a timer so that this is yields to other tasks
Timer::after(Duration::from_secs(1)).await;
embassy_time::Timer::after(embassy_time::Duration::from_millis(100)).await;
critical_section::with(|cs| {
let gnss_update_ref = GNSS_UPDATE.borrow(cs);
if let Some(fix) = gnss_update_ref.replace(None) {
state.update_with_gpsfix(&fix);
if let Some(time) = fix.get_timestamp() {
state.time = time;
info!("{}", state.print_fix()); // TODO: degrate to debug later?
}
}
});
}
}
@ -99,3 +186,42 @@ fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) {
// TODO: handle 802.11 frame
info!("Received frame with {} bytes", frame.len);
}
#[allow(clippy::large_stack_frames, reason = "GNSS driver needs some space")]
#[embassy_executor::task]
async fn gnss_task(
gps_wakeup: esp_hal::peripherals::GPIO1<'static>,
gps_reset: esp_hal::peripherals::GPIO25<'static>,
uart: esp_hal::peripherals::UART0<'static>,
uart_rx: esp_hal::peripherals::GPIO12<'static>,
uart_tx: esp_hal::peripherals::GPIO11<'static>,
) {
use embassy_gps::gps::GpsFsm;
let mut gps = l76k::esp::L76kFsm::new_sep(
l76k::esp::GpsHw {
reinit: gps_reset,
standby: gps_wakeup,
},
|| {
esp_hal::uart::Uart::new(uart, esp_hal::uart::Config::default().with_baudrate(9600))
.expect("Failed to create UART for GNSS")
.with_rx(uart_rx)
.with_tx(uart_tx)
.into_async()
},
)
.await;
loop {
if let Ok(Some(embassy_gps::types::GpsEvent::Fix(fix))) = gps.step().await {
// send to main thread
critical_section::with(|cs| {
let gnss_update_ref = GNSS_UPDATE.borrow(cs);
gnss_update_ref.replace(Some(fix));
});
} else {
// FSM will recover automatically from errors and ignore other event types
}
}
}