Skip to content

Instantly share code, notes, and snippets.

@tropicbliss
Created January 1, 2024 10:19
Show Gist options
  • Save tropicbliss/744e203377be33fe471f1cbec4e21988 to your computer and use it in GitHub Desktop.
Save tropicbliss/744e203377be33fe471f1cbec4e21988 to your computer and use it in GitHub Desktop.
Raspberry Pi Pico W Wi-Fi controller beeper
//! This example uses the RP Pico W board Wifi chip (cyw43).
//! Connects to specified Wifi network and creates a TCP endpoint on port 1234.
#![no_std]
#![no_main]
#![allow(async_fn_in_trait)]
#![feature(type_alias_impl_trait)]
use core::sync::atomic::AtomicBool;
use cyw43_pio::PioSpi;
use defmt::*;
use embassy_executor::Spawner;
use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources};
use embassy_rp::bind_interrupts;
use embassy_rp::gpio::{Level, Output};
use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0, PWM_CH2};
use embassy_rp::pio::{InterruptHandler, Pio};
use embassy_rp::pwm::Pwm;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::signal::Signal;
use embassy_time::{Duration, Timer};
use fixed::traits::ToFixed;
use heapless::Vec;
use picoserve::routing::{get, NoPathParameters, PathRouter};
use portable_atomic::Ordering;
use static_cell::{make_static, StaticCell};
use {defmt_rtt as _, panic_probe as _};
static PLAY: AtomicBool = AtomicBool::new(false);
static SIGNAL: Signal<CriticalSectionRawMutex, ()> = Signal::new();
bind_interrupts!(struct Irqs {
PIO0_IRQ_0 => InterruptHandler<PIO0>;
});
const WIFI_NETWORK: &str = "xxx";
const WIFI_PASSWORD: &str = "xxx";
#[embassy_executor::task]
async fn wifi_task(
runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>,
) -> ! {
runner.run().await
}
#[embassy_executor::task]
async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! {
stack.run().await
}
struct EmbassyTimer;
impl picoserve::Timer for EmbassyTimer {
type Duration = embassy_time::Duration;
type TimeoutError = embassy_time::TimeoutError;
async fn run_with_timeout<F: core::future::Future>(
&mut self,
duration: Self::Duration,
future: F,
) -> Result<F::Output, Self::TimeoutError> {
embassy_time::with_timeout(duration, future).await
}
}
type AppRouter = impl PathRouter<(), NoPathParameters>;
#[embassy_executor::task]
async fn web_task(
stack: &'static embassy_net::Stack<cyw43::NetDriver<'static>>,
app: &'static picoserve::Router<AppRouter>,
config: &'static picoserve::Config<Duration>,
) -> ! {
let mut rx_buffer = [0; 1024];
let mut tx_buffer = [0; 1024];
loop {
let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
info!("Listening on TCP:80...");
if let Err(e) = socket.accept(80).await {
warn!("accept error: {:?}", e);
continue;
}
info!("Received connection from {:?}", socket.remote_endpoint());
let (socket_rx, socket_tx) = socket.split();
match picoserve::serve(app, EmbassyTimer, config, &mut [0; 2048], socket_rx, socket_tx).await {
Ok(handled_requests_count) => {
info!(
"{} requests handled from {:?}",
handled_requests_count,
socket.remote_endpoint()
);
}
Err(_) => error!("an error occurred"),
}
}
}
macro_rules! buzzer_on {
($pwm: expr, $cfg: expr) => {
$cfg.compare_a = 573;
$pwm.set_config(&$cfg);
};
}
macro_rules! buzzer_off {
($pwm: expr, $cfg: expr) => {
$cfg.compare_a = 0;
$pwm.set_config(&$cfg);
};
}
#[embassy_executor::task]
async fn buzzer(mut pwm: Pwm<'static, PWM_CH2>, mut cfg: embassy_rp::pwm::Config) -> ! {
loop {
if !PLAY.load(Ordering::Relaxed) {
SIGNAL.wait().await;
}
buzzer_on!(pwm, cfg);
Timer::after(Duration::from_millis(100)).await;
buzzer_off!(pwm, cfg);
Timer::after(Duration::from_millis(100)).await;
buzzer_on!(pwm, cfg);
Timer::after(Duration::from_millis(600)).await;
buzzer_off!(pwm, cfg);
Timer::after(Duration::from_secs(1)).await;
info!("Beep!");
}
}
#[embassy_executor::main]
async fn main(spawner: Spawner) {
info!("Hello World!");
let p = embassy_rp::init(Default::default());
let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin");
let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin");
// To make flashing faster for development, you may want to flash the firmwares independently
// at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
// probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
// probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
//let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) };
//let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
let pwr = Output::new(p.PIN_23, Level::Low);
let cs = Output::new(p.PIN_25, Level::High);
let mut pio = Pio::new(p.PIO0, Irqs);
let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0);
static STATE: StaticCell<cyw43::State> = StaticCell::new();
let state = STATE.init(cyw43::State::new());
let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await;
unwrap!(spawner.spawn(wifi_task(runner)));
control.init(clm).await;
control
.set_power_management(cyw43::PowerManagementMode::PowerSave)
.await;
// let config = Config::dhcpv4(Default::default());
let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {
address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 59), 24),
dns_servers: Vec::new(),
gateway: Some(Ipv4Address::new(192, 168, 1, 254)),
});
// Generate random seed
let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random.
// Init network stack
static STACK: StaticCell<Stack<cyw43::NetDriver<'static>>> = StaticCell::new();
static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new();
let stack = &*STACK.init(Stack::new(
net_device,
config,
RESOURCES.init(StackResources::<2>::new()),
seed,
));
unwrap!(spawner.spawn(net_task(stack)));
loop {
//control.join_open(WIFI_NETWORK).await;
match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await {
Ok(_) => break,
Err(err) => {
info!("join failed with status={}", err.status);
}
}
}
// Wait for DHCP, not necessary when using static IP
info!("waiting for DHCP...");
while !stack.is_config_up() {
Timer::after_millis(100).await;
}
info!("DHCP is now up!");
// And now we can use it!
fn make_app() -> picoserve::Router<AppRouter> {
picoserve::Router::new()
.route(
"/alarmon",
get(|| async move {
PLAY.store(true, Ordering::Relaxed);
SIGNAL.signal(());
}),
)
.route(
"/alarmoff",
get(|| async move {
PLAY.store(false, Ordering::Relaxed);
}),
)
}
let mut c: embassy_rp::pwm::Config = Default::default();
c.top = 1145;
c.divider = 40.to_fixed();
// c.compare_a needs to be changed to half of top or 0
let pwm = Pwm::new_output_a(p.PWM_CH2, p.PIN_20, c.clone());
spawner.must_spawn(buzzer(pwm, c));
let app = make_static!(make_app());
let config = make_static!(picoserve::Config {
start_read_request_timeout: Some(Duration::from_secs(5)),
read_request_timeout: Some(Duration::from_secs(1)),
write_timeout: Some(Duration::from_secs(1)),
});
spawner.must_spawn(web_task(stack, app, config));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment