Allow for multiple connections at the same time

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

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;
}
}