add serde
This commit is contained in:
parent
9e93f8b9f7
commit
2bcdb34515
5 changed files with 127 additions and 94 deletions
|
|
@ -4,6 +4,12 @@ type ApiEnvelope =
|
|||
| { type: "hello" }
|
||||
| { type: "device_event"; deviceId: string; event: unknown };
|
||||
|
||||
type DeviceMessage = {
|
||||
DeviceId: string;
|
||||
} | {
|
||||
QuizResponse: number;
|
||||
}
|
||||
|
||||
const sockets = new Map<string, Socket>();
|
||||
const socketIds = new WeakMap<Socket, string>();
|
||||
const apiSocket = new WebSocket("ws://localhost:4000/api/dev-socket/ws");
|
||||
|
|
@ -16,7 +22,8 @@ function registerSocket(socket: Socket, deviceId: string) {
|
|||
const existing = sockets.get(deviceId);
|
||||
if (existing && existing !== socket) existing.end();
|
||||
sockets.set(deviceId, socket);
|
||||
socketIds.set(socket, deviceId);
|
||||
socketIds.set(socket, deviceId);
|
||||
console.log("Registered", socket.remoteAddress, deviceId);
|
||||
}
|
||||
|
||||
const listener = Bun.listen({
|
||||
|
|
@ -24,27 +31,38 @@ const listener = Bun.listen({
|
|||
hostname: "0.0.0.0",
|
||||
socket: {
|
||||
open(socket) {
|
||||
socket.setKeepAlive(true);
|
||||
socket.setKeepAlive(true);
|
||||
console.log("Connection", socket.remoteAddress, socket.remotePort);
|
||||
},
|
||||
data(socket, buf) {
|
||||
const raw = new TextDecoder().decode(buf).trim();
|
||||
if (!raw) return;
|
||||
const raw = new TextDecoder().decode(buf).trim();
|
||||
let data: DeviceMessage;
|
||||
try {
|
||||
data = JSON.parse(raw);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
console.log("Data", socket.remoteAddress, data);
|
||||
if (!data) return;
|
||||
|
||||
const currentDeviceId = socketDeviceId(socket);
|
||||
if (!currentDeviceId) {
|
||||
registerSocket(socket, raw);
|
||||
return;
|
||||
}
|
||||
if ("DeviceId" in data) {
|
||||
registerSocket(socket, data.DeviceId);
|
||||
return;
|
||||
}
|
||||
if ("QuizResponse" in data) {
|
||||
|
||||
apiSocket?.send(
|
||||
JSON.stringify({
|
||||
type: "device_message",
|
||||
deviceId: currentDeviceId,
|
||||
payload: raw,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// apiSocket?.send(
|
||||
// JSON.stringify({
|
||||
// type: "device_message",
|
||||
// deviceId: currentDeviceId,
|
||||
// payload: raw,
|
||||
// }),
|
||||
// );
|
||||
},
|
||||
close(socket) {
|
||||
console.log("Connection", socket.remoteAddress);
|
||||
const deviceId = socketDeviceId(socket);
|
||||
if (deviceId && sockets.get(deviceId) === socket) {
|
||||
sockets.delete(deviceId);
|
||||
|
|
@ -67,6 +85,8 @@ apiSocket.onmessage = (e) => {
|
|||
socket.write(`${JSON.stringify(message.event)}\n`);
|
||||
};
|
||||
|
||||
|
||||
|
||||
apiSocket.onerror = (error) => {
|
||||
console.error(error);
|
||||
};
|
||||
|
|
|
|||
21
esp32/Cargo.lock
generated
21
esp32/Cargo.lock
generated
|
|
@ -1019,6 +1019,8 @@ dependencies = [
|
|||
"owned_str",
|
||||
"panic-probe",
|
||||
"portable-atomic",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"static_cell",
|
||||
"ufmt 0.2.0",
|
||||
]
|
||||
|
|
@ -1698,6 +1700,19 @@ dependencies = [
|
|||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.34+deprecated"
|
||||
|
|
@ -2178,3 +2193,9 @@ dependencies = [
|
|||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ codegen-units = 1
|
|||
|
||||
[dependencies]
|
||||
[target.'cfg(target_arch = "xtensa")'.dependencies]
|
||||
serde = { version = "1.0.228", default-features = false, features = ["derive", "alloc"] }
|
||||
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||
arrayvec = { version = "0.7.6", default-features = false }
|
||||
ag-lcd = { version = "0.3", features = ["ufmt"] }
|
||||
as5600 = "0.8.0"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use esp_hal::{
|
|||
};
|
||||
use esp_println::println;
|
||||
use owned_str::OwnedStr;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ufmt::uwrite;
|
||||
|
||||
mod buffer;
|
||||
|
|
@ -31,8 +32,6 @@ mod screen;
|
|||
|
||||
pub use input::ANGLE;
|
||||
|
||||
use crate::screen::overwrite_lcd;
|
||||
|
||||
const WIFI_NETWORK: &str = "flamme";
|
||||
const WIFI_PASSWORD: &str = "12345678";
|
||||
const TARGET_IP: &str = "84.238.32.253";
|
||||
|
|
@ -41,6 +40,7 @@ const WHEEL_PRECISION: i32 = 32;
|
|||
const WHEEL_INVERTED: bool = false;
|
||||
const DEVICE_ID: &str = "esp32-1";
|
||||
|
||||
#[derive(Deserialize)]
|
||||
enum QuestionType {
|
||||
Choice,
|
||||
Numeric { min: i32, max: i32 },
|
||||
|
|
@ -58,7 +58,26 @@ struct QuestionData {
|
|||
text: OwnedStr<256>,
|
||||
q_type: QuestionType,
|
||||
points: i32,
|
||||
generation: usize,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct QuestionDataNet<'a> {
|
||||
text: &'a str,
|
||||
q_type: QuestionType,
|
||||
points: i32,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'a> From<QuestionDataNet<'a>> for QuestionData {
|
||||
fn from(value: QuestionDataNet<'a>) -> Self {
|
||||
QuestionData {
|
||||
text: OwnedStr::from_str(value.text).unwrap(),
|
||||
q_type: value.q_type,
|
||||
points: value.points,
|
||||
index: value.index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
|
@ -99,7 +118,6 @@ pub async fn tcp_read_loop(
|
|||
cancel: &Signal<CriticalSectionRawMutex, ()>,
|
||||
) -> Result<(), TcpDisconnect> {
|
||||
let mut buf = [0u8; 1024];
|
||||
let mut generation = 1;
|
||||
|
||||
loop {
|
||||
let read_fut = read.read(&mut buf);
|
||||
|
|
@ -120,7 +138,6 @@ pub async fn tcp_read_loop(
|
|||
let Ok(str) = core::str::from_utf8(&buf[..len]) else {
|
||||
continue;
|
||||
};
|
||||
let mut counter = 0;
|
||||
let mut question_data = None;
|
||||
let mut future_wheel = WheelData {
|
||||
value: 0,
|
||||
|
|
@ -128,70 +145,20 @@ pub async fn tcp_read_loop(
|
|||
max: 0,
|
||||
accumulated: 0,
|
||||
};
|
||||
for line in str.lines() {
|
||||
if line == "##" {
|
||||
overwrite_lcd("Waiting", DEVICE_ID).await;
|
||||
break;
|
||||
}
|
||||
if line == "$$" {
|
||||
counter = 1;
|
||||
if let Some(last) = str.lines().last() {
|
||||
let Ok(data) = serde_json::from_str::<QuestionDataNet>(last) else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let data: QuestionData = data.into();
|
||||
match data.q_type {
|
||||
QuestionType::Numeric { min, max } => {
|
||||
future_wheel.max = max;
|
||||
future_wheel.min = min;
|
||||
future_wheel.value = (min + max) / 2;
|
||||
}
|
||||
|
||||
if let QuestionType::Numeric { min, max } = q_type {
|
||||
let diff = max - min;
|
||||
future_wheel.value = min + diff / 2;
|
||||
future_wheel.accumulated = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
question_data = Some(data);
|
||||
}
|
||||
|
||||
if let Some(question_data) = question_data {
|
||||
|
|
@ -202,15 +169,28 @@ pub async fn tcp_read_loop(
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
enum WriteType<'a> {
|
||||
QuizResponse(i32),
|
||||
DeviceId(&'a str),
|
||||
}
|
||||
|
||||
pub async fn tcp_write_loop(
|
||||
mut write: TcpWriter<'_>,
|
||||
cancel: &Signal<CriticalSectionRawMutex, ()>,
|
||||
) -> Result<(), TcpDisconnect> {
|
||||
if write.write(DEVICE_ID.as_bytes()).await.is_err() {
|
||||
if write
|
||||
.write(
|
||||
serde_json::to_string(&WriteType::DeviceId(DEVICE_ID))
|
||||
.unwrap()
|
||||
.as_bytes(),
|
||||
)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
cancel.signal(());
|
||||
return Err(TcpDisconnect::WriteError);
|
||||
}
|
||||
let mut buffer = buffer::WriteBuffer::<256>::new();
|
||||
loop {
|
||||
let input_fut = input::INPUT.receive();
|
||||
let cancel_fut = cancel.wait();
|
||||
|
|
@ -219,14 +199,24 @@ pub async fn tcp_write_loop(
|
|||
Either::Second(()) => return Err(TcpDisconnect::Cancelled),
|
||||
};
|
||||
println!("button={}", data);
|
||||
let angle = *ANGLE.lock().await;
|
||||
core::writeln!(buffer, "button={} angle={}", data, angle).ok();
|
||||
let value = {
|
||||
let question = QUESTION.lock().await;
|
||||
let wheel = *WHEEL_VALUE.lock().await;
|
||||
match question.as_ref() {
|
||||
Some(q) => match q.q_type {
|
||||
QuestionType::Numeric { .. } => wheel.value,
|
||||
QuestionType::Choice => data as _,
|
||||
},
|
||||
_ => data as _,
|
||||
}
|
||||
};
|
||||
let data = WriteType::QuizResponse(value);
|
||||
let buffer = serde_json::to_string(&data).unwrap();
|
||||
println!("write: {}", &buffer);
|
||||
if write.write(&buffer).await.is_err() {
|
||||
if write.write(buffer.as_bytes()).await.is_err() {
|
||||
cancel.signal(());
|
||||
return Err(TcpDisconnect::WriteError);
|
||||
}
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -236,7 +226,7 @@ const DOT: char = char::from_u32(0b1010_0101).unwrap();
|
|||
|
||||
#[embassy_executor::task]
|
||||
pub async fn main_loop() {
|
||||
let mut last_gen = 0;
|
||||
let mut last_index = 0;
|
||||
let mut title_offset = 0;
|
||||
println!("Main loop started");
|
||||
loop {
|
||||
|
|
@ -247,8 +237,8 @@ pub async fn main_loop() {
|
|||
continue;
|
||||
};
|
||||
title_offset += 1;
|
||||
if question.generation != last_gen {
|
||||
last_gen = question.generation;
|
||||
if question.index != last_index {
|
||||
last_index = question.index;
|
||||
title_offset = 0;
|
||||
}
|
||||
let title_line = if question.text.len() > 16 {
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ 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.fill(32);
|
||||
buffer.line2.fill(32);
|
||||
let len1 = line1.len().min(buffer.line1.len());
|
||||
let len2 = line2.len().min(buffer.line2.len());
|
||||
buffer.line1[..len1].copy_from_slice(line1[..len1].as_bytes());
|
||||
|
|
|
|||
Loading…
Reference in a new issue