Files
esp-epd/src/main.rs

219 lines
7.3 KiB
Rust

use std::borrow::Cow;
use std::str::FromStr;
use chrono::{Datelike, Days, NaiveTime};
use embedded_graphics::{
mono_font::{MonoTextStyle, MonoTextStyleBuilder},
prelude::{Dimensions, Point},
text::{Baseline, Text, TextStyleBuilder},
Drawable,
};
use epd_waveshare::{color::Color, epd7in5_v2::Epd7in5, prelude::WaveshareDisplay};
use esp_idf_hal::{
delay::{Ets},
gpio::{Gpio19, PinDriver},
spi::{config, SpiDriverConfig},
};
use esp_idf_svc::http::client::{Configuration as HttpConfig, EspHttpConnection};
use esp_idf_svc::wifi::{BlockingWifi, EspWifi};
use esp_idf_svc::{
eventloop::EspSystemEventLoop,
hal::{prelude::*, spi::SpiDeviceDriver},
nvs::EspDefaultNvsPartition,
wifi::Configuration,
};
use esp_idf_sys::time;
use untis::{Date, Lesson};
use embedded_layout::{layout::linear::LinearLayout, prelude::*};
type Timetable = [[Option<Lesson>;8]; 5];
fn main() -> anyhow::Result<()> {
// 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
esp_idf_svc::sys::link_patches();
// Bind the log crate to the ESP Logging facilities
esp_idf_svc::log::EspLogger::initialize_default();
let peripherals = Peripherals::take().unwrap();
log::info!("Took peripherals");
let busy = PinDriver::input(peripherals.pins.gpio17).unwrap();
let rst = PinDriver::output(peripherals.pins.gpio16).unwrap();
let cs = peripherals.pins.gpio5; // y
let clk = peripherals.pins.gpio18; // y
let mosi = peripherals.pins.gpio23; // y
let miso = peripherals.pins.gpio19; // aka. dc
let dc = PinDriver::output(miso).unwrap();
log::info!("Initialized pins");
let config = config::Config::new();
let mut spi = SpiDeviceDriver::new_single(
peripherals.spi2, // 2 oder 3?
clk,
mosi,
Option::<Gpio19>::None,
Some(cs),
&SpiDriverConfig::default(),
&config,
)
.unwrap();
log::info!("Initialized spi interface");
// Allocating on the stack makes the chip crash. We gotta put it on the heap
let mut display = Box::new(epd_waveshare::epd7in5_v2::Display7in5::default());
display.set_rotation(epd_waveshare::prelude::DisplayRotation::Rotate0);
let mut delay = Ets;
let mut epd7in5 = Epd7in5::new(&mut spi, busy, dc, rst, &mut delay, None).unwrap();
// Create a file called wifi-creds.txt and write SSID and password into it separated by a line break
let mut creds = include_str!("../wifi-creds.txt").split('\n');
let ssid: heapless::String<32> =
heapless::String::<32>::from_str(creds.next().unwrap()).unwrap();
let password: heapless::String<64> =
heapless::String::<64>::from_str(creds.next().unwrap()).unwrap();
// Connecting to wifi
let sysloop = EspSystemEventLoop::take().unwrap();
let nvs = EspDefaultNvsPartition::take().unwrap();
let mut wifi = BlockingWifi::wrap(
EspWifi::new(peripherals.modem, sysloop.clone(), Some(nvs)).unwrap(),
sysloop.clone(),
)
.unwrap();
wifi.set_configuration(&Configuration::Client(
esp_idf_svc::wifi::ClientConfiguration {
ssid,
password,
auth_method: esp_idf_svc::wifi::AuthMethod::WPA2WPA3Personal,
..Default::default()
},
))
.unwrap();
wifi.start().unwrap();
wifi.connect().unwrap();
wifi.wait_netif_up().unwrap();
while !wifi.is_connected().unwrap() {}
println!("Connected to wifi");
let ip_info = wifi.wifi().sta_netif().get_ip_info().unwrap();
let httpconnection = EspHttpConnection::new(&HttpConfig {
use_global_ca_store: true,
crt_bundle_attach: Some(esp_idf_sys::esp_crt_bundle_attach),
..Default::default()
})?;
let tt = fetch_timetable(httpconnection);
draw_layout(&mut display, &tt);
//draw_text(&mut display, &tt[3][5].clone().unwrap().subjects.first().as_ref().unwrap().name, 5, 5);
epd7in5.update_and_display_frame(&mut spi, &*display.buffer(), &mut delay)?;
epd7in5.sleep(&mut spi, &mut delay)?;
log::info!("Completed");
Ok(())
}
fn draw_text(display: &mut epd_waveshare::epd7in5_v2::Display7in5, text: &str, x: i32, y: i32) {
let style = MonoTextStyleBuilder::new()
.font(&embedded_graphics::mono_font::ascii::FONT_10X20)
.text_color(Color::White)
.background_color(Color::Black)
.build();
let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build();
let _ = Text::with_text_style(text, Point::new(x, y), style, text_style).draw(display);
}
fn draw_layout(display: &mut epd_waveshare::epd7in5_v2::Display7in5, timetable: &Timetable){
let display_area = display.bounding_box();
let text_style = MonoTextStyle::new(&embedded_graphics::mono_font::ascii::FONT_6X9, Color::White);
let empty_str = String::new();
let mut texts = timetable[0].iter().map(|l| {
if let Some(l) = l{
Some(l.subjects.first().unwrap().name.as_str())
}else{
None
}
}).map(|s|{
Text::new(s.unwrap_or_else(|| &empty_str), Point::zero(), text_style)
}).collect::<Vec<_>>();
let vg = Views::new(&mut texts);
LinearLayout::vertical(vg)
.with_alignment(horizontal::Center)
.arrange()
.align_to(&display_area, horizontal::Center, vertical::Center)
.draw(display)
.unwrap();
}
fn transform_lessons(lessons: Vec<Lesson>) -> Box<Timetable>{
let lookup: [NaiveTime; 8] = [
(NaiveTime::from_hms_opt(8, 45, 0).unwrap()),
(NaiveTime::from_hms_opt(9, 35, 0).unwrap()),
(NaiveTime::from_hms_opt(10, 40, 0).unwrap()),
(NaiveTime::from_hms_opt(11, 30, 0).unwrap()),
(NaiveTime::from_hms_opt(12, 40, 0).unwrap()),
(NaiveTime::from_hms_opt(13, 30, 0).unwrap()),
(NaiveTime::from_hms_opt(14, 25, 0).unwrap()),
(NaiveTime::from_hms_opt(15, 15, 0).unwrap()),
];
let mut table: Box<[[Option<Lesson>; 8]; 5]> = Box::new([0u8; 5].map(|_| [0u8; 8].map(|_| Option::<Lesson>::None)));
for lesson in lessons {
let day_idx = lesson.date.weekday().num_days_from_monday() as usize;
let mut l = Cow::<Lesson>::Owned(lesson);
for (period_idx, time) in lookup.iter().enumerate(){
if l.start_time.0 < *time && &l.end_time.0 >= time{
table[day_idx][period_idx] = Some(l.into_owned());
l = Cow::Borrowed(&table[day_idx][period_idx].as_ref().unwrap());
}
}
}
table
}
fn fetch_timetable(client: EspHttpConnection) -> Box<Timetable> {
let mut creds = include_str!("../untis-creds.txt").split('\n');
let server = creds.next().unwrap();
let school_id = creds.next().unwrap();
let usr = creds.next().unwrap();
let password = creds.next().unwrap();
let mut s = untis::Client::login(
server,
school_id,
usr,
password,
client
).unwrap();
let cest = chrono::FixedOffset::east_opt(2 * 3600).unwrap();
let now = s.date().unwrap().with_timezone(&cest);
println!("Current time: {}", now);
let date = now.date_naive().checked_add_days(Days::new(2)).unwrap();
let lessons = s.own_timetable_for_week(&Date(date)).unwrap();
transform_lessons(lessons)
}