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
235
src/main.rs
235
src/main.rs
|
|
@ -14,103 +14,81 @@ use embassy_rp::clocks::RoscRng;
|
||||||
use embassy_rp::gpio::{Level, Output};
|
use embassy_rp::gpio::{Level, Output};
|
||||||
use embassy_rp::i2c::{self, Config};
|
use embassy_rp::i2c::{self, Config};
|
||||||
use embassy_rp::peripherals::{DMA_CH0, PIO0};
|
use embassy_rp::peripherals::{DMA_CH0, PIO0};
|
||||||
use embassy_rp::peripherals::USB;
|
|
||||||
use embassy_rp::peripherals::I2C1;
|
use embassy_rp::peripherals::I2C1;
|
||||||
|
use embassy_rp::peripherals::USB;
|
||||||
use embassy_rp::pio::{InterruptHandler, Pio};
|
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_rp::usb::{Driver, InterruptHandler as USBInterruptHandler};
|
||||||
|
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex};
|
||||||
use embassy_time::{Duration, Timer, Delay};
|
use embassy_time::{Duration, Timer, Delay};
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
use libscd::synchronous::scd4x::Scd4x;
|
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::extract::State;
|
||||||
|
use picoserve::{ make_static, routing::{get, get_service, PathRouter}, AppWithStateBuilder, AppRouter, response::DebugValue };
|
||||||
|
use picoserve::response::File;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
// oh no, exposing my IoT WiFi credentials to the world ;)
|
||||||
const WIFI_NETWORK: &str = "bendybogalow";
|
const WIFI_NETWORK: &str = "bendybogalow";
|
||||||
const WIFI_PASSWORD: &str = "parsnipcabbageonion";
|
const WIFI_PASSWORD: &str = "parsnipcabbageonion";
|
||||||
const INDEX: &str = include_str!("html/index.html");
|
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 {
|
bind_interrupts!(struct Irqs {
|
||||||
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
||||||
});
|
});
|
||||||
|
|
||||||
bind_interrupts!(struct UsbIrqs {
|
bind_interrupts!(struct UsbIrqs {
|
||||||
USBCTRL_IRQ => USBInterruptHandler<USB>;
|
USBCTRL_IRQ => USBInterruptHandler<USB>;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// from example code
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn logger_task(driver: Driver<'static, USB>) {
|
async fn logger_task(driver: Driver<'static, USB>) {
|
||||||
embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver);
|
embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// from example code
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! {
|
async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! {
|
||||||
runner.run().await
|
runner.run().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// from example code
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {
|
async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {
|
||||||
runner.run().await
|
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 {
|
struct CO2PPM {
|
||||||
co2ppm : u16,
|
value : u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// some sort of mutex wrapper for a CO2PPM instance?
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
struct SharedPPM(&'static Mutex<CriticalSectionRawMutex, CO2PPM>);
|
struct SharedPPM(&'static Mutex<CriticalSectionRawMutex, CO2PPM>);
|
||||||
|
|
||||||
|
// this then allows us to pass the shared CO2PPM into the picoserve interface
|
||||||
struct AppState {
|
struct AppState {
|
||||||
shared_ppm : SharedPPM,
|
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 {
|
impl picoserve::extract::FromRef<AppState> for SharedPPM {
|
||||||
fn from_ref(state: &AppState) -> Self {
|
fn from_ref(state: &AppState) -> Self {
|
||||||
state.shared_ppm
|
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;
|
struct AppProps;
|
||||||
|
|
||||||
impl AppWithStateBuilder for AppProps {
|
impl AppWithStateBuilder for AppProps {
|
||||||
|
|
@ -118,27 +96,21 @@ impl AppWithStateBuilder for AppProps {
|
||||||
type PathRouter = impl PathRouter<AppState>;
|
type PathRouter = impl PathRouter<AppState>;
|
||||||
|
|
||||||
fn build_app(self) -> picoserve::Router<Self::PathRouter, Self::State> {
|
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()
|
picoserve::Router::new()
|
||||||
.route(
|
.route(
|
||||||
"/",
|
"/",
|
||||||
get_service(File::html(INDEX)) // .replace("%{CO2}%", CO2PPM.to_string())))
|
get_service(File::html(INDEX)),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/main.css",
|
"/main.css",
|
||||||
get_service(File::css(include_str!("html/main.css")))
|
get_service(File::css(CSS)),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/data/co2",
|
"/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
|
.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]
|
#[embassy_executor::main]
|
||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
let p = embassy_rp::init(Default::default());
|
let p = embassy_rp::init(Default::default());
|
||||||
let driver = Driver::new(p.USB, UsbIrqs);
|
let driver = Driver::new(p.USB, UsbIrqs);
|
||||||
|
|
||||||
spawner.spawn(logger_task(driver)).unwrap();
|
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
|
// 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
|
// 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;
|
Timer::after_secs(1).await;
|
||||||
|
|
||||||
log::info!("main: entry");
|
|
||||||
|
|
||||||
let fw = include_bytes!("../cyw43-firmware/43439A0.bin");
|
let fw = include_bytes!("../cyw43-firmware/43439A0.bin");
|
||||||
let clm = include_bytes!("../cyw43-firmware/43439A0_clm.bin");
|
let clm = include_bytes!("../cyw43-firmware/43439A0_clm.bin");
|
||||||
|
|
||||||
|
|
@ -212,6 +210,10 @@ async fn main(spawner: Spawner) {
|
||||||
p.DMA_CH0,
|
p.DMA_CH0,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// get the WiFi up
|
||||||
|
|
||||||
log::info!("main: init wifi");
|
log::info!("main: init wifi");
|
||||||
static STATE: StaticCell<cyw43::State> = StaticCell::new();
|
static STATE: StaticCell<cyw43::State> = StaticCell::new();
|
||||||
let state = STATE.init(cyw43::State::new());
|
let state = STATE.init(cyw43::State::new());
|
||||||
|
|
@ -224,7 +226,13 @@ async fn main(spawner: Spawner) {
|
||||||
.set_power_management(cyw43::PowerManagementMode::PowerSave)
|
.set_power_management(cyw43::PowerManagementMode::PowerSave)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
//let config = Config::dhcpv4(Default::default());
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// 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");
|
log::info!("main: configure static IP");
|
||||||
let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {
|
let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {
|
||||||
address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 3, 15), 24),
|
address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 3, 15), 24),
|
||||||
|
|
@ -233,6 +241,7 @@ async fn main(spawner: Spawner) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Generate random seed
|
// Generate random seed
|
||||||
|
let mut rng = RoscRng;
|
||||||
let seed = rng.next_u64();
|
let seed = rng.next_u64();
|
||||||
|
|
||||||
// Init network stack
|
// Init network stack
|
||||||
|
|
@ -264,37 +273,43 @@ async fn main(spawner: Spawner) {
|
||||||
log::info!("DHCP is now up!");*/
|
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");
|
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
|
// this code derived from: https://github.com/SvetlinZarev/libscd/blob/main/examples/embassy-scd4x/src/main.rs
|
||||||
let sda = p.PIN_26;
|
let sda = p.PIN_26;
|
||||||
let scl = p.PIN_27;
|
let scl = p.PIN_27;
|
||||||
let i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default());
|
let i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default());
|
||||||
log::info!("Initialise Scd4x");
|
log::info!("Initialise Scd4x");
|
||||||
Timer::after_secs(1).await;
|
|
||||||
let mut scd = Scd4x::new(i2c, Delay);
|
let mut scd = Scd4x::new(i2c, Delay);
|
||||||
|
|
||||||
// When re-programming, the controller will be restarted,
|
// When re-programming, the controller will be restarted, but not the sensor. We try to stop it
|
||||||
// but not the sensor. We try to stop it in order to
|
// in order to prevent the rest of the commands failing.
|
||||||
// prevent the rest of the commands failing.
|
|
||||||
log::info!("Stop periodic measurements");
|
log::info!("Stop periodic measurements");
|
||||||
Timer::after_secs(1).await;
|
|
||||||
_ = scd.stop_periodic_measurement();
|
_ = scd.stop_periodic_measurement();
|
||||||
|
|
||||||
log::info!("Sensor serial number: {:?}", scd.serial_number());
|
log::info!("Sensor serial number: {:?}", scd.serial_number());
|
||||||
Timer::after_secs(1).await;
|
|
||||||
if let Err(e) = scd.start_periodic_measurement() {
|
if let Err(e) = scd.start_periodic_measurement() {
|
||||||
log::error!("Failed to start periodic measurement: {:?}", e );
|
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));
|
spawner.must_spawn(read_co2(scd, shared_ppm));
|
||||||
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// Set up the HTTP service
|
||||||
|
|
||||||
log::info!("Commence HTTP service");
|
log::info!("Commence HTTP service");
|
||||||
Timer::after_secs(1).await;
|
|
||||||
|
|
||||||
let app = make_static!(AppRouter<AppProps>, AppProps.build_app());
|
let app = make_static!(AppRouter<AppProps>, AppProps.build_app());
|
||||||
|
|
||||||
|
|
@ -317,77 +332,5 @@ async fn main(spawner: Spawner) {
|
||||||
AppState{ shared_ppm },
|
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