mirror of
https://github.com/FliegendeWurst/raspi-oled.git
synced 2024-11-24 11:14:58 +00:00
Simple main loop
This commit is contained in:
parent
6aeaee2fc1
commit
c39c9619ec
311
src/bin/main_loop.rs
Normal file
311
src/bin/main_loop.rs
Normal file
@ -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<D: DrawTarget<Color = Rgb565>>(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<D: DrawTarget<Color = Rgb565>>(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<D: DrawTarget<Color = Rgb565>>(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(())
|
||||||
|
}
|
BIN
src/rpi.raw
Normal file
BIN
src/rpi.raw
Normal file
Binary file not shown.
BIN
src/star.raw
Normal file
BIN
src/star.raw
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user