platform: rewrite the platform-specific display creation traits into a new Board trait that also supports tasks
This commit is contained in:
parent
57e660cbb6
commit
3af9ad408e
@ -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"
|
||||
|
52
src/main.rs
52
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::<SurfaceType>();
|
||||
let output = board.output();
|
||||
log::info!("Output: {}", core::any::type_name_of_val(&output));
|
||||
|
||||
let mut surfaces: SurfacePool<SurfaceType> = 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<Box<dyn Task + 'static>> = 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);
|
||||
|
||||
|
238
src/platform/esp32.rs
Normal file
238
src/platform/esp32.rs
Normal file
@ -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<<Self as Board>::Output>,
|
||||
surfaces: Option<SurfacePool<SurfaceType>>,
|
||||
tasks: Option<Vec<Box<dyn Task>>>
|
||||
}
|
||||
|
||||
impl<'a> Board for Esp32Board<'a> {
|
||||
type Output = StrideOutput<[Rgb<u8>; 310], FastWs2812Esp32Rmt<'a>>;
|
||||
type Surfaces = SurfacePool<SurfaceType>;
|
||||
|
||||
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<Box<dyn Task>> = 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<Box<dyn Task>> {
|
||||
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<Mutex<WifiState>>,
|
||||
last_state: WifiState,
|
||||
sys_loop: EspSystemEventLoop,
|
||||
wifi_sub: Option<EspSubscription<'static, System>>,
|
||||
ip_sub: Option<EspSubscription<'static, System>>
|
||||
}
|
||||
|
||||
impl WifiTask {
|
||||
fn new(modem: Modem, sys_loop: EspSystemEventLoop, nvs: &EspNvsPartition<NvsDefault>) -> 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::<WifiEvent, _>( 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::<IpEvent, _>(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<Utc> = 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();
|
||||
}
|
||||
}
|
@ -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<S: Surface>() -> 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<Box<dyn Task>>;
|
||||
}
|
@ -39,7 +39,7 @@ pub struct StrideOutput<P: Pixbuf, T: FastWrite> {
|
||||
}
|
||||
|
||||
impl<P: Pixbuf, T: FastWrite> StrideOutput<P, T> {
|
||||
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<u8>, LedPixelColorGrb24>;
|
||||
|
||||
@ -111,43 +105,6 @@ pub mod rmt {
|
||||
self.write_nocopy(iterator)
|
||||
}
|
||||
}
|
||||
|
||||
impl DisplayInit for FastWs2812Esp32Rmt<'_> {
|
||||
type Output = StrideOutput<[Rgb<u8>; 310], Self>;
|
||||
|
||||
fn new_display<S: Surface>() -> 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")]
|
||||
|
Loading…
x
Reference in New Issue
Block a user