From d51b298128a88d1ed6a9ed7579c2ae50678be151 Mon Sep 17 00:00:00 2001 From: FliegendeWurst <2012gdwu+github@posteo.de> Date: Wed, 4 Oct 2023 08:59:56 +0200 Subject: [PATCH] Start scheduler, screensaver and action framework --- .gitignore | 5 ++ src/bin/action/mod.rs | 3 ++ src/bin/display_all.rs | 4 +- src/bin/main_loop.rs | 33 ++++++------ src/bin/rgb_test.rs | 17 ++----- src/bin/schedule/mod.rs | 31 +++++++++++ src/bin/screensaver/duolingo.raw | Bin 0 -> 49152 bytes src/bin/screensaver/mod.rs | 63 +++++++++++++++++++++++ src/{ => bin/screensaver}/rpi.raw | Bin src/{ => bin/screensaver}/star.raw | Bin src/bin/status_check.rs | 79 +++++++++++++++++++++++++++++ 11 files changed, 205 insertions(+), 30 deletions(-) create mode 100644 src/bin/action/mod.rs create mode 100644 src/bin/schedule/mod.rs create mode 100644 src/bin/screensaver/duolingo.raw create mode 100644 src/bin/screensaver/mod.rs rename src/{ => bin/screensaver}/rpi.raw (100%) rename src/{ => bin/screensaver}/star.raw (100%) create mode 100644 src/bin/status_check.rs diff --git a/.gitignore b/.gitignore index d509312..d4d0d73 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,8 @@ events_weekly.json events.json sensors.db /result +/box.svg +/mockup.png +/*.aes +/*.py +/*.txt \ No newline at end of file diff --git a/src/bin/action/mod.rs b/src/bin/action/mod.rs new file mode 100644 index 0000000..5e31354 --- /dev/null +++ b/src/bin/action/mod.rs @@ -0,0 +1,3 @@ +pub enum Action { + Screensaver(&'static str), +} diff --git a/src/bin/display_all.rs b/src/bin/display_all.rs index 17ed79b..36d20dc 100644 --- a/src/bin/display_all.rs +++ b/src/bin/display_all.rs @@ -15,7 +15,7 @@ use embedded_graphics::{ text::{renderer::CharacterStyle, Text}, Drawable, }; -use raspi_oled::FrameOutput; + use rppal::{ gpio::Gpio, spi::{Bus, Mode, SlaveSelect, Spi}, @@ -187,7 +187,7 @@ where .font(&FONT_6X9) .text_color(Rgb565::new(0xff, 0xff, 0xff)) .build(); - let mut text_style_4x6 = MonoTextStyleBuilder::new() + let text_style_4x6 = MonoTextStyleBuilder::new() .font(&FONT_4X6) .text_color(Rgb565::new(0xff, 0xff, 0xff)) .build(); diff --git a/src/bin/main_loop.rs b/src/bin/main_loop.rs index f867d8c..633c814 100644 --- a/src/bin/main_loop.rs +++ b/src/bin/main_loop.rs @@ -7,11 +7,12 @@ use display_interface_spi::SPIInterfaceNoCS; use embedded_graphics::{ mono_font::{ascii::FONT_10X20, MonoTextStyleBuilder}, pixelcolor::Rgb565, - prelude::{DrawTarget, Point, Size}, + prelude::{DrawTarget, Point}, text::Text, Drawable, }; use gpiocdev::line::{Bias, EdgeDetection, Value}; +use rand_xoshiro::Xoroshiro128StarStar; use raspi_oled::FrameOutput; use rppal::{ gpio::{Gpio, OutputPin}, @@ -22,8 +23,12 @@ use ssd1351::display::display::Ssd1351; use time::OffsetDateTime; use time_tz::{timezones::db::europe::BERLIN, OffsetDateTimeExt}; -static STAR: &'static [u8] = include_bytes!("../star.raw"); -static RPI: &'static [u8] = include_bytes!("../rpi.raw"); +mod action; +mod schedule; +mod screensaver; + +pub type Oled = Ssd1351>; +pub type Rng = Xoroshiro128StarStar; static BLACK: Rgb565 = Rgb565::new(0, 0, 0); static TIME_COLOR: Rgb565 = Rgb565::new(0b01_111, 0b011_111, 0b01_111); @@ -43,11 +48,7 @@ fn pc_main() {} fn pc_main() { use std::num::NonZeroU32; - use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle, StyledDrawable}; - use rand_xoshiro::{ - rand_core::{RngCore, SeedableRng}, - Xoroshiro128StarStar, - }; + use rand_xoshiro::{rand_core::SeedableRng, Xoroshiro128StarStar}; use winit::{ dpi::LogicalSize, event::{Event, WindowEvent}, @@ -55,6 +56,8 @@ fn pc_main() { window::WindowBuilder, }; + use crate::screensaver::{Screensaver, DUOLINGO}; + let event_loop = EventLoop::new(); let window = WindowBuilder::new() .with_inner_size(LogicalSize::new(128, 128)) @@ -108,13 +111,16 @@ fn pc_main() { .unwrap(); // redraw - if Instant::now().duration_since(start) > Duration::from_millis(iters * 50) { + if Instant::now().duration_since(start) > Duration::from_millis(iters * 1000) { iters += 1; //loop_iter(&mut disp).unwrap(); + /* let mut time = OffsetDateTime::now_utc().to_timezone(BERLIN); //time += Duration::new(iters * 60, 0); disp.clear(Rgb565::new(0, 0, 0)).unwrap(); display_clock(&mut disp, &time).unwrap(); + */ + DUOLINGO.draw(&mut disp, &mut rng).unwrap(); /* let iters = iters % 300; let (s, c) = (iters as f32 * 0.1).sin_cos(); @@ -220,7 +226,7 @@ fn rpi_main() { main_loop(disp); } -fn main_loop(mut disp: Ssd1351>) { +fn main_loop(mut disp: Oled) { disp.clear(BLACK).unwrap(); let mut last_min = 0xff; let mut last_button = Instant::now(); @@ -309,12 +315,7 @@ fn display_clock>(disp: &mut D, time: &OffsetDateT text_style_clock, ) .draw(disp)?; - Text::new( - &":", - Point::new(64 - 3 + dx, 18 + unix_minutes % 100), - text_style_clock, - ) - .draw(disp)?; + Text::new(&":", Point::new(64 - 3 + dx, 18 + unix_minutes % 100), text_style_clock).draw(disp)?; let minute = format!("{:02}", minute); Text::new( &minute, diff --git a/src/bin/rgb_test.rs b/src/bin/rgb_test.rs index 5b583a2..62a9297 100644 --- a/src/bin/rgb_test.rs +++ b/src/bin/rgb_test.rs @@ -1,31 +1,24 @@ use std::{ - fs, thread, + thread, time::{Duration, Instant}, }; use display_interface_spi::SPIInterfaceNoCS; use embedded_graphics::{ draw_target::DrawTarget, - mono_font::{ - ascii::{FONT_10X20, FONT_5X8, FONT_6X9, FONT_9X15}, - MonoTextStyleBuilder, - }, - pixelcolor::{BinaryColor, Rgb565}, - prelude::{OriginDimensions, Point, Primitive, Size}, - primitives::{PrimitiveStyleBuilder, Rectangle}, + mono_font::{ascii::FONT_10X20, MonoTextStyleBuilder}, + pixelcolor::Rgb565, text::Text, Drawable, }; -use embedded_hal::digital::v2::OutputPin; -use image::{ImageBuffer, Rgb}; -use linux_embedded_hal::I2cdev; + use rppal::{ gpio::Gpio, hal::Delay, spi::{Bus, Mode, SlaveSelect, Spi}, }; //use ssd1351::{properties::DisplaySize, mode::{GraphicsMode, displaymode::DisplayModeTrait}}; -use time::{format_description, OffsetDateTime, PrimitiveDateTime}; + //use time_tz::{timezones::db::europe::BERLIN, OffsetDateTimeExt, PrimitiveDateTimeExt}; fn main() { diff --git a/src/bin/schedule/mod.rs b/src/bin/schedule/mod.rs new file mode 100644 index 0000000..f30e7d4 --- /dev/null +++ b/src/bin/schedule/mod.rs @@ -0,0 +1,31 @@ +use time::OffsetDateTime; + +use crate::action::Action; + +/// Task to be executed at certain times. +/// Guaranteed to be checked at least once every minute. +trait Schedule { + fn check_and_do(&mut self, time: OffsetDateTime) { + if self.check(time) { + self.execute(time); + } + } + + fn check(&self, time: OffsetDateTime) -> bool; + fn execute(&mut self, time: OffsetDateTime); +} + +struct Reminder { + hour: i32, + minute: i32, + action: Action, +} + +impl Reminder { + const fn new(hour: i32, minute: i32, action: Action) -> Self { + Reminder { hour, minute, action } + } +} + +static DUOLINGO: Reminder = Reminder::new(11, 30, Action::Screensaver("duolingo")); +static DUOLINGO_NIGHT: Reminder = Reminder::new(23, 30, Action::Screensaver("duolingo")); diff --git a/src/bin/screensaver/duolingo.raw b/src/bin/screensaver/duolingo.raw new file mode 100644 index 0000000000000000000000000000000000000000..13449033621a75e9fcca77511da8340508b447a0 GIT binary patch literal 49152 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 XAo@mGMALVy>HBsNTwg#>h57#n_kGq8 literal 0 HcmV?d00001 diff --git a/src/bin/screensaver/mod.rs b/src/bin/screensaver/mod.rs new file mode 100644 index 0000000..e37f197 --- /dev/null +++ b/src/bin/screensaver/mod.rs @@ -0,0 +1,63 @@ +use embedded_graphics::{ + pixelcolor::Rgb565, + prelude::{DrawTarget, Point, Size}, + primitives::{PrimitiveStyleBuilder, Rectangle, StyledDrawable}, +}; +use rand_xoshiro::rand_core::RngCore; + +use crate::Rng; + +pub trait Screensaver { + fn id(&self) -> &'static str; + + fn draw>(&self, disp: &mut D, rng: &mut Rng) -> Result<(), D::Error>; +} + +pub struct SimpleScreensaver { + id: &'static str, + data: &'static [u8], +} + +impl Screensaver for SimpleScreensaver { + fn id(&self) -> &'static str { + self.id + } + + fn draw>(&self, disp: &mut D, rng: &mut Rng) -> Result<(), D::Error> { + for _ in 0..512 { + let x = (rng.next_u32() % 128) as usize; + let y = (rng.next_u32() % 128) as usize; + let dx = (rng.next_u32() % 8) as i32 - 4; + let dy = (rng.next_u32() % 8) as i32 - 4; + let red = self.data[y * 128 * 3 + x * 3 + 0]; + let green = self.data[y * 128 * 3 + x * 3 + 1]; + let blue = self.data[y * 128 * 3 + x * 3 + 2]; + if red | green | blue != 0 { + let color = rng.next_u32(); + let r; + let g; + let b; + r = (red >> 3).overflowing_add(color as u8 & 0b11).0; + g = (green >> 2).overflowing_add(((color >> 2) & 0b11) as u8).0; + b = (blue >> 3).overflowing_add(((color >> 4) & 0b11) as u8).0; + let p = Rectangle::new(Point::new(x as i32 + dx, y as i32 + dy), Size::new(1, 1)); + let s = PrimitiveStyleBuilder::new().fill_color(Rgb565::new(r, g, b)).build(); + p.draw_styled(&s, disp)?; + } + } + Ok(()) + } +} + +impl SimpleScreensaver { + const fn new(id: &'static str, data: &'static [u8]) -> Self { + if data.len() != 128 * 128 * 3 { + panic!("invalid screensaver size"); + } + SimpleScreensaver { id, data } + } +} + +pub static STAR: SimpleScreensaver = SimpleScreensaver::new("star", include_bytes!("./star.raw")); +pub static RPI: SimpleScreensaver = SimpleScreensaver::new("rpi", include_bytes!("./rpi.raw")); +pub static DUOLINGO: SimpleScreensaver = SimpleScreensaver::new("duolingo", include_bytes!("./duolingo.raw")); diff --git a/src/rpi.raw b/src/bin/screensaver/rpi.raw similarity index 100% rename from src/rpi.raw rename to src/bin/screensaver/rpi.raw diff --git a/src/star.raw b/src/bin/screensaver/star.raw similarity index 100% rename from src/star.raw rename to src/bin/screensaver/star.raw diff --git a/src/bin/status_check.rs b/src/bin/status_check.rs new file mode 100644 index 0000000..b712bc5 --- /dev/null +++ b/src/bin/status_check.rs @@ -0,0 +1,79 @@ +use std::{ + process::{Command, Stdio}, + time::SystemTime, +}; + +use rusqlite::Connection; + +fn main() { + let args = std::env::args().collect::>(); + if args.len() < 2 { + panic!("missing argument: database path"); + } + let database = Connection::open(&args[1]).expect("failed to open database"); + + let timestamp: i64 = database + .query_row( + "SELECT time FROM sensor_readings ORDER BY time DESC LIMIT 1", + [], + |row| Ok(row.get(0).unwrap()), + ) + .unwrap(); + + let time = std::time::SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs() as i64; + + let recent_reading = time - timestamp <= 12 * 60; + let pi_up = Command::new("ping") + .args(["-c1", "raspberrypi.fritz.box"]) + .spawn() + .unwrap() + .wait() + .unwrap() + .success(); + let traffic_good = if pi_up { + let x = Command::new("ssh") + .args(["pi@raspberrypi", "vnstat --json h 2"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap() + .wait_with_output() + .unwrap(); + if x.status.success() { + if let Ok(x) = serde_json::from_slice::(&x.stdout) { + let it = x.pointer("/interfaces/0/traffic/hour/0/tx"); + it.map(|x| x.as_u64()).flatten().unwrap_or(0) / (60 * 60 * 1000 * 1000 / 8) > 5 + } else { + false + } + } else { + false + } + } else { + false + }; + let nixos_up = Command::new("ping") + .args(["-c1", "nixos.fritz.box"]) + .spawn() + .unwrap() + .wait() + .unwrap() + .success(); + let sync_good = if nixos_up { + if let Ok(x) = ureq::get("http://nixos.fritz.box:12783/").call() { + x.status() < 400 + } else { + false + } + } else { + false + }; + let status = format!( + "{} {} {} {} {}", + recent_reading, pi_up, traffic_good, nixos_up, sync_good + ); + std::fs::write("/run/user/1000/status.json", status).unwrap(); +}