Allow for multiple connections at the same time

This commit is contained in:
Dreaded_X 2025-01-22 02:34:51 +01:00
parent 8603d6fab4
commit 03b6e01bd8
Signed by: Dreaded_X
GPG Key ID: FA5F485356B0D2D4
3 changed files with 158 additions and 101 deletions

47
Cargo.lock generated
View File

@ -133,9 +133,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.7.0"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be"
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
[[package]]
name = "block-buffer"
@ -361,9 +361,9 @@ dependencies = [
[[package]]
name = "data-encoding"
version = "2.6.0"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f"
[[package]]
name = "debug-helper"
@ -658,9 +658,9 @@ dependencies = [
[[package]]
name = "embassy-sync"
version = "0.6.1"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3899a6e39fa3f54bf8aaf00979f9f9c0145a522f7244810533abbb748be6ce82"
checksum = "8d2c8cdff05a7a51ba0087489ea44b0b1d97a296ca6b1d6d1a33ea7423d34049"
dependencies = [
"cfg-if",
"critical-section",
@ -1018,9 +1018,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "indexmap"
version = "2.7.0"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
dependencies = [
"equivalent",
"hashbrown",
@ -1028,13 +1028,13 @@ dependencies = [
[[package]]
name = "is-terminal"
version = "0.4.13"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
checksum = "3f187290c0ed3dfe3f7c85bedddd320949b68fc86ca0ceb71adfb05b3dc3af2a"
dependencies = [
"hermit-abi",
"libc",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -1095,7 +1095,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.7.0",
"bitflags 2.8.0",
"libc",
]
@ -1117,9 +1117,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.24"
version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6ea2a48c204030ee31a7d7fc72c93294c92fe87ecb1789881c9543516e1a0d"
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
[[package]]
name = "managed"
@ -1446,9 +1446,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.92"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
@ -1459,7 +1459,7 @@ version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625"
dependencies = [
"bitflags 2.7.0",
"bitflags 2.8.0",
"memchr",
"unicase",
]
@ -1505,7 +1505,7 @@ version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [
"bitflags 2.7.0",
"bitflags 2.8.0",
]
[[package]]
@ -1971,7 +1971,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.59.0",
"windows-sys",
]
[[package]]
@ -1980,15 +1980,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"

View File

@ -1,7 +1,7 @@
[package]
name = "air_filter_types"
version = "0.1.0"
edition = "2024"
edition = "2021"
[dependencies]
bme280 = { workspace = true }

View File

@ -1,9 +1,11 @@
#![no_std]
#![no_main]
#![feature(impl_trait_in_assoc_type)]
#![feature(type_alias_impl_trait)]
use core::{cell::RefCell, str::FromStr};
use approuter::{AppRouter, AppState};
use bme280::i2c::AsyncBME280;
use cyw43::{JoinOptions, PowerManagementMode};
use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER};
@ -11,13 +13,13 @@ use defmt::{debug, info, warn};
use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig};
use embassy_embedded_hal::flash::partition::BlockingPartition;
use embassy_executor::Spawner;
use embassy_net::{Config, DhcpConfig, StackResources};
use embassy_net::{Config, DhcpConfig, Stack, StackResources};
use embassy_rp::{
bind_interrupts,
clocks::RoscRng,
flash::{Flash, WRITE_SIZE},
gpio::{Flex, Input, Level, Output, Pin, Pull},
i2c::{self, Async},
i2c::{self},
peripherals::{DMA_CH1, FLASH, I2C0, PIO0},
pio::{self, Pio},
Peripheral,
@ -31,26 +33,19 @@ use embassy_sync::{
};
use embassy_time::{Delay, Duration, Timer};
use heapless::String;
use picoserve::{
extract, make_static,
routing::{get, PathRouter},
Router,
};
use picoserve::{make_static, Router};
use rand::{rngs::StdRng, RngCore, SeedableRng};
use static_cell::StaticCell;
use updater::firmware_router;
use {defmt_rtt as _, panic_probe as _};
use air_filter_types::{FanSpeed, FanState, SensorData, SetFanSpeed};
use air_filter_types::{FanSpeed, FanState};
bind_interrupts!(struct Irqs {
PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
I2C0_IRQ => i2c::InterruptHandler<I2C0>;
});
const VERSION: &str = git_version::git_version!();
const PUBLIC_SIGNING_KEY: &[u8; 32] = include_bytes!("../key.pub");
const FLASH_SIZE: usize = 2 * 1024 * 1024;
struct Controller<'a> {
@ -156,39 +151,94 @@ impl<'a> Controller<'a> {
}
}
#[derive(Clone, Copy)]
struct SharedController(&'static Mutex<CriticalSectionRawMutex, Controller<'static>>);
// We need this because otherwise the compiler yells at us
mod approuter {
use air_filter_types::{SensorData, SetFanSpeed};
use bme280::i2c::AsyncBME280;
use defmt::debug;
use embassy_boot::BlockingFirmwareUpdater;
use embassy_rp::{
i2c::{Async, I2c},
peripherals::I2C0,
};
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex};
use embassy_time::Delay;
use picoserve::{
extract, make_static,
routing::{get, PathRouter},
Router,
};
use updater::firmware_router;
#[derive(Clone, Copy)]
struct SharedBME280(
&'static Mutex<CriticalSectionRawMutex, AsyncBME280<i2c::I2c<'static, I2C0, Async>>>,
);
use crate::{Controller, Partition};
struct AppState {
shared_controller: SharedController,
shared_bme280: SharedBME280,
}
const VERSION: &str = git_version::git_version!();
const PUBLIC_SIGNING_KEY: &[u8; 32] = include_bytes!("../key.pub");
impl picoserve::extract::FromRef<AppState> for SharedController {
fn from_ref(state: &AppState) -> Self {
state.shared_controller
#[derive(Clone, Copy)]
pub struct SharedController(pub &'static Mutex<CriticalSectionRawMutex, Controller<'static>>);
impl SharedController {
fn new(controller: Controller<'static>) -> Self {
Self(
make_static!(Mutex<CriticalSectionRawMutex, Controller<'static>>, Mutex::new(controller)),
)
}
}
}
impl picoserve::extract::FromRef<AppState> for SharedBME280 {
fn from_ref(state: &AppState) -> Self {
state.shared_bme280
#[derive(Clone, Copy)]
pub struct SharedBME280(
pub &'static Mutex<CriticalSectionRawMutex, AsyncBME280<I2c<'static, I2C0, Async>>>,
);
impl SharedBME280 {
fn new(bme280: AsyncBME280<I2c<'static, I2C0, Async>>) -> Self {
Self(
make_static!(Mutex<CriticalSectionRawMutex, AsyncBME280<I2c<'static, I2C0, Async>>>, Mutex::new(bme280)),
)
}
}
}
fn state_router() -> Router<impl PathRouter<AppState>, AppState> {
Router::new()
#[derive(Clone)]
pub struct AppState {
pub shared_controller: SharedController,
pub shared_bme280: SharedBME280,
}
impl AppState {
pub fn new(
controller: Controller<'static>,
bme280: AsyncBME280<I2c<'static, I2C0, Async>>,
) -> Self {
Self {
shared_controller: SharedController::new(controller),
shared_bme280: SharedBME280::new(bme280),
}
}
}
impl picoserve::extract::FromRef<AppState> for SharedController {
fn from_ref(state: &AppState) -> Self {
state.shared_controller
}
}
impl picoserve::extract::FromRef<AppState> for SharedBME280 {
fn from_ref(state: &AppState) -> Self {
state.shared_bme280
}
}
fn state_router() -> Router<impl PathRouter<AppState>, AppState> {
Router::new()
.route(
"/fan",
get(
|extract::State(SharedController(controller)): extract::State<
SharedController,
>| async {
debug!("Getting fan state");
let state = controller.lock().await.get_state();
picoserve::response::Json(state)
},
@ -198,6 +248,8 @@ fn state_router() -> Router<impl PathRouter<AppState>, AppState> {
SharedController,
>,
extract::Json(message): extract::Json<SetFanSpeed>| async move {
debug!("Setting fan state");
let success = controller.lock().await.set_speed(message.speed());
if success {
@ -212,6 +264,8 @@ fn state_router() -> Router<impl PathRouter<AppState>, AppState> {
"/sensor",
get(
|extract::State(SharedBME280(bme280)): extract::State<SharedBME280>| async {
debug!("Getting sensor state");
let measurement = bme280
.lock()
.await
@ -224,6 +278,20 @@ fn state_router() -> Router<impl PathRouter<AppState>, AppState> {
},
),
)
}
pub type AppRouter = impl PathRouter<AppState>;
pub fn make_app(
updater: &'static Mutex<
CriticalSectionRawMutex,
BlockingFirmwareUpdater<'static, Partition, Partition>,
>,
) -> Router<AppRouter, AppState> {
Router::new().nest("/state", state_router()).nest(
"/firmware",
firmware_router(VERSION, updater, PUBLIC_SIGNING_KEY),
)
}
}
/// Get the cyw43 firmware blobs
@ -268,18 +336,39 @@ async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'sta
runner.run().await
}
type Thingy = BlockingPartition<
type Partition = BlockingPartition<
'static,
NoopRawMutex,
Flash<'static, FLASH, embassy_rp::flash::Blocking, FLASH_SIZE>,
>;
#[embassy_executor::task]
async fn fan_task(shared_controller: SharedController) -> ! {
loop {
shared_controller.0.lock().await.check_for_manual();
Timer::after(Duration::from_millis(500)).await;
}
const WEB_TASK_POOL_SIZE: usize = 8;
#[embassy_executor::task(pool_size = WEB_TASK_POOL_SIZE)]
async fn web_task(
id: usize,
stack: Stack<'static>,
app: &'static Router<AppRouter, AppState>,
config: &'static picoserve::Config<Duration>,
state: AppState,
) -> ! {
let port = 80;
let mut tcp_rx_buffer = [0; 1024];
let mut tcp_tx_buffer = [0; 1024];
let mut http_buffer = [0; 2048];
picoserve::listen_and_serve_with_state(
id,
app,
config,
stack,
port,
&mut tcp_rx_buffer,
&mut tcp_tx_buffer,
&mut http_buffer,
&state,
)
.await;
}
#[embassy_executor::main]
@ -293,7 +382,7 @@ async fn main(spawner: Spawner) {
let config = FirmwareUpdaterConfig::from_linkerfile_blocking(flash, flash);
let aligned = make_static!(AlignedBuffer<WRITE_SIZE>, AlignedBuffer([0; WRITE_SIZE]));
let updater = BlockingFirmwareUpdater::new(config, &mut aligned.0);
let updater = make_static!(Mutex<CriticalSectionRawMutex, BlockingFirmwareUpdater<'static, Thingy, Thingy>>, Mutex::new(updater));
let updater = make_static!(Mutex<CriticalSectionRawMutex, BlockingFirmwareUpdater<'static, Partition, Partition>>, Mutex::new(updater));
let controller = Controller::new(p.PIN_28, p.PIN_27, p.PIN_26, p.PIN_22);
@ -340,7 +429,7 @@ async fn main(spawner: Spawner) {
// cryptographically secure PRNG
let mut rng = StdRng::from_rng(&mut RoscRng).unwrap();
static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
static RESOURCES: StaticCell<StackResources<16>> = StaticCell::new();
let (stack, runner) = embassy_net::new(
net_device,
config,
@ -392,39 +481,16 @@ async fn main(spawner: Spawner) {
.keep_connection_alive()
);
let app = Router::new().nest("/state", state_router()).nest(
"/firmware",
firmware_router(VERSION, updater, PUBLIC_SIGNING_KEY),
);
let app = make_static!(Router<AppRouter, AppState>, approuter::make_app(updater));
let shared_controller = SharedController(
make_static!(Mutex<CriticalSectionRawMutex, Controller<'static>>, Mutex::new(controller)),
);
let shared_bme280 = SharedBME280(
make_static!(Mutex<CriticalSectionRawMutex, AsyncBME280<i2c::I2c<'static, I2C0, Async>>>, Mutex::new(bme280)),
);
let state = AppState::new(controller, bme280);
let port = 80;
let mut tcp_rx_buffer = [0; 1024];
let mut tcp_tx_buffer = [0; 1024];
let mut http_buffer = [0; 2048];
for id in 0..WEB_TASK_POOL_SIZE {
spawner.must_spawn(web_task(id, stack, app, config, state.clone()));
}
spawner.must_spawn(fan_task(shared_controller));
// We can only handle one request at a time
picoserve::listen_and_serve_with_state(
0,
&app,
config,
stack,
port,
&mut tcp_rx_buffer,
&mut tcp_tx_buffer,
&mut http_buffer,
&AppState {
shared_controller,
shared_bme280,
},
)
.await;
loop {
state.shared_controller.0.lock().await.check_for_manual();
Timer::after(Duration::from_millis(500)).await;
}
}