From c39c9619eca7b06d608513313641592c1a3300b2 Mon Sep 17 00:00:00 2001 From: FliegendeWurst <2012gdwu+github@posteo.de> Date: Tue, 3 Oct 2023 14:35:37 +0200 Subject: [PATCH] Simple main loop --- src/bin/main_loop.rs | 311 +++++++++++++++++++++++++++++++++++++++++++ src/rpi.raw | Bin 0 -> 49152 bytes src/star.raw | Bin 0 -> 49152 bytes 3 files changed, 311 insertions(+) create mode 100644 src/bin/main_loop.rs create mode 100644 src/rpi.raw create mode 100644 src/star.raw diff --git a/src/bin/main_loop.rs b/src/bin/main_loop.rs new file mode 100644 index 0000000..0314e68 --- /dev/null +++ b/src/bin/main_loop.rs @@ -0,0 +1,311 @@ +use std::{ + fmt::Debug, + thread::sleep_ms, + time::{Duration, Instant}, +}; + +use display_interface_spi::SPIInterfaceNoCS; +use embedded_graphics::{ + mono_font::{ascii::FONT_10X20, MonoTextStyleBuilder}, + pixelcolor::Rgb565, + prelude::{DrawTarget, Point, Size}, + text::Text, + Drawable, +}; +use gpiocdev::line::{Bias, EdgeDetection}; +use raspi_oled::FrameOutput; +use rppal::{ + gpio::Gpio, + hal::Delay, + spi::{Bus, Mode, SlaveSelect, Spi}, +}; +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"); + +fn main() { + if rppal::system::DeviceInfo::new().is_ok() { + rpi_main(); + } else { + pc_main(); + } +} + +#[cfg(not(feature = "pc"))] +fn pc_main() {} + +#[cfg(feature = "pc")] +fn pc_main() { + use std::num::NonZeroU32; + + use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle, StyledDrawable}; + use rand_xoshiro::{ + rand_core::{RngCore, SeedableRng}, + Xoroshiro128StarStar, + }; + use winit::{ + dpi::LogicalSize, + event::{Event, WindowEvent}, + event_loop::EventLoop, + window::WindowBuilder, + }; + + let event_loop = EventLoop::new(); + let window = WindowBuilder::new() + .with_inner_size(LogicalSize::new(128, 128)) + .build(&event_loop) + .unwrap(); + let context = unsafe { softbuffer::Context::new(&window) }.unwrap(); + let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap(); + + let start = Instant::now(); + let mut iters = 0; + let mut disp = FrameOutput::new(128, 128); + //disp.buffer.save("/tmp/x.png").unwrap(); + let mut rng = Xoroshiro128StarStar::seed_from_u64(0); + let mut buffer_dirty = true; + + event_loop.run(move |event, _, control_flow| { + // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't + // dispatched any events. This is ideal for games and similar applications. + control_flow.set_poll(); + + match event { + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => { + println!("The close button was pressed; stopping"); + control_flow.set_exit(); + }, + Event::MainEventsCleared => { + // Application update code. + + // Queue a RedrawRequested event. + // + // You only need to call this if you've determined that you need to redraw, in + // applications which do not always need to. Applications that redraw continuously + // can just render here instead. + window.request_redraw(); + }, + Event::RedrawRequested(window_id) if window_id == window.id() => { + // Redraw the application. + // + // It's preferable for applications that do not render continuously to render in + // this event rather than in MainEventsCleared, since rendering in here allows + // the program to gracefully handle redraws requested by the OS. + let (width, height) = { + let size = window.inner_size(); + (size.width, size.height) + }; + surface + .resize(NonZeroU32::new(width).unwrap(), NonZeroU32::new(height).unwrap()) + .unwrap(); + + // redraw + if Instant::now().duration_since(start) > Duration::from_millis(iters * 50) { + 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(); + */ + /* + let iters = iters % 300; + let (s, c) = (iters as f32 * 0.1).sin_cos(); + let (mut x, mut y) = (s * iters as f32 * 0.005, c * iters as f32 * 0.005); + x *= 64.; + y *= 64.; + x += 64.; + y += 64.; + let variation = iters as u32 / 16 + 1; + for _ in 0..16 { + let dx = (rng.next_u32() % variation) as i32 - variation as i32 / 2; + let dy = (rng.next_u32() % variation) as i32 - variation as i32 / 2; + let color = rng.next_u32(); + 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( + color as u8 & 0b11111, + ((color >> 8) & 0b111111) as u8, + ((color >> 16) & 0b111111) as u8, + )) + .build(); + p.draw_styled(&s, &mut disp).unwrap(); + } + if iters % 300 == 0 { + disp.clear(Rgb565::new(0, 0, 0)).unwrap(); + } + */ + for _ in 0..16 { + 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 = STAR[y * 128 * 3 + x * 3]; + let green = STAR[y * 128 * 3 + x * 3 + 1]; + if red == 0xff { + let color = rng.next_u32(); + let r; + let g; + let b; + // star + r = (color as u8 & 0b11111).saturating_mul(2); + g = (((color >> 8) & 0b111111) as u8).saturating_mul(2); + b = ((color >> 16) & 0b111111) as u8 / 3; + // rpi + /* + if red > green { + r = (color as u8 & 0b11111).saturating_mul(2); + g = ((color >> 8) & 0b111111) as u8 / 3; + b = ((color >> 16) & 0b111111) as u8 / 3; + } else { + r = (color as u8 & 0b11111) / 2; + g = (((color >> 8) & 0b111111) as u8).saturating_mul(2); + b = ((color >> 16) & 0b111111) as u8 / 3; + } + */ + 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 as u8, g as u8, b as u8)) + .build(); + p.draw_styled(&s, &mut disp).unwrap(); + } + } + buffer_dirty = true; + } + + let mut buffer = surface.buffer_mut().unwrap(); + if buffer_dirty { + for index in 0..(width * height) { + let y = index / width; + let x = index % width; + let pixel = disp.buffer.get_pixel(x, y); + let red = pixel.0[0] << 0; + let green = pixel.0[1] << 0; + let blue = pixel.0[2] << 0; + + buffer[index as usize] = blue as u32 | ((green as u32) << 8) | ((red as u32) << 16); + } + buffer.present().unwrap(); + buffer_dirty = false; + } + }, + _ => (), + } + }); +} + +fn rpi_main() { + let spi = Spi::new(Bus::Spi0, SlaveSelect::Ss0, 19660800, Mode::Mode0).unwrap(); + let gpio = Gpio::new().unwrap(); + let dc = gpio.get(25).unwrap().into_output(); + let mut rst = gpio.get(27).unwrap().into_output(); + + // Init SPI + let spii = SPIInterfaceNoCS::new(spi, dc); + let mut disp = ssd1351::display::display::Ssd1351::new(spii); + + // Reset & init + disp.reset(&mut rst, &mut Delay).unwrap(); + disp.turn_on().unwrap(); + + main_loop(disp); +} + +fn main_loop>(mut disp: D) +where + D::Error: Debug, +{ + let mut last_min = 0xff; + let mut last_button = Instant::now(); + + let mut menu = vec![]; + let lines = gpiocdev::Request::builder() + .on_chip("/dev/gpiochip0") + .with_line(19) + .with_edge_detection(EdgeDetection::RisingEdge) + .with_debounce_period(Duration::from_millis(5)) + .with_bias(Bias::PullDown) + .with_line(6) + .with_edge_detection(EdgeDetection::RisingEdge) + .with_debounce_period(Duration::from_millis(5)) + .with_bias(Bias::PullDown) + .with_line(5) + .with_edge_detection(EdgeDetection::RisingEdge) + .with_debounce_period(Duration::from_millis(5)) + .with_bias(Bias::PullDown) + .request() + .unwrap(); + + loop { + // respond to button presses + while lines.wait_edge_event(Duration::from_millis(1)).unwrap() { + let e = lines.read_edge_event().unwrap(); + last_button = Instant::now(); + match e.offset { + 19 => { + menu.push(1); + }, + 6 => { + menu.push(2); + }, + 5 => { + menu.push(3); + }, + _ => { + println!("unknown offset: {}", e.offset); + }, + } + } + // clean up stale menu selection + if !menu.is_empty() && Instant::now().duration_since(last_button).as_secs() >= 10 { + menu.clear(); + } + let time = OffsetDateTime::now_utc().to_timezone(BERLIN); + if time.minute() == last_min { + sleep_ms(1000); + continue; + } + last_min = time.minute(); + if let Err(e) = loop_iter(&mut disp) { + println!("error: {:?}", e); + } + } +} + +fn loop_iter>(disp: &mut D) -> Result<(), D::Error> { + disp.clear(Rgb565::new(0, 0, 0))?; + let time = OffsetDateTime::now_utc().to_timezone(BERLIN); + display_clock(disp, &time) +} + +fn display_clock>(disp: &mut D, time: &OffsetDateTime) -> Result<(), D::Error> { + let text_style_clock = MonoTextStyleBuilder::new() + .font(&FONT_10X20) + .text_color(Rgb565::new(0xff, 0xff, 0xff)) + .build(); + let hour = time.hour(); + let minute = time.minute(); + let unix_minutes = minute as i32 * 5 / 3; // (time.unix_timestamp() / 60) as i32; + let dx = ((hour % 3) as i32 - 1) * 40 - 2; + let hour = format!("{:02}", hour); + Text::new( + &hour, + Point::new(64 - 20 + dx, 20 + unix_minutes % 100), + text_style_clock, + ) + .draw(disp)?; + let minute = format!("{:02}", minute); + Text::new( + &minute, + Point::new(64 + 4 + dx, 20 + unix_minutes % 100), + text_style_clock, + ) + .draw(disp)?; + Ok(()) +} diff --git a/src/rpi.raw b/src/rpi.raw new file mode 100644 index 0000000000000000000000000000000000000000..5e92a95b04eb5266c4c9924e921bb9650a8ed90c GIT binary patch literal 49152 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 X3y7W};DCP=P(>zNuKYflZ1VpBRjXt5 literal 0 HcmV?d00001 diff --git a/src/star.raw b/src/star.raw new file mode 100644 index 0000000000000000000000000000000000000000..2da69b9aebb357714079cad90e7a242de28a5de7 GIT binary patch literal 49152 zcmeI0-EJTu2!;2i@BheMvu24w`~gAHp;w3q=X}mgoBsI|KS3Y}TuI>f_xDQOuZRx* zXKyn2ALO6l=bwWbzC8Lr{`eJe<|zpNXSn~H3h?6-bmSK1`TzX<98vi8&`JIYe(+a+ z5)k~>Cn&=Y{zv!&{NS(4pM3byD^S7@{+szD_<5Dkybd^GLas>Qdip$VJMne~*}=BQ8vFy`ttfTEV~B#Ynja-{7X=0^mcyg(<67 z5r2CVU|t6@s@zkz0~G78fkTGNaIs!&;ogIQ8U>3a#mDa@DNv4>=cO2(*1;r0;N`9( zKFQ}B%-Zha$x>Ux*Lrl<`;^*Prfw-N4W@XVm~ICoFD-8YJJgA+>2g!)h7`hbXw82C zc*%hZF7+lzWV!TDxTCN%q7F@~VJWf|cRc#sDvfA?GU`)G?ZiGgld8al5i!NKAay1n z&3a2_BZ`PjTg_lEYrN4$dXiLAT)NFmu=6pp>}RP9XG6Dn=1}Q}HmPI-G_4VUlogP! z{Uo*EZ0N(n4#j>@lS(#lQyBp~SpjL;TT+FObrTkLDE5Y$)MJfqy+82e2Bcqa$#p)~ z9k|$`-WzICk2Sr;{s5C3kY;@*7ZI5r=VFI?pQuSyrT|x(1580cI`x@cL1cPXfIT1a z$+@G-6r-%U7=Aj~aq4qRhRF0(@HfTVZ~iIx`IYPg*}NP4Hv=!nt-w!beSYDGs!9T9 z;}!l%`OvA)g0swFItxCWg20rm^~(h=4hAj$lalQCgD|@hlOFBLz@vDC5gZVf1EVQo z*G>*vU_s=|rNuZWBxN$xft0k#Sbd_*xRXjWlOh6MzYHkq`T&LwcR zUWGtyfu!9^8fmCiIn1nkZ<%V*Ci8YOTJk*P7hMl6kf=LJYdu53n+sO_PD<_2Cbu?7 znpD1_Ol+2=jpW=)rp|j*u`C!B3!KW7v}o6P=)X-HOxzr7Hdv#sGl;-ug=o6f>(viw6)C4gvm#7jg4{AnA)|WG0)~B>O zl+4SxRS)^IvO&grFf_70qTiv!4sriT-aW2-Mr6OHZ=ruyaTl=XfYsTF1(s$g?udt) zJwbT|+=+f%s3_+)(W-|lkToQ4LDp)|DTjgu-=C6|Lr0}N6eG20_tY|7!QyD2zG_pY x&U*Li%oWx^=4b$-A|gggpbfZ{#~p`y6672n2y35Cm2T`~^kS&Bp)$ literal 0 HcmV?d00001