/ firmware / examples / esp32s3 / access_point.rs
access_point.rs
  1  #![no_std]
  2  #![no_main]
  3  
  4  use defmt::info;
  5  use embassy_executor::Spawner;
  6  use embassy_net::{Ipv4Cidr, Runner, Stack, StackResources, StaticConfigV4};
  7  use embassy_time::{Duration, Timer};
  8  use esp_hal::{
  9      clock::CpuClock,
 10      interrupt::software::SoftwareInterruptControl,
 11      rng::Rng,
 12      timer::timg::TimerGroup,
 13  };
 14  use esp_radio::wifi::{Config, ControllerConfig, Interface, WifiController, ap::AccessPointConfig};
 15  use panic_rtt_target as _;
 16  use picoserve::routing::get;
 17  use static_cell::StaticCell;
 18  
 19  use firmware::config::app;
 20  
 21  const AP_SSID: &str = env!("WIFI_SSID");
 22  const AP_PASSWORD: &str = env!("WIFI_PSK");
 23  const AP_CHANNEL: u8 = 6;
 24  
 25  esp_bootloader_esp_idf::esp_app_desc!();
 26  
 27  macro_rules! mk_static {
 28      ($type:ty, $value:expr) => {{
 29          static STATIC_CELL: StaticCell<$type> = StaticCell::new();
 30          STATIC_CELL.uninit().write($value)
 31      }};
 32  }
 33  
 34  fn random_seed(random_number_generator: &mut Rng) -> u64 {
 35      (u64::from(random_number_generator.random()) << 32)
 36          | u64::from(random_number_generator.random())
 37  }
 38  
 39  #[embassy_executor::task]
 40  async fn wifi_ap_task(wifi_controller: WifiController<'static>) {
 41      let _ = wifi_controller;
 42      loop {
 43          Timer::after(Duration::from_secs(60)).await;
 44      }
 45  }
 46  
 47  #[embassy_executor::task]
 48  async fn wifi_net_task(mut runner: Runner<'static, Interface<'static>>) {
 49      runner.run().await;
 50  }
 51  
 52  #[embassy_executor::task]
 53  async fn http_server_task(stack: Stack<'static>) {
 54      let app = picoserve::Router::new().route(
 55          "/",
 56          get(|| async {
 57              (
 58                  ("Content-Type", "text/html; charset=utf-8"),
 59                  concat!(
 60                      "<!DOCTYPE html><html><head><title>Ceratina Setup</title></head><body>",
 61                      "<h1>Ceratina Device Setup</h1>",
 62                      "<p>Connect this device to your Wi-Fi network.</p>",
 63                      "</body></html>",
 64                  ),
 65              )
 66          }),
 67      );
 68  
 69      let config = mk_static!(
 70          picoserve::Config,
 71          picoserve::Config::new(picoserve::Timeouts {
 72              start_read_request: Duration::from_secs(5),
 73              persistent_start_read_request: Duration::from_secs(5),
 74              read_request: Duration::from_secs(2),
 75              write: Duration::from_secs(2),
 76          })
 77          .keep_connection_alive()
 78      );
 79  
 80      let mut tcp_rx_buffer = [0u8; 2048];
 81      let mut tcp_tx_buffer = [0u8; 2048];
 82      let mut http_buffer = [0u8; 4096];
 83  
 84      loop {
 85          picoserve::Server::new(&app, config, &mut http_buffer)
 86              .listen_and_serve(0usize, stack, app::http::PORT, &mut tcp_rx_buffer, &mut tcp_tx_buffer)
 87              .await;
 88      }
 89  }
 90  
 91  #[esp_rtos::main]
 92  async fn main(spawner: Spawner) -> ! {
 93      rtt_target::rtt_init_defmt!();
 94  
 95      let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
 96      let peripherals = esp_hal::init(config);
 97  
 98      esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: 73744);
 99      esp_alloc::heap_allocator!(size: 64 * 1024);
100  
101      let timg0 = TimerGroup::new(peripherals.TIMG0);
102      let sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
103      esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0);
104  
105      info!("Embassy initialized!");
106  
107      let ap_config = Config::AccessPoint(
108          AccessPointConfig::default()
109              .with_ssid(AP_SSID)
110              .with_password(AP_PASSWORD.into())
111              .with_channel(AP_CHANNEL),
112      );
113  
114      info!("starting Wi-Fi in AP mode");
115      let (wifi_controller, interfaces) = esp_radio::wifi::new(
116          peripherals.WIFI,
117          ControllerConfig::default().with_initial_config(ap_config),
118      )
119      .expect("Failed to initialize Wi-Fi controller");
120  
121      info!("AP '{}' started on channel {}", AP_SSID, AP_CHANNEL);
122      info!("AP IP: 192.168.4.1");
123  
124      let mut random_number_generator = Rng::new();
125      let seed = random_seed(&mut random_number_generator);
126  
127      let ap_network_config = embassy_net::Config::ipv4_static(StaticConfigV4 {
128          address: Ipv4Cidr::new(
129              core::net::Ipv4Addr::new(192, 168, 4, 1),
130              24,
131          ),
132          gateway: Some(core::net::Ipv4Addr::new(192, 168, 4, 1)),
133          dns_servers: Default::default(),
134      });
135  
136      let (stack, runner) = embassy_net::new(
137          interfaces.access_point,
138          ap_network_config,
139          mk_static!(StackResources<3>, StackResources::<3>::new()),
140          seed,
141      );
142  
143      spawner.spawn(wifi_ap_task(wifi_controller).unwrap());
144      spawner.spawn(wifi_net_task(runner).unwrap());
145  
146      stack.wait_config_up().await;
147  
148      spawner.spawn(http_server_task(stack).unwrap());
149  
150      info!("HTTP server listening on port {}", app::http::PORT);
151  
152      loop {
153          Timer::after(Duration::from_secs(60)).await;
154      }
155  }