Compare commits

..

No commits in common. "63c913a8e6ad4699641ad738ad71406d8cf68181" and "e57ceeb149eb7c6f9bb6dda7a92c4e91b60d51f4" have entirely different histories.

16 changed files with 467 additions and 570 deletions

View File

@ -1 +0,0 @@
partition_table = "partitions.csv"

View File

@ -1,5 +0,0 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, , 0x6000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 3M,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, , 0x6000,
4 phy_init, data, phy, , 0x1000,
5 factory, app, factory, , 3M,

View File

@ -58,17 +58,15 @@ impl Shader for ThinkingShader {
impl<T: Surface> IdleTask<T> { impl<T: Surface> IdleTask<T> {
pub fn new<S: Surfaces<Surface = T>>(surfaces: &mut S) -> Self { pub fn new<S: Surfaces<Surface = T>>(surfaces: &mut S) -> Self {
IdleTask { IdleTask {
solid: surfaces.new_surface(Rectangle::everything()).unwrap(), solid: surfaces.new_surface(&Rectangle::everything()).unwrap(),
surface: surfaces.new_surface(Rectangle::everything()).unwrap(), surface: surfaces.new_surface(&Rectangle::everything()).unwrap(),
shimmer: surfaces.new_surface(Rectangle::everything()).unwrap(), shimmer: surfaces.new_surface(&Rectangle::everything()).unwrap(),
} }
} }
} }
impl<T: Surface> Task for IdleTask<T> { impl<T: Surface> Task for IdleTask<T> {
fn name(&self) -> &'static str { "Idle" } fn start(&mut self) {
fn start(&mut self, _bus: &mut EventBus) {
self.solid.set_shader(Box::new(SolidShader { })); self.solid.set_shader(Box::new(SolidShader { }));
self.surface.set_shader(Box::new(ThinkingShader { })); self.surface.set_shader(Box::new(ThinkingShader { }));
self.shimmer.set_shader(Box::new(ShimmerShader { })); self.shimmer.set_shader(Box::new(ShimmerShader { }));
@ -78,7 +76,9 @@ impl<T: Surface> Task for IdleTask<T> {
self.shimmer.set_opacity(64); self.shimmer.set_opacity(64);
} }
fn stop(&mut self, _bus: &mut EventBus) { fn tick(&mut self, event: &Event, bus: &mut EventBus) {}
fn stop(&mut self) {
self.solid.clear_shader(); self.solid.clear_shader();
self.surface.clear_shader(); self.surface.clear_shader();
self.shimmer.clear_shader(); self.shimmer.clear_shader();
@ -169,24 +169,22 @@ impl<T: Surface> TestPattern<T> {
} }
impl<T: Surface> Task for TestPattern<T> { impl<T: Surface> Task for TestPattern<T> {
fn name(&self) -> &'static str { "TestPattern" } fn start(&mut self) {
self.surface.set_shader(Box::new(self.pattern.clone()));
fn start(&mut self, _bus: &mut EventBus) {
self.surface.set_shader(Box::new(self.pattern));
} }
fn on_tick(&mut self, bus: &mut EventBus) { fn tick(&mut self, event: &Event, bus: &mut EventBus) {
self.updater.run(|| { self.updater.run(|| {
self.pattern = self.pattern.next(); self.pattern = self.pattern.next();
log::info!("Test pattern: {:?}", self.pattern); log::info!("Test pattern: {:?}", self.pattern);
self.frame = 0; self.frame = 0;
self.surface.set_shader(Box::new(self.pattern)); self.surface.set_shader(Box::new(self.pattern.clone()));
bus.push(Event::new_property_change("animations.test.pattern", format!("{:?}", self.pattern)));
}); });
self.stepper.run(|| { self.stepper.run(|| {
self.frame = self.frame.wrapping_add(1); self.frame = self.frame.wrapping_add(1);
self.surface.set_opacity(sin8(self.frame)); self.surface.set_opacity(sin8(self.frame));
self.surface.set_rect( match self.pattern { //log::info!("Step {}", self.frame);
self.surface.set_rect( &match self.pattern {
TestShader::SweepX => Rectangle::new( TestShader::SweepX => Rectangle::new(
Coordinates::new(self.frame, 0), Coordinates::new(self.frame, 0),
Coordinates::new(self.frame, 255) Coordinates::new(self.frame, 255)
@ -200,7 +198,7 @@ impl<T: Surface> Task for TestPattern<T> {
}); });
} }
fn stop(&mut self, _bus: &mut EventBus) { fn stop(&mut self) {
self.surface.clear_shader(); self.surface.clear_shader();
} }
} }

View File

@ -1,4 +1,4 @@
use crate::events::EventBus; use crate::events::{Event, EventBus};
use crate::geometry::*; use crate::geometry::*;
use crate::lib8::interpolate::Fract8Ops; use crate::lib8::interpolate::Fract8Ops;
use crate::power::AsMilliwatts; use crate::power::AsMilliwatts;
@ -6,6 +6,7 @@ use crate::render::{PixelView, Sample, Shader, Surface, Surfaces, HardwarePixel}
use crate::task::Task; use crate::task::Task;
use std::fmt::Debug; use std::fmt::Debug;
use std::io;
use std::ops::IndexMut; use std::ops::IndexMut;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
@ -81,9 +82,9 @@ impl Surface for BufferedSurface {
}); });
} }
fn set_rect(&mut self, rect: Rectangle<Virtual>) { fn set_rect(&mut self, rect: &Rectangle<Virtual>) {
self.updater.push(SurfaceUpdate { self.updater.push(SurfaceUpdate {
rect: Some(rect), rect: Some(rect.clone()),
slot: self.slot, slot: self.slot,
..Default::default() ..Default::default()
}); });
@ -178,12 +179,12 @@ impl Surfaces for ShaderChain {
type Error = (); type Error = ();
type Surface = BufferedSurface; type Surface = BufferedSurface;
fn new_surface(&mut self, area: Rectangle<Virtual>) -> Result<Self::Surface, Self::Error> { fn new_surface(&mut self, area: &Rectangle<Virtual>) -> Result<Self::Surface, Self::Error> {
let next_slot = self.bindings.len(); let next_slot = self.bindings.len();
self.bindings.push(ShaderBinding { self.bindings.push(ShaderBinding {
opacity: 255, opacity: 255,
shader: None, shader: None,
rect: area rect: area.clone()
}); });
Ok(BufferedSurface { Ok(BufferedSurface {
@ -224,7 +225,7 @@ impl BufferedSurfacePool {
impl Surfaces for BufferedSurfacePool { impl Surfaces for BufferedSurfacePool {
type Error = (); type Error = ();
type Surface = <ShaderChain as Surfaces>::Surface; type Surface = <ShaderChain as Surfaces>::Surface;
fn new_surface(&mut self, area: crate::geometry::Rectangle<crate::geometry::Virtual>) -> Result<Self::Surface, Self::Error> { fn new_surface(&mut self, area: &crate::geometry::Rectangle<crate::geometry::Virtual>) -> Result<Self::Surface, Self::Error> {
self.pool.write().unwrap().new_surface(area) self.pool.write().unwrap().new_surface(area)
} }
@ -235,13 +236,91 @@ impl Surfaces for BufferedSurfacePool {
impl Task for BufferedSurfacePool { impl Task for BufferedSurfacePool {
fn on_tick(&mut self, _bus: &mut EventBus) { fn tick(&mut self, event: &Event, bus: &mut EventBus) {
if self.pool.read().unwrap().is_dirty() { if self.pool.read().unwrap().is_dirty() {
self.pool.write().unwrap().commit(); self.pool.write().unwrap().commit();
} }
} }
} }
#[derive(Clone)]
pub struct SharedSurface {
binding: Arc<Mutex<ShaderBinding>>
}
impl Default for SharedSurface {
fn default() -> Self {
Self {
binding: Arc::new(Mutex::new(ShaderBinding {
shader: None,
rect: Rectangle::everything(),
opacity: 255
})),
}
}
}
impl Surface for SharedSurface {
fn set_shader(&mut self, shader: Box<dyn Shader>) {
self.binding.lock().unwrap().shader = Some(shader);
}
fn clear_shader(&mut self) {
self.binding.lock().unwrap().shader = None;
}
fn set_rect(&mut self, rect: &Rectangle<Virtual>) {
self.binding.lock().unwrap().rect = rect.clone();
}
fn set_opacity(&mut self, opacity: u8) {
self.binding.lock().unwrap().opacity = opacity
}
}
#[derive(Clone)]
pub struct SurfacePool {
surfaces: Vec<SharedSurface>
}
impl SurfacePool {
pub const fn new() -> Self {
Self {
surfaces: Vec::new()
}
}
}
impl Surfaces for SurfacePool {
type Surface = SharedSurface;
type Error = io::Error;
fn new_surface(&mut self, area: &Rectangle<Virtual>) -> Result<Self::Surface, Self::Error> {
let mut surface = SharedSurface::default();
surface.set_rect(area);
self.surfaces.push(surface.clone());
return Ok(surface);
}
fn render_to<Sampler: Sample>(&self, output: &mut Sampler, frame: usize) {
for surface in self.surfaces.iter() {
let binding = surface.binding.lock().unwrap();
let opacity = binding.opacity;
if opacity > 0 {
let rect = binding.rect;
let mut sample = output.sample(&rect);
if let Some(ref shader) = binding.shader {
while let Some((virt_coords, pixel)) = sample.next() {
*pixel = pixel.blend8(shader.draw(&virt_coords, frame).into(), opacity);
}
}
}
}
}
}
pub trait Pixbuf: AsMilliwatts + IndexMut<usize, Output=Self::Pixel> + Send { pub trait Pixbuf: AsMilliwatts + IndexMut<usize, Output=Self::Pixel> + Send {
type Pixel: HardwarePixel; type Pixel: HardwarePixel;
fn new() -> Self; fn new() -> Self;

View File

@ -1,83 +1,100 @@
use core::fmt::{Debug, Display}; use core::fmt::Debug;
use std::{collections::{LinkedList, VecDeque}, sync::{Arc, Mutex}};
use rgb::Rgb; #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InputEvent {
PowerOn,
PowerOff,
NetworkActivity,
NetworkOnline,
NetworkOffline
}
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Variant { pub enum Variant {
SignedByte(i8),
Byte(u8), Byte(u8),
UInt(u32), UInt(u32),
Int(i32), Int(i32),
BigUInt(u64), BigUInt(u64),
BigInt(i64), BigInt(i64),
Boolean(bool), Boolean(bool)
String(String),
RGB(Rgb<u8>)
} }
macro_rules! impl_variant_type { #[derive(Debug, Clone, Copy, PartialEq, Eq)]
($type:ty, $var_type:tt) => {
impl From<$type> for Variant {
fn from(value: $type) -> Self {
Variant::$var_type(value)
}
}
impl Into<$type> for Variant {
fn into(self) -> $type {
match self {
Variant::$var_type(value) => value,
_ => panic!("Expected Variant::$var_type, but got {:?}", self)
}
}
}
};
}
impl_variant_type!(u8, Byte);
impl_variant_type!(i8, SignedByte);
impl_variant_type!(u32, UInt);
impl_variant_type!(i32, Int);
impl_variant_type!(i64, BigInt);
impl_variant_type!(u64, BigUInt);
impl_variant_type!(bool, Boolean);
impl_variant_type!(String, String);
impl_variant_type!(Rgb<u8>, RGB);
impl<'a> From<&'a str> for Variant {
fn from(value: &'a str) -> Self {
Variant::String(value.to_owned())
}
}
impl Display for Variant {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Variant::BigInt(i) => (i as &dyn Display).fmt(f),
Variant::BigUInt(i) => (i as &dyn Display).fmt(f),
Variant::Boolean(i) => (i as &dyn Display).fmt(f),
Variant::Byte(i) => (i as &dyn Display).fmt(f),
Variant::Int(i) => (i as &dyn Display).fmt(f),
Variant::String(i) => (i as &dyn Display).fmt(f),
Variant::UInt(i) => (i as &dyn Display).fmt(f),
Variant::SignedByte(b) => (b as &dyn Display).fmt(f),
Variant::RGB(rgb) => f.write_fmt(format_args!("[{}, {}, {}]", rgb.r, rgb.g, rgb.b)),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Event { pub enum Event {
ReadyToRock, ReadyToRock,
Tick, Tick,
StartThing(&'static str), StartThing(&'static str),
StopThing(&'static str), StopThing(&'static str),
Input(InputEvent),
PropertyChange(&'static str, Variant) PropertyChange(&'static str, Variant)
} }
pub struct SystemState {
key: &'static str,
value: Variant,
values: Vec::<Box<SystemState>>
}
impl SystemState {
pub fn new() -> Self {
SystemState {
key: "",
value: Variant::Byte(0),
values: Vec::new()
}
}
fn get_key(&self, key: &'static str) -> Option<&Self> {
if key == self.key {
Some(self)
} else {
for next in self.values.iter() {
match next.get_key(key) {
None => (),
Some(next_val) => return Some(next_val)
}
}
return None
}
}
fn get_key_mut(&mut self, key: &'static str) -> Option<&mut Self> {
if key == self.key {
Some(self)
} else {
for next in self.values.iter_mut() {
match next.get_key_mut(key) {
None => (),
Some(next_val) => return Some(next_val)
}
}
return None
}
}
pub fn get(&self, key: &'static str) -> Option<Variant> {
match self.get_key(key) {
None => None,
Some(v) => Some(v.value)
}
}
pub fn set<V>(&mut self, key: &'static str, value: V) where Variant: From<V> {
match self.get_key_mut(key) {
None => self.values.push(Box::new(SystemState {
value: value.into(),
key: key,
values: Vec::new()
})),
Some(found_key) => {
found_key.value = value.into()
}
}
}
}
impl Event { impl Event {
pub const fn new_tick() -> Self { pub fn new_tick() -> Self {
Event::Tick Event::Tick
} }
@ -85,127 +102,62 @@ impl Event {
Event::PropertyChange(key, Variant::from(data)) Event::PropertyChange(key, Variant::from(data))
} }
pub const fn new_ready_to_rock() -> Self { pub fn new_ready_to_rock() -> Self {
Event::ReadyToRock Event::ReadyToRock
} }
pub const fn new_start_thing(name: &'static str) -> Self { pub fn new_input_event(event: InputEvent) -> Self {
Event::StartThing(name) Event::Input(event)
}
pub const fn new_stop_thing(name: &'static str) -> Self {
Event::StopThing(name)
} }
} }
#[derive(Debug, Clone, PartialEq, Eq)] impl Into<u8> for Variant {
pub struct Property { fn into(self) -> u8 {
key: &'static str, match self {
value: Variant Variant::Byte(b) => b,
} _ => 0
impl Property {
pub const fn new(key: &'static str, value: Variant) -> Self {
Property {
key,
value: value
} }
} }
} }
#[derive(Debug, Clone)] impl From<bool> for Variant {
pub struct Properties { fn from(value: bool) -> Self {
contents: LinkedList<Property> Variant::Boolean(value)
}
impl Properties {
fn new() -> Self {
Properties {
contents: LinkedList::new()
} }
} }
fn get_key(&self, key: &'static str) -> Option<&Property> { impl From<i64> for Variant {
for next in self.contents.iter() { fn from(value: i64) -> Self {
if next.key == key { Variant::BigInt(value)
return Some(next);
} }
} }
return None; impl From<u8> for Variant {
} fn from(value: u8) -> Self {
Variant::Byte(value)
fn get_key_mut(&mut self, key: &'static str) -> Option<&mut Property> {
for next in self.contents.iter_mut() {
if next.key == key {
return Some(next);
} }
} }
return None;
}
pub fn get(&self, key: &'static str) -> Option<Variant> {
match self.get_key(key) {
None => None,
Some(v) => Some(v.value.clone())
}
}
pub fn set<V>(&mut self, key: &'static str, value: V) -> bool where Variant: From<V> {
let as_variant: Variant = value.into();
match self.get_key_mut(key) {
None => {
self.contents.push_front(Property::new(key, as_variant));
return true
},
Some(found_key) => {
if found_key.value != as_variant {
found_key.value = as_variant;
return true
}
}
}
return false
}
}
#[derive(Clone)]
pub struct EventBus { pub struct EventBus {
pending: Arc<Mutex<VecDeque<Event>>>, pending: Vec<Event>
props: Arc<Mutex<Properties>>
} }
impl EventBus { impl EventBus {
pub fn new() -> Self { pub fn new() -> Self {
EventBus { EventBus {
pending: Arc::new(Mutex::new(VecDeque::with_capacity(32))), pending: Vec::new()
props: Arc::new(Mutex::new(Properties::new()))
} }
} }
pub fn next(&mut self) -> Event { pub fn next(&mut self) -> Event {
let next = self.pending.lock().unwrap().pop_front().unwrap_or(Event::new_tick()); if self.pending.len() == 0 {
match next {
Event::PropertyChange(key, value) => {
if self.props.lock().unwrap().set(key, value.clone()) {
log::trace!("prop-update key={} value={:?}", key, value);
Event::PropertyChange(key, value)
} else {
Event::new_tick() Event::new_tick()
} } else {
}, self.pending.pop().unwrap()
_ => next
} }
} }
pub fn push(&mut self, event: Event) { pub fn push(&mut self, event: Event) {
self.pending.lock().unwrap().push_back(event); self.pending.push(event);
}
pub fn property(&self, key: &'static str) -> Option<Variant> {
self.props.lock().unwrap().get(key)
} }
} }

View File

@ -64,19 +64,19 @@ impl<S: CoordinateSpace> Coordinates<S> {
} }
} }
const fn top_left() -> Self { fn top_left() -> Self {
Self::new(S::Data::MIN, S::Data::MIN) Self::new(S::Data::MIN, S::Data::MIN)
} }
const fn top_right() -> Self { fn top_right() -> Self {
Self::new(S::Data::MAX, S::Data::MIN) Self::new(S::Data::MAX, S::Data::MIN)
} }
const fn bottom_left() -> Self { fn bottom_left() -> Self {
Self::new(S::Data::MIN, S::Data::MAX) Self::new(S::Data::MIN, S::Data::MAX)
} }
const fn bottom_right() -> Self { fn bottom_right() -> Self {
Self::new(S::Data::MAX, S::Data::MAX) Self::new(S::Data::MAX, S::Data::MAX)
} }
@ -101,13 +101,15 @@ pub struct Rectangle<Space: CoordinateSpace> {
impl<Space: CoordinateSpace> Rectangle<Space> { impl<Space: CoordinateSpace> Rectangle<Space> {
pub const fn new(top_left: Coordinates<Space>, bottom_right: Coordinates<Space>) -> Self { pub const fn new(top_left: Coordinates<Space>, bottom_right: Coordinates<Space>) -> Self {
//debug_assert!(top_left.x <= bottom_right.x);
//debug_assert!(top_left.y <= bottom_right.y);
Self { Self {
top_left, top_left,
bottom_right bottom_right
} }
} }
pub const fn everything() -> Self { pub fn everything() -> Self {
Self { Self {
top_left: Coordinates::<Space>::top_left(), top_left: Coordinates::<Space>::top_left(),
bottom_right: Coordinates::<Space>::bottom_right() bottom_right: Coordinates::<Space>::bottom_right()

View File

@ -9,11 +9,8 @@ mod animations;
mod mappings; mod mappings;
mod buffers; mod buffers;
mod events; mod events;
mod scenes;
use events::Event; use events::Event;
use rgb::Rgb;
use scenes::Sequencer;
use crate::events::EventBus; use crate::events::EventBus;
use crate::platform::{DefaultBoard, Board}; use crate::platform::{DefaultBoard, Board};
@ -24,67 +21,39 @@ use crate::geometry::Rectangle;
fn main() { fn main() {
let mut board: DefaultBoard = Board::take(); let mut board: DefaultBoard = Board::take();
log::info!("🐛 Booting Renderbug!"); log::info!("Board: {}", core::any::type_name_of_val(&board));
log::info!("📡 Board {}", core::any::type_name_of_val(&board)); log::info!("Creating tasks");
log::info!("⚙️ Creating tasks");
let mut system = board.system_tasks(); let mut system = board.system_tasks();
log::info!("System scheduler: {}", core::any::type_name_of_val(&system)); log::info!("System scheduler: {}", core::any::type_name_of_val(&system));
log::info!("💡 Creating output"); log::info!("Creating output");
let output = board.output(); let output = board.output();
log::info!("Output: {}", core::any::type_name_of_val(&output)); log::info!("Output: {}", core::any::type_name_of_val(&output));
log::info!("🎨 Preparing surfaces"); log::info!("Preparing surfaces");
let mut surfaces = board.surfaces(); let mut surfaces = board.surfaces();
log::info!("Surface implementation: {}", core::any::type_name_of_val(&output)); log::info!("Surface implementation: {}", core::any::type_name_of_val(&output));
log::info!("🌌 Creating animations"); log::info!("Creating animations");
let mut animations = FixedSizeScheduler::new([ let mut animations = FixedSizeScheduler::new([
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())),
]);
let mut inputs = FixedSizeScheduler::new([
Box::new(Sequencer::new()),
]); ]);
let mut renderer = FixedSizeScheduler::new([Box::new(Renderer::new(output, surfaces))]); let mut renderer = FixedSizeScheduler::new([Box::new(Renderer::new(output, surfaces))]);
log::info!("🚌 Starting event bus"); log::info!("Starting event bus");
let mut bus = EventBus::new(); let mut bus = EventBus::new();
bus.push(Event::new_property_change("colors.primary", Rgb::new(255, 128, 128))); log::info!("Ready to rock and roll");
bus.push(Event::new_property_change("system.board.chip_id", DefaultBoard::chip_id()));
bus.push(Event::new_property_change("system.network.online", false));
log::info!("Priming events...");
let initial_tasks = [
"Renderer",
"renderbug::scenes::Sequencer",
"renderbug::buffers::BufferedSurfacePool",
"renderbug::platform::esp32::WifiTask",
"renderbug::platform::esp32::MqttTask",
"renderbug::platform::esp32::CircadianRhythm"
];
for task_name in initial_tasks {
bus.push(Event::new_start_thing(task_name));
log::info!("+ {}", task_name);
}
bus.push(Event::new_ready_to_rock()); bus.push(Event::new_ready_to_rock());
log::info!("🚀 Launching...");
loop { loop {
let next_event = bus.next(); let next_event = bus.next();
match next_event { match next_event {
events::Event::Tick => (), events::Event::Tick => (),
Event::ReadyToRock => { _ => log::info!("Event: {:?}", next_event)
log::info!("🚀 Ready to rock and roll");
} }
_ => log::info!("⚡ Event: {:?}", next_event)
}
inputs.tick(&next_event, &mut bus);
animations.tick(&next_event, &mut bus); animations.tick(&next_event, &mut bus);
system.tick(&next_event, &mut bus); system.tick(&next_event, &mut bus);
renderer.tick(&next_event, &mut bus); renderer.tick(&next_event, &mut bus);

View File

@ -20,7 +20,7 @@ pub trait Select<'a> {
#[derive(Debug)] #[derive(Debug)]
pub struct LinearCoordView { pub struct LinearCoordView {
max_x: u8, rect: Rectangle<Virtual>,
idx: usize, idx: usize,
} }
@ -34,7 +34,7 @@ pub type LinearCoords = Coordinates<LinearSpace>;
impl<'a> CoordinateView<'a> for LinearCoordView { impl<'a> CoordinateView<'a> for LinearCoordView {
type Space = LinearSpace; type Space = LinearSpace;
fn next(&mut self) -> Option<(VirtualCoordinates, LinearCoords)> { fn next(&mut self) -> Option<(VirtualCoordinates, LinearCoords)> {
if self.idx as u8 == self.max_x { if self.idx as u8 == self.rect.bottom_right.x {
None None
} else { } else {
let virt = VirtualCoordinates::new(self.idx as u8, 0); // FIXME: scale8 let virt = VirtualCoordinates::new(self.idx as u8, 0); // FIXME: scale8
@ -62,7 +62,7 @@ impl<'a> Select<'a> for LinearPixelMapping {
type View = LinearCoordView; type View = LinearCoordView;
fn select(&'a self, rect: &Rectangle<Virtual>) -> Self::View { fn select(&'a self, rect: &Rectangle<Virtual>) -> Self::View {
LinearCoordView { LinearCoordView {
max_x: rect.bottom_right.x, rect: rect.clone(),
idx: 0, idx: 0,
} }
} }
@ -169,12 +169,6 @@ impl<const STRIDE_NUM: usize> StrideMapping<STRIDE_NUM> {
]) ])
} }
pub fn new_albus() -> Self {
Self::from_json(&[
(0, 0, 50 * 3, false)
])
}
pub fn from_json(stride_json: &[(u8, u8, u8, bool)]) -> Self { pub fn from_json(stride_json: &[(u8, u8, u8, bool)]) -> Self {
let mut strides = [Stride::default(); STRIDE_NUM]; let mut strides = [Stride::default(); STRIDE_NUM];
let stride_count = stride_json.len(); let stride_count = stride_json.len();
@ -215,6 +209,8 @@ impl<const STRIDE_NUM: usize> StrideMapping<STRIDE_NUM> {
let s = size.take().unwrap(); let s = size.take().unwrap();
log::info!("size={:?}", s); log::info!("size={:?}", s);
log::info!("strides={:?}", strides);
Self { Self {
strides, strides,
pixel_count: physical_idx, pixel_count: physical_idx,

View File

@ -98,7 +98,7 @@ impl<T: LedPixelShape, S: Surface> Display<S> for EmbeddedDisplay<Ws2812DrawTarg
let mut pixel = RGB8::new(0, 0, 0); let mut pixel = RGB8::new(0, 0, 0);
for surface in self.surfaces.iter() { for surface in self.surfaces.iter() {
surface.with_shader(|shader| { surface.with_shader(|shader| {
pixel = pixel.saturating_add(shader.draw(virtCoords)); pixel = pixel.saturating_add(shader.draw(virtCoords.clone()));
}) })
} }
self.total_mw += pixel.as_milliwatts(); self.total_mw += pixel.as_milliwatts();

View File

@ -1,22 +1,24 @@
use core::borrow::BorrowMut;
use std::sync::Arc;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::Mutex;
use std::thread::JoinHandle; use std::thread::JoinHandle;
use std::thread::ScopedJoinHandle;
use chrono::DateTime; use chrono::DateTime;
use chrono::Timelike; use chrono::Timelike;
use chrono::Utc; use chrono::Utc;
use esp_idf_svc::eventloop::{EspSubscription, EspSystemEventLoop, System}; use esp_idf_svc::eventloop::{EspSubscription, EspSystemEventLoop, System};
use esp_idf_svc::hal::gpio::Pins;
use esp_idf_svc::hal::modem::Modem; use esp_idf_svc::hal::modem::Modem;
use esp_idf_svc::hal::prelude::Peripherals; use esp_idf_svc::hal::prelude::Peripherals;
use esp_idf_svc::hal::rmt::RMT;
use esp_idf_svc::hal::task::thread::ThreadSpawnConfiguration; use esp_idf_svc::hal::task::thread::ThreadSpawnConfiguration;
use esp_idf_svc::mqtt::client::EspMqttClient; 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::mqtt::client::MqttClientConfiguration;
use esp_idf_svc::netif::IpEvent; use esp_idf_svc::netif::IpEvent;
use esp_idf_svc::nvs::{EspDefaultNvsPartition, EspNvsPartition, NvsDefault}; use esp_idf_svc::nvs::{EspDefaultNvsPartition, EspNvsPartition, NvsDefault};
use esp_idf_svc::sntp::EspSntp; use esp_idf_svc::sntp::EspSntp;
use esp_idf_svc::sntp::SyncStatus;
use esp_idf_svc::sys::esp_efuse_mac_get_default; use esp_idf_svc::sys::esp_efuse_mac_get_default;
use esp_idf_svc::wifi::{AuthMethod, ClientConfiguration, Configuration, EspWifi, WifiEvent}; use esp_idf_svc::wifi::{AuthMethod, ClientConfiguration, Configuration, EspWifi, WifiEvent};
use rgb::Rgb; use rgb::Rgb;
@ -29,19 +31,72 @@ use crate::buffers::BufferedSurfacePool;
use crate::buffers::Pixbuf; use crate::buffers::Pixbuf;
use crate::events::Event; use crate::events::Event;
use crate::events::EventBus; use crate::events::EventBus;
use crate::events::Variant;
use crate::lib8::interpolate::lerp8by8; use crate::lib8::interpolate::lerp8by8;
use crate::mappings::StrideMapping; use crate::mappings::StrideMapping;
use crate::task::FixedSizeScheduler; use crate::task::FixedSizeScheduler;
use crate::task::Task; use crate::task::Task;
use crate::time::Periodically; use crate::time::Periodically;
pub mod i2s {
use esp_idf_svc::hal::i2s::*;
use rgb::ComponentBytes;
use rgb::Rgb;
use crate::mappings::*;
use crate::buffers::Pixbuf;
use crate::render::Output;
use crate::render::Sample;
pub struct I2SOutput<'d> {
driver: I2sDriver<'d, I2sTx>,
pixbuf: [Rgb<u8>; 310],
pixmap: StrideMapping,
}
impl<'d> I2SOutput<'d> {
fn new(driver: I2sDriver<'d, I2sTx>) -> Self {
I2SOutput {
driver,
pixbuf: Pixbuf::new(),
pixmap: StrideMapping::new_jar()
}
}
}
impl<'d> Output for I2SOutput<'d> {
fn on_event(&mut self, event: &crate::events::Event) {
}
fn blank(&mut self) {
self.pixbuf.blank();
}
fn commit(&mut self) {
let bytes = self.pixbuf.as_bytes();
let mut written = self.driver.preload_data(bytes).unwrap();
self.driver.tx_enable().unwrap();
while written < bytes.len() {
let next = &bytes[written..];
written += self.driver.write(next, 0).unwrap();
}
self.driver.tx_disable().unwrap();
}
}
impl<'d> Sample for I2SOutput<'d> {
type Pixel = Rgb<u8>;
fn sample(&mut self, rect: &crate::geometry::Rectangle<crate::geometry::Virtual>) -> impl crate::render::PixelView<Pixel = Self::Pixel> {
StrideSampler::new(&mut self.pixbuf, self.pixmap.select(rect))
}
}
}
pub struct Esp32Board { pub struct Esp32Board {
sys_loop: EspSystemEventLoop, sys_loop: EspSystemEventLoop,
modem: Option<Modem>, modem: Option<Modem>,
pins: Option<Pins>,
rmt: Option<RMT>,
surfaces: BufferedSurfacePool, surfaces: BufferedSurfacePool,
output: Option<<Self as Board>::Output>
} }
impl Board for Esp32Board { impl Board for Esp32Board {
@ -49,14 +104,6 @@ impl Board for Esp32Board {
type Surfaces = BufferedSurfacePool; type Surfaces = BufferedSurfacePool;
type Scheduler = FixedSizeScheduler<4>; type Scheduler = FixedSizeScheduler<4>;
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 { fn take() -> Self {
// 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
// implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
@ -68,25 +115,18 @@ impl Board for Esp32Board {
let peripherals = Peripherals::take().unwrap(); let peripherals = Peripherals::take().unwrap();
let sys_loop = EspSystemEventLoop::take().unwrap(); let sys_loop = EspSystemEventLoop::take().unwrap();
Esp32Board { let mut chip_id: [u8; 8] = [0; 8];
modem: Some(peripherals.modem), unsafe {
sys_loop: sys_loop, esp_efuse_mac_get_default(&mut chip_id as *mut u8);
surfaces: 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?}", chip_id);
log::info!("Setting up output for chip ID {:x?}", Self::chip_id());
const POWER_VOLTS : u32 = 5; const POWER_VOLTS : u32 = 5;
const POWER_MA : u32 = 500; const POWER_MA : u32 = 500;
const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA; const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA;
let pins = peripherals.pins;
let pins = self.pins.take().unwrap();
let rmt = self.rmt.take().unwrap();
ThreadSpawnConfiguration { ThreadSpawnConfiguration {
pin_to_core: Some(esp_idf_svc::hal::cpu::Core::Core1), pin_to_core: Some(esp_idf_svc::hal::cpu::Core::Core1),
..Default::default() ..Default::default()
@ -95,12 +135,12 @@ impl Board for Esp32Board {
// But the implementation spawns a thread based on the core the driver was created in, // But the implementation spawns a thread based on the core the driver was created in,
// so we create the driver in another thread briefly. // so we create the driver in another thread briefly.
// Fun stuff. // Fun stuff.
let output = match Self::chip_id().to_be_bytes() { // panel test board let output = match chip_id { // panel test board
[72, 202, 67, 89, 145, 204, 0, 0] => { [72, 202, 67, 89, 145, 204, 0, 0] => {
StrideOutput::new( StrideOutput::new(
Pixbuf::new(), Pixbuf::new(),
StrideMapping::new_panel(), StrideMapping::new_panel(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(), std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(),
MAX_POWER_MW MAX_POWER_MW
) )
}, },
@ -108,15 +148,15 @@ impl Board for Esp32Board {
StrideOutput::new( StrideOutput::new(
Pixbuf::new(), Pixbuf::new(),
StrideMapping::new_jar(), StrideMapping::new_jar(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio14).unwrap() }).join().unwrap(), std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.rmt.channel0, pins.gpio14).unwrap() }).join().unwrap(),
MAX_POWER_MW MAX_POWER_MW
) )
}, },
[0x4a, 0xca, 0x43, 0x59, 0x85, 0x58, 0x0, 0x0] => { // Albus the tree [0x4a, 0xca, 0x43, 0x59, 0x85, 0x58, 0x0, 0x0] => { // Albus the tree
StrideOutput::new( StrideOutput::new(
Pixbuf::new(), Pixbuf::new(),
StrideMapping::new_albus(), StrideMapping::new_jar(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(), std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(),
MAX_POWER_MW MAX_POWER_MW
) )
}, },
@ -124,7 +164,7 @@ impl Board for Esp32Board {
StrideOutput::new( StrideOutput::new(
Pixbuf::new(), Pixbuf::new(),
StrideMapping::new_fairylights(), StrideMapping::new_fairylights(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(), std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(),
MAX_POWER_MW MAX_POWER_MW
) )
}, },
@ -132,7 +172,7 @@ impl Board for Esp32Board {
StrideOutput::new( StrideOutput::new(
Pixbuf::new(), Pixbuf::new(),
StrideMapping::new_fairylights(), StrideMapping::new_fairylights(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(), std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(),
MAX_POWER_MW MAX_POWER_MW
) )
}, },
@ -140,7 +180,7 @@ impl Board for Esp32Board {
StrideOutput::new( StrideOutput::new(
Pixbuf::new(), Pixbuf::new(),
StrideMapping::new_cyberplague(), StrideMapping::new_cyberplague(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio13).unwrap() }).join().unwrap(), std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.rmt.channel0, pins.gpio13).unwrap() }).join().unwrap(),
MAX_POWER_MW MAX_POWER_MW
) )
}, },
@ -148,7 +188,7 @@ impl Board for Esp32Board {
StrideOutput::new( StrideOutput::new(
Pixbuf::new(), Pixbuf::new(),
StrideMapping::new(), StrideMapping::new(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(), std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(),
MAX_POWER_MW MAX_POWER_MW
) )
} }
@ -157,7 +197,16 @@ impl Board for Esp32Board {
..Default::default() ..Default::default()
}.set().unwrap(); }.set().unwrap();
output Esp32Board {
modem: Some(peripherals.modem),
sys_loop: sys_loop.clone(),
surfaces: BufferedSurfacePool::new(),
output: Some(output)
}
}
fn output(&mut self) -> Self::Output {
self.output.take().unwrap()
} }
fn surfaces(&mut self) -> Self::Surfaces { fn surfaces(&mut self) -> Self::Surfaces {
@ -175,7 +224,7 @@ impl Board for Esp32Board {
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Copy)]
struct ScheduleEntry { struct ScheduleEntry {
hour: u8, hour: u8,
brightness: u8 brightness: u8
@ -189,7 +238,7 @@ struct CircadianRhythm {
impl CircadianRhythm { impl CircadianRhythm {
fn new() -> Self { fn new() -> Self {
CircadianRhythm { CircadianRhythm {
time_check: Periodically::new_every_n_seconds(60), time_check: Periodically::new_every_n_seconds(5),
schedule: [ schedule: [
ScheduleEntry { hour: 0, brightness: 0 }, ScheduleEntry { hour: 0, brightness: 0 },
ScheduleEntry { hour: 5, brightness: 0 }, ScheduleEntry { hour: 5, brightness: 0 },
@ -205,17 +254,11 @@ impl CircadianRhythm {
} }
} }
fn update_brightness(&self, bus: &mut EventBus) {
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));
}
fn brightness_for_time(&self, hour: u8, minute: u8) -> u8 { fn brightness_for_time(&self, hour: u8, minute: u8) -> u8 {
let mut start = self.schedule.last().unwrap(); let mut start = self.schedule.last().unwrap();
let mut end = self.schedule.first().unwrap(); let mut end = self.schedule.first().unwrap();
for cur in self.schedule.iter() { for cur in self.schedule.iter() {
if cur.hour <= hour { if (cur.hour <= hour ) {
start = cur; start = cur;
} else { } else {
end = cur; end = cur;
@ -255,20 +298,11 @@ fn map_range(x: u16, in_min: u16, in_max: u16, out_min: u16, out_max: u16) -> u1
impl Task for CircadianRhythm { impl Task for CircadianRhythm {
fn on_ready(&mut self, bus: &mut EventBus) { fn tick(&mut self, event: &Event, bus: &mut EventBus) {
self.update_brightness(bus); 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);
fn on_property_change(&mut self, key: &'static str, value: &Variant, bus: &mut EventBus) { bus.push(Event::new_property_change("output.brightness", next_brightness));
match (key, value) {
("system.time.synchronized", Variant::Boolean(true)) => self.update_brightness(bus),
_ => ()
}
}
fn on_tick(&mut self, bus: &mut EventBus) {
if self.time_check.tick() {
self.update_brightness(bus);
} }
} }
} }
@ -282,17 +316,16 @@ impl MqttTask {
fn new() -> Self { fn new() -> Self {
MqttTask { MqttTask {
conn_thread: None, conn_thread: None,
client: None, client: None
} }
} }
fn start_mqtt(&mut self, bus: &EventBus) { fn start_mqtt(&mut self) {
log::info!("Starting MQTT"); log::info!("Starting MQTT");
let chip_id: u64 = bus.property("system.board.chip_id").unwrap().into();
let (client, mut conn) = EspMqttClient::new( let (client, mut conn) = EspMqttClient::new(
"mqtt://10.0.0.2:1883", "mqtt://10.0.0.2:1883",
&MqttClientConfiguration { &MqttClientConfiguration {
client_id: Some(&format!("{:X}", chip_id)), client_id: Some("renderbug-rs"),
..Default::default() ..Default::default()
} }
).unwrap(); ).unwrap();
@ -305,50 +338,39 @@ impl MqttTask {
}).unwrap()); }).unwrap());
self.client = Some(client); self.client = Some(client);
} }
fn topic_prefix(bus: &EventBus) -> String {
let chip_id: u64 = bus.property("system.board.chip_id").unwrap().into();
format!("homeassistant-test/renderbug/{:X}", chip_id)
}
} }
impl Task for MqttTask { impl Task for MqttTask {
fn start(&mut self, bus: &mut EventBus) { fn tick(&mut self, event: &Event, bus: &mut EventBus) {
bus.push(Event::new_property_change("mqtt.online", false)); match event {
} Event::Input(crate::events::InputEvent::NetworkOnline) => {
fn on_property_change(&mut self, key: &'static str, value: &Variant, bus: &mut EventBus) {
match (key, value) {
("system.network.online", Variant::Boolean(true)) => {
log::info!("Registering with MQTT"); log::info!("Registering with MQTT");
self.start_mqtt(bus); self.start_mqtt();
if let Some(ref mut client) = self.client { if let Some(ref mut client) = self.client {
client.enqueue( client.enqueue(
Self::topic_prefix(bus).as_str(), "homeassistant-test/renderbug/rust",
esp_idf_svc::mqtt::client::QoS::AtLeastOnce, esp_idf_svc::mqtt::client::QoS::AtLeastOnce,
false, false,
"hello, world".as_bytes() "hello, world".as_bytes()
).unwrap(); ).unwrap();
log::info!("MQTT should be online!"); log::info!("MQTT should be online!");
bus.push(Event::new_property_change("mqtt.online", true));
} }
}, },
(name, value) => { Event::PropertyChange(name, value) => {
if let Some(ref mut client) = self.client { if let Some(ref mut client) = self.client {
let prefix = Self::topic_prefix(bus); let payload = format!("name={} value={:?}", name, value);
client.enqueue( client.enqueue(
format!("{}/properties/{}", prefix, name).as_str(), "homeassistant-test/renderbug/rust/property-change",
esp_idf_svc::mqtt::client::QoS::AtLeastOnce, esp_idf_svc::mqtt::client::QoS::AtLeastOnce,
false, false,
format!("{}", value).as_bytes() payload.as_bytes()
).unwrap(); ).unwrap();
log::info!("property change bump: {}", payload);
} }
} }
_ => ()
} }
} }
} }
@ -359,36 +381,33 @@ impl Debug for WifiTask {
} }
} }
#[derive(Debug, PartialEq, Clone, Copy)]
enum WifiState {
Stopped,
Disconnected,
Connecting,
Connected
}
struct WifiTask { struct WifiTask {
wifi: EspWifi<'static>, wifi: EspWifi<'static>,
ntp: EspSntp<'static>, ntp: EspSntp<'static>,
connection_check: Periodically, connection_check: Periodically,
state: Arc<Mutex<WifiState>>,
last_state: WifiState,
sys_loop: EspSystemEventLoop, sys_loop: EspSystemEventLoop,
wifi_sub: Option<EspSubscription<'static, System>>, wifi_sub: Option<EspSubscription<'static, System>>,
ip_sub: Option<EspSubscription<'static, System>>, ip_sub: Option<EspSubscription<'static, System>>
} }
impl WifiTask { impl WifiTask {
fn new(modem: Modem, sys_loop: EspSystemEventLoop, nvs: &EspNvsPartition<NvsDefault>) -> Self { fn new(modem: Modem, sys_loop: EspSystemEventLoop, nvs: &EspNvsPartition<NvsDefault>) -> Self {
log::info!("Installing wifi driver"); log::info!("Installing wifi driver");
let wifi = EspWifi::new( let mut wifi = EspWifi::new(
modem, modem,
sys_loop.clone(), sys_loop.clone(),
Some(nvs.clone()) Some(nvs.clone())
).unwrap(); ).unwrap();
WifiTask {
wifi,
ntp: EspSntp::new_default().unwrap(),
connection_check: Periodically::new_every_n_seconds(1),
sys_loop,
wifi_sub: None,
ip_sub: None,
}
}
fn connect(&mut self) {
log::info!("Connecting wifi");
let wifi_config = Configuration::Client(ClientConfiguration { let wifi_config = Configuration::Client(ClientConfiguration {
ssid: "The Frequency".try_into().unwrap(), ssid: "The Frequency".try_into().unwrap(),
bssid: None, bssid: None,
@ -397,7 +416,22 @@ impl WifiTask {
channel: None, channel: None,
..Default::default() ..Default::default()
}); });
self.wifi.set_configuration(&wifi_config).unwrap(); 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.start().unwrap();
self.wifi.connect().unwrap(); self.wifi.connect().unwrap();
} }
@ -410,44 +444,69 @@ impl WifiTask {
} }
impl Task for WifiTask { impl Task for WifiTask {
fn start(&mut self, bus: &mut EventBus) { fn start(&mut self) {
log::info!("Starting wifi!"); log::info!("Starting wifi!");
let mut wifi_bus = bus.clone();
let wifi_state = self.state.clone();
self.wifi_sub = Some(self.sys_loop.subscribe::<WifiEvent, _>( move |evt| { self.wifi_sub = Some(self.sys_loop.subscribe::<WifiEvent, _>( move |evt| {
log::debug!("wifi event {:?}", evt); log::warn!("wifi event {:?}", evt);
wifi_bus.push(Event::new_property_change("system.network.online", false)); 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()); }).unwrap());
let mut ip_bus = bus.clone(); let ip_state = self.state.clone();
self.ip_sub = Some(self.sys_loop.subscribe::<IpEvent, _>(move |evt| { self.ip_sub = Some(self.sys_loop.subscribe::<IpEvent, _>(move |evt| {
log::debug!("ip event {:?}", evt); log::warn!("ip event {:?}", evt);
match evt { match evt {
IpEvent::DhcpIpAssigned(addr) => { IpEvent::DhcpIpAssigned(_) => {
ip_bus.push(Event::new_property_change("system.network.ip", addr.ip().to_string())); let mut state = ip_state.lock().unwrap();
ip_bus.push(Event::new_property_change("system.network.gateway", addr.gateway().to_string())); *state = WifiState::Connected;
ip_bus.push(Event::new_property_change("system.network.online", true));
}, },
_ => () _ => ()
} }
}).unwrap()); }).unwrap());
self.connect(); self.connect();
} }
fn on_tick(&mut self, bus: &mut EventBus) { fn tick(&mut self, event: &Event, bus: &mut EventBus) {
if self.connection_check.tick() { if self.connection_check.tick() {
if bus.property("system.network.online").unwrap() == Variant::Boolean(true) { let cur_state = *self.state.lock().unwrap();
match self.ntp.get_sync_status() {
SyncStatus::Completed => bus.push(Event::new_property_change("system.time.synchronized", true)), if self.last_state != cur_state {
_ => bus.push(Event::new_property_change("system.time.synchronized", false)) 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;
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))
} }
} }
} }
} }
fn stop(&mut self, bus: &mut EventBus) { fn stop(&mut self) {
log::info!("Stopping wifi"); log::info!("Stopping wifi");
self.wifi_sub.take().unwrap();
self.ip_sub.take().unwrap();
self.disconnect(); self.disconnect();
bus.push(Event::new_property_change("system.network.online", false));
} }
} }

View File

@ -20,5 +20,4 @@ pub trait Board {
fn output(&mut self) -> Self::Output; fn output(&mut self) -> Self::Output;
fn surfaces(&mut self) -> Self::Surfaces; fn surfaces(&mut self) -> Self::Surfaces;
fn system_tasks(&mut self) -> Self::Scheduler; fn system_tasks(&mut self) -> Self::Scheduler;
fn chip_id() -> u64;
} }

View File

@ -57,7 +57,7 @@ impl<P: Pixbuf<Pixel=T::Color>, T: FastWrite> Output for StrideOutput<P, T> {
fn on_event(&mut self, event: &crate::events::Event) { fn on_event(&mut self, event: &crate::events::Event) {
match event { match event {
crate::events::Event::PropertyChange("output.brightness", Variant::Byte(new_brightness)) => self.brightness = *new_brightness, crate::events::Event::PropertyChange("output.brightness", new_brightness) => self.brightness = new_brightness.clone().into(),
_ => () _ => ()
} }
} }

View File

@ -30,7 +30,7 @@ pub trait Shader: Send + Sync {
pub trait Surfaces: Send + Sync { pub trait Surfaces: Send + Sync {
type Surface: Surface; type Surface: Surface;
type Error: Debug; type Error: Debug;
fn new_surface(&mut self, area: Rectangle<Virtual>) -> Result<Self::Surface, Self::Error>; fn new_surface(&mut self, area: &Rectangle<Virtual>) -> Result<Self::Surface, Self::Error>;
fn render_to<S: Sample>(&self, output: &mut S, frame: usize); fn render_to<S: Sample>(&self, output: &mut S, frame: usize);
} }
@ -38,7 +38,7 @@ pub trait Surface: Send + Sync {
fn set_shader(&mut self, shader: Box<dyn Shader>); fn set_shader(&mut self, shader: Box<dyn Shader>);
fn clear_shader(&mut self); fn clear_shader(&mut self);
fn set_rect(&mut self, rect: Rectangle<Virtual>); fn set_rect(&mut self, rect: &Rectangle<Virtual>);
fn set_opacity(&mut self, opacity: u8); fn set_opacity(&mut self, opacity: u8);
} }
@ -54,8 +54,7 @@ pub struct Renderer<T: Output, S: Surfaces> {
surfaces: S, surfaces: S,
fps: RealTimeRunningAverage<u32>, fps: RealTimeRunningAverage<u32>,
fps_display: Periodically, fps_display: Periodically,
frame: usize, frame: usize
frame_count: usize
} }
impl<T: Output, S: Surfaces> Renderer<T, S> { impl<T: Output, S: Surfaces> Renderer<T, S> {
@ -65,8 +64,7 @@ impl<T: Output, S: Surfaces> Renderer<T, S> {
surfaces: surfaces, surfaces: surfaces,
fps: RealTimeRunningAverage::default(), fps: RealTimeRunningAverage::default(),
fps_display: Periodically::new_every_n_seconds(5), fps_display: Periodically::new_every_n_seconds(5),
frame: 0, frame: 0
frame_count: 0
} }
} }
} }
@ -74,23 +72,22 @@ impl<T: Output, S: Surfaces> Renderer<T, S> {
impl<T: Output, S: Surfaces> Task for Renderer<T, S> { impl<T: Output, S: Surfaces> Task for Renderer<T, S> {
fn name(&self) -> &'static str { "Renderer" } fn name(&self) -> &'static str { "Renderer" }
fn on_property_change(&mut self, key: &'static str, value: &crate::events::Variant, _bus: &mut EventBus) { fn tick(&mut self, event: &Event, _bus: &mut EventBus) {
self.output.on_event(&Event::new_property_change(key, value.clone())); match event {
} crate::events::Event::Tick => {
fn on_tick(&mut self, bus: &mut EventBus) {
self.output.blank(); self.output.blank();
self.surfaces.render_to(&mut self.output, self.frame); self.surfaces.render_to(&mut self.output, self.frame);
self.output.commit(); self.output.commit();
self.fps.insert(1);
self.frame += 1; self.frame += 1;
self.fps_display.run(|| { self.fps_display.run(|| {
self.fps.insert((self.frame - self.frame_count) as u32); log::info!("FPS: {}", self.fps.measurement());
self.frame_count = self.frame;
let fps = self.fps.measurement();
bus.push(Event::new_property_change("output.fps", fps.rate() as u32));
}); });
},
_ => self.output.on_event(event)
}
} }
} }

View File

@ -1,111 +0,0 @@
use std::str::FromStr;
use crate::task::Task;
use crate::events::*;
#[derive(Debug, PartialEq, Eq, Clone)]
struct Scene {
name: &'static str,
patterns: Vec<&'static str>,
trigger: Trigger
}
#[derive(Debug, PartialEq, Eq, Clone)]
enum Trigger {
Startup,
PropertyEquals(&'static str, Variant)
}
pub struct Sequencer {
scenes: Vec<Scene>,
cur_scene: String
}
impl Sequencer {
pub fn new() -> Self {
Sequencer {
cur_scene: String::new(),
scenes: vec![
Scene {
name: "Start",
patterns: vec!["Idle"],
trigger: Trigger::Startup
},
Scene {
name: "Online",
patterns: vec!["Idle"],
trigger: Trigger::PropertyEquals("system.network.online", Variant::Boolean(true))
}
]
}
}
fn get_scene(&self, name: &String) -> Option<&Scene> {
for scene in self.scenes.iter() {
if scene.name == name {
return Some(scene);
}
}
return None;
}
fn apply_scene(&mut self, name: &String, bus: &mut EventBus) {
if let Some(dst_tasks) = self.get_scene(name) {
if let Some(src_tasks) = self.get_scene(&self.cur_scene) {
let stop_queue = src_tasks.patterns.iter().filter(|i| !dst_tasks.patterns.contains(i));
let start_queue = dst_tasks.patterns.iter().filter(|i| !src_tasks.patterns.contains(i));
log::info!("Switching scene from {} to {}", self.cur_scene, name);
for stop in stop_queue {
bus.push(Event::new_stop_thing(stop));
}
for start in start_queue {
bus.push(Event::new_start_thing(start));
}
} else {
log::info!("Starting new scene {}", name);
log::info!("start={:?}", dst_tasks.patterns);
for start in dst_tasks.patterns.iter() {
bus.push(Event::new_start_thing(start));
}
}
self.cur_scene = name.clone();
} else {
panic!("Could not apply scene {:?} scenes={:?}", name, self.scenes);
}
}
}
impl Task for Sequencer {
fn start(&mut self, bus: &mut EventBus) {
log::info!("Starting sequencer!!!");
let startup_scene = self.scenes.iter().filter(|i| { i.trigger == Trigger::Startup }).next().unwrap();
bus.push(Event::new_property_change("scenes.current", startup_scene.name));
}
fn on_property_change(&mut self, key: &'static str, value: &Variant, bus: &mut EventBus) {
match (key, value) {
("scenes.current", Variant::String(scene_name)) => {
log::info!("Applying scene");
self.apply_scene(scene_name, bus);
},
(key, value) => {
for scene in self.scenes.iter() {
match scene.trigger {
Trigger::PropertyEquals(trigger_key, ref trigger_value) => {
if trigger_key == key && trigger_value == value {
log::info!("Triggering scene {}", scene.name);
bus.push(Event::new_property_change("scenes.current", scene.name))
}
},
_ => ()
}
}
}
_ => ()
}
}
}

View File

@ -1,20 +1,17 @@
use core::fmt; use core::fmt;
use crate::events::{Event, EventBus, Variant}; use crate::events::{Event, EventBus};
pub trait Task: Send { pub trait Task: Send {
fn on_ready(&mut self, bus: &mut EventBus) {} fn tick(&mut self, event: &Event, bus: &mut EventBus) {}
fn on_tick(&mut self, bus: &mut EventBus) {} fn start(&mut self) {}
fn on_property_change(&mut self, key: &'static str, value: &Variant, bus: &mut EventBus) {} fn stop(&mut self) {}
fn start(&mut self, bus: &mut EventBus) {}
fn stop(&mut self, bus: &mut EventBus) {}
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
core::any::type_name::<Self>() core::any::type_name::<Self>()
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Copy, Clone)]
enum ScheduledState { enum ScheduledState {
Stopped, Stopped,
Start, Start,
@ -39,110 +36,76 @@ impl core::fmt::Debug for ScheduledTask {
impl ScheduledTask { impl ScheduledTask {
fn new(task: Box<dyn Task>) -> Self { fn new(task: Box<dyn Task>) -> Self {
ScheduledTask { ScheduledTask {
state: ScheduledState::Stopped, state: ScheduledState::Start,
task: task, task: task,
} }
} }
fn start(&mut self) { fn start(&mut self) {
match self.state { self.state = match self.state {
ScheduledState::Stopped => self.state = ScheduledState::Start, ScheduledState::Stopped => ScheduledState::Start,
ScheduledState::Stop => self.state = ScheduledState::Running, ScheduledState::Stop => ScheduledState::Running,
_ => () _ => self.state
} }
} }
fn stop(&mut self) { fn stop(&mut self) {
match self.state { self.state = match self.state {
ScheduledState::Running => self.state = ScheduledState::Stop, ScheduledState::Running => ScheduledState::Stop,
ScheduledState::Start => self.state = ScheduledState::Stopped, ScheduledState::Start => ScheduledState::Stopped,
_ => () _ => self.state
} }
} }
fn tick(&mut self, event: &Event, bus: &mut EventBus) { fn tick(&mut self, event: &Event, bus: &mut EventBus) {
match self.state { self.state = match self.state {
ScheduledState::Start => { ScheduledState::Start => {
log::info!("Starting task {}", self.task.name()); log::info!("Starting task {}", self.task.name());
self.task.start(bus); self.task.start();
self.state = ScheduledState::Running ScheduledState::Running
},
ScheduledState::Running => {
self.task.tick(event, bus);
ScheduledState::Running
}, },
ScheduledState::Stop => { ScheduledState::Stop => {
log::info!("Stopping task {}", self.task.name()); log::info!("Stopping task {}", self.task.name());
self.task.stop(bus); self.task.stop();
self.state = ScheduledState::Stopped ScheduledState::Stopped
}, },
_ => () ScheduledState::Stopped => ScheduledState::Stopped
};
match self.state {
ScheduledState::Running => {
match event {
Event::Tick => self.task.on_tick(bus),
Event::ReadyToRock => self.task.on_ready(bus),
Event::PropertyChange(key, value) => self.task.on_property_change(key, value, bus),
_ => ()
}
},
_ => ()
} }
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct FixedSizeScheduler<const TASK_COUNT: usize> { pub struct FixedSizeScheduler<const TASK_COUNT: usize> {
tasks: [ScheduledTask; TASK_COUNT], tasks: [Option<ScheduledTask>; TASK_COUNT],
} }
impl<const TASK_COUNT: usize> FixedSizeScheduler<TASK_COUNT> { impl<const TASK_COUNT: usize> FixedSizeScheduler<TASK_COUNT> {
pub fn new(tasks: [Box<dyn Task>; TASK_COUNT]) -> Self { pub fn new(tasks: [Box<dyn Task>; TASK_COUNT]) -> Self {
let mut scheduled: [ScheduledTask; TASK_COUNT] = unsafe { std::mem::MaybeUninit::zeroed().assume_init() }; let mut scheduled = [const { None }; TASK_COUNT];
let mut idx = 0; let mut idx = 0;
for task in tasks { for task in tasks {
log::info!("Scheduling task {}", task.name()); scheduled[idx] = Some(ScheduledTask::new(task));
let slot = &mut scheduled[idx];
unsafe { std::ptr::write(slot, ScheduledTask::new(task)) };
idx += 1; idx += 1;
} }
FixedSizeScheduler { FixedSizeScheduler {
tasks: scheduled tasks: scheduled
} }
} }
fn find_task(&mut self, name: &str) -> Option<&mut ScheduledTask> {
for slot in &mut self.tasks {
if slot.task.name() == name {
return Some(slot);
}
}
None
}
} }
impl<const TASK_COUNT: usize> Scheduler for FixedSizeScheduler<TASK_COUNT> { impl<const TASK_COUNT: usize> Scheduler for FixedSizeScheduler<TASK_COUNT> {
fn tick(&mut self, event: &Event, bus: &mut EventBus) { fn tick(&mut self, event: &Event, bus: &mut EventBus) {
match event {
Event::StartThing(task_name) => {
if let Some(slot) = self.find_task(task_name) {
log::debug!("Starting {}", task_name);
slot.start();
}
},
Event::StopThing(task_name) => {
if let Some(slot) = self.find_task(task_name) {
log::debug!("Stopping {}", task_name);
slot.stop();
}
},
_ => {
for slot in &mut self.tasks { for slot in &mut self.tasks {
slot.tick(event, bus); match slot {
Some(task) => task.tick(event, bus),
_ => ()
} }
} }
} }
}
} }
pub trait Scheduler { pub trait Scheduler {

View File

@ -1,7 +1,7 @@
use core::time::Duration; use core::time::Duration;
use std::time::Instant; use std::time::Instant;
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub struct Periodically { pub struct Periodically {
last_run: Instant, last_run: Instant,
duration: Duration duration: Duration