#![no_std] #![no_main] use core::str::FromStr; use embassy_net::tcp::{TcpReader, TcpWriter}; use embassy_rp::multicore::{Stack, spawn_core1}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::Mutex; use embassy_sync::signal::Signal; use embedded_io::Write; use log::info; use owned_str::OwnedStr; use ufmt::uwrite; use crate::buffer::WriteBuffer; use crate::input::{ANGLE, INPUT, InputConfig}; use crate::net::network_setup_task; use crate::owned_str_writer::{OwnedStrWriter, center_str}; use crate::screen::{lcd_display_task, overwrite_lcd}; use ag_lcd::{Blink, Cursor, Display, LcdDisplay}; use defmt::*; use embassy_executor::{Executor, Spawner}; use embassy_rp::gpio::{Level, Output}; use embassy_rp::i2c::{self}; use embassy_rp::peripherals::{DMA_CH0, I2C0, PIO0}; use embassy_rp::pio::InterruptHandler; use embassy_rp::{bind_interrupts, dma}; use embassy_rp::{peripherals::USB, usb}; use embassy_time::{Delay, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; mod buffer; mod input; mod net; mod owned_str_writer; mod screen; const WIFI_NETWORK: &str = "flamme"; const WIFI_PASSWORD: &str = "12345678"; const TARGET_IP: &str = "84.238.32.253"; const TARGET_PORT: u16 = 7070; bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; DMA_IRQ_0 => dma::InterruptHandler; USBCTRL_IRQ => usb::InterruptHandler; I2C0_IRQ => i2c::InterruptHandler; }); #[embassy_executor::task] async fn logger_task(usb: embassy_rp::Peri<'static, embassy_rp::peripherals::USB>) { let driver = embassy_rp::usb::Driver::new(usb, Irqs); embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver); } enum QuestionType { Choice, Numeric { min: i32, max: i32 }, } struct QuestionData { text: OwnedStr<256>, q_type: QuestionType, points: i32, generation: usize, } #[derive(Clone, Copy)] struct WheelData { value: i32, min: i32, max: i32, } static QUESTION: Mutex> = Mutex::new(None); static QUESTION_UPDATE: Signal = Signal::new(); static WHEEL_VALUE: Mutex = Mutex::new(WheelData { value: 0, min: 0, max: 0, }); pub async fn tcp_read_loop(mut read: TcpReader<'_>) { let mut buf = [0u8; 1024]; let mut generation = 1; while let Ok(len) = read.read(&mut buf).await { if len == 0 { break; } let Ok(str) = str::from_utf8(&buf[..len]) else { continue; }; let mut counter = 0; let mut question_data = None; let mut future_wheel = WheelData { value: 0, min: 0, max: 0, }; for line in str.lines() { if line == "$$" { counter = 1; continue; } if counter == 1 { let mut q_type = QuestionType::Choice; let mut points = -1; for pairs in line.split(" ") { let (key, value) = pairs.split_once("=").unwrap(); if key == "type" { q_type = if value == "choice" { QuestionType::Choice } else { QuestionType::Numeric { min: -1, max: -1 } }; } if key == "points" { points = value.parse().unwrap(); } if key == "rangeMin" || key == "rangeMax" { match q_type { QuestionType::Choice => {} QuestionType::Numeric { ref mut min, ref mut max, } => { if key == "rangeMin" { *min = value.parse().unwrap(); future_wheel.min = *min; } else { *max = value.parse().unwrap(); future_wheel.max = *max; } } } } } match q_type { QuestionType::Numeric { min, max } => { let diff = max - min; future_wheel.value = min + diff / 2; } _ => {} } question_data = Some(QuestionData { text: OwnedStr::new(), q_type, points, generation, }); counter = 2; continue; } if counter == 2 { question_data.as_mut().unwrap().text = OwnedStr::from_str(line).unwrap(); generation += 1; counter = 0; } } if let Some(question_data) = question_data { *QUESTION.lock().await = Some(question_data); *WHEEL_VALUE.lock().await = future_wheel; QUESTION_UPDATE.signal(()); } } } pub async fn tcp_write_loop(mut write: TcpWriter<'_>) { let mut buffer = WriteBuffer::<256>::new(); loop { let data = INPUT.receive().await; info!("button={}", data); let angle = *ANGLE.lock().await; core::writeln!(buffer, "button={} angle={}", data, angle).ok(); info!("write: {}", &buffer); write.write(&buffer).await.ok(); buffer.clear(); } } const ARROW_RIGHT: char = char::from_u32(0b0111_1110).unwrap(); const ARROW_LEFT: char = char::from_u32(0b0111_1111).unwrap(); const DOT: char = char::from_u32(0b1010_0101).unwrap(); #[embassy_executor::task] pub async fn main_loop() { let mut last_gen = 0; let mut title_offset = 0; info!("Main loop started"); loop { Timer::after_millis(50).await; let wheel = *WHEEL_VALUE.lock().await; let question = QUESTION.lock().await; let Some(question) = question.as_ref() else { continue; }; title_offset += 1; if question.generation != last_gen { last_gen = question.generation; title_offset = 0; } let title_line = if question.text.len() > 16 { title_offset %= question.text.len() - 16; &question.text[title_offset..title_offset + 16] } else { &question.text }; let number_str: OwnedStr<16> = match question.q_type { QuestionType::Choice => { let mut writer = OwnedStrWriter::new(); writer.push(DOT).unwrap(); writer.push(' ').unwrap(); uwrite!(writer, "{}", question.points).unwrap(); writer.into() } QuestionType::Numeric { min, max } => { let mut writer = OwnedStrWriter::new(); if wheel.value > min { writer.push(ARROW_LEFT).unwrap(); writer.push(' ').unwrap(); } uwrite!(writer, "{}", wheel.value).unwrap(); if wheel.value < max { writer.push(ARROW_RIGHT).unwrap(); writer.push(' ').unwrap(); } writer.into() } }; let second_line = center_str::<16>(&number_str, 16).unwrap(); info!("lcd: {} {}", title_line, second_line); overwrite_lcd(title_line, &second_line).await; } } static mut CORE1_STACK: Stack<4096> = Stack::new(); // static EXECUTOR0: StaticCell = StaticCell::new(); static EXECUTOR1: StaticCell = StaticCell::new(); // #[cortex_m_rt::entry] #[embassy_executor::main] async fn main(spawner: Spawner) -> () { let p = embassy_rp::init(Default::default()); let lcd: LcdDisplay<_, _> = LcdDisplay::new( Output::new(p.PIN_10, Level::Low), Output::new(p.PIN_11, Level::Low), Delay, ) .with_half_bus( Output::new(p.PIN_12, Level::Low), Output::new(p.PIN_13, Level::Low), Output::new(p.PIN_14, Level::Low), Output::new(p.PIN_15, Level::Low), ) .with_autoscroll(ag_lcd::AutoScroll::Off) .with_display(Display::On) .with_blink(Blink::On) .with_cursor(Cursor::On) .with_lines(ag_lcd::Lines::TwoLines); spawn_core1( p.CORE1, unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) }, move || { let executor1 = EXECUTOR1.init(Executor::new()); executor1.run(|spawner| { let mut lcd = lcd.build(); lcd.set_blink(Blink::Off); lcd.set_cursor(Cursor::Off); spawner.spawn(unwrap!(lcd_display_task(lcd))); }); }, ); // let executor0 = EXECUTOR0.init(Executor::new()); // executor0.run(|spawner| { spawner.spawn(unwrap!(logger_task(p.USB))); spawner.spawn(unwrap!(network_setup_task( p.PIN_23, p.PIN_25, p.PIO0, p.PIN_24, p.PIN_29, p.DMA_CH0, spawner ))); // let mut lcd = lcd.build(); // lcd.set_blink(Blink::Off); // lcd.set_cursor(Cursor::Off); // spawner.spawn(unwrap!(lcd_display_task(lcd))); crate::input::setup( spawner, InputConfig { i2c0: p.I2C0, scl: p.PIN_5, sda: p.PIN_4, button_pins: [ (p.PIN_18.into(), 1), (p.PIN_19.into(), 2), (p.PIN_20.into(), 3), (p.PIN_21.into(), 4), ], }, ); // }); }