diff --git a/dev-proxy/index.ts b/dev-proxy/index.ts index cc50584..9f2beb1 100644 --- a/dev-proxy/index.ts +++ b/dev-proxy/index.ts @@ -67,4 +67,4 @@ ws.onmessage = (e) => { } }; -console.log("Started on :7070"); +console.log(`Started on :${socket.port}`); diff --git a/pico/src/input.rs b/pico/src/input.rs index 45f187e..1fa4f2d 100644 --- a/pico/src/input.rs +++ b/pico/src/input.rs @@ -9,7 +9,7 @@ use embassy_rp::{ use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, channel::Channel, mutex::Mutex}; use embassy_time::{Duration, Timer}; -use crate::{Irqs, unwrap}; +use crate::{Irqs, WHEEL_VALUE, unwrap}; /// Button input notification channel pub static INPUT: Channel = Channel::new(); @@ -43,10 +43,20 @@ pub async fn rotation_read_task( let left_diff = old - angle as i32; let right_diff = angle as i32 - old; let diff = if left_diff.abs() < right_diff.abs() { - left_diff + -left_diff } else { right_diff }; + let mut wheel = WHEEL_VALUE.lock().await; + if wheel.max != wheel.min { + let wheel_range = wheel.max - wheel.min; + wheel.value += diff * wheel_range / 4096; + if wheel.value < wheel.min { + wheel.value = wheel.min; + } else if wheel.value > wheel.max { + wheel.value = wheel.max; + } + } Timer::after(Duration::from_millis(50)).await; } } diff --git a/pico/src/main.rs b/pico/src/main.rs index bef0558..5405fb0 100644 --- a/pico/src/main.rs +++ b/pico/src/main.rs @@ -2,6 +2,7 @@ #![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; @@ -9,11 +10,13 @@ use embassy_sync::mutex::Mutex; use embassy_sync::signal::Signal; use embedded_io::Write; 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::screen::lcd_display_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; @@ -23,7 +26,7 @@ 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; +use embassy_time::{Delay, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -31,6 +34,7 @@ use {defmt_rtt as _, panic_probe as _}; mod buffer; mod input; mod net; +mod owned_str_writer; mod screen; const WIFI_NETWORK: &str = "flamme"; @@ -61,14 +65,27 @@ 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(0); +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 { @@ -79,7 +96,11 @@ pub async fn tcp_read_loop(mut read: TcpReader<'_>) { }; let mut counter = 0; let mut question_data = None; - let mut future_wheel = 0; + let mut future_wheel = WheelData { + value: 0, + min: 0, + max: 0, + }; for line in str.lines() { if line == "$$" { counter = 1; @@ -110,8 +131,10 @@ pub async fn tcp_read_loop(mut read: TcpReader<'_>) { } => { if key == "rangeMin" { *min = value.parse().unwrap(); + future_wheel.min = *min; } else { *max = value.parse().unwrap(); + future_wheel.max = *max; } } } @@ -121,7 +144,7 @@ pub async fn tcp_read_loop(mut read: TcpReader<'_>) { match q_type { QuestionType::Numeric { min, max } => { let diff = max - min; - future_wheel = min + diff / 2; + future_wheel.value = min + diff / 2; } _ => {} } @@ -130,12 +153,14 @@ pub async fn tcp_read_loop(mut read: TcpReader<'_>) { 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; } } @@ -159,6 +184,60 @@ pub async fn tcp_write_loop(mut write: TcpWriter<'_>) { } } +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; + 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); + writer.push(' '); + uwrite!(writer, "{}", question.points).unwrap(); + writer.into() + } + QuestionType::Numeric { min, max } => { + let mut writer = OwnedStrWriter::new(); + if wheel.value > min { + writer.push(ARROW_LEFT); + writer.push(' '); + } + uwrite!(writer, "{}", wheel.value); + if wheel.value < max { + writer.push(ARROW_RIGHT); + writer.push(' '); + } + + writer.into() + } + }; + let second_line = center_str::<16>(&number_str, 16).unwrap(); + overwrite_lcd(title_line, &second_line); + } +} + static mut CORE1_STACK: Stack<4096> = Stack::new(); static EXECUTOR0: StaticCell = StaticCell::new(); static EXECUTOR1: StaticCell = StaticCell::new(); diff --git a/pico/src/net.rs b/pico/src/net.rs index 1aaec09..f3a607a 100644 --- a/pico/src/net.rs +++ b/pico/src/net.rs @@ -6,7 +6,8 @@ use embassy_net::tcp::TcpSocket; use crate::buffer::wait_for_config; use crate::screen::SCREEN_BUFFER; use crate::{ - Irqs, TARGET_IP, TARGET_PORT, WIFI_NETWORK, WIFI_PASSWORD, tcp_read_loop, tcp_write_loop, + Irqs, TARGET_IP, TARGET_PORT, WIFI_NETWORK, WIFI_PASSWORD, main_loop, tcp_read_loop, + tcp_write_loop, }; use cyw43::{JoinOptions, aligned_bytes}; use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi}; @@ -131,5 +132,7 @@ pub async fn network_setup_task( let (read, write) = socket.split(); + spawner.spawn(unwrap!(main_loop())); + join(tcp_read_loop(read), tcp_write_loop(write)).await; } diff --git a/pico/src/owned_str_writer.rs b/pico/src/owned_str_writer.rs new file mode 100644 index 0000000..c740050 --- /dev/null +++ b/pico/src/owned_str_writer.rs @@ -0,0 +1,50 @@ +use owned_str::{Error, OwnedStr}; +use ufmt::uWrite; + +pub struct OwnedStrWriter(OwnedStr); + +impl OwnedStrWriter { + pub fn new() -> Self { + Self(OwnedStr::new()) + } + + pub fn push(&mut self, c: char) -> Result<(), Error> { + self.0.try_push(c).map(|_| ()) + } +} + +impl From> for OwnedStrWriter { + fn from(owned_str: OwnedStr) -> Self { + Self(owned_str) + } +} + +impl From> for OwnedStr { + fn from(writer: OwnedStrWriter) -> Self { + writer.0 + } +} + +impl uWrite for OwnedStrWriter { + type Error = owned_str::Error; + fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { + self.0.try_push_str(s).map(|_| ()) + } + fn write_char(&mut self, c: char) -> Result<(), Self::Error> { + self.0.try_push(c).map(|_| ()) + } +} + +pub fn center_str( + str: &str, + width: usize, +) -> Result, owned_str::Error> { + let mut res = OwnedStr::new(); + let len = str.len(); + let padding = (width.saturating_sub(len) + 1) / 2; + for _ in 0..padding { + res.try_push(' ')?; + } + res.try_push_str(str)?; + Ok(res) +} diff --git a/pico/src/screen.rs b/pico/src/screen.rs index 201e1cf..6be00ba 100644 --- a/pico/src/screen.rs +++ b/pico/src/screen.rs @@ -1,7 +1,6 @@ use core::convert::Infallible; use ag_lcd::LcdDisplay; -use embassy_futures::block_on; use embassy_rp::gpio::Output; use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex, signal::Signal}; use embassy_time::{Delay, Timer}; @@ -94,7 +93,11 @@ pub async fn lcd_display_task(mut lcd: LcdDisplay, Delay>) { pub async fn overwrite_lcd(line1: &str, line2: &str) { let mut buffer = SCREEN_BUFFER.lock().await; - buffer.line1.copy_from_slice(line1.as_bytes()); - buffer.line2.copy_from_slice(line2.as_bytes()); + buffer.line1_ptr = 0; + buffer.line2_ptr = 0; + buffer.line1.fill(0); + buffer.line2.fill(0); + buffer.line1[..line1.len()].copy_from_slice(line1.as_bytes()); + buffer.line2[..line2.len()].copy_from_slice(line2.as_bytes()); LCD_UPDATE.signal(()); }