diff --git a/mqtt_display/src/bin/main.rs b/mqtt_display/src/bin/main.rs index 16e7557..73a8c72 100644 --- a/mqtt_display/src/bin/main.rs +++ b/mqtt_display/src/bin/main.rs @@ -6,10 +6,13 @@ clippy::mem_forget, reason = "mem::forget is generally not safe to do with esp_hal types" )] +// TODO WARNING core 1 should be logic, core 0 wifi, its flipped now use embassy_executor::Spawner; use embassy_futures::select::{select3, Either3}; use embassy_net::{Runner, StackResources}; +use embassy_sync::signal::Signal; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_time::{Duration, Timer}; use projekt_final::bus::I2cInner; @@ -20,6 +23,7 @@ use esp_hal::{ clock::CpuClock, i2c::master::{Config as I2cConfig, I2c}, rng::Rng, + system::{CpuControl, Stack}, timer::timg::TimerGroup, }; use esp_wifi::{ @@ -43,6 +47,9 @@ extern crate alloc; use alloc::format; static I2C_BUS: StaticCell> = StaticCell::new(); +static APP_CORE_STACK: StaticCell> = StaticCell::new(); +static EXECUTOR_CORE1: StaticCell = StaticCell::new(); +static NETWORK_READY: Signal = Signal::new(); macro_rules! mk_static { ($t:ty,$val:expr) => {{ @@ -97,21 +104,25 @@ async fn main(spawner: Spawner) -> ! { let timg1 = TimerGroup::new(peripherals.TIMG1); esp_hal_embassy::init(timg1.timer0); - let net_config = embassy_net::Config::dhcpv4(Default::default()); let seed = (rng.random() as u64) << 32 | rng.random() as u64; - let (stack, runner) = embassy_net::new( - wifi_interface, - net_config, - mk_static!(StackResources<3>, StackResources::<3>::new()), - seed, - ); + // Start core 1 for WiFi and MQTT (network stack created there) + let mut cpu_control = CpuControl::new(peripherals.CPU_CTRL); + let _guard = cpu_control.start_app_core( + APP_CORE_STACK.init(Stack::new()), + move || { + let executor = EXECUTOR_CORE1.init(esp_hal_embassy::Executor::new()); + executor.run(|spawner| { + spawner.spawn(core1_network_task(spawner, controller, wifi_interface, seed)).ok(); + }); + } + ).unwrap(); - spawner.spawn(connection_task(controller)).expect("spawn connection_task"); - spawner.spawn(net_task(runner)).expect("spawn net_task"); - wait_for_network(stack).await; + // Wait for network to be ready (signaled from core 1) + NETWORK_READY.wait().await; + info!("Network ready, starting core 0 tasks"); - spawner.spawn(mqtt_task(stack)).expect("spawn mqtt_task"); + // Core 0: display and MPU tasks spawner.spawn(display::task::display_task(display_i2c)).expect("spawn display_task"); spawner.spawn(mpu::task::mpu_task(mpu_i2c)).expect("spawn mpu_task"); @@ -156,7 +167,27 @@ async fn main(spawner: Spawner) -> ! { } } -async fn wait_for_network(stack: embassy_net::Stack<'static>) { +// Runs on core 1 - creates and owns the network stack +#[embassy_executor::task] +async fn core1_network_task( + spawner: Spawner, + controller: WifiController<'static>, + wifi_interface: WifiDevice<'static>, + seed: u64, +) { + spawner.spawn(connection_task(controller)).ok(); + + let net_config = embassy_net::Config::dhcpv4(Default::default()); + let (stack, runner) = embassy_net::new( + wifi_interface, + net_config, + mk_static!(StackResources<3>, StackResources::<3>::new()), + seed, + ); + + spawner.spawn(net_task(runner)).ok(); + + // Wait for network loop { if stack.is_link_up() { break; } Timer::after(Duration::from_millis(500)).await; @@ -168,6 +199,12 @@ async fn wait_for_network(stack: embassy_net::Stack<'static>) { } Timer::after(Duration::from_millis(500)).await; } + + // Signal core 0 that network is ready + NETWORK_READY.signal(()); + + // Start MQTT on this core (it needs the stack) + spawner.spawn(mqtt_task(stack)).ok(); } async fn handle_mqtt_message(msg: IncomingMsg) { diff --git a/mqtt_display/src/bus/mod.rs b/mqtt_display/src/bus/mod.rs index ab2cf68..4a90edf 100644 --- a/mqtt_display/src/bus/mod.rs +++ b/mqtt_display/src/bus/mod.rs @@ -7,17 +7,16 @@ use embedded_hal_bus::i2c::RefCellDevice; use esp_hal::i2c::master::I2c; use esp_hal::Async; -/// The underlying I2C peripheral type for esp-hal 1.0.0-rc.0 +/// The underlying I2C peripheral type pub type I2cInner = I2c<'static, Async>; -/// We use RefCell to share the bus on a single core. -/// It's zero-overhead and safe because Embassy tasks don't preempt each other. +/// RefCell to share the bus on a single core. pub type SharedI2c = RefCell; /// A handle to a shared I2C device. pub type I2cDevice = RefCellDevice<'static, I2cInner>; -/// Create a new I2C device handle from the shared bus. +/// New I2C device handle from the shared bus. pub fn new_device(bus: &'static SharedI2c) -> I2cDevice { RefCellDevice::new(bus) } diff --git a/mqtt_display/src/display/api.rs b/mqtt_display/src/display/api.rs index d5d2069..2191402 100644 --- a/mqtt_display/src/display/api.rs +++ b/mqtt_display/src/display/api.rs @@ -6,7 +6,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::{Channel, Receiver, TrySendError}; -use heapless::String as HString; +use heapless::String; use crate::contracts::{DisplayCommand, ImuReading}; @@ -58,14 +58,14 @@ pub async fn show_imu(reading: ImuReading) { /// Set the status line. pub async fn set_status(text: &str) { - let mut s = HString::<32>::new(); + let mut s = String::<32>::new(); let _ = s.push_str(&text[..text.len().min(32)]); send(DisplayCommand::SetStatus(s)).await; } /// Show an error message. pub async fn show_error(text: &str) { - let mut s = HString::<64>::new(); + let mut s = String::<64>::new(); let _ = s.push_str(&text[..text.len().min(64)]); send(DisplayCommand::ShowError(s)).await; } diff --git a/mqtt_display/src/display/task.rs b/mqtt_display/src/display/task.rs index 1a799b5..d647ff2 100644 --- a/mqtt_display/src/display/task.rs +++ b/mqtt_display/src/display/task.rs @@ -6,7 +6,7 @@ use log::{error, info}; use alloc::boxed::Box; use alloc::format; -use heapless::String as HString; +use heapless::String; use mousefood::{EmbeddedBackend, EmbeddedBackendConfig}; use ratatui::{ layout::{Constraint, Direction, Layout}, @@ -27,9 +27,9 @@ const REFRESH_INTERVAL_MS: u64 = 100; /// Internal state for what to render. struct DisplayState { - status: HString<32>, + status: String<32>, last_imu: Option, - last_error: Option>, + last_error: Option>, mqtt_connected: bool, mqtt_msg_count: u32, } @@ -37,7 +37,7 @@ struct DisplayState { impl Default for DisplayState { fn default() -> Self { Self { - status: HString::new(), + status: String::new(), last_imu: None, last_error: None, mqtt_connected: false, @@ -66,7 +66,7 @@ impl DisplayState { DisplayCommand::Clear => { self.last_imu = None; self.last_error = None; - self.status = HString::new(); + self.status = String::new(); } } } diff --git a/mqtt_display/src/i2c/com.rs b/mqtt_display/src/i2c/com.rs index 3698fd3..069d285 100644 --- a/mqtt_display/src/i2c/com.rs +++ b/mqtt_display/src/i2c/com.rs @@ -1,6 +1,5 @@ // src/i2c/com.rs -use embassy_executor::task; use embassy_time::{Duration, Timer}; use esp_hal::{ i2c::master::{Config, I2c}, @@ -9,7 +8,7 @@ use esp_hal::{ use ssd1306::mode::BufferedGraphicsMode; use log::info; -#[task] +#[embassy_executor::task] pub async fn display_task() { use mousefood::{EmbeddedBackend, EmbeddedBackendConfig}; use ratatui::{ @@ -73,7 +72,7 @@ pub async fn display_task() { } } -#[task] +#[embassy_executor::task] pub async fn i2c_check() { let peripherals = unsafe { Peripherals::steal() }; let mut i2c = I2c::new(peripherals.I2C0, Config::default())