From 03b6e01bd8c1cf994c9345715f9beeeda4b6de6d Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Wed, 22 Jan 2025 02:34:51 +0100 Subject: [PATCH] Allow for multiple connections at the same time --- Cargo.lock | 47 ++++---- air_filter_types/Cargo.toml | 2 +- src/main.rs | 210 +++++++++++++++++++++++------------- 3 files changed, 158 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 433077e..17af344 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/air_filter_types/Cargo.toml b/air_filter_types/Cargo.toml index e0f4e79..07f2c1e 100644 --- a/air_filter_types/Cargo.toml +++ b/air_filter_types/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "air_filter_types" version = "0.1.0" -edition = "2024" +edition = "2021" [dependencies] bme280 = { workspace = true } diff --git a/src/main.rs b/src/main.rs index 95fc0c5..75ec044 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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; I2C0_IRQ => i2c::InterruptHandler; }); -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>); +// 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>>, -); + 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 for SharedController { - fn from_ref(state: &AppState) -> Self { - state.shared_controller + #[derive(Clone, Copy)] + pub struct SharedController(pub &'static Mutex>); + + impl SharedController { + fn new(controller: Controller<'static>) -> Self { + Self( + make_static!(Mutex>, Mutex::new(controller)), + ) + } } -} -impl picoserve::extract::FromRef for SharedBME280 { - fn from_ref(state: &AppState) -> Self { - state.shared_bme280 + #[derive(Clone, Copy)] + pub struct SharedBME280( + pub &'static Mutex>>, + ); + + impl SharedBME280 { + fn new(bme280: AsyncBME280>) -> Self { + Self( + make_static!(Mutex>>, Mutex::new(bme280)), + ) + } } -} -fn state_router() -> Router, 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>, + ) -> Self { + Self { + shared_controller: SharedController::new(controller), + shared_bme280: SharedBME280::new(bme280), + } + } + } + + impl picoserve::extract::FromRef for SharedController { + fn from_ref(state: &AppState) -> Self { + state.shared_controller + } + } + + impl picoserve::extract::FromRef for SharedBME280 { + fn from_ref(state: &AppState) -> Self { + state.shared_bme280 + } + } + + fn state_router() -> Router, 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, AppState> { SharedController, >, extract::Json(message): extract::Json| 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, AppState> { "/sensor", get( |extract::State(SharedBME280(bme280)): extract::State| async { + debug!("Getting sensor state"); + let measurement = bme280 .lock() .await @@ -224,6 +278,20 @@ fn state_router() -> Router, AppState> { }, ), ) + } + + pub type AppRouter = impl PathRouter; + pub fn make_app( + updater: &'static Mutex< + CriticalSectionRawMutex, + BlockingFirmwareUpdater<'static, Partition, Partition>, + >, + ) -> Router { + 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, + config: &'static picoserve::Config, + 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, AlignedBuffer([0; WRITE_SIZE])); let updater = BlockingFirmwareUpdater::new(config, &mut aligned.0); - let updater = make_static!(Mutex>, Mutex::new(updater)); + let updater = make_static!(Mutex>, 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> = StaticCell::new(); + static RESOURCES: StaticCell> = 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::make_app(updater)); - let shared_controller = SharedController( - make_static!(Mutex>, Mutex::new(controller)), - ); - let shared_bme280 = SharedBME280( - make_static!(Mutex>>, 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; + } }