Just cleaning up the code, no functional changes.
This commit is contained in:
parent
a77576b26a
commit
07959bf1b7
1 changed files with 89 additions and 146 deletions
233
src/main.rs
233
src/main.rs
|
|
@ -14,103 +14,81 @@ use embassy_rp::clocks::RoscRng;
|
|||
use embassy_rp::gpio::{Level, Output};
|
||||
use embassy_rp::i2c::{self, Config};
|
||||
use embassy_rp::peripherals::{DMA_CH0, PIO0};
|
||||
use embassy_rp::peripherals::USB;
|
||||
use embassy_rp::peripherals::I2C1;
|
||||
use embassy_rp::peripherals::USB;
|
||||
use embassy_rp::pio::{InterruptHandler, Pio};
|
||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex};
|
||||
use embassy_rp::usb::{Driver, InterruptHandler as USBInterruptHandler};
|
||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex};
|
||||
use embassy_time::{Duration, Timer, Delay};
|
||||
use heapless::Vec;
|
||||
use libscd::synchronous::scd4x::Scd4x;
|
||||
use picoserve::{
|
||||
make_static,
|
||||
routing::{get, get_service, PathRouter},
|
||||
AppWithStateBuilder, AppRouter,
|
||||
response::DebugValue
|
||||
};
|
||||
use picoserve::response::File;
|
||||
use picoserve::extract::State;
|
||||
use picoserve::{ make_static, routing::{get, get_service, PathRouter}, AppWithStateBuilder, AppRouter, response::DebugValue };
|
||||
use picoserve::response::File;
|
||||
use rand::RngCore;
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
// oh no, exposing my IoT WiFi credentials to the world ;)
|
||||
const WIFI_NETWORK: &str = "bendybogalow";
|
||||
const WIFI_PASSWORD: &str = "parsnipcabbageonion";
|
||||
const INDEX: &str = include_str!("html/index.html");
|
||||
const CSS: &str = include_str!("html/main.css");
|
||||
|
||||
// TODO: I think these calls can be combined?
|
||||
bind_interrupts!(struct Irqs {
|
||||
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
||||
});
|
||||
|
||||
bind_interrupts!(struct UsbIrqs {
|
||||
USBCTRL_IRQ => USBInterruptHandler<USB>;
|
||||
});
|
||||
|
||||
// from example code
|
||||
#[embassy_executor::task]
|
||||
async fn logger_task(driver: Driver<'static, USB>) {
|
||||
embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver);
|
||||
}
|
||||
|
||||
// from example code
|
||||
#[embassy_executor::task]
|
||||
async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! {
|
||||
runner.run().await
|
||||
}
|
||||
|
||||
// from example code
|
||||
#[embassy_executor::task]
|
||||
async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {
|
||||
runner.run().await
|
||||
}
|
||||
|
||||
/*
|
||||
async fn co2ppm_to_str {
|
||||
let mut buff : [u8; 20] = [0u8; 20];
|
||||
unsafe {
|
||||
CO2PPM.numtoa(10, &mut buff);
|
||||
}
|
||||
let buffstr: &str = core::str::from_utf8(&mut buff).unwrap();
|
||||
log::info!("CO2PPM from build_app: {}", buffstr);
|
||||
"1234" // works
|
||||
// buffstr // fails for the obvious reason of lifetime
|
||||
}
|
||||
*/
|
||||
/*
|
||||
impl Content for AtomicU16 {
|
||||
fn content_type(&self) -> &'static str {
|
||||
"text/plain; charset=utf-8"
|
||||
}
|
||||
fn content_length(&self) -> usize {
|
||||
5;
|
||||
}
|
||||
async fn write_content<W: Write>(self, writer: W) -> Result<(), W::Error> {
|
||||
"fooo".as_bytes().write_content(writer).await
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*
|
||||
struct Number {
|
||||
value: u16;
|
||||
}
|
||||
|
||||
async fn get_number(Number { value }: Number) -> impl IntoResponse {
|
||||
picoserve::response::DebugValue(value)
|
||||
}
|
||||
*/
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Shared state related code
|
||||
|
||||
// an instance of this will be shared between the sensor read task and the web serve task
|
||||
struct CO2PPM {
|
||||
co2ppm : u16,
|
||||
value : u16,
|
||||
}
|
||||
|
||||
// some sort of mutex wrapper for a CO2PPM instance?
|
||||
#[derive(Clone, Copy)]
|
||||
struct SharedPPM(&'static Mutex<CriticalSectionRawMutex, CO2PPM>);
|
||||
|
||||
// this then allows us to pass the shared CO2PPM into the picoserve interface
|
||||
struct AppState {
|
||||
shared_ppm : SharedPPM,
|
||||
}
|
||||
// this is used in the route closure below to extract the SharedPPM struct from the app state
|
||||
impl picoserve::extract::FromRef<AppState> for SharedPPM {
|
||||
fn from_ref(state: &AppState) -> Self {
|
||||
state.shared_ppm
|
||||
}
|
||||
}
|
||||
|
||||
// picoserve HTTP code kicked off using: https://github.com/sammhicks/picoserve/blob/main/examples/embassy/hello_world/src/main.rs
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// HTTP handler code
|
||||
|
||||
struct AppProps;
|
||||
|
||||
impl AppWithStateBuilder for AppProps {
|
||||
|
|
@ -118,27 +96,21 @@ impl AppWithStateBuilder for AppProps {
|
|||
type PathRouter = impl PathRouter<AppState>;
|
||||
|
||||
fn build_app(self) -> picoserve::Router<Self::PathRouter, Self::State> {
|
||||
//let Self { } = self;
|
||||
|
||||
/*let mut buff : [u8; 20] = [0u8; 20];
|
||||
unsafe {
|
||||
let _ = CO2PPM.numtoa(10, &mut buff);
|
||||
}
|
||||
let buffstr: &str = core::str::from_utf8(&mut buff).unwrap();
|
||||
log::info!("CO2PPM from build_app: {}", buffstr);*/
|
||||
|
||||
picoserve::Router::new()
|
||||
.route(
|
||||
"/",
|
||||
get_service(File::html(INDEX)) // .replace("%{CO2}%", CO2PPM.to_string())))
|
||||
get_service(File::html(INDEX)),
|
||||
)
|
||||
.route(
|
||||
"/main.css",
|
||||
get_service(File::css(include_str!("html/main.css")))
|
||||
get_service(File::css(CSS)),
|
||||
)
|
||||
.route(
|
||||
"/data/co2",
|
||||
get(|State(SharedPPM(co2ppm)): State<SharedPPM>| async move { DebugValue( co2ppm.lock().await.co2ppm ) }),
|
||||
get(
|
||||
|State(SharedPPM(co2ppm)): State<SharedPPM>| //newbie note: | delimits a closure
|
||||
async move { DebugValue( co2ppm.lock().await.value) }
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -173,20 +145,46 @@ async fn web_task(
|
|||
.await
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// CO2 Sensor Code
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn read_co2(
|
||||
mut scd: Scd4x<i2c::I2c<'static, I2C1, i2c::Blocking>, Delay>,
|
||||
shared_ppm: SharedPPM
|
||||
) {
|
||||
log::info!("Enter sensor read loop");
|
||||
loop {
|
||||
if scd.data_ready().unwrap() {
|
||||
let m = scd.read_measurement().unwrap();
|
||||
shared_ppm.0.lock().await.value = m.co2;
|
||||
log::info!(
|
||||
"CO2: {}\nHumidity: {}\nTemperature: {}", m.co2, m.humidity, m.temperature
|
||||
)
|
||||
}
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// main!
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let driver = Driver::new(p.USB, UsbIrqs);
|
||||
|
||||
spawner.spawn(logger_task(driver)).unwrap();
|
||||
let mut rng = RoscRng;
|
||||
log::info!("main: entry");
|
||||
|
||||
// TODO: this was required to make log entries before the loop actually reach the TTY - so I am
|
||||
// guessing there is some setup happening in the background and wonder if there is a better way
|
||||
// to wait for that than a sleep...
|
||||
// to wait for TTY to be ready that than a sleep... (the sleep here is not in fact a 100%
|
||||
// reliable way to ensure the TTY is ready it seems, but works most of the time.)
|
||||
Timer::after_secs(1).await;
|
||||
|
||||
log::info!("main: entry");
|
||||
|
||||
let fw = include_bytes!("../cyw43-firmware/43439A0.bin");
|
||||
let clm = include_bytes!("../cyw43-firmware/43439A0_clm.bin");
|
||||
|
||||
|
|
@ -212,6 +210,10 @@ async fn main(spawner: Spawner) {
|
|||
p.DMA_CH0,
|
||||
);
|
||||
|
||||
|
||||
////////////////////////////////////////////
|
||||
// get the WiFi up
|
||||
|
||||
log::info!("main: init wifi");
|
||||
static STATE: StaticCell<cyw43::State> = StaticCell::new();
|
||||
let state = STATE.init(cyw43::State::new());
|
||||
|
|
@ -224,7 +226,13 @@ async fn main(spawner: Spawner) {
|
|||
.set_power_management(cyw43::PowerManagementMode::PowerSave)
|
||||
.await;
|
||||
|
||||
|
||||
////////////////////////////////////////////
|
||||
// get an IP sorted
|
||||
|
||||
// if DHCP then use this code:
|
||||
// let config = Config::dhcpv4(Default::default());
|
||||
// if static IP then use this code:
|
||||
log::info!("main: configure static IP");
|
||||
let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {
|
||||
address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 3, 15), 24),
|
||||
|
|
@ -233,6 +241,7 @@ async fn main(spawner: Spawner) {
|
|||
});
|
||||
|
||||
// Generate random seed
|
||||
let mut rng = RoscRng;
|
||||
let seed = rng.next_u64();
|
||||
|
||||
// Init network stack
|
||||
|
|
@ -264,37 +273,43 @@ async fn main(spawner: Spawner) {
|
|||
log::info!("DHCP is now up!");*/
|
||||
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Shared state required by our to main tasks (sensor reader, web server)
|
||||
|
||||
let co2ppm = CO2PPM { value: 0 };
|
||||
let shared_ppm = SharedPPM(
|
||||
make_static!(Mutex<CriticalSectionRawMutex, CO2PPM>, Mutex::new( co2ppm )),
|
||||
);
|
||||
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Set up the SCD40 I2C sensor
|
||||
|
||||
log::info!("Starting I2C Comms with SCD40");
|
||||
Timer::after_secs(1).await;
|
||||
// this code derived from: https://github.com/SvetlinZarev/libscd/blob/main/examples/embassy-scd4x/src/main.rs
|
||||
let sda = p.PIN_26;
|
||||
let scl = p.PIN_27;
|
||||
let i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default());
|
||||
log::info!("Initialise Scd4x");
|
||||
Timer::after_secs(1).await;
|
||||
let mut scd = Scd4x::new(i2c, Delay);
|
||||
|
||||
// When re-programming, the controller will be restarted,
|
||||
// but not the sensor. We try to stop it in order to
|
||||
// prevent the rest of the commands failing.
|
||||
// When re-programming, the controller will be restarted, but not the sensor. We try to stop it
|
||||
// in order to prevent the rest of the commands failing.
|
||||
log::info!("Stop periodic measurements");
|
||||
Timer::after_secs(1).await;
|
||||
_ = scd.stop_periodic_measurement();
|
||||
|
||||
log::info!("Sensor serial number: {:?}", scd.serial_number());
|
||||
Timer::after_secs(1).await;
|
||||
if let Err(e) = scd.start_periodic_measurement() {
|
||||
log::error!("Failed to start periodic measurement: {:?}", e );
|
||||
|
||||
}
|
||||
|
||||
let co2ppm = 69;
|
||||
let shared_ppm = SharedPPM(
|
||||
make_static!(Mutex<CriticalSectionRawMutex, CO2PPM>, Mutex::new(CO2PPM { co2ppm })),
|
||||
);
|
||||
spawner.must_spawn(read_co2(scd, shared_ppm));
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Set up the HTTP service
|
||||
|
||||
log::info!("Commence HTTP service");
|
||||
Timer::after_secs(1).await;
|
||||
|
||||
let app = make_static!(AppRouter<AppProps>, AppProps.build_app());
|
||||
|
||||
|
|
@ -317,77 +332,5 @@ async fn main(spawner: Spawner) {
|
|||
AppState{ shared_ppm },
|
||||
));
|
||||
}
|
||||
/*
|
||||
let mut rx_buffer = [0; 4096];
|
||||
let mut tx_buffer = [0; 4096];
|
||||
let mut buf = [0; 4096];
|
||||
|
||||
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
|
||||
|
||||
//let delay = Duration::from_secs(1);
|
||||
log::info!("main: pre-loop");
|
||||
loop {
|
||||
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
|
||||
socket.set_timeout(Some(Duration::from_secs(10)));
|
||||
|
||||
control.gpio_set(0, false).await;
|
||||
log::info!("Listening on TCP:00...");
|
||||
if let Err(e) = socket.accept(1234).await {
|
||||
log::warn!("accept error: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
|
||||
log::info!("Received connection from {:?}", socket.remote_endpoint());
|
||||
|
||||
control.gpio_set(0, true).await;
|
||||
|
||||
loop {
|
||||
let n = match socket.read(&mut buf).await {
|
||||
Ok(0) => {
|
||||
log::warn!("read EOF");
|
||||
break;
|
||||
}
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
log::warn!("read error: {:?}", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
log::info!("rxd {}", from_utf8(&buf[..n]).unwrap());
|
||||
|
||||
match socket.write_all(&buf[..n]).await {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
log::warn!("write error: {:?}", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
log::info!("LED off!");
|
||||
control.gpio_set(0, false).await;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn read_co2(
|
||||
mut scd: Scd4x<i2c::I2c<'static, I2C1, i2c::Blocking>, Delay>,
|
||||
shared_ppm: SharedPPM
|
||||
) {
|
||||
log::info!("Enter sensor read loop");
|
||||
Timer::after_secs(1).await;
|
||||
loop {
|
||||
if scd.data_ready().unwrap() {
|
||||
let m = scd.read_measurement().unwrap();
|
||||
shared_ppm.0.lock().await.co2ppm = m.co2;
|
||||
log::info!(
|
||||
"CO2: {}\nHumidity: {}\nTemperature: {}", m.co2, m.humidity, m.temperature
|
||||
)
|
||||
}
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue