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;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::::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::>(); 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) -> Box{ 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; 8]; 5]> = Box::new([0u8; 5].map(|_| [0u8; 8].map(|_| Option::::None))); for lesson in lessons { let day_idx = lesson.date.weekday().num_days_from_monday() as usize; let mut l = Cow::::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 { 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) }