improved project structure and hardware init

This commit is contained in:
Djeeberjr 2025-07-26 16:53:23 +02:00
parent 2e6094ea11
commit c91d2f070f
5 changed files with 258 additions and 233 deletions

61
src/bin/init/mod.rs Normal file
View File

@ -0,0 +1,61 @@
use embassy_executor::Spawner;
use embassy_net::Stack;
use esp_hal::peripherals::{GPIO1, GPIO2, UART1};
use esp_hal::{
Async,
clock::CpuClock,
timer::{systimer::SystemTimer, timg::TimerGroup},
uart::Uart,
};
use esp_println::logger::init_logger;
use log::error;
mod network;
mod wifi;
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
loop {}
}
esp_bootloader_esp_idf::esp_app_desc!();
pub async fn hardware_init(spawner: &mut Spawner) -> (Uart<'static, Async>, Stack<'static>) {
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
let peripherals = esp_hal::init(config);
esp_alloc::heap_allocator!(size: 72 * 1024);
let timer0 = SystemTimer::new(peripherals.SYSTIMER);
esp_hal_embassy::init(timer0.alarm0);
init_logger(log::LevelFilter::Debug);
let timer1 = TimerGroup::new(peripherals.TIMG0);
let mut rng = esp_hal::rng::Rng::new(peripherals.RNG);
let network_seed = (rng.random() as u64) << 32 | rng.random() as u64;
wifi::set_antenna_mode(peripherals.GPIO3, peripherals.GPIO14).await;
let interfaces = wifi::setup_wifi(timer1.timer0, rng, peripherals.WIFI, spawner);
let stack = network::setup_network(network_seed, interfaces.ap, spawner);
let uart_devie = setup_uart(peripherals.UART1, peripherals.GPIO1, peripherals.GPIO2);
(uart_devie, stack)
}
fn setup_uart(
uart1: UART1<'static>,
gpio1: GPIO1<'static>,
gpio2: GPIO2<'static>,
) -> Uart<'static, Async> {
let uard_device = Uart::new(uart1, esp_hal::uart::Config::default().with_baudrate(9600));
match uard_device {
Ok(block) => block.with_rx(gpio1).with_tx(gpio2).into_async(),
Err(e) => {
error!("Failed to initialize UART: {e}");
panic!();
}
}
}

72
src/bin/init/network.rs Normal file
View File

@ -0,0 +1,72 @@
use core::{net::Ipv4Addr, str::FromStr};
use embassy_executor::Spawner;
use embassy_net::{Ipv4Cidr, Runner, Stack, StackResources, StaticConfigV4};
use embassy_time::{Duration, Timer};
use esp_wifi::wifi::WifiDevice;
use static_cell::make_static;
pub fn setup_network<'a>(seed: u64, wifi: WifiDevice<'static>, spawner: &mut Spawner) -> Stack<'a> {
let gw_ip_addr_str = "192.168.2.1";
let gw_ip_addr = Ipv4Addr::from_str(gw_ip_addr_str).expect("failed to parse gateway ip");
let config = embassy_net::Config::ipv4_static(StaticConfigV4 {
address: Ipv4Cidr::new(gw_ip_addr, 24),
gateway: Some(gw_ip_addr),
dns_servers: Default::default(),
});
let (stack, runner) =
embassy_net::new(wifi, config, make_static!(StackResources::<3>::new()), seed);
spawner.must_spawn(net_task(runner));
spawner.must_spawn(run_dhcp(stack, gw_ip_addr_str));
stack
}
#[embassy_executor::task]
async fn run_dhcp(stack: Stack<'static>, gw_ip_addr: &'static str) {
use core::net::{Ipv4Addr, SocketAddrV4};
use edge_dhcp::{
io::{self, DEFAULT_SERVER_PORT},
server::{Server, ServerOptions},
};
use edge_nal::UdpBind;
use edge_nal_embassy::{Udp, UdpBuffers};
let ip = Ipv4Addr::from_str(gw_ip_addr).expect("dhcp task failed to parse gw ip");
let mut buf = [0u8; 1500];
let mut gw_buf = [Ipv4Addr::UNSPECIFIED];
let buffers = UdpBuffers::<3, 1024, 1024, 10>::new();
let unbound_socket = Udp::new(stack, &buffers);
let mut bound_socket = unbound_socket
.bind(core::net::SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::UNSPECIFIED,
DEFAULT_SERVER_PORT,
)))
.await
.unwrap();
loop {
_ = io::server::run(
&mut Server::<_, 64>::new_with_et(ip),
&ServerOptions::new(ip, Some(&mut gw_buf)),
&mut bound_socket,
&mut buf,
)
.await
.inspect_err(|e| log::warn!("DHCP server error: {e:?}"));
Timer::after(Duration::from_millis(500)).await;
}
}
#[embassy_executor::task]
async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) {
runner.run().await;
}

