#![no_std] #![no_main] #![deny( clippy::mem_forget, reason = "mem::forget is generally not safe to do with esp_hal types, especially those \ holding buffers for the duration of a data transfer." )] #![deny(clippy::large_stack_frames)] use creaturedex::background_tasks; use creaturedex::drivers::i2c_bus::I2cBusPinConfiguration; use creaturedex::drivers::nfc_pn532::NfcPn532Driver; use creaturedex::drivers::secondary_oled::SecondaryDisplay; use creaturedex::drivers::spi_bus; use creaturedex::drivers::tertiary_lcd::{ BOX, EMPTY, EXAMPLE, HEART, TertiaryDisplay, TertiaryDisplayPinConfiguration, }; use creaturedex::navigation::inputs::Inputs; use creaturedex::navigation::navigation::{self}; use creaturedex::navigation::outputs::Outputs; use creaturedex::network::wifi::setup_wifi; use embassy_executor::Spawner; use embedded_graphics::pixelcolor::BinaryColor; use embedded_graphics::{ mono_font::{MonoTextStyle, ascii::FONT_6X10}, prelude::*, text::Text, }; use embedded_hal_bus::i2c::AtomicDevice; use esp_hal::clock::CpuClock; use esp_hal::gpio::{Input, InputConfig, Pull}; use esp_hal::timer::timg::TimerGroup; use log::error; #[panic_handler] fn panic(panic_info: &core::panic::PanicInfo) -> ! { error!("{}", panic_info); loop {} } extern crate alloc; const HEAP_SIZE: usize = 73744; // This creates a default app-descriptor required by the esp-idf bootloader. // For more information see: esp_bootloader_esp_idf::esp_app_desc!(); #[allow( clippy::large_stack_frames, reason = "it's not unusual to allocate larger buffers etc. in main" )] #[esp_rtos::main] async fn main(spawner: Spawner) { creaturedex::logging::init(); let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: HEAP_SIZE); let sw_interrupt = esp_hal::interrupt::software::SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); let timg0 = TimerGroup::new(peripherals.TIMG0); esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0); let (primary, mut secondaries) = retry( 5, spi_bus::DisplayPinConfiguration { spi_peripheral: peripherals.SPI2, sck: peripherals.GPIO36, mosi: peripherals.GPIO35, miso: peripherals.GPIO37, cs_primary: peripherals.GPIO11, cs_secondary_1: peripherals.GPIO13, cs_secondary_2: peripherals.GPIO14, cs_secondary_3: peripherals.GPIO21, reset_primary: peripherals.GPIO10, reset_secondary: peripherals.GPIO47, dc_pin: peripherals.GPIO12, }, spi_bus::DisplayPinConfiguration::build, ); // Initialize the shared I2C bus using the refactored i2c_bus module. let shared_i2c = { use alloc::boxed::Box; let bus = retry(5, I2cBusPinConfiguration { i2c_peripheral: peripherals.I2C0, scl: peripherals.GPIO18, sda: peripherals.GPIO17, }, I2cBusPinConfiguration::build); Box::leak(Box::new(bus)) }; let mut lcd: TertiaryDisplay = retry( 5, TertiaryDisplayPinConfiguration { i2c: shared_i2c }, TertiaryDisplayPinConfiguration::build, ); lcd.load_charset(EXAMPLE).unwrap(); let h = EXAMPLE[HEART]; let e = EXAMPLE[EMPTY]; let b = EXAMPLE[BOX]; if let Err(e) = lcd.write(&alloc::format!( "Custom symbols: {h}{e}{b} And more stuff to wrap and cut off please." )) { log::error!( "Tertiary LCD startup text write failed over shared I2C bus; check the shared bus, wiring, and device responses: {e}" ); } setup_wifi(peripherals.WIFI, peripherals.FLASH, &spawner).await; let mut nfc_driver = NfcPn532Driver::new(AtomicDevice::new(shared_i2c)); if nfc_driver.configure_sam().await.is_ok() { log::info!("NFC SAM configuration successful on the shared I2C bus"); } else { log::error!("NFC SAM configuration failed on the shared I2C bus"); } let button_config = InputConfig::default().with_pull(Pull::Up); let inputs = Inputs { up: Input::new(peripherals.GPIO38, button_config), down: Input::new(peripherals.GPIO39, button_config), left: Input::new(peripherals.GPIO40, button_config), right: Input::new(peripherals.GPIO41, button_config), back: Input::new(peripherals.GPIO8, button_config), ok: Input::new(peripherals.GPIO0, button_config), }; display_shit(&mut secondaries[0], "foo 1"); display_shit(&mut secondaries[1], "bar 1"); display_shit(&mut secondaries[2], "baz 1"); let outputs = { let [sec_1, sec_2, sec_3] = secondaries; Outputs { primary_display: primary, secondary_display_1: sec_1, secondary_display_2: sec_2, secondary_display_3: sec_3, tertiary_display: lcd, _led_a: (), } }; log::info!("Setup complete, entering main loop"); spawner.spawn(navigation::run(inputs, outputs).expect("run task failed")); spawner.spawn(background_tasks::nfc_scanner(nfc_driver).expect("nfc scanner task failed")); core::future::pending::<()>().await } fn display_shit(oled: &mut SecondaryDisplay<'static>, text: &str) { oled.clear().unwrap(); let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); Text::new(text, Point::new(10, 20), text_style) .draw(oled) .unwrap(); oled.flush().unwrap(); } fn retry Result>(repetitions: u32, input: T, mut action: F) -> U { let mut initial_state = input; for iteration in 0..repetitions { log::debug!("Trying to initialize. Retries: {iteration}"); match action(initial_state) { Ok(success) => return success, Err(recovered_input) => initial_state = recovered_input, } } panic!("Init failed after {repetitions} retries.") }