The Pico W now serves a basic HTML page.

This has not been so simple. The only way I could find to make picoserve
and embassy compatible was to get a local copy of picoserve and change
its embassy dependencies to use git as their source. Otherwise there are
conflicts about embassy-timer-driver versions. Using picoserve also
required changing my Rust "channel" to "nightly". A bunch of stuff I
am not keen on, but necessary to progress rather than get bogged down in
build system meta.

Meanwhile also I've changed this to use a static IP at 192.168.3.14 for
now. For currently unknownr reasons the loop waiting for the DHCP lease
to work isn't exiting. I have rebooted the DHCP server and that didn't
help and given the WiFi on the Pico W is working I don't really know
whats up.
This commit is contained in:
Yvan 2025-03-14 17:42:07 +00:00
parent f5ce5e7958
commit bd9389cfd5
7 changed files with 1039 additions and 323 deletions

12
src/html/index.html Normal file
View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Meat-Pi</title>
<link rel="stylesheet" href="main.css">
</head>
<body>
<h1>Meat-Pi 🍖🌡️</h1>
</body>
</html>

19
src/html/main.css Normal file
View file

@ -0,0 +1,19 @@
:root {
color-scheme: light dark;
--light-bg: #ddffdd;
--light-color: #002200;
--dark-bg: #002200;
--dark-color: #ddffdd;
}
* {
background-color: light-dark(var(--light-bg), var(--dark-bg));
color: light-dark(var(--light-color), var(--dark-color));
}
body {
display: flex;
flex-flow: column nowrap;
align-items: center;
font-family: Arial, sans-serif;
}

View file

@ -1,17 +1,14 @@
//! This example test the RP Pico W on board LED.
//!
//! It does not work with the RP Pico board. See blinky.rs.
//! Raspberry Pi Pico W meat thermometer
#![no_std]
#![no_main]
use core::str::from_utf8;
// required for impl in AppProps code for picoserve
#![feature(impl_trait_in_assoc_type)]
use cyw43::JoinOptions;
use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER};
use embassy_executor::Spawner;
use embassy_net::{Config, StackResources};
use embassy_net::tcp::TcpSocket;
use embassy_net::{Config, StackResources, Ipv4Cidr, Ipv4Address};
use embassy_rp::bind_interrupts;
use embassy_rp::clocks::RoscRng;
use embassy_rp::gpio::{Level, Output};
@ -20,7 +17,13 @@ use embassy_rp::peripherals::USB;
use embassy_rp::pio::{InterruptHandler, Pio};
use embassy_rp::usb::{Driver, InterruptHandler as USBInterruptHandler};
use embassy_time::{Duration, Timer};
use embedded_io_async::Write;
use heapless::Vec;
use picoserve::{
make_static,
routing::{get_service, PathRouter},
AppBuilder, AppRouter
};
use picoserve::response::File;
use rand::RngCore;
use static_cell::StaticCell;
use {defmt_rtt as _, panic_probe as _};
@ -51,6 +54,54 @@ async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'sta
runner.run().await
}
// picoserve HTTP code kicked off using: https://github.com/sammhicks/picoserve/blob/main/examples/embassy/hello_world/src/main.rs
struct AppProps;
impl AppBuilder for AppProps {
type PathRouter = impl PathRouter;
fn build_app(self) -> picoserve::Router<Self::PathRouter> {
picoserve::Router::new()
.route(
"/",
get_service(File::html(include_str!("html/index.html")))
)
.route(
"/main.css",
get_service(File::css(include_str!("html/main.css")))
)
}
}
// 2 is plenty of a little IoT thermometer, right?
const WEB_TASK_POOL_SIZE: usize = 2;
#[embassy_executor::task(pool_size = WEB_TASK_POOL_SIZE)]
async fn web_task(
id: usize,
stack: embassy_net::Stack<'static>,
app: &'static AppRouter<AppProps>,
config: &'static picoserve::Config<Duration>,
) -> ! {
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(
id,
app,
config,
stack,
port,
&mut tcp_rx_buffer,
&mut tcp_tx_buffer,
&mut http_buffer,
)
.await
}
#[embassy_executor::main]
async fn main(spawner: Spawner) {
let p = embassy_rp::init(Default::default());
@ -102,8 +153,13 @@ async fn main(spawner: Spawner) {
.set_power_management(cyw43::PowerManagementMode::PowerSave)
.await;
let config = Config::dhcpv4(Default::default());
//let config = Config::dhcpv4(Default::default());
let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {
address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 3, 14), 24),
dns_servers: Vec::new(),
gateway: Some(Ipv4Address::new(192, 168, 3, 1)),
});
// Generate random seed
let seed = rng.next_u64();
@ -126,18 +182,35 @@ async fn main(spawner: Spawner) {
}
// Wait for DHCP, not necessary when using static IP
log::info!("waiting for DHCP...");
/*log::info!("waiting for DHCP...");
while !stack.is_config_up() {
Timer::after_millis(100).await;
}
log::info!("DHCP is now up!");
log::info!("DHCP is now up!");*/
// And now we can use it!
let app = make_static!(AppRouter<AppProps>, AppProps.build_app());
let config = make_static!(
picoserve::Config<Duration>,
picoserve::Config::new(picoserve::Timeouts {
start_read_request: Some(Duration::from_secs(5)),
read_request: Some(Duration::from_secs(1)),
write: Some(Duration::from_secs(1)),
})
.keep_connection_alive()
);
for id in 0..WEB_TASK_POOL_SIZE {
spawner.must_spawn(web_task(id, stack, app, config));
}
/*
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 {
@ -145,7 +218,7 @@ async fn main(spawner: Spawner) {
socket.set_timeout(Some(Duration::from_secs(10)));
control.gpio_set(0, false).await;
log::info!("Listening on TCP:1234...");
log::info!("Listening on TCP:00...");
if let Err(e) = socket.accept(1234).await {
log::warn!("accept error: {:?}", e);
continue;
@ -183,4 +256,5 @@ async fn main(spawner: Spawner) {
log::info!("LED off!");
control.gpio_set(0, false).await;
}
*/
}