Compare commits
3 commits
f0d7abdf0e
...
9a1c09bbf1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a1c09bbf1 | ||
|
|
4dd80e53fd | ||
|
|
b01495ee00 |
7 changed files with 553 additions and 232 deletions
|
|
@ -1,56 +1,70 @@
|
||||||
import type {
|
import type { Socket } from "bun";
|
||||||
PartySocketEvent,
|
import type { PartySocketEvent, PartyState } from "../api/src/party-types";
|
||||||
PartyState,
|
|
||||||
} from "../api/src/party-types";
|
|
||||||
|
|
||||||
let last_ep_port: null|number = null
|
const sockets = new Set<Socket>();
|
||||||
let last_ep_addr: null|string = null
|
let lastData: string;
|
||||||
|
|
||||||
const socket = await Bun.udpSocket({
|
const socket = Bun.listen({
|
||||||
port: 7070,
|
port: 7070,
|
||||||
socket: {
|
hostname: "0.0.0.0",
|
||||||
data(socket, buf, port, addr) {
|
socket: {
|
||||||
// console.log(`message from ${addr}:${port}:`);
|
data(socket, buf) {
|
||||||
last_ep_addr = addr
|
const str = new TextDecoder().decode(buf);
|
||||||
last_ep_port = port
|
},
|
||||||
const str = buf.toString();
|
open(socket) {
|
||||||
console.log(str);
|
sockets.add(socket);
|
||||||
const opts = str.split(" ").map(t => t.trim().split("="))
|
if (lastData) socket.write(lastData);
|
||||||
const parsedOpts = Object.fromEntries(opts)
|
},
|
||||||
const { in1, in2, in3, in4, angle } = parsedOpts
|
close(socket) {
|
||||||
},
|
sockets.delete(socket);
|
||||||
},
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ws: WebSocket | null = new WebSocket(
|
||||||
|
"ws://localhost:4000/api/dev-socket/ws",
|
||||||
|
);
|
||||||
|
|
||||||
let ws: WebSocket | null = new WebSocket("ws://localhost:4000/api/dev-socket/ws");
|
ws.onerror = (e) => {
|
||||||
|
console.error(e);
|
||||||
ws.onerror = e => {
|
};
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
console.log("WebSocket open")
|
console.log("WebSocket open");
|
||||||
}
|
};
|
||||||
|
|
||||||
ws.onmessage = e => {
|
ws.onmessage = (e) => {
|
||||||
const data = JSON.parse(e.data) as PartySocketEvent;
|
const data = JSON.parse(e.data) as PartySocketEvent;
|
||||||
console.log(data)
|
console.log(data);
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case "party_status":
|
case "party_status": {
|
||||||
const { party } = data;
|
const { party } = data;
|
||||||
if (!party) return;
|
if (!party) return;
|
||||||
const partyData = party.data;
|
const partyData = party.data;
|
||||||
if (!partyData) return;
|
if (!partyData) return;
|
||||||
const { currentQuestion } = partyData
|
const { currentQuestion } = partyData;
|
||||||
console.log(currentQuestion)
|
console.log(currentQuestion);
|
||||||
let text = currentQuestion?.text
|
const text = currentQuestion?.text;
|
||||||
if (text && last_ep_port !== null && last_ep_addr !== null) {
|
if (!text) return;
|
||||||
socket.send(text, last_ep_port, last_ep_addr)
|
const obj = {
|
||||||
// ws?.send(text)
|
type: currentQuestion.type,
|
||||||
}
|
points: currentQuestion.points,
|
||||||
break;
|
} as Record<string, string | number>;
|
||||||
}
|
if (currentQuestion.type === "numeric") {
|
||||||
}
|
obj.rangeMin = currentQuestion.range.min;
|
||||||
|
obj.rangeMax = currentQuestion.range.max;
|
||||||
|
}
|
||||||
|
const objText = Object.entries(obj)
|
||||||
|
.map(([k, v]) => `${k}=${v}`)
|
||||||
|
.join(" ");
|
||||||
|
const writeData = `$$\n${objText}\n${text}\n`;
|
||||||
|
lastData = writeData;
|
||||||
|
for (const socket of sockets) {
|
||||||
|
socket.write(writeData);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
console.log("Started on :7070")
|
console.log("Started on :7070");
|
||||||
|
|
|
||||||
8
pico/Cargo.lock
generated
8
pico/Cargo.lock
generated
|
|
@ -1259,6 +1259,12 @@ version = "1.21.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "owned_str"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57b42c259c35d8ad3e87b6dbedd4818b7b54a112f6370fdf11cd45d2f355d0f4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "panic-probe"
|
name = "panic-probe"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
|
@ -1335,12 +1341,14 @@ dependencies = [
|
||||||
"embassy-futures",
|
"embassy-futures",
|
||||||
"embassy-net",
|
"embassy-net",
|
||||||
"embassy-rp",
|
"embassy-rp",
|
||||||
|
"embassy-sync 0.8.0",
|
||||||
"embassy-time",
|
"embassy-time",
|
||||||
"embassy-usb-logger",
|
"embassy-usb-logger",
|
||||||
"embedded-hal-compat",
|
"embedded-hal-compat",
|
||||||
"embedded-io 0.7.1",
|
"embedded-io 0.7.1",
|
||||||
"embedded-io-async 0.7.0",
|
"embedded-io-async 0.7.0",
|
||||||
"log",
|
"log",
|
||||||
|
"owned_str",
|
||||||
"panic-probe",
|
"panic-probe",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"static_cell",
|
"static_cell",
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ as5600 = "0.8.0"
|
||||||
embassy-futures = "0.1.2"
|
embassy-futures = "0.1.2"
|
||||||
embedded-io-async = "0.7.0"
|
embedded-io-async = "0.7.0"
|
||||||
embedded-io = "0.7.1"
|
embedded-io = "0.7.1"
|
||||||
|
owned_str = "0.1.2"
|
||||||
|
embassy-sync = "0.8.0"
|
||||||
arrayvec = { version = "0.7.6", default-features = false }
|
arrayvec = { version = "0.7.6", default-features = false }
|
||||||
embassy-net = { version = "0.9.1",features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"]}
|
embassy-net = { version = "0.9.1",features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"]}
|
||||||
embassy-executor = { version = "0.10.0", features = [
|
embassy-executor = { version = "0.10.0", features = [
|
||||||
|
|
|
||||||
75
pico/src/input.rs
Normal file
75
pico/src/input.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
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 crate::{Irqs, unwrap};
|
||||||
|
|
||||||
|
/// Button input notification channel
|
||||||
|
pub static INPUT: Channel<CriticalSectionRawMutex, u8, 64> = Channel::new();
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
/// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static ANGLE: Mutex<CriticalSectionRawMutex, u16> = Mutex::new(0);
|
||||||
|
|
||||||
|
#[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());
|
||||||
|
let mut as5600 = As5600::new(i2c);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let angle = as5600.angle().unwrap_or(0);
|
||||||
|
let mut locked = ANGLE.lock().await;
|
||||||
|
let old = *locked as i32;
|
||||||
|
*locked = angle;
|
||||||
|
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
|
||||||
|
};
|
||||||
|
Timer::after(Duration::from_millis(50)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
357
pico/src/main.rs
357
pico/src/main.rs
|
|
@ -1,33 +1,42 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
use core::net::SocketAddrV4;
|
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
use embassy_net::tcp::TcpSocket;
|
use embassy_net::tcp::{TcpReader, TcpWriter};
|
||||||
use embassy_net::udp::{PacketMetadata, UdpMetadata, UdpSocket};
|
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 owned_str::OwnedStr;
|
||||||
|
|
||||||
use crate::buffer::{WriteBuffer, wait_for_config};
|
use crate::buffer::WriteBuffer;
|
||||||
|
use crate::input::{ANGLE, INPUT, InputConfig};
|
||||||
|
use crate::net::network_setup_task;
|
||||||
|
use crate::screen::lcd_display_task;
|
||||||
use ag_lcd::{Blink, Cursor, Display, LcdDisplay};
|
use ag_lcd::{Blink, Cursor, Display, LcdDisplay};
|
||||||
use as5600::As5600;
|
|
||||||
use cyw43::{JoinOptions, aligned_bytes};
|
|
||||||
use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi};
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Executor;
|
||||||
use embassy_net::{Stack, StackResources};
|
use embassy_rp::gpio::{Level, Output};
|
||||||
use embassy_rp::clocks::RoscRng;
|
use embassy_rp::i2c::{self};
|
||||||
use embassy_rp::gpio::{Input, Level, Output};
|
|
||||||
use embassy_rp::i2c::{self, Config, I2c};
|
|
||||||
use embassy_rp::peripherals::{DMA_CH0, I2C0, PIO0};
|
use embassy_rp::peripherals::{DMA_CH0, I2C0, PIO0};
|
||||||
use embassy_rp::pio::{InterruptHandler, Pio};
|
use embassy_rp::pio::InterruptHandler;
|
||||||
use embassy_rp::{bind_interrupts, dma};
|
use embassy_rp::{bind_interrupts, dma};
|
||||||
use embassy_rp::{peripherals::USB, usb};
|
use embassy_rp::{peripherals::USB, usb};
|
||||||
use embassy_time::{Delay, Duration, Timer};
|
use embassy_time::Delay;
|
||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
use ufmt::{uWrite, uwrite};
|
|
||||||
|
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
mod buffer;
|
mod buffer;
|
||||||
|
mod input;
|
||||||
|
mod net;
|
||||||
|
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 {
|
bind_interrupts!(struct Irqs {
|
||||||
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
||||||
|
|
@ -36,69 +45,132 @@ bind_interrupts!(struct Irqs {
|
||||||
I2C0_IRQ => i2c::InterruptHandler<I2C0>;
|
I2C0_IRQ => i2c::InterruptHandler<I2C0>;
|
||||||
});
|
});
|
||||||
|
|
||||||
#[embassy_executor::task]
|
|
||||||
async fn cyw43_task(
|
|
||||||
runner: cyw43::Runner<'static, cyw43::SpiBus<Output<'static>, PioSpi<'static, PIO0, 0>>>,
|
|
||||||
) -> ! {
|
|
||||||
runner.run().await
|
|
||||||
}
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn logger_task(usb: embassy_rp::Peri<'static, embassy_rp::peripherals::USB>) {
|
async fn logger_task(usb: embassy_rp::Peri<'static, embassy_rp::peripherals::USB>) {
|
||||||
let driver = embassy_rp::usb::Driver::new(usb, Irqs);
|
let driver = embassy_rp::usb::Driver::new(usb, Irqs);
|
||||||
|
|
||||||
embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver);
|
embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver);
|
||||||
}
|
}
|
||||||
#[embassy_executor::task]
|
|
||||||
async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {
|
enum QuestionType {
|
||||||
runner.run().await
|
Choice,
|
||||||
|
Numeric { min: i32, max: i32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
const WIFI_NETWORK: &str = "flamme";
|
struct QuestionData {
|
||||||
const WIFI_PASSWORD: &str = "12345678";
|
text: OwnedStr<256>,
|
||||||
|
q_type: QuestionType,
|
||||||
|
points: i32,
|
||||||
|
}
|
||||||
|
|
||||||
#[embassy_executor::main]
|
static QUESTION: Mutex<CriticalSectionRawMutex, Option<QuestionData>> = Mutex::new(None);
|
||||||
async fn main(spawner: Spawner) {
|
static QUESTION_UPDATE: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
||||||
|
static WHEEL_VALUE: Mutex<CriticalSectionRawMutex, i32> = Mutex::new(0);
|
||||||
|
|
||||||
|
pub async fn tcp_read_loop(mut read: TcpReader<'_>) {
|
||||||
|
let mut buf = [0u8; 1024];
|
||||||
|
|
||||||
|
while let Ok(len) = read.read(&mut buf).await {
|
||||||
|
if len == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let Ok(str) = str::from_utf8(&buf[..len]) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let mut counter = 0;
|
||||||
|
let mut question_data = None;
|
||||||
|
let mut future_wheel = 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();
|
||||||
|
} else {
|
||||||
|
*max = value.parse().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match q_type {
|
||||||
|
QuestionType::Numeric { min, max } => {
|
||||||
|
let diff = max - min;
|
||||||
|
future_wheel = min + diff / 2;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
question_data = Some(QuestionData {
|
||||||
|
text: OwnedStr::new(),
|
||||||
|
q_type,
|
||||||
|
points,
|
||||||
|
});
|
||||||
|
counter = 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if counter == 2 {
|
||||||
|
question_data.as_mut().unwrap().text = OwnedStr::from_str(line).unwrap();
|
||||||
|
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;
|
||||||
|
let angle = *ANGLE.lock().await;
|
||||||
|
core::writeln!(buffer, "button={} angle={}", data, angle).ok();
|
||||||
|
write.write(&buffer).await.ok();
|
||||||
|
buffer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut CORE1_STACK: Stack<4096> = Stack::new();
|
||||||
|
static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
|
||||||
|
static EXECUTOR1: StaticCell<Executor> = StaticCell::new();
|
||||||
|
|
||||||
|
#[cortex_m_rt::entry]
|
||||||
|
fn main() -> ! {
|
||||||
let p = embassy_rp::init(Default::default());
|
let p = embassy_rp::init(Default::default());
|
||||||
|
|
||||||
let fw = aligned_bytes!("../firmware/43439A0.bin");
|
let lcd: LcdDisplay<_, _> = LcdDisplay::new(
|
||||||
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(p.PIN_23, Level::Low);
|
|
||||||
let cs = Output::new(p.PIN_25, Level::High);
|
|
||||||
let mut pio = Pio::new(p.PIO0, Irqs);
|
|
||||||
let spi = PioSpi::new(
|
|
||||||
&mut pio.common,
|
|
||||||
pio.sm0,
|
|
||||||
DEFAULT_CLOCK_DIVIDER,
|
|
||||||
pio.irq0,
|
|
||||||
cs,
|
|
||||||
p.PIN_24,
|
|
||||||
p.PIN_29,
|
|
||||||
dma::Channel::new(p.DMA_CH0, Irqs),
|
|
||||||
);
|
|
||||||
|
|
||||||
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)));
|
|
||||||
spawner.spawn(unwrap!(logger_task(p.USB)));
|
|
||||||
|
|
||||||
let edelay = &mut Delay;
|
|
||||||
|
|
||||||
let mut lcd: LcdDisplay<_, _> = LcdDisplay::new(
|
|
||||||
Output::new(p.PIN_15, Level::Low),
|
Output::new(p.PIN_15, Level::Low),
|
||||||
Output::new(p.PIN_14, Level::Low),
|
Output::new(p.PIN_14, Level::Low),
|
||||||
edelay,
|
Delay,
|
||||||
)
|
)
|
||||||
.with_half_bus(
|
.with_half_bus(
|
||||||
Output::new(p.PIN_13, Level::Low),
|
Output::new(p.PIN_13, Level::Low),
|
||||||
|
|
@ -106,130 +178,45 @@ async fn main(spawner: Spawner) {
|
||||||
Output::new(p.PIN_11, Level::Low),
|
Output::new(p.PIN_11, Level::Low),
|
||||||
Output::new(p.PIN_10, Level::Low),
|
Output::new(p.PIN_10, Level::Low),
|
||||||
)
|
)
|
||||||
|
.with_autoscroll(ag_lcd::AutoScroll::Off)
|
||||||
.with_display(Display::On)
|
.with_display(Display::On)
|
||||||
.with_blink(Blink::On)
|
.with_blink(Blink::On)
|
||||||
.with_cursor(Cursor::On)
|
.with_cursor(Cursor::On)
|
||||||
.with_lines(ag_lcd::Lines::TwoLines)
|
.with_lines(ag_lcd::Lines::TwoLines);
|
||||||
// .with_autoscroll(ag_lcd::AutoScroll::On)
|
|
||||||
.build();
|
|
||||||
lcd.set_cursor(Cursor::Off);
|
|
||||||
lcd.set_blink(Blink::Off);
|
|
||||||
|
|
||||||
control.init(clm).await;
|
spawn_core1(
|
||||||
control
|
p.CORE1,
|
||||||
.set_power_management(cyw43::PowerManagementMode::PowerSave)
|
unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },
|
||||||
.await;
|
move || {
|
||||||
|
let executor1 = EXECUTOR1.init(Executor::new());
|
||||||
static RESOURCES: StaticCell<StackResources<5>> = StaticCell::new();
|
executor1.run(|spawner| {
|
||||||
let (stack, runner) = embassy_net::new(
|
let mut lcd = lcd.build();
|
||||||
net_device,
|
lcd.set_blink(Blink::Off);
|
||||||
embassy_net::Config::dhcpv4(Default::default()),
|
lcd.set_cursor(Cursor::Off);
|
||||||
RESOURCES.init(StackResources::new()),
|
spawner.spawn(unwrap!(lcd_display_task(lcd)));
|
||||||
rng.next_u64(),
|
});
|
||||||
|
},
|
||||||
);
|
);
|
||||||
spawner.spawn(unwrap!(net_task(runner)));
|
|
||||||
uwrite!(lcd, "con.");
|
|
||||||
|
|
||||||
while let Err(err) = control
|
let executor0 = EXECUTOR0.init(Executor::new());
|
||||||
.join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes()))
|
executor0.run(|spawner| {
|
||||||
.await
|
spawner.spawn(unwrap!(logger_task(p.USB)));
|
||||||
{
|
spawner.spawn(unwrap!(network_setup_task(
|
||||||
lcd.clear();
|
p.PIN_23, p.PIN_25, p.PIO0, p.PIN_24, p.PIN_29, p.DMA_CH0, spawner
|
||||||
let num = match err {
|
)));
|
||||||
cyw43::JoinError::AuthenticationFailure => 200,
|
crate::input::setup(
|
||||||
cyw43::JoinError::JoinFailure(e) => e,
|
spawner,
|
||||||
cyw43::JoinError::NetworkNotFound => 201,
|
InputConfig {
|
||||||
};
|
i2c0: p.I2C0,
|
||||||
uwrite!(lcd, "join {}", num);
|
scl: p.PIN_5,
|
||||||
}
|
sda: p.PIN_4,
|
||||||
|
button_pins: [
|
||||||
uwrite!(lcd, "link.");
|
(p.PIN_18.into(), 1),
|
||||||
stack.wait_link_up().await;
|
(p.PIN_19.into(), 2),
|
||||||
|
(p.PIN_20.into(), 3),
|
||||||
lcd.clear();
|
(p.PIN_21.into(), 4),
|
||||||
// lcd.home();
|
],
|
||||||
uwrite!(lcd, "dhcp.");
|
},
|
||||||
stack.wait_config_up().await;
|
)
|
||||||
let cfg = wait_for_config(stack).await;
|
});
|
||||||
let local_addr = cfg.address.address();
|
|
||||||
info!("IP address: {:?}", local_addr);
|
|
||||||
// uwrite!(lcd, "IP address: {:?}", local_addr.octets());
|
|
||||||
|
|
||||||
let i2c = I2c::new_async(p.I2C0, p.PIN_5, p.PIN_4, Irqs, Config::default());
|
|
||||||
let mut as5600 = As5600::new(i2c);
|
|
||||||
|
|
||||||
let mut led = Output::new(p.PIN_1, Level::Low);
|
|
||||||
|
|
||||||
let in1 = Input::new(p.PIN_18, embassy_rp::gpio::Pull::Up);
|
|
||||||
let in2 = Input::new(p.PIN_19, embassy_rp::gpio::Pull::Up);
|
|
||||||
let in3 = Input::new(p.PIN_20, embassy_rp::gpio::Pull::Up);
|
|
||||||
let in4 = Input::new(p.PIN_21, embassy_rp::gpio::Pull::Up);
|
|
||||||
|
|
||||||
let mut rx_buffer = [0; 4096];
|
|
||||||
// let mut rx_meta = [PacketMetadata::EMPTY; 16];
|
|
||||||
let mut tx_buffer = [0; 4096];
|
|
||||||
// let mut tx_meta = [PacketMetadata::EMPTY; 16];
|
|
||||||
|
|
||||||
// let mut socket = UdpSocket::new(
|
|
||||||
// stack,
|
|
||||||
// &mut rx_meta,
|
|
||||||
// &mut rx_buffer,
|
|
||||||
// &mut tx_meta,
|
|
||||||
// &mut tx_buffer,
|
|
||||||
// );
|
|
||||||
// socket.bind(7070).unwrap();
|
|
||||||
|
|
||||||
let host_addr = embassy_net::Ipv4Address::from_str("84.238.32.253").unwrap();
|
|
||||||
// let target = UdpMetadata::from(SocketAddrV4::new(host_addr, 7070));
|
|
||||||
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
|
|
||||||
socket.set_timeout(Some(Duration::from_secs(10)));
|
|
||||||
if let Err(e) = socket.connect(SocketAddrV4::new(host_addr, 7070)).await {
|
|
||||||
uwrite!(lcd, "tcperr");
|
|
||||||
error!("tcp connect error: {}", e);
|
|
||||||
} else {
|
|
||||||
uwrite!(lcd, "tcpok");
|
|
||||||
}
|
|
||||||
|
|
||||||
let delay = Duration::from_millis(100);
|
|
||||||
let mut buffer = WriteBuffer::<1024>::new();
|
|
||||||
loop {
|
|
||||||
let in1 = in1.is_low();
|
|
||||||
let in2 = in2.is_low();
|
|
||||||
let in3 = in3.is_low();
|
|
||||||
let in4 = in4.is_low();
|
|
||||||
let angle = as5600.angle().unwrap_or(0);
|
|
||||||
{
|
|
||||||
use embedded_io::Write;
|
|
||||||
let _ = core::writeln!(
|
|
||||||
&mut buffer,
|
|
||||||
"in1={} in2={} in3={} in4={} angle={}",
|
|
||||||
in1,
|
|
||||||
in2,
|
|
||||||
in3,
|
|
||||||
in4,
|
|
||||||
angle
|
|
||||||
);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let _ = socket.write(&*buffer).await;
|
|
||||||
buffer.clear();
|
|
||||||
}
|
|
||||||
if socket.may_recv() {
|
|
||||||
let mut rx_buffer = [0; 4096];
|
|
||||||
let n = socket.read(&mut rx_buffer).await.unwrap();
|
|
||||||
if n > 0 {
|
|
||||||
lcd.clear();
|
|
||||||
lcd.home();
|
|
||||||
let s = core::str::from_utf8(&rx_buffer[..n]).unwrap_or("");
|
|
||||||
let npos = s.find('\n').unwrap_or(n);
|
|
||||||
let display_text = &s[..npos];
|
|
||||||
lcd.write_str(display_text).ok();
|
|
||||||
|
|
||||||
Timer::after(Duration::from_micros(100)).await;
|
|
||||||
lcd.set_position(0, 1);
|
|
||||||
lcd.write_str(&s[npos..]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Timer::after(delay).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
135
pico/src/net.rs
Normal file
135
pico/src/net.rs
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
use core::net::SocketAddrV4;
|
||||||
|
use core::str::FromStr;
|
||||||
|
use embassy_futures::join::join;
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
use cyw43::{JoinOptions, 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;
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
use ufmt::uwrite;
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn cyw43_task(
|
||||||
|
runner: cyw43::Runner<'static, cyw43::SpiBus<Output<'static>, PioSpi<'static, PIO0, 0>>>,
|
||||||
|
) -> ! {
|
||||||
|
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),
|
||||||
|
);
|
||||||
|
|
||||||
|
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)));
|
||||||
|
|
||||||
|
control.init(clm).await;
|
||||||
|
control
|
||||||
|
.set_power_management(cyw43::PowerManagementMode::PowerSave)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
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(),
|
||||||
|
);
|
||||||
|
spawner.spawn(unwrap!(net_task(runner)));
|
||||||
|
uwrite!(SCREEN_BUFFER.lock().await.line1(), "con");
|
||||||
|
|
||||||
|
while let Err(err) = control
|
||||||
|
.join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes()))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
let num = match err {
|
||||||
|
cyw43::JoinError::AuthenticationFailure => 200,
|
||||||
|
cyw43::JoinError::JoinFailure(e) => e,
|
||||||
|
cyw43::JoinError::NetworkNotFound => 201,
|
||||||
|
};
|
||||||
|
uwrite!(SCREEN_BUFFER.lock().await.line1(), "join {}", num);
|
||||||
|
}
|
||||||
|
|
||||||
|
uwrite!(SCREEN_BUFFER.lock().await.line1(), "link.");
|
||||||
|
stack.wait_link_up().await;
|
||||||
|
|
||||||
|
let mut buf = SCREEN_BUFFER.lock().await;
|
||||||
|
buf.clear();
|
||||||
|
uwrite!(buf.line1(), "dhcp.");
|
||||||
|
drop(buf);
|
||||||
|
stack.wait_config_up().await;
|
||||||
|
let cfg = wait_for_config(stack).await;
|
||||||
|
let local_addr = cfg.address.address();
|
||||||
|
info!("IP address: {:?}", local_addr);
|
||||||
|
|
||||||
|
let host_addr = embassy_net::Ipv4Address::from_str(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)));
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
let (read, write) = socket.split();
|
||||||
|
|
||||||
|
join(tcp_read_loop(read), tcp_write_loop(write)).await;
|
||||||
|
}
|
||||||
100
pico/src/screen.rs
Normal file
100
pico/src/screen.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
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};
|
||||||
|
use ufmt::uWrite;
|
||||||
|
|
||||||
|
pub struct ScreenBuffer {
|
||||||
|
line1: [u8; 16],
|
||||||
|
line2: [u8; 16],
|
||||||
|
line1_ptr: u8,
|
||||||
|
line2_ptr: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScreenBuffer {
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.line1.fill(0);
|
||||||
|
self.line2.fill(0);
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
|
||||||
|
static LCD_UPDATE: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
pub async fn lcd_display_task(mut lcd: LcdDisplay<Output<'static>, Delay>) {
|
||||||
|
loop {
|
||||||
|
LCD_UPDATE.wait().await;
|
||||||
|
let buffer = SCREEN_BUFFER.lock().await;
|
||||||
|
lcd.clear();
|
||||||
|
for byte in &buffer.line1 {
|
||||||
|
lcd.write(*byte);
|
||||||
|
}
|
||||||
|
lcd.set_position(0, 1);
|
||||||
|
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.copy_from_slice(line1.as_bytes());
|
||||||
|
buffer.line2.copy_from_slice(line2.as_bytes());
|
||||||
|
LCD_UPDATE.signal(());
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue