initial port
This commit is contained in:
parent
406c6a4235
commit
6b38c36a28
6 changed files with 203 additions and 394 deletions
2
esp32/Cargo.lock
generated
2
esp32/Cargo.lock
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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<CriticalSectionRawMutex, u8, 64> = 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<CriticalSectionRawMutex, u16> = 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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<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 },
|
||||
|
|
@ -84,6 +57,11 @@ static WHEEL_VALUE: Mutex<CriticalSectionRawMutex, WheelData> = 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<Executor> = StaticCell::new();
|
||||
static EXECUTOR1: StaticCell<Executor> = 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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
196
esp32/src/net.rs
196
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<Output<'static>, 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<cyw43::State> = 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<StackResources<5>> = 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<embassy_net::StackResources<5>> = 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<CriticalSectionRawMutex, ScreenBuffer> = 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<CriticalSectionRawMutex, ()> = Signal::new();
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn lcd_display_task(mut lcd: LcdDisplay<Output<'static>, 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(());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue