Initial commit
This commit is contained in:
1378
Cargo.lock
generated
Normal file
1378
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
70
Cargo.toml
Normal file
70
Cargo.toml
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
[package]
|
||||||
|
name = "wallas-esp32c3"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
esp-backtrace = { version = "0.14.2", features = [
|
||||||
|
"esp32c3",
|
||||||
|
"exception-handler",
|
||||||
|
"panic-handler",
|
||||||
|
"println",
|
||||||
|
]}
|
||||||
|
|
||||||
|
esp-hal = { version = "0.22.0", features = [
|
||||||
|
"esp32c3",
|
||||||
|
] }
|
||||||
|
esp-println = { version = "0.12.0", features = ["esp32c3", "log"] }
|
||||||
|
log = { version = "0.4.21" }
|
||||||
|
esp-alloc = { version = "0.5.0" }
|
||||||
|
embedded-io = "0.6.1"
|
||||||
|
|
||||||
|
embedded-io-async = "0.6.1"
|
||||||
|
embassy-net = { version = "0.5.0", features = [ "tcp", "udp", "dhcpv4", "medium-ethernet"] }
|
||||||
|
|
||||||
|
esp-wifi = { version = "0.11.0", default-features=false, features = [
|
||||||
|
"esp32c3",
|
||||||
|
"utils",
|
||||||
|
"wifi",
|
||||||
|
"esp-alloc",
|
||||||
|
"log",
|
||||||
|
] }
|
||||||
|
embassy-sync = "0.6.1"
|
||||||
|
rand_core = "0.6.4"
|
||||||
|
heapless = { version = "0.8.0", default-features = false }
|
||||||
|
smoltcp = { version = "0.11.0", default-features = false, features = [
|
||||||
|
"medium-ethernet",
|
||||||
|
"proto-dhcpv4",
|
||||||
|
"proto-igmp",
|
||||||
|
"proto-ipv4",
|
||||||
|
"socket-dhcpv4",
|
||||||
|
"socket-icmp",
|
||||||
|
"socket-raw",
|
||||||
|
"socket-tcp",
|
||||||
|
"socket-udp",
|
||||||
|
] }
|
||||||
|
embassy-executor = { version = "0.6.0", features = [
|
||||||
|
"task-arena-size-40960"
|
||||||
|
] }
|
||||||
|
embassy-time = { version = "0.3.1", features = ["generic-queue-8"] }
|
||||||
|
esp-hal-embassy = { version = "0.5.0", features = ["esp32c3"] }
|
||||||
|
static_cell = { version = "2.1.0", features = ["nightly"] }
|
||||||
|
critical-section = "1.2.0"
|
||||||
|
maud = { path = "/home/jda/src/rust/maud/target/package/maud-0.26.0", features = ["alloc"] }
|
||||||
|
picoserve = { version = "0.13.3", default-features = false, features = [
|
||||||
|
"embassy"
|
||||||
|
] }
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
# Rust debug is too slow.
|
||||||
|
# For debug builds always builds with some optimization
|
||||||
|
opt-level = "s"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1 # LLVM can perform better optimizations using a single thread
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = false
|
||||||
|
incremental = false
|
||||||
|
lto = 'fat'
|
||||||
|
opt-level = 's'
|
||||||
|
overflow-checks = false
|
||||||
3
build.rs
Normal file
3
build.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tlinkall.x");
|
||||||
|
}
|
||||||
4
rust-toolchain.toml
Normal file
4
rust-toolchain.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
components = ["rust-src"]
|
||||||
|
targets = ["riscv32imc-unknown-none-elf"]
|
||||||
5
set-env.sh
Normal file
5
set-env.sh
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
echo "Execute as '. ./set-env.sh'"
|
||||||
|
read -p "wifi ssid " WIFI_SSID
|
||||||
|
read -p "wifi password " WIFI_PASSWORD
|
||||||
|
read -p "esp loglevel (trace, debug, info, warn, error) " ESP_LOGLEVEL
|
||||||
|
export WIFI_SSID WIFI_PASSWORD ESP_LOGLEVEL
|
||||||
58
src/httpd.rs
Normal file
58
src/httpd.rs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
use picoserve::{make_static, routing::get, AppBuilder, AppRouter, Router};
|
||||||
|
use picoserve::routing::PathRouter;
|
||||||
|
use embassy_time::Duration;
|
||||||
|
use embassy_net::Stack;
|
||||||
|
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
|
static PICO_CONFIG : 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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
struct AppProps;
|
||||||
|
|
||||||
|
impl AppBuilder for AppProps {
|
||||||
|
type PathRouter = impl picoserve::routing::PathRouter;
|
||||||
|
|
||||||
|
fn build_app(self) -> picoserve::Router<Self::PathRouter> {
|
||||||
|
picoserve::Router::new().route("/", get(|| async move { "Hello World" }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub fn httpd_spawn(spawner: embassy_executor::Spawner, size: usize, stack: Stack<'static>) -> Result<(), super::Error> {
|
||||||
|
for i in 0..size {
|
||||||
|
spawner.must_spawn(web_task(i, stack));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task(pool_size = super::MAX_CONCURRENT_SOCKETS)]
|
||||||
|
async fn web_task(
|
||||||
|
id: usize,
|
||||||
|
stack: Stack<'static>,
|
||||||
|
) -> ! {
|
||||||
|
let app =
|
||||||
|
picoserve::Router::new().route("/", get(|| async { "Hello World" })).route("/test", get(|| async { "Test" }));
|
||||||
|
|
||||||
|
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,
|
||||||
|
&PICO_CONFIG,
|
||||||
|
stack,
|
||||||
|
port,
|
||||||
|
&mut tcp_rx_buffer,
|
||||||
|
&mut tcp_tx_buffer,
|
||||||
|
&mut http_buffer,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
111
src/main.rs
Normal file
111
src/main.rs
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use esp_backtrace as _;
|
||||||
|
use esp_hal::prelude::*;
|
||||||
|
use log::{info, error};
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use heapless::String;
|
||||||
|
use esp_hal::rng::Rng;
|
||||||
|
use esp_hal::timer::systimer::SystemTimer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Embassy includes
|
||||||
|
*/
|
||||||
|
use esp_hal_embassy::init as initialize_embassy;
|
||||||
|
use esp_hal::timer::systimer::Target;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::{Duration, Timer};
|
||||||
|
use esp_hal::timer::timg::TimerGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rng functionality
|
||||||
|
*/
|
||||||
|
mod random;
|
||||||
|
use self::random::RngWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wifi functionality
|
||||||
|
*/
|
||||||
|
mod wifi;
|
||||||
|
use self::wifi::connect as connect_to_wifi;
|
||||||
|
use self::wifi::Error as WifiError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* httpd
|
||||||
|
*/
|
||||||
|
mod httpd;
|
||||||
|
use httpd::httpd_spawn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* serial
|
||||||
|
*/
|
||||||
|
mod serial;
|
||||||
|
use serial::serial_spawn;
|
||||||
|
|
||||||
|
/// SSID for WiFi network
|
||||||
|
const WIFI_SSID: &str = env!("WIFI_SSID");
|
||||||
|
|
||||||
|
/// Password for WiFi network
|
||||||
|
const WIFI_PASSWORD: &str = env!("WIFI_PASSWORD");
|
||||||
|
|
||||||
|
/// Size of heap for dynamically-allocated memory
|
||||||
|
const HEAP_MEMORY_SIZE: usize = 72 * 1024;
|
||||||
|
|
||||||
|
const MAX_CONCURRENT_SOCKETS: usize = 5;
|
||||||
|
|
||||||
|
#[main]
|
||||||
|
async fn main(spawner: Spawner) {
|
||||||
|
|
||||||
|
info!("booting firmware");
|
||||||
|
|
||||||
|
if let Err(error) = main_fallible(spawner).await {
|
||||||
|
error!("Error while running firmware: {error:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/v0.22.0/examples/src/bin
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn main_fallible(
|
||||||
|
spawner: Spawner,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let peripherals = esp_hal::init({
|
||||||
|
let mut config = esp_hal::Config::default();
|
||||||
|
config.cpu_clock = CpuClock::max();
|
||||||
|
config
|
||||||
|
});
|
||||||
|
|
||||||
|
esp_alloc::heap_allocator!(HEAP_MEMORY_SIZE);
|
||||||
|
|
||||||
|
esp_println::logger::init_logger_from_env();
|
||||||
|
|
||||||
|
let systimer = SystemTimer::new(peripherals.SYSTIMER).split::<Target>();
|
||||||
|
initialize_embassy(systimer.alarm0);
|
||||||
|
|
||||||
|
let rng = Rng::new(peripherals.RNG);
|
||||||
|
|
||||||
|
let ssid = String::<32>::try_from(WIFI_SSID).map_err(|()| Error::ParseCredentials)?;
|
||||||
|
let password =
|
||||||
|
String::<64>::try_from(WIFI_PASSWORD).map_err(|()| Error::ParseCredentials)?;
|
||||||
|
|
||||||
|
let stack = connect_to_wifi(spawner, TimerGroup::new(peripherals.TIMG0), rng, peripherals.WIFI, peripherals.RADIO_CLK, (ssid, password)).await.unwrap();
|
||||||
|
|
||||||
|
httpd_spawn(spawner, MAX_CONCURRENT_SOCKETS-1, stack);
|
||||||
|
|
||||||
|
serial_spawn(spawner, peripherals.UART0.into(), peripherals.GPIO20.into(), peripherals.GPIO21.into());
|
||||||
|
|
||||||
|
info!("firmware done booting");
|
||||||
|
|
||||||
|
// we got here - all is fine
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Error {
|
||||||
|
/// Error while parsing SSID or password
|
||||||
|
ParseCredentials,
|
||||||
|
}
|
||||||
|
|
||||||
60
src/random.rs
Normal file
60
src/random.rs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright Claudio Mattera 2024.
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
//! Random numbers generator
|
||||||
|
|
||||||
|
use rand_core::CryptoRng;
|
||||||
|
use rand_core::Error;
|
||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
|
use esp_hal::rng::Rng;
|
||||||
|
|
||||||
|
/// A wrapper for ESP random number generator that implement traits form
|
||||||
|
/// `rand_core`
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct RngWrapper(Rng);
|
||||||
|
|
||||||
|
impl From<Rng> for RngWrapper {
|
||||||
|
fn from(rng: Rng) -> Self {
|
||||||
|
Self(rng)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RngCore for RngWrapper {
|
||||||
|
fn next_u32(&mut self) -> u32 {
|
||||||
|
self.0.random()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_u64(&mut self) -> u64 {
|
||||||
|
u32_pair_to_u64(self.next_u32(), self.next_u32())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||||
|
for value in dest.iter_mut() {
|
||||||
|
let [random_value, _, _, _] = self.next_u32().to_ne_bytes();
|
||||||
|
*value = random_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
|
||||||
|
self.fill_bytes(dest);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CryptoRng for RngWrapper {}
|
||||||
|
|
||||||
|
/// Join a pair of `u32` into a `u64`
|
||||||
|
#[allow(
|
||||||
|
clippy::many_single_char_names,
|
||||||
|
clippy::min_ident_chars,
|
||||||
|
reason = "This is still readable"
|
||||||
|
)]
|
||||||
|
fn u32_pair_to_u64(first: u32, second: u32) -> u64 {
|
||||||
|
let [a, b, c, d] = first.to_ne_bytes();
|
||||||
|
let [e, f, g, h] = second.to_ne_bytes();
|
||||||
|
u64::from_ne_bytes([a, b, c, d, e, f, g, h])
|
||||||
|
}
|
||||||
36
src/serial.rs
Normal file
36
src/serial.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
use esp_hal::{
|
||||||
|
// clock::ClockControl,
|
||||||
|
peripherals::{Peripherals},
|
||||||
|
prelude::*,
|
||||||
|
uart::{AtCmdConfig, AnyUart, Uart, UartRx, UartTx, Config},
|
||||||
|
gpio::AnyPin,
|
||||||
|
Async,
|
||||||
|
};
|
||||||
|
use log::{info, error};
|
||||||
|
|
||||||
|
const BUFFER_SIZE: usize = 64;
|
||||||
|
|
||||||
|
pub fn serial_spawn(spawner: embassy_executor::Spawner, peri_uart: AnyUart, rx_pin: AnyPin, tx_pin: AnyPin) {
|
||||||
|
// Initialize and configure UART0
|
||||||
|
let config = Config::default().rx_fifo_full_threshold(BUFFER_SIZE as u16);
|
||||||
|
let my_uart = Uart::new_with_config(peri_uart, config, rx_pin, tx_pin).unwrap().into_async();
|
||||||
|
// Split UART0 to create seperate Tx and Rx handles
|
||||||
|
let (rx, _tx) = my_uart.split();
|
||||||
|
|
||||||
|
spawner.spawn(reader(rx)).ok();
|
||||||
|
//spawner.spawn(writer(tx)).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn reader(mut rx: UartRx<'static, Async>) {
|
||||||
|
let mut rbuf: [u8; BUFFER_SIZE] = [0u8; BUFFER_SIZE];
|
||||||
|
loop {
|
||||||
|
let r = embedded_io_async::Read::read(&mut rx, &mut rbuf[0..]).await;
|
||||||
|
match r {
|
||||||
|
Ok(len) => {
|
||||||
|
info!("Read: {len}, data: {:?}", &rbuf[..len]);
|
||||||
|
}
|
||||||
|
Err(e) => error!("RX Error: {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
203
src/wifi.rs
Normal file
203
src/wifi.rs
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
// Copyright Claudio Mattera 2024.
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
//! Functions and task for WiFi connection
|
||||||
|
|
||||||
|
use log::debug;
|
||||||
|
use log::error;
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
|
||||||
|
use embassy_net::new as new_network_stack;
|
||||||
|
|
||||||
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
use embassy_sync::signal::Signal;
|
||||||
|
|
||||||
|
use esp_wifi::init as initialize_wifi;
|
||||||
|
use esp_wifi::wifi::new_with_mode as new_wifi_with_mode;
|
||||||
|
use esp_wifi::wifi::wifi_state;
|
||||||
|
use esp_wifi::wifi::ClientConfiguration;
|
||||||
|
use esp_wifi::wifi::Configuration;
|
||||||
|
use esp_wifi::wifi::WifiController;
|
||||||
|
use esp_wifi::wifi::WifiDevice;
|
||||||
|
use esp_wifi::wifi::WifiError as EspWifiError;
|
||||||
|
use esp_wifi::wifi::WifiEvent;
|
||||||
|
use esp_wifi::wifi::WifiStaDevice;
|
||||||
|
use esp_wifi::wifi::WifiState;
|
||||||
|
use esp_wifi::EspWifiController;
|
||||||
|
use esp_wifi::InitializationError as WifiInitializationError;
|
||||||
|
|
||||||
|
use embassy_net::Config;
|
||||||
|
use embassy_net::DhcpConfig;
|
||||||
|
use embassy_net::Runner;
|
||||||
|
use embassy_net::Stack;
|
||||||
|
use embassy_net::StackResources;
|
||||||
|
|
||||||
|
use embassy_time::Duration;
|
||||||
|
use embassy_time::Timer;
|
||||||
|
|
||||||
|
use esp_hal::peripherals::RADIO_CLK;
|
||||||
|
use esp_hal::peripherals::TIMG0;
|
||||||
|
use esp_hal::peripherals::WIFI;
|
||||||
|
use esp_hal::rng::Rng;
|
||||||
|
use esp_hal::timer::timg::TimerGroup;
|
||||||
|
use esp_hal::Blocking;
|
||||||
|
|
||||||
|
use heapless::String;
|
||||||
|
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
|
use rand_core::RngCore as _;
|
||||||
|
|
||||||
|
use crate::RngWrapper;
|
||||||
|
|
||||||
|
/// max number of concurrent connections
|
||||||
|
static STACK_COUNT : usize = super::MAX_CONCURRENT_SOCKETS;
|
||||||
|
|
||||||
|
/// Static cell for network stack resources
|
||||||
|
static STACK_RESOURCES: StaticCell<StackResources<STACK_COUNT>> = StaticCell::new();
|
||||||
|
|
||||||
|
/// Static cell for WiFi controller
|
||||||
|
static WIFI_CONTROLLER: StaticCell<EspWifiController<'static>> = StaticCell::new();
|
||||||
|
|
||||||
|
/// Signal to request to stop WiFi
|
||||||
|
pub static STOP_WIFI_SIGNAL: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
||||||
|
|
||||||
|
/// Connect to WiFi
|
||||||
|
pub async fn connect(
|
||||||
|
spawner: Spawner,
|
||||||
|
timg0: TimerGroup<'static, TIMG0, Blocking>,
|
||||||
|
rng: Rng,
|
||||||
|
wifi: WIFI,
|
||||||
|
radio_clock_control: RADIO_CLK,
|
||||||
|
(ssid, password): (String<32>, String<64>),
|
||||||
|
) -> Result<Stack<'static>, Error> {
|
||||||
|
let mut rng_wrapper = RngWrapper::from(rng);
|
||||||
|
let seed = rng_wrapper.next_u64();
|
||||||
|
debug!("Use random seed 0x{seed:016x}");
|
||||||
|
|
||||||
|
let wifi_controller = initialize_wifi(timg0.timer0, rng, radio_clock_control)?;
|
||||||
|
let wifi_controller: &'static mut _ = WIFI_CONTROLLER.init(wifi_controller);
|
||||||
|
|
||||||
|
let (wifi_interface, controller) = new_wifi_with_mode(wifi_controller, wifi, WifiStaDevice)?;
|
||||||
|
|
||||||
|
let config = Config::dhcpv4(DhcpConfig::default());
|
||||||
|
|
||||||
|
debug!("Initialize network stack");
|
||||||
|
let stack_resources: &'static mut _ = STACK_RESOURCES.init(StackResources::new());
|
||||||
|
let (stack, runner) = new_network_stack(wifi_interface, config, stack_resources, seed);
|
||||||
|
|
||||||
|
spawner.must_spawn(connection(controller, ssid, password));
|
||||||
|
spawner.must_spawn(net_task(runner));
|
||||||
|
|
||||||
|
debug!("Wait for network link");
|
||||||
|
loop {
|
||||||
|
if stack.is_link_up() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("Wait for IP address");
|
||||||
|
loop {
|
||||||
|
if let Some(config) = stack.config_v4() {
|
||||||
|
info!("Connected to WiFi with IP address {}", config.address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Task for ongoing network processing
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn net_task(mut runner: Runner<'static, WifiDevice<'static, WifiStaDevice>>) {
|
||||||
|
runner.run().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Task for WiFi connection
|
||||||
|
///
|
||||||
|
/// This will wrap [`connection_fallible()`] and trap any error.
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn connection(controller: WifiController<'static>, ssid: String<32>, password: String<64>) {
|
||||||
|
if let Err(error) = connection_fallible(controller, ssid, password).await {
|
||||||
|
error!("Cannot connect to WiFi: {error:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fallible task for WiFi connection
|
||||||
|
async fn connection_fallible(
|
||||||
|
mut controller: WifiController<'static>,
|
||||||
|
ssid: String<32>,
|
||||||
|
password: String<64>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
debug!("Start connection");
|
||||||
|
debug!("Device capabilities: {:?}", controller.capabilities());
|
||||||
|
loop {
|
||||||
|
if wifi_state() == WifiState::StaConnected {
|
||||||
|
// wait until we're no longer connected
|
||||||
|
controller.wait_for_event(WifiEvent::StaDisconnected).await;
|
||||||
|
Timer::after(Duration::from_millis(5000)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matches!(controller.is_started(), Ok(true)) {
|
||||||
|
let client_config = Configuration::Client(ClientConfiguration {
|
||||||
|
ssid: ssid.clone(),
|
||||||
|
password: password.clone(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
controller.set_configuration(&client_config)?;
|
||||||
|
debug!("Starting WiFi controller");
|
||||||
|
controller.start_async().await?;
|
||||||
|
debug!("WiFi controller started");
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("Connect to WiFi network");
|
||||||
|
|
||||||
|
match controller.connect_async().await {
|
||||||
|
Ok(()) => {
|
||||||
|
debug!("Connected to WiFi network");
|
||||||
|
|
||||||
|
debug!("Wait for request to stop wifi");
|
||||||
|
STOP_WIFI_SIGNAL.wait().await;
|
||||||
|
info!("Received signal to stop wifi");
|
||||||
|
controller.stop_async().await?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
error!("Failed to connect to WiFi network: {error:?}");
|
||||||
|
Timer::after(Duration::from_millis(5000)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Leave connection task");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error within WiFi connection
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Error during WiFi initialization
|
||||||
|
WifiInitialization(#[expect(unused, reason = "Never read directly")] WifiInitializationError),
|
||||||
|
|
||||||
|
/// Error during WiFi operation
|
||||||
|
Wifi(#[expect(unused, reason = "Never read directly")] EspWifiError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<WifiInitializationError> for Error {
|
||||||
|
fn from(error: WifiInitializationError) -> Self {
|
||||||
|
Self::WifiInitialization(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<EspWifiError> for Error {
|
||||||
|
fn from(error: EspWifiError) -> Self {
|
||||||
|
Self::Wifi(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user