From 3af9ad408e97bc5a761840d48b246166ada3730d Mon Sep 17 00:00:00 2001 From: Victoria Fischer Date: Sat, 30 Nov 2024 16:06:17 +0100 Subject: [PATCH] platform: rewrite the platform-specific display creation traits into a new Board trait that also supports tasks --- Cargo.toml | 1 + src/main.rs | 52 +++---- src/platform/esp32.rs | 238 +++++++++++++++++++++++++++++++++ src/platform/mod.rs | 18 ++- src/platform/smart_leds_lib.rs | 47 +------ 5 files changed, 276 insertions(+), 80 deletions(-) create mode 100644 src/platform/esp32.rs diff --git a/Cargo.toml b/Cargo.toml index 60c7fdf..d101e02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ smart-leds = { version = "0.4.0", optional = true } embedded-graphics = { version = "0.8.1", optional = true, features = ["fixed_point", "defmt"] } ansi_term = "0.12.1" num = "0.4.3" +chrono = "0.4.38" [build-dependencies] embuild = "0.32.0" diff --git a/src/main.rs b/src/main.rs index 0f3a923..7bbc715 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,31 +9,14 @@ mod animations; mod mappings; mod buffers; -use buffers::SurfacePool; +use esp_idf_svc::hal::prelude::Peripherals; -use crate::platform::DisplayInit; -use crate::render::Surfaces; +use crate::platform::Board; +use crate::task::{Task, Scheduler}; +use crate::render::{Surfaces, Renderer}; use crate::geometry::Rectangle; -#[cfg(feature="embedded-graphics")] -#[cfg(feature="rmt")] -use crate::platform::embedded_graphics_lib::PonderjarTarget as DisplayType; - -#[cfg(feature="smart-leds")] -#[cfg(feature="rmt")] -use crate::platform::smart_leds_lib::rmt::FastWs2812Esp32Rmt as DisplayType; - -#[cfg(feature="smart-leds")] -#[cfg(feature="spi")] -use crate::platform::smart_leds_lib::spi::SPIDisplay as DisplayType; - -#[cfg(feature="threads")] -use crate::buffers::SharedSurface as SurfaceType; - -#[cfg(not(feature="threads"))] -use crate::buffers::SimpleSurface as SurfaceType; - -use crate::render::Renderer; +use crate::platform::esp32::Esp32Board as BoardType; fn main() { // It is necessary to call this function once. Otherwise some patches to the runtime @@ -43,20 +26,27 @@ fn main() { // Bind the log crate to the ESP Logging facilities esp_idf_svc::log::EspLogger::initialize_default(); - log::info!("Setting up display"); + let peripherals = Peripherals::take().unwrap(); + let mut board: BoardType = Board::new(peripherals); + + log::info!("Board: {}", core::any::type_name_of_val(&board)); - let display = DisplayType::new_display::(); + let output = board.output(); + log::info!("Output: {}", core::any::type_name_of_val(&output)); - let mut surfaces: SurfacePool = SurfacePool::new(); + let mut surfaces = board.surfaces(); + log::info!("Surface implementation: {}", core::any::type_name_of_val(&output)); - log::info!("Created new display type {}", core::any::type_name_of_val(&display)); - - log::info!("Creating runner"); - let mut runner = task::Scheduler::new(vec![ + log::info!("Creating animations"); + let mut tasks: Vec> = vec![ Box::new(animations::IdleTask::new(&mut surfaces)), Box::new(animations::TestPattern::new(surfaces.new_surface(&Rectangle::everything()).unwrap())), - Box::new(Renderer::new(display, surfaces)), - ]); + ]; + + tasks.append(&mut board.system_tasks()); + tasks.push(Box::new(Renderer::new(output, surfaces))); + + let mut runner = Scheduler::new(tasks); log::info!("Runner ready: {:?}", runner); diff --git a/src/platform/esp32.rs b/src/platform/esp32.rs new file mode 100644 index 0000000..c1cb13a --- /dev/null +++ b/src/platform/esp32.rs @@ -0,0 +1,238 @@ +use std::sync::Arc; +use std::fmt::Debug; +use std::sync::Mutex; + +use chrono::DateTime; +use chrono::Utc; + +use esp_idf_svc::eventloop::{EspSubscription, EspSystemEventLoop, System}; +use esp_idf_svc::hal::modem::Modem; +use esp_idf_svc::hal::prelude::Peripherals; +use esp_idf_svc::netif::IpEvent; +use esp_idf_svc::nvs::{EspDefaultNvsPartition, EspNvsPartition, NvsDefault}; +use esp_idf_svc::sntp::EspSntp; +use esp_idf_svc::sys::esp_efuse_mac_get_default; +use esp_idf_svc::wifi::{AuthMethod, ClientConfiguration, Configuration, EspWifi, WifiEvent}; + +use rgb::Rgb; + +use super::Board; + +use crate::task::Task; +use crate::buffers::{Pixbuf, SurfacePool}; +use crate::mappings::StrideMapping; +use crate::platform::smart_leds_lib::StrideOutput; +use crate::platform::smart_leds_lib::rmt::FastWs2812Esp32Rmt; +use crate::time::Periodically; + +#[cfg(feature="threads")] +use crate::buffers::SharedSurface as SurfaceType; + +#[cfg(not(feature="threads"))] +use crate::buffers::SimpleSurface as SurfaceType; + +pub struct Esp32Board<'a> { + output: Option<::Output>, + surfaces: Option>, + tasks: Option>> +} + +impl<'a> Board for Esp32Board<'a> { + type Output = StrideOutput<[Rgb; 310], FastWs2812Esp32Rmt<'a>>; + type Surfaces = SurfacePool; + + fn new(peripherals: Peripherals) -> Self { + + let sys_loop = EspSystemEventLoop::take().unwrap(); + let nvs = EspDefaultNvsPartition::take().unwrap(); + + let channel = peripherals.rmt.channel0; + let pins = peripherals.pins; + let mut chip_id: [u8; 8] = [0; 8]; + unsafe { + esp_efuse_mac_get_default(&mut chip_id as *mut u8); + } + + log::info!("Setting up for chip ID {:?}", chip_id); + + let (pixmap, target) = match chip_id { + [72, 202, 67, 89, 145, 204, 0, 0] => { + (StrideMapping::new_panel(), FastWs2812Esp32Rmt::new(channel, pins.gpio5).unwrap()) + }, + [140, 170, 181, 131, 95, 116, 0, 0] => { + (StrideMapping::new_jar(), FastWs2812Esp32Rmt::new(channel, pins.gpio14).unwrap()) + }, + _ => { + (StrideMapping::new(), FastWs2812Esp32Rmt::new(channel, pins.gpio5).unwrap()) + } + }; + + let tasks: Vec> = vec![ + Box::new(WifiTask::new(peripherals.modem, sys_loop.clone(), &nvs)), + ]; + + const POWER_VOLTS : u32 = 5; + const POWER_MA : u32 = 500; + const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA; + + let output = StrideOutput::new( + Pixbuf::new(), + pixmap, + target, + MAX_POWER_MW + ); + + Esp32Board { + surfaces: Some(SurfacePool::new()), + output: Some(output), + tasks: Some(tasks), + } + } + + fn output(&mut self) -> Self::Output { + self.output.take().unwrap() + } + + fn surfaces(&mut self) -> Self::Surfaces { + self.surfaces.take().unwrap() + } + + fn system_tasks(&mut self) -> Vec> { + self.tasks.take().unwrap() + } +} + +impl Debug for WifiTask { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("WifiTask").finish() + } +} + +#[derive(Debug, PartialEq, Clone, Copy)] +enum WifiState { + Stopped, + Disconnected, + Connecting, + Connected +} + +struct WifiTask { + wifi: EspWifi<'static>, + ntp: EspSntp<'static>, + connection_check: Periodically, + state: Arc>, + last_state: WifiState, + sys_loop: EspSystemEventLoop, + wifi_sub: Option>, + ip_sub: Option> +} + +impl WifiTask { + fn new(modem: Modem, sys_loop: EspSystemEventLoop, nvs: &EspNvsPartition) -> Self { + let mut wifi = EspWifi::new( + modem, + sys_loop.clone(), + Some(nvs.clone()) + ).unwrap(); + let wifi_config = Configuration::Client(ClientConfiguration { + ssid: "The Frequency".try_into().unwrap(), + bssid: None, + auth_method: AuthMethod::WPA2Personal, + password: "thepasswordkenneth".try_into().unwrap(), + channel: None, + ..Default::default() + }); + wifi.set_configuration(&wifi_config).unwrap(); + + WifiTask { + wifi, + ntp: EspSntp::new_default().unwrap(), + connection_check: Periodically::new_every_n_seconds(1), + state: Arc::new(Mutex::new(WifiState::Stopped)), + last_state: WifiState::Stopped, + sys_loop, + wifi_sub: None, + ip_sub: None + } + } + + fn connect(&mut self) { + log::info!("Connecting wifi"); + self.wifi.start().unwrap(); + self.wifi.connect().unwrap(); + } + + fn disconnect(&mut self) { + log::info!("Disconnecting wifi"); + self.wifi.disconnect().unwrap(); + self.wifi.stop().unwrap(); + } +} + +impl Task for WifiTask { + fn start(&mut self) { + log::info!("Starting wifi!"); + + log::info!("waiting for loop"); + + let wifi_state = self.state.clone(); + self.wifi_sub = Some(self.sys_loop.subscribe::( move |evt| { + log::warn!("wifi event {:?}", evt); + let next_state = match evt { + WifiEvent::StaStopped => Some(WifiState::Disconnected), + WifiEvent::StaDisconnected => Some(WifiState::Disconnected), + WifiEvent::StaStarted => Some(WifiState::Connecting), + WifiEvent::StaConnected => Some(WifiState::Connecting), + _ => None + }; + + match next_state { + Some(s) => { + let mut state = wifi_state.lock().unwrap(); + *state = s + }, + None => () + } + }).unwrap()); + let ip_state = self.state.clone(); + self.ip_sub = Some(self.sys_loop.subscribe::(move |evt| { + log::warn!("ip event {:?}", evt); + match evt { + IpEvent::DhcpIpAssigned(_) => { + let mut state = ip_state.lock().unwrap(); + *state = WifiState::Connected; + }, + _ => () + } + }).unwrap()); + + self.connect(); + } + + fn tick(&mut self ) { + if self.connection_check.tick() { + let cur_state = *self.state.lock().unwrap(); + + if self.last_state != cur_state { + match cur_state { + WifiState::Connected => log::info!("Wifi connected!"), + WifiState::Connecting => log::info!("Connecting!"), + WifiState::Stopped => log::info!("Stopped!"), + WifiState::Disconnected => log::info!("Disconnected!") + } + + log::info!("online: {:?}", cur_state); + + self.last_state = cur_state; + } + + let now: DateTime = std::time::SystemTime::now().into(); + log::info!("Current time: {} status={:?}", now.format("%d/%m/%Y %T"), self.ntp.get_sync_status()); + } + } + + fn stop(&mut self) { + log::info!("Stopping wifi"); + self.disconnect(); + } +} diff --git a/src/platform/mod.rs b/src/platform/mod.rs index e19442e..3f2a766 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -4,9 +4,19 @@ pub mod embedded_graphics_lib; #[cfg(feature="smart-leds")] pub mod smart_leds_lib; -use crate::render::{Surface, Output}; +pub mod esp32; -pub trait DisplayInit { +use esp_idf_svc::hal::prelude::Peripherals; + +use crate::render::{Output, Surfaces}; +use crate::task::Task; + +pub trait Board { type Output: Output; - fn new_display() -> Self::Output; -} + type Surfaces: Surfaces; + + fn new(peripherals: Peripherals) -> Self; + fn output(&mut self) -> Self::Output; + fn surfaces(&mut self) -> Self::Surfaces; + fn system_tasks(&mut self) -> Vec>; +} \ No newline at end of file diff --git a/src/platform/smart_leds_lib.rs b/src/platform/smart_leds_lib.rs index 62b8c41..c95817b 100644 --- a/src/platform/smart_leds_lib.rs +++ b/src/platform/smart_leds_lib.rs @@ -39,7 +39,7 @@ pub struct StrideOutput { } impl StrideOutput { - fn new(pixbuf: P, stride_map: StrideMapping, target: T, max_mw: u32) -> Self { + pub fn new(pixbuf: P, stride_map: StrideMapping, target: T, max_mw: u32) -> Self { assert!(stride_map.pixel_count <= pixbuf.pixel_count(), "map needs {} pixels, I only have PIXEL_NUM={}", stride_map.pixel_count, pixbuf.pixel_count()); StrideOutput { pixbuf, @@ -85,18 +85,12 @@ pub trait FastWrite { #[cfg(feature="rmt")] pub mod rmt { - use esp_idf_svc::{hal::prelude::Peripherals, sys::esp_efuse_mac_get_default}; use ws2812_esp32_rmt_driver::driver::color::LedPixelColorGrb24; use smart_leds::SmartLedsWrite; use rgb::Rgb; use ws2812_esp32_rmt_driver::LedPixelEsp32Rmt; - use crate::mappings::StrideMapping; - use crate::platform::smart_leds_lib::StrideOutput; - use crate::render::Surface; - use crate::platform::DisplayInit; - - use super::{Pixbuf, FastWrite}; + use super::FastWrite; pub type FastWs2812Esp32Rmt<'a> = LedPixelEsp32Rmt<'a, Rgb, LedPixelColorGrb24>; @@ -111,43 +105,6 @@ pub mod rmt { self.write_nocopy(iterator) } } - - impl DisplayInit for FastWs2812Esp32Rmt<'_> { - type Output = StrideOutput<[Rgb; 310], Self>; - - fn new_display() -> Self::Output { - const POWER_VOLTS : u32 = 5; - const POWER_MA : u32 = 500; - const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA; - let peripherals = Peripherals::take().unwrap(); - let channel = peripherals.rmt.channel0; - let pins = peripherals.pins; - - let mut chip_id: [u8; 8] = [0; 8]; - unsafe { - esp_efuse_mac_get_default(&mut chip_id as *mut u8); - } - - let (pixmap, target) = match chip_id { - [72, 202, 67, 89, 145, 204, 0, 0] => { - (StrideMapping::new_panel(), Self::new(channel, pins.gpio5).unwrap()) - }, - [140, 170, 181, 131, 95, 116, 0, 0] => { - (StrideMapping::new_jar(), Self::new(channel, pins.gpio14).unwrap()) - }, - _ => { - (StrideMapping::new(), Self::new(channel, pins.gpio5).unwrap()) - } - }; - - StrideOutput::new( - Pixbuf::new(), - pixmap, - target, - MAX_POWER_MW - ) - } - } } #[cfg(feature="spi")]