feat: wifi

This commit is contained in:
Kat Inskip 2025-10-18 07:38:04 -07:00
parent 77826ad4cd
commit 065c6dfae9
Signed by: kat
GPG key ID: 465E64DECEA8CF0F
3 changed files with 183 additions and 88 deletions

8
Cargo.lock generated
View file

@ -757,6 +757,7 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d01dbf6e54315b6e05da3ad828c7da1ba98f541eb8b0c5d5d260b8eb3b64a8cd" checksum = "d01dbf6e54315b6e05da3ad828c7da1ba98f541eb8b0c5d5d260b8eb3b64a8cd"
dependencies = [ dependencies = [
"allocator-api2",
"cfg-if", "cfg-if",
"document-features", "document-features",
"embassy-executor", "embassy-executor",
@ -767,6 +768,7 @@ dependencies = [
"esp-hal", "esp-hal",
"esp-hal-procmacros", "esp-hal-procmacros",
"esp-metadata-generated", "esp-metadata-generated",
"esp-radio-rtos-driver",
"esp-sync", "esp-sync",
"portable-atomic", "portable-atomic",
] ]
@ -1072,9 +1074,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.27" version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]] [[package]]
name = "managed" name = "managed"
@ -1089,6 +1091,7 @@ dependencies = [
"display-interface-spi", "display-interface-spi",
"embassy-executor", "embassy-executor",
"embassy-net", "embassy-net",
"embassy-sync 0.7.2",
"embassy-time", "embassy-time",
"embedded-graphics", "embedded-graphics",
"embedded-hal-bus", "embedded-hal-bus",
@ -1102,6 +1105,7 @@ dependencies = [
"esp-radio", "esp-radio",
"esp-rtos", "esp-rtos",
"ili9341", "ili9341",
"log",
"static_cell", "static_cell",
] ]

View file

@ -5,6 +5,22 @@ authors = ["kittywitch"]
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
[features]
default = [
"log",
"esp32c3"
]
log = [
"dep:log"
]
esp32c3 = [
"esp-backtrace/esp32c3",
"esp-bootloader-esp-idf/esp32c3",
"esp-hal/esp32c3",
"esp-rtos/esp32c3",
"esp-radio/esp32c3",
]
[dependencies] [dependencies]
display-interface-spi = "0.5.0" display-interface-spi = "0.5.0"
embassy-executor = "0.9.1" embassy-executor = "0.9.1"
@ -24,10 +40,13 @@ esp-bootloader-esp-idf = { version = "0.3.0", features = ["esp32c3"]}
esp-hal = { version = "1.0.0-rc.1", features = ["esp32c3", "unstable"] } esp-hal = { version = "1.0.0-rc.1", features = ["esp32c3", "unstable"] }
esp-println = { version = "0.16.0", features = ["esp32c3", "log-04"] } esp-println = { version = "0.16.0", features = ["esp32c3", "log-04"] }
esp-radio = { version = "0.16.0", features = ["smoltcp", "wifi", "esp32c3", "unstable"] } esp-radio = { version = "0.16.0", features = ["smoltcp", "wifi", "esp32c3", "unstable"] }
esp-rtos = { version = "0.1.1", features = ["esp32c3", "embassy"] } esp-rtos = { version = "0.1.1", features = ["esp32c3", "embassy", "esp-radio"] }
ili9341 = "0.6.0" ili9341 = "0.6.0"
static_cell = "2.1.1" static_cell = "2.1.1"
log = { version = "0.4.28", optional = true }
embassy-sync = "0.7.2"
# https://docs.espressif.com/projects/rust/esp-wifi/0.15.0/esp32c3/esp_wifi/index.html#optimization-level # https://docs.espressif.com/projects/rust/esp-wifi/0.15.0/esp32c3/esp_wifi/index.html#optimization-level
[profile.dev.package.esp-wifi] [profile.dev.package.esp-wifi]
opt-level = 3 opt-level = 3

View file

@ -4,52 +4,45 @@
extern crate alloc; extern crate alloc;
use alloc::format;
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, once_lock::OnceLock, rwlock::RwLock};
use esp_alloc as _;
use esp_backtrace as _;
use { use {
core::ptr::addr_of_mut, embassy_net::Runner,
display_interface_spi::{SPIInterface, *}, embassy_time::{Duration, Timer},
embedded_graphics::{ display_interface_spi::SPIInterface, embassy_executor::Spawner, embedded_graphics::{
mono_font::{ascii::{FONT_8X13, FONT_6X10}, MonoFont, MonoTextStyle}, mono_font::{ascii::FONT_6X10, MonoTextStyle},
pixelcolor::{Rgb565, Bgr565, RgbColor}, pixelcolor::{Bgr565, Rgb565, RgbColor},
prelude::*, prelude::*,
primitives::{Styled, Line, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, Triangle}, primitives::{PrimitiveStyle, PrimitiveStyleBuilder, Rectangle},
text::{Alignment, Text}, text::{Alignment, Text},
}, }, embedded_hal_bus::spi::{ExclusiveDevice, NoDelay}, embedded_layout::{
embedded_text::{ layout::linear::LinearLayout, prelude::*, view_group::ViewGroup
TextBox, }, esp_backtrace as _, esp_hal::{
style::{ ram,
TextBoxStyleBuilder, clock::CpuClock, delay::Delay, gpio::{InputPin, Level, Output, OutputConfig, OutputPin}, init, peripherals::{Peripherals, SPI2, TIMG1}, rng::Rng, spi::{
HeightMode,
},
alignment::HorizontalAlignment as TextHorizontalAlignment,
},
embedded_layout::{
view_group::ViewGroup,
layout::{
linear::LinearLayout,
},
prelude::*,
},
embedded_hal_bus::spi::{ExclusiveDevice, NoDelay},
esp_backtrace as _,
esp_hal::{
clock::CpuClock,
delay::Delay,
gpio::{AnyPin, Input, InputPin, Level, Output, OutputConfig, OutputPin},
init, main,
peripherals::{Peripherals, ADC1, SPI2},
rng::Rng,
spi::{
master::{Config, Spi}, master::{Config, Spi},
Mode, Mode,
}, time::Rate, timer::timg::TimerGroup, Blocking
}, },
time::Rate,
timer::timg::TimerGroup,
Blocking,
},
esp_println::println,
ili9341::{DisplaySize240x320, Ili9341, Orientation}, ili9341::{DisplaySize240x320, Ili9341, Orientation},
embassy_executor::Spawner, };
embassy_time::{Duration, Timer},
#[cfg(feature = "log")]
use {
esp_println::println
};
use embassy_net::{Stack, StackResources};
#[cfg(target_arch = "riscv32")]
use esp_hal::interrupt::software::SoftwareInterruptControl;
use esp_radio::{
wifi::{
ClientConfig, Interfaces, ModeConfig, ScanConfig, WifiController, WifiDevice, WifiEvent, WifiStaState
}, Controller as RadioController
}; };
const SSID: &str = env!("WIFI_SSID"); const SSID: &str = env!("WIFI_SSID");
@ -118,7 +111,7 @@ impl<'a, 'spi> DrawTarget for DrawFlipper<'a, 'spi> {
colors: I, colors: I,
) -> Result<(), Self::Error> ) -> Result<(), Self::Error>
where I: IntoIterator<Item = Self::Color> { where I: IntoIterator<Item = Self::Color> {
self.display.fill_contiguous(area, colors.into_iter().map(|c| candyflip(c))) self.display.fill_contiguous(area, colors.into_iter().map(candyflip))
} }
fn fill_solid( fn fill_solid(
&mut self, &mut self,
@ -195,7 +188,7 @@ impl<'spi> TFT<'spi> {
let spi_device = ExclusiveDevice::new_no_delay(spi, cs_output).unwrap(); let spi_device = ExclusiveDevice::new_no_delay(spi, cs_output).unwrap();
let interface = SPIInterface::new(spi_device, dc_output); let interface = SPIInterface::new(spi_device, dc_output);
let mut display = Ili9341::new( let display = Ili9341::new(
interface, interface,
rst_output, rst_output,
&mut Delay::new(), &mut Delay::new(),
@ -250,13 +243,13 @@ impl<'spi> TFT<'spi> {
pub fn fullscreen_alert(&mut self, text: &str, clear: bool) { pub fn fullscreen_alert(&mut self, text: &str, clear: bool) {
if clear { if clear {
let _ = self.clear_root(); self.clear_root();
} }
let display_area = self.display.bounding_box(); let display_area = self.display.bounding_box();
LinearLayout::vertical( LinearLayout::vertical(
Chain::new( Chain::new(
LinearLayout::horizontal( LinearLayout::horizontal(
Self::contained_text("Initialized controller!", 16) Self::contained_text(text, 16)
) )
) )
).with_alignment(horizontal::Center) ).with_alignment(horizontal::Center)
@ -321,48 +314,103 @@ async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) {
runner.run().await runner.run().await
}*/ }*/
struct Controller<'tft> { struct Controller<'tft> {
pub display: TFT<'tft>, pub display: TFT<'tft>,
pub wifi: ControllerWifi,
} }
impl Controller<'_> { impl <'tft>Controller<'tft> {
async fn init(peripherals: Peripherals) -> Self { async fn init(spawner: Spawner, mut display: TFT<'tft>, stack: Stack<'static>) -> Self {
let mut display = Self::init_screen(peripherals).await; let wifi = ControllerWifi::init_wifi(spawner, &mut display, stack).await;
let mut controller = Self { let mut controller = Self {
display, display,
wifi,
}; };
controller.display.fullscreen_alert("Controller initialized!", true); if let Some(config) = controller.wifi.stack.config_v4() {
controller.display.fullscreen_alert(&format!("Controller initialized!\nCurrent IP address: {}", config.address), true);
}
controller controller
} }
async fn init_screen<'tft>(peripherals: Peripherals) -> TFT<'tft> {
// Refer to https://www.espboards.dev/esp32/esp32-c3-super-mini/#esp32-c3-super-mini-pinout
let rst = peripherals.GPIO0;
let sclk = peripherals.GPIO4;
let miso = peripherals.GPIO5;
let mosi = peripherals.GPIO6;
let cs = peripherals.GPIO7;
let dc = peripherals.GPIO9;
let mut tft = TFT::new(peripherals.SPI2, sclk, miso, mosi, cs, rst, dc);
tft
}
} }
/*struct WifiController { struct ControllerWifi {
timer: TimerGroup, stack: Stack<'static>,
}
impl ControllerWifi {
async fn init_wifi(spawner: Spawner, display: &mut TFT<'_>, stack: Stack<'static>) -> Self {
let mut rx_buffer = [0; 4096];
let mut tx_buffer = [0; 4096];
println!("Waiting to get IP address...");
display.fullscreen_alert("Waiting to obtain an IP address", true);
loop {
if let Some(config) = stack.config_v4() {
println!("Got IP: {}", config.address);
display.fullscreen_alert(&format!("IP address obtained: {}", config.address), true);
break;
}
Timer::after(Duration::from_millis(500)).await;
} }
impl WifiController {
async fn init_wifi(peripherals: Peripherals) -> Self {
let timer = TimerGroup::new(peripherals.TIMG1);
let rng = Rng::new();
Self { Self {
timer stack
} }
} }
}*/ }
#[embassy_executor::task]
async fn connection(mut controller: WifiController<'static>) {
println!("start connection task");
println!("Device capabilities: {:?}", controller.capabilities());
loop {
match esp_radio::wifi::sta_state() {
WifiStaState::Connected => {
// 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 = ModeConfig::Client(
ClientConfig::default()
.with_ssid(SSID.into())
.with_password(PASSWORD.into()),
);
controller.set_config(&client_config).unwrap();
println!("Starting wifi");
controller.start_async().await.unwrap();
println!("Wifi started!");
println!("Scan");
let scan_config = ScanConfig::default().with_max(10);
let result = controller
.scan_with_config_async(scan_config)
.await
.unwrap();
for ap in result {
println!("{:?}", ap);
}
}
println!("About to connect...");
match controller.connect_async().await {
Ok(_) => println!("Wifi connected!"),
Err(e) => {
println!("Failed to connect to wifi: {e:?}");
Timer::after(Duration::from_millis(5000)).await
}
}
}
}
#[embassy_executor::task]
async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) {
runner.run().await
}
#[esp_rtos::main] #[esp_rtos::main]
async fn main(spawner: Spawner) { async fn main(spawner: Spawner) {
@ -370,32 +418,56 @@ async fn main(spawner: Spawner) {
esp_alloc::heap_allocator!(size: 64*1024); esp_alloc::heap_allocator!(size: 64*1024);
#[cfg(feature = "log")] #[cfg(feature = "log")]
{
// The default log level can be specified here. // The default log level can be specified here.
// You can see the esp-println documentation https://docs.rs/esp-println // You can see the esp-println documentation https://docs.rs/esp-println
esp_println::logger::init_logger(log::LevelFilter::Info); esp_println::logger::init_logger(log::LevelFilter::Info);
}
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
let peripherals: Peripherals = init(config); let peripherals: Peripherals = init(config);
/* let timer1 = TimerGroup::new(peripherals.TIMG1);
let rng = Rng::new(); let spi2 = peripherals.SPI2;
let esp_wifi_ctrl = &*mk_static!( let rst = peripherals.GPIO0;
EspWifiController<'static>, let sclk = peripherals.GPIO4;
init(timer1.timer0, rng).unwrap() let miso = peripherals.GPIO5;
let mosi = peripherals.GPIO6;
let cs = peripherals.GPIO7;
let dc = peripherals.GPIO9;
let display = TFT::new(spi2, sclk, miso, mosi, cs, rst, dc);
let timg0 = TimerGroup::new(peripherals.TIMG0);
#[cfg(target_arch = "riscv32")]
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(
timg0.timer0,
#[cfg(target_arch = "riscv32")]
sw_int.software_interrupt0,
); );
let (controller, interfaces) = esp_wifi::wifi::new(esp_wifi_ctrl, peripherals.WIFI).unwrap();
let wifi_interface = interfaces.sta; let esp_radio_ctrl = &*mk_static!(RadioController<'static>, esp_radio::init().unwrap());
let seed = rng.random();
let wifi_config = Config::dhcpv4(Default::default()); let (wifi_controller, wifi_interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
let wifi_interface = wifi_interfaces.sta;
let config = embassy_net::Config::dhcpv4(Default::default());
let rng = Rng::new();
let seed = (rng.random() as u64) << 32 | rng.random() as u64;
// Init network stack
let (stack, runner) = embassy_net::new( let (stack, runner) = embassy_net::new(
wifi_interface, wifi_interface,
wifi_config, config,
mk_static!(StackResources<8>, StackResources::<8>::new()), mk_static!(StackResources<3>, StackResources::<3>::new()),
seed, seed,
);*/ );
let mut controller = Controller::init(peripherals).await; spawner.spawn(connection(wifi_controller)).ok();
spawner.spawn(net_task(runner)).ok();
let controller = Controller::init(spawner, display, stack).await;
loop { loop {
// your business logic // your business logic