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"] }
|
embedded-graphics = { version = "0.8.1", optional = true, features = ["fixed_point", "defmt"] }
|
||||||
ansi_term = "0.12.1"
|
ansi_term = "0.12.1"
|
||||||
num = "0.4.3"
|
num = "0.4.3"
|
||||||
|
chrono = "0.4.38"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
embuild = "0.32.0"
|
embuild = "0.32.0"
|
||||||
|
52
src/main.rs
52
src/main.rs
@ -9,31 +9,14 @@ mod animations;
|
|||||||
mod mappings;
|
mod mappings;
|
||||||
mod buffers;
|
mod buffers;
|
||||||
|
|
||||||
use buffers::SurfacePool;
|
use esp_idf_svc::hal::prelude::Peripherals;
|
||||||
|
|
||||||
use crate::platform::DisplayInit;
|
use crate::platform::Board;
|
||||||
use crate::render::Surfaces;
|
use crate::task::{Task, Scheduler};
|
||||||
|
use crate::render::{Surfaces, Renderer};
|
||||||
use crate::geometry::Rectangle;
|
use crate::geometry::Rectangle;
|
||||||
|
|
||||||
#[cfg(feature="embedded-graphics")]
|
use crate::platform::esp32::Esp32Board as BoardType;
|
||||||
#[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;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// It is necessary to call this function once. Otherwise some patches to the runtime
|
// 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
|
// Bind the log crate to the ESP Logging facilities
|
||||||
esp_idf_svc::log::EspLogger::initialize_default();
|
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 animations");
|
||||||
|
let mut tasks: Vec<Box<dyn Task + 'static>> = vec![
|
||||||
log::info!("Creating runner");
|
|
||||||
let mut runner = task::Scheduler::new(vec![
|
|
||||||
Box::new(animations::IdleTask::new(&mut surfaces)),
|
Box::new(animations::IdleTask::new(&mut surfaces)),
|
||||||
Box::new(animations::TestPattern::new(surfaces.new_surface(&Rectangle::everything()).unwrap())),
|
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);
|
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")]
|
#[cfg(feature="smart-leds")]
|
||||||
pub mod smart_leds_lib;
|
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;
|
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> {
|
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());
|
assert!(stride_map.pixel_count <= pixbuf.pixel_count(), "map needs {} pixels, I only have PIXEL_NUM={}", stride_map.pixel_count, pixbuf.pixel_count());
|
||||||
StrideOutput {
|
StrideOutput {
|
||||||
pixbuf,
|
pixbuf,
|
||||||
@ -85,18 +85,12 @@ pub trait FastWrite {
|
|||||||
|
|
||||||
#[cfg(feature="rmt")]
|
#[cfg(feature="rmt")]
|
||||||
pub mod 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 ws2812_esp32_rmt_driver::driver::color::LedPixelColorGrb24;
|
||||||
use smart_leds::SmartLedsWrite;
|
use smart_leds::SmartLedsWrite;
|
||||||
use rgb::Rgb;
|
use rgb::Rgb;
|
||||||
use ws2812_esp32_rmt_driver::LedPixelEsp32Rmt;
|
use ws2812_esp32_rmt_driver::LedPixelEsp32Rmt;
|
||||||
|
|
||||||
use crate::mappings::StrideMapping;
|
use super::FastWrite;
|
||||||
use crate::platform::smart_leds_lib::StrideOutput;
|
|
||||||
use crate::render::Surface;
|
|
||||||
use crate::platform::DisplayInit;
|
|
||||||
|
|
||||||
use super::{Pixbuf, FastWrite};
|
|
||||||
|
|
||||||
pub type FastWs2812Esp32Rmt<'a> = LedPixelEsp32Rmt<'a, Rgb<u8>, LedPixelColorGrb24>;
|
pub type FastWs2812Esp32Rmt<'a> = LedPixelEsp32Rmt<'a, Rgb<u8>, LedPixelColorGrb24>;
|
||||||
|
|
||||||
@ -111,43 +105,6 @@ pub mod rmt {
|
|||||||
self.write_nocopy(iterator)
|
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")]
|
#[cfg(feature="spi")]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user