diff --git a/esp32/Cargo.lock b/esp32/Cargo.lock index 3bb3b51..066a390 100644 --- a/esp32/Cargo.lock +++ b/esp32/Cargo.lock @@ -1007,7 +1007,7 @@ dependencies = [ "embassy-time", "embedded-hal-compat", "embedded-io 0.7.1", - "embedded-io-async 0.7.0", + "embedded-io-async 0.6.1", "esp-alloc", "esp-backtrace", "esp-bootloader-esp-idf", diff --git a/esp32/Cargo.toml b/esp32/Cargo.toml index eb11b7c..dda3118 100644 --- a/esp32/Cargo.toml +++ b/esp32/Cargo.toml @@ -13,6 +13,13 @@ codegen-units = 1 [dependencies] [target.'cfg(target_arch = "xtensa")'.dependencies] arrayvec = { version = "0.7.6", default-features = false } +ag-lcd = { version = "0.3", features = ["ufmt"] } +as5600 = "0.8.0" +embassy-executor = { version = "0.10.0", default-features = false } +embassy-futures = "0.1.2" +embassy-net = { version = "0.9.1", features = ["dhcpv4", "dns", "icmp", "tcp", "udp", "raw", "proto-ipv4", "proto-ipv6", "multicast"] } +embassy-sync = "0.8.0" +embedded-io-async = "0.6.1" embedded-io = "0.7.1" esp-bootloader-esp-idf = { version = "0.5.0", features = ["esp32"] } esp-hal = { version = "1.1.1", features = ["esp32", "rt", "unstable"] } @@ -20,7 +27,6 @@ esp-alloc = { version = "0.10.0", features = ["esp32"] } esp-println = { version = "0.17.0", features = ["esp32"] } esp-radio = { version = "0.18.0", features = ["esp32", "wifi", "esp-alloc", "unstable"] } esp-rtos = { version = "0.3.0", features = ["esp32", "embassy", "esp-alloc", "esp-radio"] } -embassy-executor = { version = "0.10.0", default-features = false } embassy-time = { version = "0.5.1", features = [ "defmt", "defmt-timestamp-uptime", @@ -28,6 +34,8 @@ embassy-time = { version = "0.5.1", features = [ esp-backtrace = { version = "0.19.0", features = ["esp32", "println"] } static_cell = "2.1.1" log = "0.4" +ufmt = "0.2.0" +owned_str = "0.1.2" [target.'cfg(target_arch = "arm")'.dependencies] embedded-hal-compat = "0.13.0" @@ -36,7 +44,7 @@ log = "0.4" ag-lcd={ version = "0.3", features = ["ufmt"]} as5600 = "0.8.0" embassy-futures = "0.1.2" -embedded-io-async = "0.7.0" +embedded-io-async = "0.6.1" embedded-io = "0.7.1" owned_str = "0.1.2" embassy-sync = "0.8.0" diff --git a/esp32/src/input.rs b/esp32/src/input.rs index 8bf35d9..1ed4a07 100644 --- a/esp32/src/input.rs +++ b/esp32/src/input.rs @@ -1,41 +1,30 @@ use as5600::As5600; -use embassy_executor::Spawner; -use embassy_rp::{ - Peri, - gpio::{AnyPin, Input}, - i2c::{Config, I2c}, - peripherals::{I2C0, PIN_4, PIN_5}, -}; use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, channel::Channel, mutex::Mutex}; use embassy_time::{Duration, Timer}; -use ufmt::uwrite; +use esp_hal::gpio::Input; +use esp_hal::i2c::master::{Config, I2c}; +use esp_hal::peripherals::{GPIO4, GPIO5, I2C0}; +use esp_hal::time::Rate; +use esp_println::println; -use crate::{Irqs, WHEEL_VALUE, screen::SCREEN_BUFFER, unwrap}; +use crate::WHEEL_VALUE; -/// Button input notification channel pub static INPUT: Channel = Channel::new(); - -#[embassy_executor::task(pool_size = 4)] -/// Polls a single pin for falling edge (assumes buttons are pulled up and shorted when pressed) -/// Sends [INPUT] with given id -pub async fn button_poll_task(mut button: Input<'static>, id: u8) { - loop { - button.wait_for_falling_edge().await; - INPUT.send(id).await; - // uwrite!(SCREEN_BUFFER.lock().await.line1(), "btn {}", id); - Timer::after(Duration::from_millis(50)).await; - } -} - pub static ANGLE: Mutex = Mutex::new(0); +pub struct RotationConfig { + pub i2c0: I2C0<'static>, + pub scl: GPIO5<'static>, + pub sda: GPIO4<'static>, +} + #[embassy_executor::task] -pub async fn rotation_read_task( - i2c: Peri<'static, I2C0>, - scl: Peri<'static, PIN_5>, - sda: Peri<'static, PIN_4>, -) { - let i2c = I2c::new_async(i2c, scl, sda, Irqs, Config::default()); +pub async fn rotation_read_task(config: RotationConfig) { + let i2c = I2c::new(config.i2c0, Config::default().with_frequency(Rate::from_khz(400))) + .unwrap() + .with_sda(config.sda) + .with_scl(config.scl) + .into_async(); let mut as5600 = As5600::new(i2c); loop { @@ -47,11 +36,7 @@ pub async fn rotation_read_task( drop(locked); 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 - } else { - right_diff - }; + let diff = if left_diff.abs() < right_diff.abs() { -left_diff } else { right_diff }; let mut wheel = WHEEL_VALUE.lock().await; if wheel.max != wheel.min { let wheel_range = wheel.max - wheel.min; @@ -62,29 +47,17 @@ pub async fn rotation_read_task( wheel.value = wheel.max; } } - drop(wheel); } } -pub struct InputConfig { - pub i2c0: Peri<'static, I2C0>, - pub scl: Peri<'static, PIN_5>, - pub sda: Peri<'static, PIN_4>, - - pub button_pins: [(Peri<'static, AnyPin>, u8); 4], -} - -pub fn setup(spawner: Spawner, config: InputConfig) { - spawner.spawn(unwrap!(rotation_read_task( - config.i2c0, - config.scl, - config.sda, - ))); - - for (pin, id) in config.button_pins { - spawner.spawn(unwrap!(button_poll_task( - Input::new(pin, embassy_rp::gpio::Pull::Up), - id, - ))); +#[embassy_executor::task(pool_size = 4)] +pub async fn button_task(button: Input<'static>, id: u8) { + loop { + if button.is_low() { + INPUT.send(id).await; + println!("button={}", id); + Timer::after(Duration::from_millis(50)).await; + } + Timer::after(Duration::from_millis(10)).await; } } diff --git a/esp32/src/main.rs b/esp32/src/main.rs index b856ebb..47ee9be 100644 --- a/esp32/src/main.rs +++ b/esp32/src/main.rs @@ -3,60 +3,33 @@ use core::str::FromStr; +use embassy_executor::Spawner; 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 esp_backtrace as _; +use esp_hal::{ + gpio::{Input, InputConfig as GpioInputConfig, Pin, Pull}, +}; +use esp_println::println; 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; +pub use input::ANGLE; + 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 }, @@ -84,6 +57,11 @@ static WHEEL_VALUE: Mutex = Mutex::new(Wheel max: 0, }); +#[panic_handler] +fn panic(_: &core::panic::PanicInfo) -> ! { + loop {} +} + pub async fn tcp_read_loop(mut read: TcpReader<'_>) { let mut buf = [0u8; 1024]; let mut generation = 1; @@ -92,7 +70,7 @@ pub async fn tcp_read_loop(mut read: TcpReader<'_>) { if len == 0 { break; } - let Ok(str) = str::from_utf8(&buf[..len]) else { + let Ok(str) = core::str::from_utf8(&buf[..len]) else { continue; }; let mut counter = 0; @@ -111,8 +89,8 @@ pub async fn tcp_read_loop(mut read: TcpReader<'_>) { let mut q_type = QuestionType::Choice; let mut points = -1; - for pairs in line.split(" ") { - let (key, value) = pairs.split_once("=").unwrap(); + for pairs in line.split(' ') { + let (key, value) = pairs.split_once('=').unwrap(); if key == "type" { q_type = if value == "choice" { QuestionType::Choice @@ -126,10 +104,7 @@ pub async fn tcp_read_loop(mut read: TcpReader<'_>) { if key == "rangeMin" || key == "rangeMax" { match q_type { QuestionType::Choice => {} - QuestionType::Numeric { - ref mut min, - ref mut max, - } => { + QuestionType::Numeric { ref mut min, ref mut max } => { if key == "rangeMin" { *min = value.parse().unwrap(); future_wheel.min = *min; @@ -142,12 +117,9 @@ pub async fn tcp_read_loop(mut read: TcpReader<'_>) { } } - match q_type { - QuestionType::Numeric { min, max } => { - let diff = max - min; - future_wheel.value = min + diff / 2; - } - _ => {} + if let QuestionType::Numeric { min, max } = q_type { + let diff = max - min; + future_wheel.value = min + diff / 2; } question_data = Some(QuestionData { @@ -175,13 +147,13 @@ pub async fn tcp_read_loop(mut read: TcpReader<'_>) { } pub async fn tcp_write_loop(mut write: TcpWriter<'_>) { - let mut buffer = WriteBuffer::<256>::new(); + let mut buffer = buffer::WriteBuffer::<256>::new(); loop { - let data = INPUT.receive().await; - info!("button={}", data); + let data = input::INPUT.receive().await; + println!("button={}", data); let angle = *ANGLE.lock().await; core::writeln!(buffer, "button={} angle={}", data, angle).ok(); - info!("write: {}", &buffer); + println!("write: {}", &buffer); write.write(&buffer).await.ok(); buffer.clear(); } @@ -195,9 +167,9 @@ const DOT: char = char::from_u32(0b1010_0101).unwrap(); pub async fn main_loop() { let mut last_gen = 0; let mut title_offset = 0; - info!("Main loop started"); + println!("Main loop started"); loop { - Timer::after_millis(50).await; + embassy_time::Timer::after_millis(50).await; let wheel = *WHEEL_VALUE.lock().await; let question = QUESTION.lock().await; let Some(question) = question.as_ref() else { @@ -216,14 +188,14 @@ pub async fn main_loop() { }; let number_str: OwnedStr<16> = match question.q_type { QuestionType::Choice => { - let mut writer = OwnedStrWriter::new(); + let mut writer = owned_str_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(); + let mut writer = owned_str_writer::OwnedStrWriter::new(); if wheel.value > min { writer.push(ARROW_LEFT).unwrap(); writer.push(' ').unwrap(); @@ -237,76 +209,48 @@ pub async fn main_loop() { 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; + let second_line = owned_str_writer::center_str::<16>(&number_str, 16).unwrap(); + println!("lcd: {} {}", title_line, second_line); + screen::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(); +#[esp_rtos::main] +async fn main(spawner: Spawner) -> ! { + let p = esp_hal::init(esp_hal::Config::default().with_cpu_clock(esp_hal::clock::CpuClock::max())); -// #[cortex_m_rt::entry] + esp_alloc::heap_allocator!(size: 64 * 1024); -#[embassy_executor::main] -async fn main(spawner: Spawner) -> () { - let p = embassy_rp::init(Default::default()); + let lcd = screen::LcdPins { + rs: p.GPIO10, + e: p.GPIO11, + d4: p.GPIO12, + d5: p.GPIO13, + d6: p.GPIO14, + d7: p.GPIO15, + }; - 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); + spawner.spawn(screen::lcd_display_task(screen::LcdConfig { pins: lcd }).expect("spawn lcd")); + spawner.spawn(input::rotation_read_task(input::RotationConfig { + i2c0: p.I2C0, + scl: p.GPIO5, + sda: p.GPIO4, + }).expect("spawn rotation")); - 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))); - }); - }, - ); + spawner.spawn(input::button_task(Input::new(p.GPIO18.degrade(), GpioInputConfig::default().with_pull(Pull::Up)), 1).expect("spawn btn1")); + spawner.spawn(input::button_task(Input::new(p.GPIO19.degrade(), GpioInputConfig::default().with_pull(Pull::Up)), 2).expect("spawn btn2")); + spawner.spawn(input::button_task(Input::new(p.GPIO20.degrade(), GpioInputConfig::default().with_pull(Pull::Up)), 3).expect("spawn btn3")); + spawner.spawn(input::button_task(Input::new(p.GPIO21.degrade(), GpioInputConfig::default().with_pull(Pull::Up)), 4).expect("spawn btn4")); - // 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), - ], - }, - ); - // }); + spawner.spawn(net::network_setup_task(spawner, net::NetworkConfig { + wifi: p.WIFI, + wifi_network: WIFI_NETWORK, + wifi_password: WIFI_PASSWORD, + target_ip: TARGET_IP, + target_port: TARGET_PORT, + }).expect("spawn net")); + + loop { + embassy_time::Timer::after_millis(1000).await; + } } diff --git a/esp32/src/net.rs b/esp32/src/net.rs index d7dd4f9..58943f7 100644 --- a/esp32/src/net.rs +++ b/esp32/src/net.rs @@ -1,183 +1,81 @@ use core::net::SocketAddrV4; use core::str::FromStr; + use embassy_futures::join::join; use embassy_net::tcp::TcpSocket; +use embassy_time::Duration; +use esp_hal::peripherals::WIFI; +use esp_hal::rng::Rng; +use esp_println::println; +use esp_radio::wifi::{scan::ScanConfig, Config, ControllerConfig}; +use esp_radio::wifi::sta::StationConfig; -use crate::buffer::wait_for_config; -use crate::screen::SCREEN_BUFFER; -use crate::{ - Irqs, TARGET_IP, TARGET_PORT, WIFI_NETWORK, WIFI_PASSWORD, main_loop, tcp_read_loop, - tcp_write_loop, -}; -use cyw43::{JoinOptions, ScanOptions, aligned_bytes}; -use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi}; -use defmt::*; -use embassy_executor::Spawner; -use embassy_net::StackResources; -use embassy_rp::clocks::RoscRng; -use embassy_rp::gpio::{Level, Output}; -use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_24, PIN_25, PIN_29, PIO0}; -use embassy_rp::pio::Pio; -use embassy_rp::{Peri, dma}; -use embassy_time::{Duration, Timer}; -use static_cell::StaticCell; -use ufmt::uwrite; +use crate::{buffer::wait_for_config, tcp_read_loop, tcp_write_loop}; + +pub struct NetworkConfig<'a> { + pub wifi: WIFI<'a>, + pub wifi_network: &'a str, + pub wifi_password: &'a str, + pub target_ip: &'a str, + pub target_port: u16, +} #[embassy_executor::task] -async fn cyw43_task( - runner: cyw43::Runner<'static, cyw43::SpiBus, PioSpi<'static, PIO0, 0>>>, -) -> ! { +async fn net_task(mut runner: embassy_net::Runner<'static, esp_radio::wifi::Interface<'static>>) -> ! { runner.run().await } #[embassy_executor::task] -async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! { - runner.run().await -} - -#[embassy_executor::task] -pub async fn network_setup_task( - pin23: Peri<'static, PIN_23>, - pin25: Peri<'static, PIN_25>, - pio: Peri<'static, PIO0>, - pin24: Peri<'static, PIN_24>, - pin29: Peri<'static, PIN_29>, - dma: Peri<'static, DMA_CH0>, - spawner: Spawner, -) { - let fw = aligned_bytes!("../firmware/43439A0.bin"); - let clm = aligned_bytes!("../firmware/43439A0_clm.bin"); - let nvram = aligned_bytes!("../firmware/nvram_rp2040.bin"); - let mut rng = RoscRng; - - // To make flashing faster for development, you may want to flash the firmwares independently - // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: - // probe-rs download ../../cyw43-firmware/43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000 - // probe-rs download ../../cyw43-firmware/43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000 - //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) }; - //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; - - let pwr = Output::new(pin23, Level::Low); - let cs = Output::new(pin25, Level::High); - let mut pio = Pio::new(pio, Irqs); - let spi = PioSpi::new( - &mut pio.common, - pio.sm0, - DEFAULT_CLOCK_DIVIDER, - pio.irq0, - cs, - pin24, - pin29, - dma::Channel::new(dma, Irqs), +pub async fn network_setup_task(spawner: embassy_executor::Spawner, config: NetworkConfig<'static>) { + let wifi_config = Config::Station( + StationConfig::default() + .with_ssid(config.wifi_network) + .with_password(config.wifi_password.into()), ); - static STATE: StaticCell = StaticCell::new(); - let state = STATE.init(cyw43::State::new()); - let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw, nvram).await; - spawner.spawn(unwrap!(cyw43_task(runner))); + println!("Starting wifi"); + let (mut controller, interfaces) = esp_radio::wifi::new( + config.wifi, + ControllerConfig::default().with_initial_config(wifi_config), + ) + .unwrap(); + println!("Wifi configured and started!"); - control.init(clm).await; - control - .set_power_management(cyw43::PowerManagementMode::Performance) - .await; + let wifi_interface = interfaces.station; + let net_config = embassy_net::Config::dhcpv4(Default::default()); + let rng = Rng::new(); + let seed = (rng.random() as u64) << 32 | rng.random() as u64; - static RESOURCES: StaticCell> = StaticCell::new(); let (stack, runner) = embassy_net::new( - net_device, - embassy_net::Config::dhcpv4(Default::default()), - RESOURCES.init(StackResources::new()), - rng.next_u64(), + wifi_interface, + net_config, + crate::screen::stack_resources(), + seed, ); - spawner.spawn(unwrap!(net_task(runner))); - Timer::after_millis(1000).await; - uwrite!(SCREEN_BUFFER.lock().await.line1(), "con - scan"); + spawner.spawn(net_task(runner).expect("spawn net task")); - info!("scan started"); - - let mut opts = ScanOptions::default(); - opts.scan_type = cyw43::ScanType::Active; - let mut scanner = control.scan(opts).await; - - while let Some(bss) = scanner.next().await { - let mut buf = SCREEN_BUFFER.lock().await; - if let Ok(ssid_str) = str::from_utf8(&bss.ssid) { - info!("scanned {} == {:x}", ssid_str, bss.bssid); - uwrite!(buf.line1(), "{}", ssid_str); - // uwrite!(buf.line2(), "{:x}", bss.bssid); - } else { - info!("scanned {:x}", bss.bssid); - uwrite!(buf.line1(), "not-utf8"); - // uwrite!(buf.line2(), "{:x}", bss.bssid); - } - drop(buf); - Timer::after_millis(500).await; + println!("Scan"); + let scan_config = ScanConfig::default().with_max(10); + let result = controller.scan_async(&scan_config).await.unwrap(); + for ap in result { + println!("{:?}", ap); } - info!("scan completed"); - - uwrite!(SCREEN_BUFFER.lock().await.line1(), "scan end."); - Timer::after_millis(500).await; - drop(scanner); - - while let Err(err) = control - .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes())) - .await - { - let mut buf = SCREEN_BUFFER.lock().await; - buf.clear(); - match err { - cyw43::JoinError::AuthenticationFailure => { - uwrite!(buf.line1(), "join failure"); - uwrite!(buf.line2(), "bad password"); - info!("authentication failure"); - } - cyw43::JoinError::JoinFailure(e) => { - uwrite!(buf.line1(), "join failure"); - uwrite!(buf.line2(), "code {}", e); - info!("join failure: {}", e); - } - cyw43::JoinError::NetworkNotFound => { - uwrite!(buf.line1(), "join failure"); - uwrite!(buf.line2(), "wifi not found"); - info!("network not found"); - } - }; - Timer::after_millis(200).await; - } - - let mut buf = SCREEN_BUFFER.lock().await; - buf.clear(); - uwrite!(buf.line1(), "link."); - drop(buf); - - stack.wait_link_up().await; - - uwrite!(SCREEN_BUFFER.lock().await.line1(), "dhcp."); - stack.wait_config_up().await; let cfg = wait_for_config(stack).await; let local_addr = cfg.address.address(); - info!("IP address: {:?}", local_addr); + println!("IP address: {:?}", local_addr); - let host_addr = embassy_net::Ipv4Address::from_str(TARGET_IP).unwrap(); + let host_addr = embassy_net::Ipv4Address::from_str(config.target_ip).unwrap(); let mut rx_buffer = [0; 4096]; let mut tx_buffer = [0; 4096]; let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); socket.set_timeout(Some(Duration::from_secs(10))); socket.set_keep_alive(Some(Duration::from_secs(1))); - if let Err(e) = socket - .connect(SocketAddrV4::new(host_addr, TARGET_PORT)) - .await - { - uwrite!(SCREEN_BUFFER.lock().await.line1(), "tcperr"); - error!("tcp connect error: {}", e); - } else { - uwrite!(SCREEN_BUFFER.lock().await.line1(), "tcpok"); + if let Err(e) = socket.connect(SocketAddrV4::new(host_addr, config.target_port)).await { + println!("tcp connect error: {:?}", e); } let (read, write) = socket.split(); - - spawner.spawn(unwrap!(main_loop())); - join(tcp_read_loop(read), tcp_write_loop(write)).await; } diff --git a/esp32/src/screen.rs b/esp32/src/screen.rs index 6be00ba..2e5793e 100644 --- a/esp32/src/screen.rs +++ b/esp32/src/screen.rs @@ -1,11 +1,41 @@ use core::convert::Infallible; use ag_lcd::LcdDisplay; -use embassy_rp::gpio::Output; use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex, signal::Signal}; use embassy_time::{Delay, Timer}; +use esp_hal::gpio::{Level, Output, OutputConfig}; +use esp_hal::peripherals::{GPIO10, GPIO11, GPIO12, GPIO13, GPIO14, GPIO15}; use ufmt::uWrite; +pub struct LcdPins { + pub rs: GPIO10<'static>, + pub e: GPIO11<'static>, + pub d4: GPIO12<'static>, + pub d5: GPIO13<'static>, + pub d6: GPIO14<'static>, + pub d7: GPIO15<'static>, +} + +pub struct LcdConfig { + pub pins: LcdPins, +} + +pub async fn overwrite_lcd(line1: &str, line2: &str) { + let mut buffer = SCREEN_BUFFER.lock().await; + 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(()); +} + +pub fn stack_resources() -> &'static mut embassy_net::StackResources<5> { + static RESOURCES: static_cell::StaticCell> = static_cell::StaticCell::new(); + RESOURCES.init(embassy_net::StackResources::new()) +} + pub struct ScreenBuffer { line1: [u8; 16], line2: [u8; 16], @@ -20,84 +50,40 @@ impl ScreenBuffer { self.line1_ptr = 0; self.line2_ptr = 0; } - - pub fn line1(&mut self) -> ScreenBufferWriter<'_> { - ScreenBufferWriter { buf: self, line: 0 } - } - - pub fn line2(&mut self) -> ScreenBufferWriter<'_> { - ScreenBufferWriter { buf: self, line: 1 } - } -} - -pub struct ScreenBufferWriter<'a> { - buf: &'a mut ScreenBuffer, - line: u8, -} - -impl<'a> uWrite for ScreenBufferWriter<'a> { - type Error = Infallible; - - fn write_char(&mut self, c: char) -> Result<(), Self::Error> { - if self.line == 0 { - let ptr = self.buf.line1_ptr as usize; - self.buf.line1[ptr] = c as u8; - self.buf.line1_ptr = (self.buf.line1_ptr + 1) % 16; - } else { - let ptr = self.buf.line2_ptr as usize; - self.buf.line2[ptr] = c as u8; - self.buf.line2_ptr = (self.buf.line2_ptr + 1) % 16; - } - Ok(()) - } - - fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { - for c in s.chars() { - self.write_char(c)?; - } - Ok(()) - } -} - -impl Drop for ScreenBufferWriter<'_> { - fn drop(&mut self) { - LCD_UPDATE.signal(()); - } } pub static SCREEN_BUFFER: Mutex = Mutex::new(ScreenBuffer { - line1: [0; 16], - line1_ptr: 0, - line2: [0; 16], - line2_ptr: 0, + line1: [0; 16], line1_ptr: 0, line2: [0; 16], line2_ptr: 0, }); - static LCD_UPDATE: Signal = Signal::new(); #[embassy_executor::task] -pub async fn lcd_display_task(mut lcd: LcdDisplay, Delay>) { +pub async fn lcd_display_task(config: LcdConfig) { + let mut lcd = LcdDisplay::new( + Output::new(config.pins.rs, Level::Low, OutputConfig::default()), + Output::new(config.pins.e, Level::Low, OutputConfig::default()), + Delay, + ) + .with_half_bus( + Output::new(config.pins.d4, Level::Low, OutputConfig::default()), + Output::new(config.pins.d5, Level::Low, OutputConfig::default()), + Output::new(config.pins.d6, Level::Low, OutputConfig::default()), + Output::new(config.pins.d7, Level::Low, OutputConfig::default()), + ) + .with_autoscroll(ag_lcd::AutoScroll::Off) + .with_display(ag_lcd::Display::On) + .with_blink(ag_lcd::Blink::On) + .with_cursor(ag_lcd::Cursor::On) + .with_lines(ag_lcd::Lines::TwoLines) + .build(); + loop { LCD_UPDATE.wait().await; let buffer = SCREEN_BUFFER.lock().await; lcd.clear(); - for byte in &buffer.line1 { - lcd.write(*byte); - } + for byte in &buffer.line1 { lcd.write(*byte); } lcd.set_position(0, 1); - for byte in &buffer.line2 { - lcd.write(*byte); - } + for byte in &buffer.line2 { lcd.write(*byte); } Timer::after_millis(20).await; } } - -pub async fn overwrite_lcd(line1: &str, line2: &str) { - let mut buffer = SCREEN_BUFFER.lock().await; - 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(()); -}