Created
January 1, 2024 10:19
-
-
Save tropicbliss/744e203377be33fe471f1cbec4e21988 to your computer and use it in GitHub Desktop.
Raspberry Pi Pico W Wi-Fi controller beeper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! 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