itpdp/pico/src/main.rs
2026-05-10 16:40:02 +02:00

312 lines
9.6 KiB
Rust

#![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<PIO0>;
DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;
USBCTRL_IRQ => usb::InterruptHandler<USB>;
I2C0_IRQ => i2c::InterruptHandler<I2C0>;
});
#[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<CriticalSectionRawMutex, Option<QuestionData>> = Mutex::new(None);
static QUESTION_UPDATE: Signal<CriticalSectionRawMutex, ()> = Signal::new();
static WHEEL_VALUE: Mutex<CriticalSectionRawMutex, WheelData> = 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<Executor> = StaticCell::new();
static EXECUTOR1: StaticCell<Executor> = 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),
],
},
);
// });
}