wip
This commit is contained in:
@@ -2,14 +2,20 @@ use core::borrow::BorrowMut;
|
||||
use std::sync::Arc;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Mutex;
|
||||
use std::thread::JoinHandle;
|
||||
use std::thread::ScopedJoinHandle;
|
||||
|
||||
use chrono::DateTime;
|
||||
use chrono::Timelike;
|
||||
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::hal::task::thread::ThreadSpawnConfiguration;
|
||||
use esp_idf_svc::mqtt::client::EspMqttClient;
|
||||
use esp_idf_svc::mqtt::client::EspMqttConnection;
|
||||
use esp_idf_svc::mqtt::client::MqttClientConfiguration;
|
||||
use esp_idf_svc::netif::IpEvent;
|
||||
use esp_idf_svc::nvs::{EspDefaultNvsPartition, EspNvsPartition, NvsDefault};
|
||||
use esp_idf_svc::sntp::EspSntp;
|
||||
@@ -23,6 +29,9 @@ use super::Board;
|
||||
|
||||
use crate::buffers::BufferedSurfacePool;
|
||||
use crate::buffers::Pixbuf;
|
||||
use crate::events::Event;
|
||||
use crate::events::EventBus;
|
||||
use crate::lib8::interpolate::lerp8by8;
|
||||
use crate::mappings::StrideMapping;
|
||||
use crate::task::FixedSizeScheduler;
|
||||
use crate::task::Task;
|
||||
@@ -55,6 +64,10 @@ pub mod i2s {
|
||||
}
|
||||
|
||||
impl<'d> Output for I2SOutput<'d> {
|
||||
fn on_event(&mut self, event: &crate::events::Event) {
|
||||
|
||||
}
|
||||
|
||||
fn blank(&mut self) {
|
||||
self.pixbuf.blank();
|
||||
}
|
||||
@@ -89,7 +102,7 @@ pub struct Esp32Board {
|
||||
impl Board for Esp32Board {
|
||||
type Output = StrideOutput<[Rgb<u8>; 310], FastWs2812Esp32Rmt<'static>>;
|
||||
type Surfaces = BufferedSurfacePool;
|
||||
type Scheduler = FixedSizeScheduler<2>;
|
||||
type Scheduler = FixedSizeScheduler<4>;
|
||||
|
||||
fn take() -> Self {
|
||||
// It is necessary to call this function once. Otherwise some patches to the runtime
|
||||
@@ -122,7 +135,7 @@ impl Board for Esp32Board {
|
||||
// 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 chip_id {
|
||||
let output = match chip_id { // panel test board
|
||||
[72, 202, 67, 89, 145, 204, 0, 0] => {
|
||||
StrideOutput::new(
|
||||
Pixbuf::new(),
|
||||
@@ -131,7 +144,7 @@ impl Board for Esp32Board {
|
||||
MAX_POWER_MW
|
||||
)
|
||||
},
|
||||
[0x8C, 0xAA, 0xB5, 0x83, 0x5f, 0x74, 0x0, 0x0] => {
|
||||
[0x8C, 0xAA, 0xB5, 0x83, 0x5f, 0x74, 0x0, 0x0] => { //ponderjar
|
||||
StrideOutput::new(
|
||||
Pixbuf::new(),
|
||||
StrideMapping::new_jar(),
|
||||
@@ -139,6 +152,30 @@ impl Board for Esp32Board {
|
||||
MAX_POWER_MW
|
||||
)
|
||||
},
|
||||
[0x4a, 0xca, 0x43, 0x59, 0x85, 0x58, 0x0, 0x0] => { // Albus the tree
|
||||
StrideOutput::new(
|
||||
Pixbuf::new(),
|
||||
StrideMapping::new_jar(),
|
||||
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(),
|
||||
MAX_POWER_MW
|
||||
)
|
||||
},
|
||||
[0x48, 0xca, 0x43, 0x59, 0x9d, 0x48, 0x0, 0x0] => { // kitchen cabinets
|
||||
StrideOutput::new(
|
||||
Pixbuf::new(),
|
||||
StrideMapping::new_fairylights(),
|
||||
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.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(peripherals.rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(),
|
||||
MAX_POWER_MW
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
StrideOutput::new(
|
||||
Pixbuf::new(),
|
||||
@@ -172,11 +209,164 @@ impl Board for Esp32Board {
|
||||
let nvs = EspDefaultNvsPartition::take().unwrap();
|
||||
FixedSizeScheduler::new([
|
||||
Box::new(WifiTask::new(self.modem.take().unwrap(), self.sys_loop.clone(), &nvs)),
|
||||
Box::new(CircadianRhythm::new()),
|
||||
Box::new(MqttTask::new()),
|
||||
Box::new(self.surfaces.clone())
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct ScheduleEntry {
|
||||
hour: u8,
|
||||
brightness: u8
|
||||
}
|
||||
|
||||
struct CircadianRhythm {
|
||||
time_check: Periodically,
|
||||
schedule: [ScheduleEntry;10]
|
||||
}
|
||||
|
||||
impl CircadianRhythm {
|
||||
fn new() -> Self {
|
||||
CircadianRhythm {
|
||||
time_check: Periodically::new_every_n_seconds(5),
|
||||
schedule: [
|
||||
ScheduleEntry { hour: 0, brightness: 0 },
|
||||
ScheduleEntry { hour: 5, brightness: 0 },
|
||||
ScheduleEntry { hour: 6, brightness: 10 },
|
||||
ScheduleEntry { hour: 7, brightness: 20 },
|
||||
ScheduleEntry { hour: 8, brightness: 80 },
|
||||
ScheduleEntry { hour: 11, brightness: 120 },
|
||||
ScheduleEntry { hour: 18, brightness: 200 },
|
||||
ScheduleEntry { hour: 19, brightness: 255 },
|
||||
ScheduleEntry { hour: 22, brightness: 120 },
|
||||
ScheduleEntry { hour: 23, brightness: 5 }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn brightness_for_time(&self, hour: u8, minute: u8) -> u8 {
|
||||
let mut start = self.schedule.last().unwrap();
|
||||
let mut end = self.schedule.first().unwrap();
|
||||
for cur in self.schedule.iter() {
|
||||
if (cur.hour <= hour ) {
|
||||
start = cur;
|
||||
} else {
|
||||
end = cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("hour={:?} minute={:?} start={:?} end={:?}", hour, minute, start, end);
|
||||
|
||||
let mut adjusted_end = end.clone();
|
||||
if start.hour > end.hour {
|
||||
adjusted_end.hour += 24;
|
||||
}
|
||||
|
||||
let start_time = start.hour * 60;
|
||||
let end_time = end.hour * 60;
|
||||
let now_time = hour * 60 + minute;
|
||||
|
||||
let duration = end_time - start_time;
|
||||
let cur_duration = now_time - start_time;
|
||||
|
||||
let frac = map_range(cur_duration.into(), 0, duration.into(), 0, 255) as u8;
|
||||
|
||||
lerp8by8(start.brightness, end.brightness, frac)
|
||||
}
|
||||
}
|
||||
|
||||
fn map_range(x: u16, in_min: u16, in_max: u16, out_min: u16, out_max: u16) -> u16 {
|
||||
let run = in_max - in_min;
|
||||
if run == 0 {
|
||||
return 0;
|
||||
}
|
||||
let rise = out_max - out_min;
|
||||
let delta = x - in_min;
|
||||
return (delta * rise) / run + out_min;
|
||||
}
|
||||
|
||||
|
||||
impl Task for CircadianRhythm {
|
||||
fn tick(&mut self, event: &Event, bus: &mut EventBus) {
|
||||
if self.time_check.tick() || event.eq(&Event::ReadyToRock) {
|
||||
let now: DateTime<Utc> = std::time::SystemTime::now().into();
|
||||
let next_brightness = self.brightness_for_time(now.hour() as u8, now.minute() as u8);
|
||||
bus.push(Event::new_property_change("output.brightness", next_brightness));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MqttTask {
|
||||
client: Option<EspMqttClient<'static>>,
|
||||
conn_thread: Option<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl MqttTask {
|
||||
fn new() -> Self {
|
||||
MqttTask {
|
||||
conn_thread: None,
|
||||
client: None
|
||||
}
|
||||
}
|
||||
|
||||
fn start_mqtt(&mut self) {
|
||||
log::info!("Starting MQTT");
|
||||
let (client, mut conn) = EspMqttClient::new(
|
||||
"mqtt://10.0.0.2:1883",
|
||||
&MqttClientConfiguration {
|
||||
client_id: Some("renderbug-rs"),
|
||||
..Default::default()
|
||||
}
|
||||
).unwrap();
|
||||
log::info!("Connected!");
|
||||
|
||||
self.conn_thread = Some(std::thread::Builder::new()
|
||||
.stack_size(6000)
|
||||
.spawn(move || {
|
||||
conn.next().unwrap();
|
||||
}).unwrap());
|
||||
self.client = Some(client);
|
||||
}
|
||||
}
|
||||
|
||||
impl Task for MqttTask {
|
||||
fn tick(&mut self, event: &Event, bus: &mut EventBus) {
|
||||
match event {
|
||||
Event::Input(crate::events::InputEvent::NetworkOnline) => {
|
||||
log::info!("Registering with MQTT");
|
||||
|
||||
self.start_mqtt();
|
||||
|
||||
if let Some(ref mut client) = self.client {
|
||||
client.enqueue(
|
||||
"homeassistant-test/renderbug/rust",
|
||||
esp_idf_svc::mqtt::client::QoS::AtLeastOnce,
|
||||
false,
|
||||
"hello, world".as_bytes()
|
||||
).unwrap();
|
||||
log::info!("MQTT should be online!");
|
||||
}
|
||||
},
|
||||
Event::PropertyChange(name, value) => {
|
||||
if let Some(ref mut client) = self.client {
|
||||
let payload = format!("name={} value={:?}", name, value);
|
||||
client.enqueue(
|
||||
"homeassistant-test/renderbug/rust/property-change",
|
||||
esp_idf_svc::mqtt::client::QoS::AtLeastOnce,
|
||||
false,
|
||||
payload.as_bytes()
|
||||
).unwrap();
|
||||
log::info!("property change bump: {}", payload);
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for WifiTask {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("WifiTask").finish()
|
||||
@@ -283,7 +473,7 @@ impl Task for WifiTask {
|
||||
self.connect();
|
||||
}
|
||||
|
||||
fn tick(&mut self ) {
|
||||
fn tick(&mut self, event: &Event, bus: &mut EventBus) {
|
||||
if self.connection_check.tick() {
|
||||
let cur_state = *self.state.lock().unwrap();
|
||||
|
||||
@@ -298,10 +488,12 @@ impl Task for WifiTask {
|
||||
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());
|
||||
match cur_state {
|
||||
WifiState::Connected => bus.push(Event::new_input_event(crate::events::InputEvent::NetworkOnline)),
|
||||
_ => bus.push(Event::new_input_event(crate::events::InputEvent::NetworkOffline))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
44
src/platform/esp32_nostd.rs
Normal file
44
src/platform/esp32_nostd.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use rgb::Rgb;
|
||||
use super::smart_leds_lib::rmt::FastWs2812Esp32Rmt;
|
||||
use super::smart_leds_lib::StrideOutput;
|
||||
|
||||
use crate::task::{FixedSizeScheduler, Task};
|
||||
use crate::buffers::StaticSurfacePool;
|
||||
|
||||
use super::Board;
|
||||
|
||||
pub struct Esp32Board<'a> {
|
||||
output: Option<<Self as Board>::Output>,
|
||||
surfaces: Option<StaticSurfacePool>,
|
||||
tasks: Option<[&'a mut dyn Task; 1]>
|
||||
}
|
||||
|
||||
impl<'a> Board for Esp32Board<'a> {
|
||||
type Output = StrideOutput<[Rgb<u8>; 310], FastWs2812Esp32Rmt<'a>>;
|
||||
type Surfaces = StaticSurfacePool;
|
||||
type Scheduler = FixedSizeScheduler<0>;
|
||||
fn take() -> Self {
|
||||
let peripherals = esp_hal::init(esp_hal::Config::default());
|
||||
//esp_alloc::heap_allocator!(72 * 1024);
|
||||
|
||||
const POWER_VOLTS : u32 = 5;
|
||||
const POWER_MA : u32 = 500;
|
||||
const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA;
|
||||
let pins = peripherals.pins;
|
||||
|
||||
|
||||
Esp32Board { output: None, surfaces: None, tasks: None }
|
||||
}
|
||||
|
||||
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) -> Self::Scheduler {
|
||||
FixedSizeScheduler::new([])
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
use smart_leds_trait::SmartLedsWrite;
|
||||
|
||||
use crate::buffers::Pixbuf;
|
||||
use crate::events::Variant;
|
||||
use crate::render::{HardwarePixel, Output, PixelView, Sample};
|
||||
use crate::power::brightness_for_mw;
|
||||
use crate::geometry::*;
|
||||
@@ -18,7 +19,8 @@ pub struct StrideOutput<P: Pixbuf, T: FastWrite> {
|
||||
pixbuf: P,
|
||||
stride_map: StrideMapping,
|
||||
target: T,
|
||||
max_mw: u32
|
||||
max_mw: u32,
|
||||
brightness: u8
|
||||
}
|
||||
|
||||
impl<P: Pixbuf, T: FastWrite> StrideOutput<P, T> {
|
||||
@@ -28,7 +30,8 @@ impl<P: Pixbuf, T: FastWrite> StrideOutput<P, T> {
|
||||
pixbuf,
|
||||
stride_map,
|
||||
target,
|
||||
max_mw
|
||||
max_mw,
|
||||
brightness: 255
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,11 +49,18 @@ impl<P: Pixbuf<Pixel=T::Color>, T: FastWrite> Output for StrideOutput<P, T> {
|
||||
}
|
||||
|
||||
fn commit(&mut self) {
|
||||
let b = brightness_for_mw(self.pixbuf.as_milliwatts(), 255, self.max_mw);
|
||||
let b = brightness_for_mw(self.pixbuf.as_milliwatts(), self.brightness, self.max_mw);
|
||||
if self.target.fast_write(self.pixbuf.iter_with_brightness(b)).is_err() {
|
||||
panic!("Could not write frame!");
|
||||
};
|
||||
}
|
||||
|
||||
fn on_event(&mut self, event: &crate::events::Event) {
|
||||
match event {
|
||||
crate::events::Event::PropertyChange("output.brightness", new_brightness) => self.brightness = new_brightness.clone().into(),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FastWrite: Send {
|
||||
|
||||
Reference in New Issue
Block a user