56
src/bin/init/wifi.rs Normal file
View File

@ -0,0 +1,56 @@
use embassy_executor::Spawner;
use embassy_time::{Duration, Timer};
use esp_hal::gpio::{Output, OutputConfig};
use esp_hal::peripherals::{GPIO3, GPIO14, WIFI};
use esp_wifi::wifi::{AccessPointConfiguration, Configuration, WifiController, WifiEvent, WifiState};
use esp_wifi::{EspWifiRngSource, EspWifiTimerSource, wifi::Interfaces};
use static_cell::make_static;
pub async fn set_antenna_mode(gpio3: GPIO3<'static>, gpio14: GPIO14<'static>) {
let mut rf_switch = Output::new(gpio3, esp_hal::gpio::Level::Low, OutputConfig::default());
rf_switch.set_low();
Timer::after_millis(150).await;
let mut antenna_mode = Output::new(gpio14, esp_hal::gpio::Level::Low, OutputConfig::default());
antenna_mode.set_low();
}
pub fn setup_wifi<'d: 'static>(
timer: impl EspWifiTimerSource + 'd,
rng: impl EspWifiRngSource + 'd,
wifi: WIFI<'static>,
spawner: &mut Spawner,
) -> Interfaces<'d> {
let esp_wifi_ctrl = make_static!(esp_wifi::init(timer, rng).unwrap());
let (controller, interfaces) = esp_wifi::wifi::new(esp_wifi_ctrl, wifi).unwrap();
spawner.must_spawn(connection(controller));
interfaces
}
#[embassy_executor::task]
async fn connection(mut controller: WifiController<'static>) {
loop {
match esp_wifi::wifi::wifi_state() {
WifiState::ApStarted => {
// wait until we're no longer connected
controller.wait_for_event(WifiEvent::ApStop).await;
Timer::after(Duration::from_millis(5000)).await
}
_ => {}
}
if !matches!(controller.is_started(), Ok(true)) {
let client_config = Configuration::AccessPoint(AccessPointConfiguration {
ssid: "esp-wifi".try_into().unwrap(),
..Default::default()
});
controller.set_configuration(&client_config).unwrap();
controller.start_async().await.unwrap();
}
}
}

View File

