Compare commits
No commits in common. "main" and "dhcp" have entirely different histories.
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -348,12 +348,6 @@ dependencies = [
|
|||||||
"crypto-common",
|
"crypto-common",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dnsparse"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "18b5892f4beae62ac9681eb96df926ebcebca009e6f2e23a216acf7d3f5c5a97"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "document-features"
|
name = "document-features"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
@ -1099,7 +1093,6 @@ dependencies = [
|
|||||||
"defmt",
|
"defmt",
|
||||||
"defmt-rtt",
|
"defmt-rtt",
|
||||||
"dhcparse",
|
"dhcparse",
|
||||||
"dnsparse",
|
|
||||||
"embassy-executor",
|
"embassy-executor",
|
||||||
"embassy-net",
|
"embassy-net",
|
||||||
"embassy-rp",
|
"embassy-rp",
|
||||||
|
@ -9,8 +9,7 @@ wifi-connect = [
|
|||||||
"dep:serde",
|
"dep:serde",
|
||||||
] # you need to add a wifi.conf file for this to work
|
] # you need to add a wifi.conf file for this to work
|
||||||
dhcp = ["dep:dhcparse"]
|
dhcp = ["dep:dhcparse"]
|
||||||
dns = ["dep:dnsparse"]
|
default = ["dhcp"]
|
||||||
default = ["dhcp", "dns"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-executor = { git = "https://github.com/embassy-rs/embassy", features = [
|
embassy-executor = { git = "https://github.com/embassy-rs/embassy", features = [
|
||||||
@ -55,5 +54,4 @@ serde-json-core = { version = "*", optional = true }
|
|||||||
serde = { version = "*", optional = true, default-features = false, features = [
|
serde = { version = "*", optional = true, default-features = false, features = [
|
||||||
"derive",
|
"derive",
|
||||||
] }
|
] }
|
||||||
dhcparse = { version = "*", default-features = false, optional = true }
|
dhcparse = { version = "*", default-features = false, optional = true }
|
||||||
dnsparse = { version = "*", optional = true }
|
|
@ -1,11 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<head> </head>
|
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
<h1>Apps</h1>
|
|
||||||
<ul>
|
|
||||||
<li><a href="http://pico.wifi:8080">Tic Tac Toe</a> (team blue)</li>
|
|
||||||
<li><a href="http://pico.wifi:8081">Tic Tac Toe</a> (team red)</li>
|
|
||||||
</ul>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,18 +0,0 @@
|
|||||||
use crate::socket::HttpResCode;
|
|
||||||
|
|
||||||
use super::App;
|
|
||||||
|
|
||||||
pub struct IndexApp;
|
|
||||||
impl App for IndexApp {
|
|
||||||
fn socket_name(&self) -> &'static str {
|
|
||||||
"index"
|
|
||||||
}
|
|
||||||
async fn handle_request<'a>(&'a mut self, path: &str) -> (HttpResCode, &'static str, &'a [u8]) {
|
|
||||||
match path {
|
|
||||||
"/" | "/index" | "/index.html" => {
|
|
||||||
(HttpResCode::Ok, "html", include_bytes!("./index.html"))
|
|
||||||
}
|
|
||||||
_ => (HttpResCode::NotFound, "", &[]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
use crate::socket::HttpResCode;
|
|
||||||
|
|
||||||
pub mod index;
|
|
||||||
pub mod ttt;
|
|
||||||
|
|
||||||
pub trait App {
|
|
||||||
fn socket_name(&self) -> &'static str;
|
|
||||||
async fn handle_request<'a>(&'a mut self, path: &str) -> (HttpResCode, &'static str, &'a [u8]);
|
|
||||||
}
|
|
16
src/dhcp.rs
16
src/dhcp.rs
@ -149,15 +149,15 @@ async fn write_dhcp_opts<const N: usize>(buf: &mut Vec<u8, N>, op_codes: &[u8])
|
|||||||
1 => (4, &[255, 255, 255, 0]), // DhcpOption::SubnetMask(&dhcpv4::Addr([255, 255, 255, 0])),
|
1 => (4, &[255, 255, 255, 0]), // DhcpOption::SubnetMask(&dhcpv4::Addr([255, 255, 255, 0])),
|
||||||
2 => (4, &3600_i32.to_be_bytes()), // DhcpOption::TimeOffset(3600),
|
2 => (4, &3600_i32.to_be_bytes()), // DhcpOption::TimeOffset(3600),
|
||||||
3 => (4, &[192, 254, 0, 2]), // DhcpOption::Router(&[dhcpv4::Addr([192, 254, 0, 2])]),
|
3 => (4, &[192, 254, 0, 2]), // DhcpOption::Router(&[dhcpv4::Addr([192, 254, 0, 2])]),
|
||||||
6 => (4, &[192, 254, 0, 2]), // DhcpOption::DomainNameServer(&[dhcpv4::Addr([0, 0, 0, 0])]),
|
6 => (4, &[0, 0, 0, 0]), // DhcpOption::DomainNameServer(&[dhcpv4::Addr([0, 0, 0, 0])]),
|
||||||
12 => (4, b"blue"), // DhcpOption::HostName(b"blue"),
|
12 => (4, b"blue"), // DhcpOption::HostName(b"blue"),
|
||||||
15 => (4, b"wifi"), // DhcpOption::DomainName(b"LocalDomain"),
|
15 => (11, b"LocalDomain"), // DhcpOption::DomainName(b"LocalDomain"),
|
||||||
26 => (2, &1514_u16.to_be_bytes()), // mtu
|
26 => (2, &1514_u16.to_be_bytes()), // DhcpOption::Unknown(26, &[0x5, 0xEA]), // mtu
|
||||||
28 => (4, &[192, 254, 0, 255]), // broadcast
|
28 => (4, &[192, 254, 0, 255]), // DhcpOption::Unknown(28, &[192, 254, 0, 255]), // broadcast
|
||||||
51 => (4, &3600_u32.to_be_bytes()), // DhcpOption::AddressLeaseTime(3600),
|
51 => (4, &700_u32.to_be_bytes()), // DhcpOption::AddressLeaseTime(700),
|
||||||
54 => (4, &[192, 254, 0, 2]), // DhcpOption::ServerIdentifier(&dhcpv4::Addr([192, 254, 0, 2])),
|
54 => (4, &[192, 254, 0, 2]), // DhcpOption::ServerIdentifier(&dhcpv4::Addr([192, 254, 0, 2])),
|
||||||
58 => (4, &3400_u32.to_be_bytes()), // renewal time
|
58 => (4, &500_u32.to_be_bytes()), // DhcpOption::Unknown(58, &[0, 0, 0x1, 0xF4]), // renewal time = 500s
|
||||||
59 => (4, &3500_u32.to_be_bytes()), // rebinding time
|
59 => (4, &600_u32.to_be_bytes()), // DhcpOption::Unknown(59, &[0, 0, 0x2, 0x58]), // rebinding time = 600s
|
||||||
80 => (0, &[]),
|
80 => (0, &[]),
|
||||||
_ => {
|
_ => {
|
||||||
info!("Dhcp: unhandled requested option {}", o);
|
info!("Dhcp: unhandled requested option {}", o);
|
||||||
|
88
src/dns.rs
88
src/dns.rs
@ -1,88 +0,0 @@
|
|||||||
use dnsparse::{Answer, Header, HeaderKind, Message, OpCode, QueryClass, QueryKind, ResponseCode};
|
|
||||||
use embassy_net::{
|
|
||||||
Stack,
|
|
||||||
udp::{PacketMetadata, UdpSocket},
|
|
||||||
};
|
|
||||||
use embassy_time::Timer;
|
|
||||||
use log::{info, warn};
|
|
||||||
use pico_website::unwrap;
|
|
||||||
|
|
||||||
#[embassy_executor::task(pool_size = 1)]
|
|
||||||
pub async fn dns_server(stack: Stack<'static>) {
|
|
||||||
let mut rx_buffer = [0; 4096];
|
|
||||||
let mut tx_buffer = [0; 4096];
|
|
||||||
let mut rx_meta = [PacketMetadata::EMPTY; 16];
|
|
||||||
let mut tx_meta = [PacketMetadata::EMPTY; 16];
|
|
||||||
let mut buf = [0; 4096];
|
|
||||||
let mut res_buf = [0; 2048];
|
|
||||||
loop {
|
|
||||||
let mut socket = UdpSocket::new(
|
|
||||||
stack,
|
|
||||||
&mut rx_meta,
|
|
||||||
&mut rx_buffer,
|
|
||||||
&mut tx_meta,
|
|
||||||
&mut tx_buffer,
|
|
||||||
);
|
|
||||||
unwrap(socket.bind(53)).await;
|
|
||||||
|
|
||||||
info!("Starting DNS server");
|
|
||||||
loop {
|
|
||||||
let (n, meta) = unwrap(socket.recv_from(&mut buf).await).await;
|
|
||||||
|
|
||||||
let msg = match dnsparse::Message::parse(&mut buf[..n]) {
|
|
||||||
Ok(msg) => msg,
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Dns: Error while parsing DNS message : {:#?}", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if msg.header().opcode() != OpCode::Query {
|
|
||||||
info!(
|
|
||||||
"Dns: Received unknown dns opcode ({:?}), ignoring",
|
|
||||||
msg.header().opcode()
|
|
||||||
);
|
|
||||||
Timer::after_micros(10).await;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut res = Message::builder(&mut res_buf)
|
|
||||||
.header(
|
|
||||||
Header::builder()
|
|
||||||
.id(msg.header().id())
|
|
||||||
.kind(HeaderKind::Response)
|
|
||||||
.recursion_available(false)
|
|
||||||
.recursion_desired(msg.header().recursion_desired())
|
|
||||||
.response_code(ResponseCode::NoError)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.build();
|
|
||||||
for q in msg.questions() {
|
|
||||||
match q.kind() {
|
|
||||||
QueryKind::A => {
|
|
||||||
if q.name() == "pico.wifi" || q.name() == "www.pico.wifi" {
|
|
||||||
res.add_question(&q);
|
|
||||||
res.add_answer(&Answer {
|
|
||||||
name: q.name().clone(),
|
|
||||||
kind: QueryKind::A,
|
|
||||||
class: QueryClass::IN,
|
|
||||||
ttl: 600,
|
|
||||||
rdata: &[192, 254, 0, 2],
|
|
||||||
});
|
|
||||||
info!("Dns: Giving {}", q.name());
|
|
||||||
} else {
|
|
||||||
info!("Dns: Unknown uri, ignoring ({})", q.name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = socket.send_to(res.as_bytes(), meta).await {
|
|
||||||
warn!("Dns: Error while sending dns response : {:?}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +1,30 @@
|
|||||||
use core::fmt::Write;
|
|
||||||
use core::{ops::Not, sync::atomic::Ordering};
|
use core::{ops::Not, sync::atomic::Ordering};
|
||||||
use embassy_time::{Duration, Instant};
|
use embassy_time::{Duration, Instant};
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
use pico_website::unwrap;
|
use pico_website::unwrap;
|
||||||
use portable_atomic::{AtomicBool, AtomicU32};
|
use portable_atomic::{AtomicBool, AtomicU32};
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
use crate::socket::HttpResCode;
|
use crate::socket::HttpResCode;
|
||||||
|
|
||||||
use super::App;
|
|
||||||
|
|
||||||
static TURN: AtomicBool = AtomicBool::new(false);
|
static TURN: AtomicBool = AtomicBool::new(false);
|
||||||
// bits [0; 8] : player zero board / bits [9; 17] : player one board
|
// bits [0; 8] : player zero board / bits [9; 17] : player one board
|
||||||
static BOARD: AtomicU32 = AtomicU32::new(0);
|
static BOARD: AtomicU32 = AtomicU32::new(0);
|
||||||
|
|
||||||
pub struct TttApp {
|
pub struct GameClient {
|
||||||
res_buf: Vec<u8, 4096>,
|
res_buf: Vec<u8, 4096>,
|
||||||
/// State of the board last time it has been sent
|
/// State of the board last time it has been sent
|
||||||
last_board: u32,
|
last_board: u32,
|
||||||
team: Team,
|
team: Team,
|
||||||
end: Option<(Instant, Option<Team>)>,
|
end: Option<(Instant, Option<Team>)>
|
||||||
}
|
}
|
||||||
impl TttApp {
|
impl GameClient {
|
||||||
pub fn new(team: Team) -> Self {
|
pub fn new(team: Team) -> Self {
|
||||||
Self {
|
Self {
|
||||||
res_buf: Vec::new(),
|
res_buf: Vec::new(),
|
||||||
last_board: 0,
|
last_board: 0,
|
||||||
team,
|
team,
|
||||||
end: None,
|
end: None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_ended(&self, board: u32) -> (bool, Option<Team>) {
|
pub fn is_ended(&self, board: u32) -> (bool, Option<Team>) {
|
||||||
@ -42,21 +40,21 @@ impl TttApp {
|
|||||||
0b010010010,
|
0b010010010,
|
||||||
0b001001001,
|
0b001001001,
|
||||||
0b100010001,
|
0b100010001,
|
||||||
0b001010100,
|
0b001010100
|
||||||
] {
|
] {
|
||||||
if board & (w << m) == (w << m) {
|
if board & (w<<m) == (w<<m) {
|
||||||
return (true, Some(t));
|
return (true, Some(t))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((board | (board >> 9)) & 0b111111111) == 0b111111111 {
|
if ((board | (board>>9)) & 0b111111111) == 0b111111111 {
|
||||||
return (true, None);
|
return (true, None)
|
||||||
}
|
}
|
||||||
(false, None)
|
(false, None)
|
||||||
}
|
}
|
||||||
pub fn update_end_state(&mut self, board: &mut u32) {
|
pub fn update_end_state(&mut self, board: &mut u32) {
|
||||||
if let Some((i, _)) = self.end {
|
if let Some((i, _)) = self.end {
|
||||||
if i + Duration::from_secs(7) < Instant::now() {
|
if i+Duration::from_secs(7) < Instant::now() {
|
||||||
self.end = None;
|
self.end = None;
|
||||||
BOARD.store(0, Ordering::Release);
|
BOARD.store(0, Ordering::Release);
|
||||||
*board = 0;
|
*board = 0;
|
||||||
@ -68,12 +66,7 @@ impl TttApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Generate board html
|
/// Generate board html
|
||||||
async fn generate_board_res<'a>(
|
pub async fn generate_board_res<'a>(&'a mut self, board: u32, turn: Team, outer_html: bool) -> &'a [u8] {
|
||||||
&'a mut self,
|
|
||||||
board: u32,
|
|
||||||
turn: Team,
|
|
||||||
outer_html: bool,
|
|
||||||
) -> &'a [u8] {
|
|
||||||
self.res_buf.clear();
|
self.res_buf.clear();
|
||||||
if outer_html {
|
if outer_html {
|
||||||
unwrap(self.res_buf.extend_from_slice(
|
unwrap(self.res_buf.extend_from_slice(
|
||||||
@ -83,35 +76,33 @@ impl TttApp {
|
|||||||
hx-swap=\"innerHTML\" \
|
hx-swap=\"innerHTML\" \
|
||||||
hx-trigger=\"every 100ms\" \
|
hx-trigger=\"every 100ms\" \
|
||||||
hx-target=\"this\"\
|
hx-target=\"this\"\
|
||||||
>",
|
>"
|
||||||
))
|
)).await;
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
unwrap(write!(
|
unwrap(write!(
|
||||||
self.res_buf,
|
self.res_buf,
|
||||||
"<h3>Team : <span style=\"color:{}\">{}</span></h3>",
|
"<h3>Team : <span style=\"color:{}\">{}</span></h3>",
|
||||||
self.team.color(),
|
self.team.color(),
|
||||||
self.team.name()
|
self.team.name()
|
||||||
))
|
)).await;
|
||||||
.await;
|
|
||||||
match self.end {
|
match self.end {
|
||||||
Some((_, Some(t))) => {
|
Some((_, Some(t))) => unwrap(write!(
|
||||||
unwrap(write!(
|
|
||||||
self.res_buf,
|
self.res_buf,
|
||||||
"<br><h3>Team <span style=\"color:{}\">{}</span> has won!</h3><br>",
|
"<br><h3>Team <span style=\"color:{}\">{}</span> has won!</h3><br>",
|
||||||
t.color(),
|
t.color(),
|
||||||
t.name()
|
t.name()
|
||||||
))
|
)).await,
|
||||||
.await
|
Some((_, None)) => unwrap(write!(
|
||||||
}
|
self.res_buf,
|
||||||
Some((_, None)) => unwrap(write!(self.res_buf, "<br><h3>Draw!</h3><br>",)).await,
|
"<br><h3>Draw!</h3><br>",
|
||||||
|
)).await,
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
unwrap(self.res_buf.extend_from_slice(b"<div id=\"grid\">")).await;
|
unwrap(self.res_buf.extend_from_slice(b"<div id=\"grid\">")).await;
|
||||||
for c in 0..=8 {
|
for c in 0..=8 {
|
||||||
let picked_by = if board & (1 << c) != 0 {
|
let picked_by = if board & (1<<c) != 0 {
|
||||||
Some(Team::Zero)
|
Some(Team::Zero)
|
||||||
} else if board & (1 << (9 + c)) != 0 {
|
} else if board & (1<<(9 + c)) != 0 {
|
||||||
Some(Team::One)
|
Some(Team::One)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -122,23 +113,18 @@ impl TttApp {
|
|||||||
self.res_buf,
|
self.res_buf,
|
||||||
"<div class=\"cell\" style=\"background-color:{}\"></div>",
|
"<div class=\"cell\" style=\"background-color:{}\"></div>",
|
||||||
t.color()
|
t.color()
|
||||||
))
|
)).await;
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
None => {
|
None => if self.team == turn.into() && self.end.is_none() {
|
||||||
if self.team == turn.into() && self.end.is_none() {
|
unwrap(write!(
|
||||||
unwrap(write!(
|
|
||||||
self.res_buf,
|
self.res_buf,
|
||||||
"<button class=\"cell\" hx-post=\"/ttt/cell{}\" hx-trigger=\"click\" hx-target=\"#game\" hx-swap=\"innerHTML\"></button>",
|
"<button class=\"cell\" hx-post=\"/ttt/cell{}\" hx-trigger=\"click\" hx-target=\"#game\" hx-swap=\"innerHTML\"></button>",
|
||||||
c
|
c
|
||||||
)).await;
|
)).await;
|
||||||
} else {
|
} else {
|
||||||
unwrap(
|
unwrap(self.res_buf.extend_from_slice(
|
||||||
self.res_buf
|
b"<div class=\"cell\"></div>",
|
||||||
.extend_from_slice(b"<div class=\"cell\"></div>"),
|
)).await;
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -148,63 +134,45 @@ impl TttApp {
|
|||||||
}
|
}
|
||||||
&self.res_buf
|
&self.res_buf
|
||||||
}
|
}
|
||||||
}
|
pub async fn handle_request<'a>(&'a mut self, path: &str) -> (HttpResCode, &'static str, &'a [u8]) {
|
||||||
|
if (path.starts_with("/ttt/cell") && path.len() == 10) || path == "/ttt/game" {
|
||||||
|
let mut board = BOARD.load(Ordering::Acquire);
|
||||||
|
let mut turn = TURN.load(Ordering::Acquire);
|
||||||
|
|
||||||
impl App for TttApp {
|
// just return correct board in case of unauthorized move
|
||||||
fn socket_name(&self) -> &'static str {
|
if path.starts_with("/ttt/cell") && self.team == turn.into() {
|
||||||
self.team.name()
|
let clicked_c: Cell = match TryInto::<Cell>::try_into(
|
||||||
}
|
unwrap(path.chars().nth(9).ok_or("no 9th char")).await,
|
||||||
async fn handle_request<'a>(&'a mut self, path: &str) -> (HttpResCode, &'static str, &'a [u8]) {
|
) {
|
||||||
match path {
|
Ok(c) => c,
|
||||||
"/" | "/index" | "/index.html" | "/ttt" | "/ttt.html" => {
|
Err(_) => return (HttpResCode::NotFound, "", &[]),
|
||||||
(HttpResCode::Ok, "html", include_bytes!("./ttt.html"))
|
};
|
||||||
}
|
if board
|
||||||
"/ttt/initial_game" => {
|
& (
|
||||||
let board = BOARD.load(Ordering::Acquire);
|
(1<<(clicked_c as u32)) + (1<<(9 + clicked_c as u32))
|
||||||
let turn = TURN.load(Ordering::Acquire);
|
)
|
||||||
(
|
!= 0
|
||||||
HttpResCode::Ok,
|
{
|
||||||
"html",
|
return (HttpResCode::Forbidden, "", &[]);
|
||||||
self.generate_board_res(board, turn.into(), true).await,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
path => {
|
|
||||||
if (path.starts_with("/ttt/cell") && path.len() == 10) || path == "/ttt/game" {
|
|
||||||
let mut board = BOARD.load(Ordering::Acquire);
|
|
||||||
let mut turn = TURN.load(Ordering::Acquire);
|
|
||||||
|
|
||||||
// just return correct board in case of unauthorized move
|
|
||||||
if path.starts_with("/ttt/cell") && self.team == turn.into() {
|
|
||||||
let clicked_c: Cell = match TryInto::<Cell>::try_into(
|
|
||||||
unwrap(path.chars().nth(9).ok_or("no 9th char")).await,
|
|
||||||
) {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(_) => return (HttpResCode::NotFound, "", &[]),
|
|
||||||
};
|
|
||||||
if board & ((1 << (clicked_c as u32)) + (1 << (9 + clicked_c as u32))) != 0
|
|
||||||
{
|
|
||||||
return (HttpResCode::Forbidden, "", &[]);
|
|
||||||
}
|
|
||||||
board = board | (1 << ((self.team as u32 * 9) + clicked_c as u32));
|
|
||||||
turn = (!self.team).into();
|
|
||||||
BOARD.store(board, Ordering::Release);
|
|
||||||
TURN.store(turn, Ordering::Release);
|
|
||||||
}
|
|
||||||
self.update_end_state(&mut board);
|
|
||||||
if self.last_board != board {
|
|
||||||
self.last_board = board;
|
|
||||||
(
|
|
||||||
HttpResCode::Ok,
|
|
||||||
"html",
|
|
||||||
self.generate_board_res(board, turn.into(), false).await,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(HttpResCode::NoContent, "", &[])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(HttpResCode::NotFound, "", &[])
|
|
||||||
}
|
}
|
||||||
|
board = board | (1<<((self.team as u32 * 9) + clicked_c as u32));
|
||||||
|
turn = (!self.team).into();
|
||||||
|
BOARD.store(board, Ordering::Release);
|
||||||
|
TURN.store(turn, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
self.update_end_state(&mut board);
|
||||||
|
if self.last_board != board {
|
||||||
|
self.last_board = board;
|
||||||
|
(HttpResCode::Ok, "html", self.generate_board_res(board, turn.into(), false).await)
|
||||||
|
} else {
|
||||||
|
(HttpResCode::NoContent, "", &[])
|
||||||
|
}
|
||||||
|
} else if path == "/ttt/initial_game" {
|
||||||
|
let board = BOARD.load(Ordering::Acquire);
|
||||||
|
let turn = TURN.load(Ordering::Acquire);
|
||||||
|
(HttpResCode::Ok, "html", self.generate_board_res(board, turn.into(), true).await)
|
||||||
|
} else {
|
||||||
|
(HttpResCode::NotFound, "", &[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -223,7 +191,7 @@ impl Into<bool> for Team {
|
|||||||
fn into(self) -> bool {
|
fn into(self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Team::Zero => false,
|
Team::Zero => false,
|
||||||
Team::One => true,
|
Team::One => true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,7 +200,7 @@ impl Not for Team {
|
|||||||
fn not(self) -> Self::Output {
|
fn not(self) -> Self::Output {
|
||||||
match self {
|
match self {
|
||||||
Team::Zero => Team::One,
|
Team::Zero => Team::One,
|
||||||
Team::One => Team::Zero,
|
Team::One => Team::Zero
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,13 +208,13 @@ impl Team {
|
|||||||
fn color(self) -> &'static str {
|
fn color(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Team::Zero => "dodgerblue",
|
Team::Zero => "dodgerblue",
|
||||||
Team::One => "firebrick",
|
Team::One => "firebrick"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn name(self) -> &'static str {
|
fn name(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Team::Zero => "blue",
|
Team::Zero => "blue",
|
||||||
Team::One => "red",
|
Team::One => "red"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -296,4 +264,4 @@ impl TryFrom<u8> for Cell {
|
|||||||
_ => return Err(()),
|
_ => return Err(()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
29
src/main.rs
29
src/main.rs
@ -22,10 +22,7 @@ use {defmt_rtt as _, panic_probe as _};
|
|||||||
#[cfg(feature = "dhcp")]
|
#[cfg(feature = "dhcp")]
|
||||||
mod dhcp;
|
mod dhcp;
|
||||||
|
|
||||||
#[cfg(feature = "dns")]
|
mod game;
|
||||||
mod dns;
|
|
||||||
|
|
||||||
mod apps;
|
|
||||||
mod socket;
|
mod socket;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
bind_interrupts!(struct Irqs {
|
||||||
@ -115,7 +112,7 @@ async fn main(spawner: Spawner) {
|
|||||||
let seed = rng.next_u64();
|
let seed = rng.next_u64();
|
||||||
|
|
||||||
// Init network stack
|
// Init network stack
|
||||||
static RESOURCES: StaticCell<StackResources<10>> = StaticCell::new();
|
static RESOURCES: StaticCell<StackResources<4>> = StaticCell::new();
|
||||||
let (stack, runner) = embassy_net::new(
|
let (stack, runner) = embassy_net::new(
|
||||||
net_device,
|
net_device,
|
||||||
config,
|
config,
|
||||||
@ -126,8 +123,8 @@ async fn main(spawner: Spawner) {
|
|||||||
unwrap(spawner.spawn(net_task(runner))).await;
|
unwrap(spawner.spawn(net_task(runner))).await;
|
||||||
|
|
||||||
#[cfg(not(feature = "wifi-connect"))]
|
#[cfg(not(feature = "wifi-connect"))]
|
||||||
control.start_ap_open("pico", 5).await;
|
//control.start_ap_open("cyw43", 5).await;
|
||||||
// control.start_ap_wpa2("pico", "password", 5).await;
|
control.start_ap_wpa2("cyw43", "password", 5).await;
|
||||||
|
|
||||||
#[cfg(feature = "wifi-connect")]
|
#[cfg(feature = "wifi-connect")]
|
||||||
{
|
{
|
||||||
@ -166,22 +163,8 @@ async fn main(spawner: Spawner) {
|
|||||||
#[cfg(feature = "dhcp")]
|
#[cfg(feature = "dhcp")]
|
||||||
unwrap(spawner.spawn(dhcp::dhcp_server(stack))).await;
|
unwrap(spawner.spawn(dhcp::dhcp_server(stack))).await;
|
||||||
|
|
||||||
#[cfg(feature = "dns")]
|
unwrap(spawner.spawn(socket::listen_task(stack, game::Team::Zero, 80))).await;
|
||||||
unwrap(spawner.spawn(dns::dns_server(stack))).await;
|
unwrap(spawner.spawn(socket::listen_task(stack, game::Team::One, 81))).await;
|
||||||
|
|
||||||
unwrap(spawner.spawn(socket::index_listen_task(stack, 80))).await;
|
|
||||||
unwrap(spawner.spawn(socket::ttt_listen_task(
|
|
||||||
stack,
|
|
||||||
apps::ttt::TttApp::new(apps::ttt::Team::Zero),
|
|
||||||
8080,
|
|
||||||
)))
|
|
||||||
.await;
|
|
||||||
unwrap(spawner.spawn(socket::ttt_listen_task(
|
|
||||||
stack,
|
|
||||||
apps::ttt::TttApp::new(apps::ttt::Team::One),
|
|
||||||
8081,
|
|
||||||
)))
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wifi-connect")]
|
#[cfg(feature = "wifi-connect")]
|
||||||
|
@ -1,24 +1,16 @@
|
|||||||
use core::fmt::Write;
|
|
||||||
use core::str::from_utf8;
|
use core::str::from_utf8;
|
||||||
use embassy_net::tcp::TcpSocket;
|
use embassy_net::tcp::TcpSocket;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use embedded_io_async::Write as _;
|
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
use pico_website::unwrap;
|
||||||
|
use embedded_io_async::Write as _;
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
use crate::apps::{App, index::IndexApp, ttt};
|
use crate::game::{GameClient, Team};
|
||||||
|
|
||||||
#[embassy_executor::task(pool_size = 2)]
|
#[embassy_executor::task(pool_size = 2)]
|
||||||
pub async fn ttt_listen_task(stack: embassy_net::Stack<'static>, app: ttt::TttApp, port: u16) {
|
pub async fn listen_task(stack: embassy_net::Stack<'static>, team: Team, port: u16) {
|
||||||
listen_task(stack, app, port).await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::task(pool_size = 2)]
|
|
||||||
pub async fn index_listen_task(stack: embassy_net::Stack<'static>, port: u16) {
|
|
||||||
listen_task(stack, IndexApp, port).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl App, port: u16) {
|
|
||||||
// loop {
|
// loop {
|
||||||
// info!("team:{:?}", team);
|
// info!("team:{:?}", team);
|
||||||
// Timer::after_millis(0).await;
|
// Timer::after_millis(0).await;
|
||||||
@ -28,20 +20,22 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl App,
|
|||||||
let mut buf = [0; 4096];
|
let mut buf = [0; 4096];
|
||||||
let mut res_head_buf = Vec::<u8, 4096>::new();
|
let mut res_head_buf = Vec::<u8, 4096>::new();
|
||||||
|
|
||||||
|
let mut game_client = GameClient::new(team);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
Timer::after_secs(0).await;
|
Timer::after_secs(0).await;
|
||||||
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
|
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
|
||||||
socket.set_timeout(Some(Duration::from_secs(30)));
|
socket.set_timeout(Some(Duration::from_secs(30)));
|
||||||
|
|
||||||
info!("Socket {}: Listening on TCP:{}...", app.socket_name(), port);
|
info!("Socket {:?}: Listening on TCP:{}...", team, port);
|
||||||
if let Err(e) = socket.accept(port).await {
|
if let Err(e) = socket.accept(port).await {
|
||||||
warn!("accept error: {:?}", e);
|
warn!("accept error: {:?}", e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Socket {}: Received connection from {:?}",
|
"Socket {:?}: Received connection from {:?}",
|
||||||
app.socket_name(),
|
team,
|
||||||
socket.remote_endpoint()
|
socket.remote_endpoint()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -54,7 +48,7 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl App,
|
|||||||
}
|
}
|
||||||
Ok(n) => n,
|
Ok(n) => n,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Socket {}: read error: {:?}", app.socket_name(), e);
|
warn!("Socket {:?}: read error: {:?}", team, e);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -83,17 +77,7 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl App,
|
|||||||
Some(b"GET") => HttpRequestType::Get,
|
Some(b"GET") => HttpRequestType::Get,
|
||||||
Some(b"POST") => HttpRequestType::Post,
|
Some(b"POST") => HttpRequestType::Post,
|
||||||
Some(t) => {
|
Some(t) => {
|
||||||
match from_utf8(t) {
|
warn!("Unknown request type : {}", unwrap(from_utf8(t)).await);
|
||||||
Ok(t) => {
|
|
||||||
warn!("Unknown request type : {}", t);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!(
|
|
||||||
"Error while parsing request type : {}\nRaw type : {:?}",
|
|
||||||
e, t
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@ -102,16 +86,7 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl App,
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
match l1.next() {
|
match l1.next() {
|
||||||
Some(path) => match from_utf8(path) {
|
Some(path) => unwrap(from_utf8(path)).await,
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => {
|
|
||||||
warn!(
|
|
||||||
"Error while parsing requested path : {}\nRaw path : {:?}",
|
|
||||||
e, path
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => {
|
None => {
|
||||||
warn!("No path");
|
warn!("No path");
|
||||||
break;
|
break;
|
||||||
@ -122,23 +97,26 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl App,
|
|||||||
};
|
};
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Socket {}: {:?} request for {}",
|
"Socket {:?}: {:?} request for {}",
|
||||||
app.socket_name(),
|
team,
|
||||||
request_type,
|
request_type,
|
||||||
path
|
path
|
||||||
);
|
);
|
||||||
Timer::after_secs(0).await;
|
Timer::after_secs(0).await;
|
||||||
|
|
||||||
let (code, res_type, res_content): (HttpResCode, &str, &[u8]) = match path {
|
let (code, res_type, res_content): (HttpResCode, &str, &[u8]) = match path {
|
||||||
|
"/" => (HttpResCode::Ok, "html", include_bytes!("../static/index.html")),
|
||||||
|
"/htmx.min.js" => (
|
||||||
|
HttpResCode::Ok,
|
||||||
|
"javascript",
|
||||||
|
include_bytes!("../static/htmx.min.js"),
|
||||||
|
),
|
||||||
"/htmx.js" => (
|
"/htmx.js" => (
|
||||||
HttpResCode::Ok,
|
HttpResCode::Ok,
|
||||||
"javascript",
|
"javascript",
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
include_bytes!("../static/htmx.js"),
|
include_bytes!("../static/htmx.js"),
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
include_bytes!("../static/htmx.min.js"),
|
|
||||||
),
|
),
|
||||||
p => app.handle_request(p).await,
|
p => game_client.handle_request(p).await
|
||||||
};
|
};
|
||||||
|
|
||||||
res_head_buf.clear();
|
res_head_buf.clear();
|
||||||
@ -182,7 +160,7 @@ impl Into<&str> for HttpRequestType {
|
|||||||
fn into(self) -> &'static str {
|
fn into(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::Get => "GET",
|
Self::Get => "GET",
|
||||||
Self::Post => "POST",
|
Self::Post => "POST"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,4 +181,4 @@ impl Into<&str> for HttpResCode {
|
|||||||
HttpResCode::Forbidden => "HTTP/1.1 403 FORBIDDEN",
|
HttpResCode::Forbidden => "HTTP/1.1 403 FORBIDDEN",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user