2024-12-15 19:27:27 +01:00

143 lines
5.7 KiB
Rust

use esp_idf_svc::{eventloop::EspSystemEventLoop, hal::{gpio::Pins, modem::Modem, prelude::Peripherals, rmt::RMT, task::thread::ThreadSpawnConfiguration}, nvs::EspDefaultNvsPartition, sys::esp_efuse_mac_get_default};
use rgb::Rgb;
use crate::{buffers::{BufferedSurfacePool, Pixbuf}, mappings::StrideMapping, platform::{smart_leds_lib::{rmt::FastWs2812Esp32Rmt, StrideOutput}, Board}, task::FixedSizeScheduler};
use super::{mqtt::MqttTask, wifi::WifiTask};
pub struct Esp32Board {
sys_loop: EspSystemEventLoop,
modem: Option<Modem>,
pins: Option<Pins>,
rmt: Option<RMT>,
surfaces: Option<BufferedSurfacePool>,
}
impl Board for Esp32Board {
type Output = StrideOutput<[Rgb<u8>; 310], FastWs2812Esp32Rmt<'static>>;
type Surfaces = BufferedSurfacePool;
type Scheduler = FixedSizeScheduler<2>;
fn chip_id() -> u64 {
let mut chip_id: [u8; 8] = [0; 8];
unsafe {
esp_efuse_mac_get_default(&mut chip_id as *mut u8);
}
return u64::from_be_bytes(chip_id);
}
fn take() -> Self {
// It is necessary to call this function once. Otherwise some patches to the runtime
// implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
esp_idf_svc::sys::link_patches();
// Bind the log crate to the ESP Logging facilities
esp_idf_svc::log::EspLogger::initialize_default();
let peripherals = Peripherals::take().unwrap();
let sys_loop = EspSystemEventLoop::take().unwrap();
Esp32Board {
modem: Some(peripherals.modem),
sys_loop: sys_loop,
surfaces: Some(BufferedSurfacePool::new()),
pins: Some(peripherals.pins),
rmt: Some(peripherals.rmt)
}
}
fn output(&mut self) -> Self::Output {
log::info!("Setting up output for chip ID {:x?}", Self::chip_id());
const POWER_VOLTS : u32 = 5;
const POWER_MA : u32 = 500;
const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA;
let pins = self.pins.take().unwrap();
let rmt = self.rmt.take().unwrap();
ThreadSpawnConfiguration {
pin_to_core: Some(esp_idf_svc::hal::cpu::Core::Core1),
..Default::default()
}.set().unwrap();
// Wifi driver creates too many interrupts on core0, so we need to use RMT on core1.
// But the implementation spawns a thread based on the core the driver was created in,
// so we create the driver in another thread briefly.
// Fun stuff.
let output = match Self::chip_id().to_be_bytes() { // panel test board
[72, 202, 67, 89, 145, 204, 0, 0] => {
StrideOutput::new(
Pixbuf::new(),
StrideMapping::new_panel(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(),
MAX_POWER_MW
)
},
[0x8C, 0xAA, 0xB5, 0x83, 0x5f, 0x74, 0x0, 0x0] => { //ponderjar
StrideOutput::new(
Pixbuf::new(),
StrideMapping::new_jar(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio14).unwrap() }).join().unwrap(),
MAX_POWER_MW
)
},
[0x4a, 0xca, 0x43, 0x59, 0x85, 0x58, 0x0, 0x0] => { // Albus the tree
StrideOutput::new(
Pixbuf::new(),
StrideMapping::new_albus(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(),
POWER_VOLTS * 2_400
)
},
[0x48, 0xca, 0x43, 0x59, 0x9d, 0x48, 0x0, 0x0] => { // kitchen cabinets
StrideOutput::new(
Pixbuf::new(),
StrideMapping::new_fairylights(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(),
MAX_POWER_MW
)
},
[0x48, 0xca, 0x43, 0x59, 0x9e, 0xdc, 0x0, 0x0] => { // front window
StrideOutput::new(
Pixbuf::new(),
StrideMapping::new_fairylights(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(),
MAX_POWER_MW
)
},
[0xfc, 0xf5, 0xc4, 0x05, 0xb8, 0x30, 0x0, 0x0] => { // cyberplague
StrideOutput::new(
Pixbuf::new(),
StrideMapping::new_cyberplague(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio13).unwrap() }).join().unwrap(),
MAX_POWER_MW
)
},
_ => {
StrideOutput::new(
Pixbuf::new(),
StrideMapping::new(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(),
MAX_POWER_MW
)
}
};
ThreadSpawnConfiguration {
..Default::default()
}.set().unwrap();
output
}
fn surfaces(&mut self) -> Self::Surfaces {
self.surfaces.take().unwrap()
}
fn system_tasks(&mut self) -> Self::Scheduler {
let nvs = EspDefaultNvsPartition::take().unwrap();
FixedSizeScheduler::new([
Box::new(WifiTask::new(self.modem.take().unwrap(), self.sys_loop.clone(), &nvs)),
Box::new(MqttTask::new())
])
}
}