@ -3,115 +3,29 @@
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#![feature(impl_trait_in_assoc_type)] #![feature(impl_trait_in_assoc_type)]
use core::net::Ipv4Addr;
use core::str::FromStr;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_net::{Ipv4Cidr, Runner, Stack, StackResources, StaticConfigV4}; use embassy_net::Stack;
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use esp_hal::clock::CpuClock; use esp_hal::Async;
use esp_hal::gpio::{Output, OutputConfig}; use esp_hal::uart::Uart;
use esp_hal::peripherals::{GPIO1, GPIO2, UART1};
use esp_hal::timer::systimer::SystemTimer;
use esp_hal::timer::timg::TimerGroup;
use esp_hal::uart::{Config, Uart};
use esp_println::logger::init_logger;
use esp_wifi::wifi::{
AccessPointConfiguration, Configuration, WifiController, WifiDevice, WifiEvent, WifiState,
};
use log::{debug, info}; use log::{debug, info};
use picoserve::routing::get;
use picoserve::{AppBuilder, AppRouter};
use static_cell::make_static;
#[panic_handler] use crate::webserver::start_webserver;
fn panic(_: &core::panic::PanicInfo) -> ! {
loop {}
}
extern crate alloc; mod init;
mod webserver;
esp_bootloader_esp_idf::esp_app_desc!();
#[esp_hal_embassy::main] #[esp_hal_embassy::main]
async fn main(spawner: Spawner) { async fn main(mut spawner: Spawner) {
// ------------------- init --------------------------- let (uart_device, stack) = init::hardware_init(&mut spawner).await;
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
let peripherals = esp_hal::init(config);
info!("starting up..."); wait_for_stack_up(stack).await;
esp_alloc::heap_allocator!(size: 72 * 1024); start_webserver(&mut spawner, stack);
spawner.must_spawn(rfid_reader_task(uart_device));
let timer0 = SystemTimer::new(peripherals.SYSTIMER); }
esp_hal_embassy::init(timer0.alarm0);
init_logger(log::LevelFilter::Debug);
let timer1 = TimerGroup::new(peripherals.TIMG0);
let mut rng = esp_hal::rng::Rng::new(peripherals.RNG);
debug!("set wlan antenna..");
let mut rf_switch = Output::new(
peripherals.GPIO3,
esp_hal::gpio::Level::Low,
OutputConfig::default(),
);
rf_switch.set_low();
Timer::after_secs(1).await;
let mut antenna_mode = Output::new(
peripherals.GPIO14,
esp_hal::gpio::Level::Low,
OutputConfig::default(),
);
antenna_mode.set_low();
Timer::after_secs(1).await;
// Setup wifi deivce
debug!("setup wifi..");
let esp_wifi_ctrl =
make_static!(esp_wifi::init(timer1.timer0, rng).unwrap());
let (controller, interfaces) = esp_wifi::wifi::new(esp_wifi_ctrl, peripherals.WIFI).unwrap();
// let wifi_interface = interfaces.sta;
let wifi_ap = interfaces.ap;
let gw_ip_addr_str = "192.168.2.1";
let gw_ip_addr = Ipv4Addr::from_str(gw_ip_addr_str).expect("failed to parse gateway ip");
let config = embassy_net::Config::ipv4_static(StaticConfigV4 {
address: Ipv4Cidr::new(gw_ip_addr, 24),
gateway: Some(gw_ip_addr),
dns_servers: Default::default(),
});
let seed = (rng.random() as u64) << 32 | rng.random() as u64;
// Init network stack
let (stack, runner) = embassy_net::new(
wifi_ap,
config,
make_static!(StackResources::<3>::new()),
seed,
);
debug!("Setup complete. Running network tasks");
spawner.spawn(connection(controller)).ok();
spawner.spawn(net_task(runner)).ok();
spawner.spawn(run_dhcp(stack, gw_ip_addr_str)).ok();
spawner
.spawn(rfid_reader_task(
peripherals.UART1,
peripherals.GPIO1,
peripherals.GPIO2,
))
.ok();
async fn wait_for_stack_up(stack: Stack<'static>) {
loop { loop {
if stack.is_link_up() { if stack.is_link_up() {
break; break;
@ -122,154 +36,24 @@ async fn main(spawner: Spawner) {
} }
Timer::after(Duration::from_millis(500)).await; Timer::after(Duration::from_millis(500)).await;
} }
debug!("Starting webserver");
let app = make_static!(AppProps.build_app());
let config = make_static!(picoserve::Config::new(picoserve::Timeouts {
start_read_request: Some(Duration::from_secs(5)),
persistent_start_read_request: Some(Duration::from_secs(1)),
read_request: Some(Duration::from_secs(1)),
write: Some(Duration::from_secs(1)),
}));
let _ = spawner.spawn(webserver_task(0, stack, app, config));
}
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" }))
}
} }
#[embassy_executor::task] #[embassy_executor::task]
async fn webserver_task( async fn rfid_reader_task(mut uart_device: Uart<'static, Async>) {
id: usize,
stack: embassy_net::Stack<'static>,
app: &'static AppRouter<AppProps>,
config: &'static picoserve::Config<Duration>,
) -> ! {
let mut tcp_rx_buffer = [0u8; 1024];
let mut tcp_tx_buffer = [0u8; 1024];
let mut http_buffer = [0u8; 2048];
picoserve::listen_and_serve(
id,
app,
config,
stack,
80,
&mut tcp_rx_buffer,
&mut tcp_tx_buffer,
&mut http_buffer,
)
.await
}
#[embassy_executor::task]
async fn run_dhcp(stack: Stack<'static>, gw_ip_addr: &'static str) {
debug!("start dhcp");
use core::net::{Ipv4Addr, SocketAddrV4};
use edge_dhcp::{
io::{self, DEFAULT_SERVER_PORT},
server::{Server, ServerOptions},
};
use edge_nal::UdpBind;
use edge_nal_embassy::{Udp, UdpBuffers};
let ip = Ipv4Addr::from_str(gw_ip_addr).expect("dhcp task failed to parse gw ip");
let mut buf = [0u8; 1500];
let mut gw_buf = [Ipv4Addr::UNSPECIFIED];
let buffers = UdpBuffers::<3, 1024, 1024, 10>::new();
let unbound_socket = Udp::new(stack, &buffers);
let mut bound_socket = unbound_socket
.bind(core::net::SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::UNSPECIFIED,
DEFAULT_SERVER_PORT,
)))
.await
.unwrap();
loop {
_ = io::server::run(
&mut Server::<_, 64>::new_with_et(ip),
&ServerOptions::new(ip, Some(&mut gw_buf)),
&mut bound_socket,
&mut buf,
)
.await
.inspect_err(|e| log::warn!("DHCP server error: {e:?}"));
Timer::after(Duration::from_millis(500)).await;
}
}
#[embassy_executor::task]
async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) {
runner.run().await;
}
#[embassy_executor::task]
async fn connection(mut controller: WifiController<'static>) {
debug!("start connection task");
debug!("Device capabilities: {:?}", controller.capabilities());
loop {
match esp_wifi::wifi::wifi_state() {
WifiState::ApStarted => {
// wait until we're no longer connected
controller.wait_for_event(WifiEvent::ApStop).await;
Timer::after(Duration::from_millis(5000)).await
}
_ => {}
}
if !matches!(controller.is_started(), Ok(true)) {
let client_config = Configuration::AccessPoint(AccessPointConfiguration {
ssid: "esp-wifi".try_into().unwrap(),
..Default::default()
});
controller.set_configuration(&client_config).unwrap();
debug!("Starting wifi");
controller.start_async().await.unwrap();
debug!("Wifi started!");
}
}
}
#[embassy_executor::task]
async fn rfid_reader_task(uart1: UART1<'static>, gpio1: GPIO1<'static>, gpio2: GPIO2<'static>) {
debug!("init rfid reader..");
let uart1_block_result = Uart::new(uart1, Config::default().with_baudrate(9600));
let mut nfc_reader = match uart1_block_result {
Ok(block) => block.with_rx(gpio1).with_tx(gpio2).into_async(),
Err(e) => {
log::error!("Failed to initialize UART: {:?}", e);
return;
}
};
let mut uart_buffer = [0u8; 64]; let mut uart_buffer = [0u8; 64];
loop { loop {
debug!("Looking for NFC..."); debug!("Looking for NFC...");
match nfc_reader.read_async(&mut uart_buffer).await { match uart_device.read_async(&mut uart_buffer).await {
Ok(n) => { Ok(n) => {
let mut hex_str = heapless::String::<128>::new(); let mut hex_str = heapless::String::<128>::new();
for byte in &uart_buffer[..n] { for byte in &uart_buffer[..n] {
core::fmt::Write::write_fmt(&mut hex_str, format_args!("{:02X} ", byte)).ok(); core::fmt::Write::write_fmt(&mut hex_str, format_args!("{:02X} ", byte)).ok();
} }
info!("Read {} bytes from UART: {}", n, hex_str); info!("Read {n} bytes from UART: {hex_str}");
} }
Err(e) => { Err(e) => {
log::error!("Error reading from UART: {:?}", e); log::error!("Error reading from UART: {e}");
} }
} }
Timer::after(Duration::from_millis(200)).await; Timer::after(Duration::from_millis(200)).await;

52
src/bin/webserver/mod.rs Normal file
View File

@ -0,0 +1,52 @@
use embassy_executor::Spawner;
use embassy_net::Stack;
use embassy_time::Duration;
use picoserve::{AppBuilder, AppRouter, routing::get};
use static_cell::make_static;
pub fn start_webserver(spawner: &mut Spawner, stack: Stack<'static>) {
let app = make_static!(AppProps.build_app());
let config = make_static!(picoserve::Config::new(picoserve::Timeouts {
start_read_request: Some(Duration::from_secs(5)),
persistent_start_read_request: Some(Duration::from_secs(1)),
read_request: Some(Duration::from_secs(1)),
write: Some(Duration::from_secs(1)),
}));
let _ = spawner.spawn(webserver_task(0, stack, app, config));
}
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" }))
}
}
#[embassy_executor::task]
async fn webserver_task(
id: usize,
stack: embassy_net::Stack<'static>,
app: &'static AppRouter<AppProps>,
config: &'static picoserve::Config<Duration>,
) -> ! {
let mut tcp_rx_buffer = [0u8; 1024];
let mut tcp_tx_buffer = [0u8; 1024];
let mut http_buffer = [0u8; 2048];
picoserve::listen_and_serve(
id,
app,
config,
stack,
80,
&mut tcp_rx_buffer,
&mut tcp_tx_buffer,
&mut http_buffer,
)
.await
}