From 90d04aeb10b9c81a7d9e87abc66565fb27cff20a Mon Sep 17 00:00:00 2001 From: FliegendeWurst <2012gdwu+github@posteo.de> Date: Fri, 6 May 2022 10:48:38 +0200 Subject: [PATCH] Colored events + ... --- .gitignore | 5 + Cargo.lock | 113 ++++++++++++++++--- Cargo.toml | 9 +- README.md | 17 +-- events_weekly.json | 16 +++ images/events.png | Bin 0 -> 1296 bytes images/temps.png | Bin 0 -> 843 bytes src/bin/display_all.rs | 174 +++++++++++++----------------- src/bin/refresh_json.rs | 22 ++++ src/lib.rs | 40 +++++++ src/main.rs | 234 ++++++++++++++++++---------------------- 11 files changed, 370 insertions(+), 260 deletions(-) create mode 100644 .gitignore create mode 100644 events_weekly.json create mode 100644 images/events.png create mode 100644 images/temps.png create mode 100644 src/bin/refresh_json.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..042d370 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/target +**/*.rs.bk +events_weekly.json +events.json +sensors.db diff --git a/Cargo.lock b/Cargo.lock index 498fdb0..d3644fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,6 +37,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f771a5d1f5503f7f4279a30f3643d3421ba149848b89ecaaec0ea2acf04a5ac4" +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + [[package]] name = "bit_field" version = "0.10.1" @@ -110,6 +116,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "chunked_transfer" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" + [[package]] name = "color_quant" version = "1.1.0" @@ -179,15 +191,6 @@ dependencies = [ "adler32", ] -[[package]] -name = "dht-hal" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5423e6132fe67189d1aeac8a7412e7d09816a7374e22201a49a2d5081c8549" -dependencies = [ - "embedded-hal 0.2.7", -] - [[package]] name = "display-interface" version = "0.4.1" @@ -315,6 +318,16 @@ dependencies = [ "spin", ] +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + [[package]] name = "futures-core" version = "0.3.21" @@ -417,6 +430,17 @@ dependencies = [ "nix 0.23.1", ] +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "image" version = "0.24.1" @@ -545,10 +569,10 @@ dependencies = [ ] [[package]] -name = "machine-ip" -version = "0.2.1" +name = "matches" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8197ad28c61566fb74b2167563865906215fbc32c61fd866bd54e7c195eb84ed" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "memchr" @@ -705,6 +729,12 @@ dependencies = [ "regex", ] +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + [[package]] name = "phf" version = "0.10.1" @@ -839,7 +869,6 @@ dependencies = [ name = "raspi-oled" version = "0.1.0" dependencies = [ - "dht-hal", "display-interface-spi", "embedded-graphics", "embedded-hal 0.2.7", @@ -847,7 +876,6 @@ dependencies = [ "image", "libc", "linux-embedded-hal", - "machine-ip", "rppal 0.13.1", "rusqlite", "serde", @@ -856,6 +884,7 @@ dependencies = [ "ssd1351", "time 0.3.9", "time-tz", + "ureq", ] [[package]] @@ -1077,6 +1106,7 @@ dependencies = [ [[package]] name = "ssd1351" version = "0.3.0" +source = "git+https://github.com/FliegendeWurst/ssd1351-rust#9192fde637a2cc672e27ed1dc324f82e6bb65f72" dependencies = [ "chrono", "display-interface", @@ -1199,12 +1229,67 @@ dependencies = [ "time 0.3.9", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "ureq" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9399fa2f927a3d327187cbd201480cee55bee6ac5d3c77dd27f0c6814cff16d5" +dependencies = [ + "base64", + "chunked_transfer", + "log", + "once_cell", + "url", +] + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index 1cd4082..0ba1737 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "raspi-oled" version = "0.1.0" -authors = ["chux0519 "] +authors = ["FliegendeWurst <2012gdwu@posteo.de>"] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,11 +10,8 @@ edition = "2018" embedded-graphics = "0.7.1" linux-embedded-hal = "0.3.0" embedded-hal = "0.2.5" -machine-ip = "0.2.1" -#ssd1306 = "0.6.0" libc = "0.2.98" gpio-cdev = "0.4" -dht-hal = "0.0.1" rusqlite = "0.25.3" time = { version = "0.3.9", features = ["parsing"] } time-tz = "1.0.1" @@ -23,9 +20,9 @@ serde_json = "1.0.79" serde_derive = "1.0.136" serde = "1.0.136" rppal = { version = "0.13.1", features = ["hal"] } -#ssd1351 = "0.4.0" -ssd1351 = { path = "../ssd1351-rust" } +ssd1351 = { git = "https://github.com/FliegendeWurst/ssd1351-rust" } display-interface-spi = "0.4.1" +ureq = { version = "2.4.0", default-features = false } [profile.release] codegen-units = 1 diff --git a/README.md b/README.md index 85f1097..23b47f6 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,20 @@ -# raspi demo for oled ssd1306 display +# raspi demo for OLED ssd1351 display ## Quick start +```bash > nix-shell -> > rustup target add arm-unknown-linux-musleabihf -> > cargo build --release --target arm-unknown-linux-musleabihf - -Then scp the release file to your raspi. +> scp target/arm-unknown-linux-musleabihf/release/{display_all,display_off,refresh_json,take_measurement} 'pi@raspberrypi:~' +> # on the Pi: +> patchelf --set-interpreter /lib/ld-linux-armhf.so.3 display_all +> ./display_off on +> ./display_all sensors.db events.json temps +``` ## Example -![picture](./images/01.jpg) +![picture](./images/temps.png) -![primitive](./images/02.jpg) +![primitive](./images/events.png) diff --git a/events_weekly.json b/events_weekly.json new file mode 100644 index 0000000..ab1bdfa --- /dev/null +++ b/events_weekly.json @@ -0,0 +1,16 @@ +"weekly": [ + { + "name": "Example 1", + "day": 0, + "hour": 9, + "minute": 45, + "duration": 90 + }, + { + "name": "Example 2", + "day": 3, + "hour": 11, + "minute": 15, + "duration": 45 + } +] \ No newline at end of file diff --git a/images/events.png b/images/events.png new file mode 100644 index 0000000000000000000000000000000000000000..c77ec10120a6863d789ce2a31de11ff8e9038c0b GIT binary patch literal 1296 zcmV+r1@HQaP)hW@`Ax#;die;@o+O``4!0RCP)#@B(1v%|(bYsLFHnKS(qF42d4PKp0s)lw&V=^{a5k=CXkH8Yuy~WfD;UrS z1V8{3MNt$*Q4~c{6h%=KMNt$*Q4~c{LAm!)Q){h%E_}=MVv3Lay;D*<+NT5n-R3_+ zRAg+hPw(^>k9{y>-4p=Sv$QwHKZZ~&he`|cPNeI;ZvH?x(Nd5Y=2ikla3 zBo;+Gvl{^9HgwRYxb@SCB|j975^sh$tsuQ203-=y!utKE)4zc24*)q2yCKe=i18vs zGQ3a}b&uZxXQ6B1NV{?QcTvWZJptgk4GzopBrXi7m*xpuThA#KXs zh)eWPEL?1N{A+ZwuhX{^0DyP^2mmoI;#+qDh;{~S2YqO=cu0uzrh`%rfVgWUyof`@ zc3;Fz?SQdRkDU5=dtrI@n;!vCVS#r}SEUT?0(j@s04@d1i1q)%2ms0gpj?i={w)fR zDpf!>vD^6y0DVD50MPqHRT$VlQGo&h$o4{pZPi;K0Cgf}j9DbYRCDvH6Sdo+ZXX{s z!Gor!^{E>MTIeUNt^0TBo#<^w3(wvw?{*-vBBn4{YW+5;ou{nt_5ofI1Iiln+%U{- z(tsgysn+y9H*fH@;34$wfb)QV9cm_k1{Bh!%#FB255+>0KC(O6*Xi2{04S3V5A+Gd zx9$WG?F`rsI1!T%331+ZP|5)ica4OXVs6;7ZrZ$vy-JUq`uO9(^6WQ10-(ZzCt~R} zv5M6hx%6OMpMCxHBzPjW8o-FLMixxp2S9NMa7|3R_z%*JE}d0x`RDcPZ|FS0S`!n4 zK#d%m)?md}Yo@HfJ;WdaAY_z552)F7vmAiu0v9?FbJoO=dDY^beQ4|FW zToFsBf1UNfgqv-0Qx6l#lcPKSjR0=#A((4j`z?*Vxshi1(lYW?1mxi`_TIt%AbFwt z9cjk&4?&y{<8^H3aG#l=^*$OWLp`)Rk38qWeHcfd3P@-No`{VCh;Eg@@CX1q;|%n` z4$w^y=LLWHt)+R#W6KR8$ao@H$TJ)d*js$Q5dlyXrRfjMl;eK8hyddN00008a5Mp@#yuLf5l7}Is z`!M4icZI3zuOHRbw^bOvY`yzw&F%BK)sAcoMfxXZ2rx*TVQ_8~ zWvKDX>}EGGDmlTSpuoJR%F&bYh`D3oV+MwjmM1?vDz=2rl4vNJmR6v*g8Rub$qgxa zi=Im)00Xo^z==b#1(}%p>5o3I(T@7sjnls#RkeHb-EZovm9<<9SKmHXcIob6{q}6G zp3Lu4qOW5ZOvFVmySvSsmR@tx!Is~)K{si?Wb@IOmz&n?+%)xr|2y3~;Y=HJ&#dVD z-aGAGhNVO$}IT*J@`9cL>g|85cK0!J*wuA7Vrp8T1%p*6CTw-JX0rAlfhfvx>}VA-1y> z_2P#QSFb-CU1D3Uu5!?vF`%UQ&&(i(4ZG#%vAkOm#=cm^wVCC{32n-iH%~={f7NB+PX1u^WqZ{N297mr&AAyrL=|sQY1q;H?r4JaP38sC z&Bu=%@E zGchn%HV9NQFx2rMXjmulq0z=6^W9PhaF|=VOcw#BkOM6YJpIn;Gv2Z-sF } #[derive(Deserialize)] @@ -83,7 +82,7 @@ fn main() { hour.sort(); let mut min = hour[1]; let mut max = hour[hour.len() - 2]; - println!("min {} max {}", min, max); + //println!("min {} max {}", min, max); // sanity check value if max > 300 { if vals.is_empty() { @@ -98,27 +97,22 @@ fn main() { global_max = max.max(global_max); vals.push((min, max)); } - println!("global {} | {}", global_min, global_max); - - let hour = time.hour(); - let minute = time.minute(); - //let i2c = I2cdev::new("/dev/i2c-1").unwrap(); - //let interface = I2CDisplayInterface::new(i2c); - //let mut disp = Ssd1306::new(interface, DisplaySize128x64, DisplayRotation::Rotate0).into_buffered_graphics_mode(); - //disp.init().unwrap(); - //let mut disp = FrameOutput { buffer: ImageBuffer::new(128, 64) }; + //println!("global {} | {}", global_min, global_max); 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); + let disp = ssd1351::display::display::Ssd1351::new(spii); + //let mut disp = FrameOutput::new(128, 128); - // Reset & init - //disp.reset(&mut rst, &mut Delay).unwrap(); - //disp.turn_on().unwrap(); + let mut disp = draw(disp, time, rh, temp, events, &args, global_min, global_max, vals); + let _ = disp.flush(); + //disp.buffer.save("/tmp/x.png"); +} + +fn draw>(mut disp: D, time: OffsetDateTime, rh: i64, temp: i64, events: Events, args: &[String], global_min: i32, global_max: i32, mut vals: Vec<(i32, i32)>) -> D where ::Error: Debug { + let hour = time.hour(); + let minute = time.minute(); let text_style_clock = MonoTextStyleBuilder::new() .font(&FONT_10X20) @@ -128,7 +122,7 @@ fn main() { .font(&FONT_9X15) .text_color(Rgb565::new(0xff, 0xff, 0xff)) .build(); - let text_style3 = MonoTextStyleBuilder::new() + let mut text_style_6x9 = MonoTextStyleBuilder::new() .font(&FONT_6X9) .text_color(Rgb565::new(0xff, 0xff, 0xff)) .build(); @@ -163,7 +157,7 @@ fn main() { Text::new(&rh, Point::new(64 + 3, 64 - 4), text_style2) .draw(&mut disp) .unwrap(); - Text::new("%", Point::new(64 + 3 + 18, 64 - 4), text_style3) + Text::new("%", Point::new(64 + 3 + 18, 64 - 4), text_style_6x9) .draw(&mut disp) .unwrap(); let temp_int = format!("{:02}", temp / 10); @@ -175,7 +169,7 @@ fn main() { .draw(&mut disp) .unwrap(); let temp_fract = format!("{}", temp % 10); - Text::new(&temp_fract, Point::new(64 + 32 + 3 + 18 + 2, 64 - 4), text_style3) + Text::new(&temp_fract, Point::new(64 + 32 + 3 + 18 + 2, 64 - 4), text_style_6x9) .draw(&mut disp) .unwrap(); @@ -227,7 +221,7 @@ fn main() { ("S", "o"), ]; for i in 0..5 { - Text::new(days[day].0, (x + 12 * i + 4, y + 6).into(), text_style3) + Text::new(days[day].0, (x + 12 * i + 4, y + 6).into(), text_style_6x9) .draw(&mut disp) .unwrap(); Text::new(days[day].1, (x + 12 * i + 10, y + 6).into(), text_style4) @@ -240,9 +234,12 @@ fn main() { // events let mut all_events = vec![]; for event in events.weekly { - all_events.push((event.day, event.hour, event.minute, event.duration, event.name)); + let mut event_time = time.clone(); + while event_time.weekday().number_days_from_monday() as i32 != event.day { + event_time += Duration::from_secs(24 * 60 * 60); + } + all_events.push((event.day, event.hour, event.minute, event.duration, event.name, event_time.to_julian_day())); } - let today = time.date().to_julian_day(); let format = format_description::parse("[year]-[month]-[day]T[hour]:[minute]:[second]").unwrap(); for event in events.events { let dt = PrimitiveDateTime::parse(&event.start_time, &format) @@ -250,21 +247,41 @@ fn main() { .assume_timezone(BERLIN) .unwrap(); let julian_day = dt.to_julian_day(); - if dt < time || julian_day - today > 4 { + if dt < time { continue; } + let duration = if let Some(end_time) = event.end_time.as_ref() { + let dt2 = PrimitiveDateTime::parse(end_time, &format) + .unwrap() + .assume_timezone(BERLIN) + .unwrap(); + (dt2.sub(dt).as_seconds_f32() / 60.0) as i32 + } else { + 30 + }; all_events.push(( dt.weekday().number_days_from_monday() as _, dt.hour() as _, dt.minute() as _, - 30, + duration, event.name, - )); // TODO length + julian_day, + )); } + let today = time.date().to_julian_day(); let weekday = time.weekday().number_days_from_monday() as i32; - all_events.sort_by_key(|x| (((x.0 + 7) - weekday) % 7, x.1, x.2)); + all_events.sort_by_key(|x| (x.5, ((x.0 + 7) - weekday) % 7, x.1, x.2)); println!("{:?}", all_events); let mut time_until_first = None; + let colors = vec![ + Rgb565::new(0xff >> 3, 0xff >> 2, 0x00 >> 3), + Rgb565::new(0xff >> 3, 0x00 >> 2, 0xff >> 3), + Rgb565::new(0x00 >> 3, 0xff >> 2, 0xff >> 3), + Rgb565::new(0xff >> 3, 0x00 >> 2, 0x00 >> 3), + Rgb565::new(0x00 >> 3, 0xff >> 2, 0x00 >> 3), + Rgb565::new(0x00 >> 3, 0x00 >> 2, 0xff >> 3), + Rgb565::new(0xff >> 3, 0xff >> 2, 0xff >> 3), + ]; for i in 0..5 { let day = (weekday + i) % 7; for hour in 0..24 { @@ -274,11 +291,11 @@ fn main() { } if i == 0 && hour == time.hour() as i32 && minute == (time.minute() as i32 / 6) * 6 { - bits.push((i, hour, minute / 6, Rgb565::new(0xff, 0x00, 0xff))); + bits.push((i, hour, minute / 6, Some(Rgb565::new(0xff, 0x00, 0xff)))); } - for event in &all_events { - if event.0 != day { + for (event_idx, event) in all_events.iter().enumerate() { + if event.0 != day || event.5 < today || event.5 - today > 4 { continue; } let event_start = event.1 * 60 + event.2; @@ -286,9 +303,9 @@ fn main() { let now = hour * 60 + minute; let now2 = hour * 60 + minute + 6; if now2 > event_start && now < event_end { - bits.push((i, hour, minute / 6, Rgb565::new(0xff, 0xff, 0x10))); + bits.push((i, hour, minute / 6, colors.get(event_idx).copied())); } - if time_until_first.is_none() { + if time_until_first.is_none() && (i > 0 || event.1 > time.hour() as i32 || (event.1 == time.hour() as i32 && event.2 >= time.minute() as i32)) { time_until_first = Some( ((i * 24 + event.1) * 60 + event.2) * 60 - (time.hour() as i32 * 60 + time.minute() as i32) * 60, @@ -302,14 +319,24 @@ fn main() { // calculate position let x = x + 4 + d * 12 + m; let y = y + 8 + h; - disp.fill_solid(&Rectangle::new((x, y).into(), (1, 1).into()), color) + disp.fill_solid(&Rectangle::new((x, y).into(), (1, 1).into()), color.unwrap_or(Rgb565::new(0xff, 0xff, 0x10))) .unwrap(); //Rectangle::new((x, y).into(), (1, 1).into()).into_styled(rect_style).draw(&mut disp).unwrap(); } if args[3] == "events" { for (i, event) in all_events.iter().take(7).enumerate() { - let text = if event.4.len() > 10 { &event.4[0..10] } else { &event.4 }; - Text::new(text, (x + 2, y + 64 + 9 * i as i32 + 5).into(), text_style3) + let text = if event.4.len() > 19 { &event.4[0..19] } else { &event.4 }; + let day = event.0 as usize; + let y = y + 64 + 9 * i as i32 + 5; + text_style_6x9.set_text_color(Some(Rgb565::new(0xff, 0xff, 0xff))); + Text::new(days[day].0, (x, y).into(), text_style_6x9) + .draw(&mut disp) + .unwrap(); + Text::new(days[day].1, (x + 6, y).into(), text_style4) + .draw(&mut disp) + .unwrap(); + text_style_6x9.set_text_color(Some(colors[i])); + Text::new(text, (x + 14, y).into(), text_style_6x9) .draw(&mut disp) .unwrap(); } @@ -331,14 +358,14 @@ fn main() { Text::new( &format!("{}", global_max as f32 / 10.0), (100, 64 + 10).into(), - text_style3, + text_style_6x9, ) .draw(&mut disp) .unwrap(); Text::new( &format!("{}", global_min as f32 / 10.0), (100, 64 + 50).into(), - text_style3, + text_style_6x9, ) .draw(&mut disp) .unwrap(); @@ -360,63 +387,6 @@ fn main() { .draw(&mut disp) .unwrap(); } - /* - sleep(Duration::from_secs(2)); - disp.clear(); - let base_y = 0.0; - let max_dy = 32.0; - let mut tick = 0; - loop { - let y = if tick % 32 < 16 { - base_y + (tick % 16) as f32 / 16.0 * max_dy - } else { - base_y + max_dy - (tick % 16) as f32 / 16.0 * max_dy - } as i32; - tick += 1; - Line::new(Point::new(8, y + 16), Point::new(8 + 16, y + 16)) - .into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1)) - .draw(&mut disp).unwrap(); - Line::new(Point::new(8, y + 16), Point::new(8 + 8, y)) - .into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1)) - .draw(&mut disp).unwrap(); - - Line::new(Point::new(8 + 16, y + 16), Point::new(8 + 8, y)) - .into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1)) - .draw(&mut disp).unwrap(); - - Rectangle::new(Point::new(48, y), Size::new(16, 16)) - .into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1)) - .draw(&mut disp).unwrap(); - - - Circle::new(Point::new(88, y), 16) - .into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1)) - .draw(&mut disp).unwrap(); - - /* - Text::new(&format!("Hello from frame {}", tick), Point::new(0, 56), text_style) - .draw(&mut disp) - .unwrap(); - */ - disp.flush().unwrap(); - - sleep(Duration::from_millis(10)); - - disp.clear(); - - /* - let im: ImageRaw = ImageRaw::new(IMG_DATA, 64); - let img = Image::new(&im, Point::new(32, 0)); - img.draw(&mut disp).unwrap(); - disp.flush().unwrap(); - - sleep(Duration::from_secs(2)); - disp.clear(); - */ - } - */ - - let _ = disp.flush(); - //disp.buffer.save("/tmp/frame.png").unwrap(); + disp } diff --git a/src/bin/refresh_json.rs b/src/bin/refresh_json.rs new file mode 100644 index 0000000..fcc7f50 --- /dev/null +++ b/src/bin/refresh_json.rs @@ -0,0 +1,22 @@ +use std::{error::Error, fs}; + +static WEEKLY: &'static str = include_str!("../../events_weekly.json"); + +fn main() { + let url = "http://nixos.fritz.box:12783/custom/event_alerts"; + + if let Ok(json) = get_json(url) { + let mut buf = String::new(); + buf += "{"; + buf += "\"events\": "; + buf += &json; + buf += ","; + buf += WEEKLY; + buf += "}"; + fs::write("events.json", buf.as_bytes()).unwrap(); + } +} + +fn get_json(url: &str) -> Result> { + Ok(ureq::get(url).call()?.into_string()?) +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index a0d34d0..2241af3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,47 @@ use std::{ time::{self, Duration}, }; +use embedded_graphics::{pixelcolor::Rgb565, draw_target::DrawTarget, prelude::{OriginDimensions, Size, RgbColor}}; use gpio_cdev::{EventType, Line, LineRequestFlags}; +use image::{ImageBuffer, Rgb}; + +pub struct FrameOutput { + pub buffer: ImageBuffer, Vec>, +} + +impl FrameOutput { + pub fn new(width: u32, height: u32) -> Self { + FrameOutput { + buffer: ImageBuffer::new(width, height) + } + } +} + +impl DrawTarget for FrameOutput { + type Color = Rgb565; + + type Error = (); + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + for pos in pixels { + if pos.0.x < 0 || pos.0.y < 0 || pos.0.x as u32 >= self.buffer.width() || pos.0.y as u32 >= self.buffer.height() { + continue; + } + self.buffer.put_pixel(pos.0.x as u32, pos.0.y as u32, Rgb([pos.1.r() << 3, pos.1.g() << 2, pos.1.b() << 3])); + } + Ok(()) + } +} + +impl OriginDimensions for FrameOutput { + fn size(&self) -> Size { + Size::new(self.buffer.width(), self.buffer.height()) + } +} + fn read_events(line: &gpio_cdev::Line, timeout: std::time::Duration) -> Result, SensorError> { let input = line.request(LineRequestFlags::INPUT, 0, "read-data")?; diff --git a/src/main.rs b/src/main.rs index 2889acb..60da857 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,136 +1,6 @@ -use embedded_graphics::{ - draw_target::DrawTarget, - pixelcolor::BinaryColor, - prelude::{OriginDimensions, Size}, - primitives::Rectangle, -}; -use image::{ImageBuffer, Rgb}; -use rusqlite::Connection; - -struct FrameOutput { - buffer: ImageBuffer, Vec>, -} - -impl DrawTarget for FrameOutput { - type Color = BinaryColor; - - type Error = (); - - fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> - where - I: IntoIterator>, - { - for pos in pixels { - if pos.0.x < 0 || pos.0.y < 0 || pos.0.x >= 128 || pos.0.y >= 64 { - continue; - } - let color = if pos.1 == BinaryColor::On { - Rgb([0, 255, 255]) - } else { - Rgb([0, 0, 0]) - }; - self.buffer.put_pixel(pos.0.x as u32, pos.0.y as u32, color); - } - Ok(()) - } -} - -impl OriginDimensions for FrameOutput { - fn size(&self) -> Size { - Size::new(self.buffer.width(), self.buffer.height()) - } -} - -fn main() { - let args = std::env::args().collect::>(); - if args.len() < 2 { - panic!("missing argument: database path"); - } - - let mut disp = FrameOutput { - buffer: ImageBuffer::new(128, 64), - }; - - let database = Connection::open(&args[1]).expect("failed to open database"); - - let mut query = database - .prepare("SELECT celsius FROM sensor_readings ORDER BY sensor_readings.time DESC LIMIT 288") - .unwrap(); - let mut temps: Vec = query - .query_map([], |r| Ok(r.get(0))) - .unwrap() - .map(Result::unwrap) - .map(Result::unwrap) - .collect(); - let mut global_min = 1000; - let mut global_max = 0; - let mut vals: Vec<(i32, i32)> = vec![]; - for hour in temps.chunks_mut(6) { - hour.sort(); - let min = hour[1]; - let mut max = hour[hour.len() - 2]; - println!("min {} max {}", min, max); - // sanity check value - if max > 300 { - if vals.is_empty() { - max = min; - } else { - max = vals.last().unwrap().1; - } - } - - global_min = min.min(global_min); - global_max = max.max(global_max); - vals.push((min, max)); - } - println!("global {} | {}", global_min, global_max); - let diff = global_max - global_min; - let x = 1; - let y = 1; - let scaley = 64; - let scalex = 2; - vals.reverse(); - for (i, (a, b)) in vals.into_iter().enumerate() { - let x = x + i as i32 * scalex; - let y1 = y + (global_max - b) * scaley / diff; - let y2 = y + (global_max - a) * scaley / diff; - let height = y2 - y1 + 1; - disp.fill_solid( - &Rectangle::new((x, y1).into(), (scalex as u32, height as u32).into()), - BinaryColor::On, - ) - .unwrap(); - } - - disp.buffer.save("/tmp/frame.png").unwrap(); -} -/* -use dht_hal::{Dht22, Reading}; -use embedded_graphics::image::{Image, ImageRaw}; -use embedded_graphics::mono_font::iso_8859_7::FONT_9X18; -use embedded_graphics::pixelcolor::BinaryColor; -use embedded_graphics::prelude::*; -use embedded_graphics::primitives::{Circle, Line, PrimitiveStyle, Rectangle}; - -use embedded_graphics::{ - mono_font::{ascii::FONT_6X10, MonoTextStyleBuilder}, - text::Text, -}; -use embedded_hal::digital::v2::{InputPin, OutputPin}; -use embedded_hal::prelude::_embedded_hal_blocking_i2c_Write; -use gpio_cdev::{Chip, EventRequestFlags, EventType, LineRequestFlags}; use linux_embedded_hal::i2cdev::core::I2CDevice; use linux_embedded_hal::i2cdev::linux::LinuxI2CError; -use linux_embedded_hal::{Delay, I2cdev}; -use machine_ip; -use rusqlite::{params, Connection}; -use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306}; -use std::intrinsics::transmute; -use std::thread::sleep; -use std::time::{Duration, SystemTime}; -use std::{env, mem, time}; - -static IMG_DATA: &[u8; 512] = include_bytes!("../rust.raw"); +use linux_embedded_hal::I2cdev; const CCS811_ADDR: u8 = 0x5A; // or 0x5B @@ -218,6 +88,108 @@ enum CCS811DriveMode { Every250Milliseconds = 4, } +fn main() { + let i2c = I2cdev::new("/dev/i2c-1").unwrap(); + let mut ccs = CCS811::new(i2c, CCS811_ADDR); + println!("HW ID, should be 0x81 {:x}", ccs.hardware_id()); + println!("Error code, should be None: {:?}", ccs.check_for_error()); + println!("app valid = {:?}", ccs.app_valid()); + println!("baseline = {:x}", ccs.get_baseline()); + println!("reading {:?}", ccs.get_reading()); + println!("setting drive mode to 1: {:?}", ccs.set_drive_mode(CCS811DriveMode::EverySecond)); + + /* + let args = std::env::args().collect::>(); + if args.len() < 2 { + panic!("missing argument: database path"); + } + + let mut disp = FrameOutput { + buffer: ImageBuffer::new(128, 64), + }; + + let database = Connection::open(&args[1]).expect("failed to open database"); + + let mut query = database + .prepare("SELECT celsius FROM sensor_readings ORDER BY sensor_readings.time DESC LIMIT 288") + .unwrap(); + let mut temps: Vec = query + .query_map([], |r| Ok(r.get(0))) + .unwrap() + .map(Result::unwrap) + .map(Result::unwrap) + .collect(); + let mut global_min = 1000; + let mut global_max = 0; + let mut vals: Vec<(i32, i32)> = vec![]; + for hour in temps.chunks_mut(6) { + hour.sort(); + let min = hour[1]; + let mut max = hour[hour.len() - 2]; + println!("min {} max {}", min, max); + // sanity check value + if max > 300 { + if vals.is_empty() { + max = min; + } else { + max = vals.last().unwrap().1; + } + } + + global_min = min.min(global_min); + global_max = max.max(global_max); + vals.push((min, max)); + } + println!("global {} | {}", global_min, global_max); + let diff = global_max - global_min; + let x = 1; + let y = 1; + let scaley = 64; + let scalex = 2; + vals.reverse(); + for (i, (a, b)) in vals.into_iter().enumerate() { + let x = x + i as i32 * scalex; + let y1 = y + (global_max - b) * scaley / diff; + let y2 = y + (global_max - a) * scaley / diff; + let height = y2 - y1 + 1; + disp.fill_solid( + &Rectangle::new((x, y1).into(), (scalex as u32, height as u32).into()), + BinaryColor::On, + ) + .unwrap(); + } + + disp.buffer.save("/tmp/frame.png").unwrap(); + */ +} +/* +use dht_hal::{Dht22, Reading}; +use embedded_graphics::image::{Image, ImageRaw}; +use embedded_graphics::mono_font::iso_8859_7::FONT_9X18; +use embedded_graphics::pixelcolor::BinaryColor; +use embedded_graphics::prelude::*; +use embedded_graphics::primitives::{Circle, Line, PrimitiveStyle, Rectangle}; + +use embedded_graphics::{ + mono_font::{ascii::FONT_6X10, MonoTextStyleBuilder}, + text::Text, +}; +use embedded_hal::digital::v2::{InputPin, OutputPin}; +use embedded_hal::prelude::_embedded_hal_blocking_i2c_Write; +use gpio_cdev::{Chip, EventRequestFlags, EventType, LineRequestFlags}; +use linux_embedded_hal::i2cdev::core::I2CDevice; +use linux_embedded_hal::i2cdev::linux::LinuxI2CError; +use linux_embedded_hal::{Delay, I2cdev}; +use machine_ip; +use rusqlite::{params, Connection}; +use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306}; +use std::intrinsics::transmute; +use std::thread::sleep; +use std::time::{Duration, SystemTime}; +use std::{env, mem, time}; + +static IMG_DATA: &[u8; 512] = include_bytes!("../rust.raw"); + fn main() { let args = env::args().collect::>(); if args.len() < 2 {