Final: Access the Website
We have covered how to set up the Wi-Fi connection with Embassy. Since the previous chapter became quite lengthy, we didn't dive into the details of the access_website function. In this chapter, we will focus on how to send an HTTP request and receive a response from a website using the reqwless crate, and then we finally will run the code.
Sending HTTP Request
We begin by initializing the DNS socket and the TCP client.
TLS Config:
Next, we set up the TLS configuration using the tls_seed, which is the random number we generated earlier and passed into the function. We configure TLS to skip SSL certificate verification by setting TlsVerify::None. While this is not recommended for production environments, we use this insecure setting for simplicity. In production, you would need to configure TLS with certificate verification and provide the certificate chain so that it can use for verification.
The remaining part is pretty straightforward. We initialize the HTTP client with the TCP client, DNS socket, and TLS config. Then, we send a GET request to the jsonplaceholder website, which returns JSON content. We read the content and then print it to the console.
#![allow(unused)] fn main() { async fn access_website(stack: &'static Stack<WifiDevice<'static, WifiStaDevice>>, tls_seed: u64) { let mut rx_buffer = [0; 4096]; let mut tx_buffer = [0; 4096]; let dns = DnsSocket::new(&stack); let tcp_state = TcpClientState::<1, 4096, 4096>::new(); let tcp = TcpClient::new(stack, &tcp_state); let tls = TlsConfig::new( tls_seed, &mut rx_buffer, &mut tx_buffer, reqwless::client::TlsVerify::None, ); let mut client = HttpClient::new_with_tls(&tcp, &dns, tls); let mut buffer = [0u8; 4096]; let mut http_req = client .request( reqwless::request::Method::GET, "https://jsonplaceholder.typicode.com/posts/1", ) .await .unwrap(); let response = http_req.send(&mut buffer).await.unwrap(); info!("Got response"); let res = response.body().read_to_end().await.unwrap(); let content = core::str::from_utf8(res).unwrap(); println!("{}", content); } }
Clone the existing project
You can also clone (or refer) project I created and navigate to the wifi-async-http
folder.
git clone https://github.com/ImplFerris/esp32-projects
cd esp32-projects/wifi-async-http
How to run?
Normally, we would simply run cargo run --release
, but this time we also need to pass the environment variables for the Wi-Fi connection.
SSID=YOUR_WIFI_NAME PASSWORD=YOUR_WIFI_PASSWORD cargo run --release
The Full code
#![no_std] #![no_main] use defmt::info; use embassy_executor::Spawner; use embassy_net::dns::DnsSocket; use embassy_net::tcp::client::{TcpClient, TcpClientState}; use embassy_net::{DhcpConfig, Runner, Stack, StackResources}; use embassy_time::{Duration, Timer}; use esp_hal::clock::CpuClock; use esp_hal::rng::Rng; use esp_hal::timer::timg::TimerGroup; use esp_println as _; use esp_println::println; use esp_wifi::wifi::{self, WifiController, WifiDevice, WifiEvent, WifiState}; use esp_wifi::EspWifiController; use reqwless::client::{HttpClient, TlsConfig}; #[panic_handler] fn panic(_: &core::panic::PanicInfo) -> ! { loop {} } extern crate alloc; // If you are okay with using a nightly compiler, you can use the macro provided by the static_cell crate: https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html macro_rules! mk_static { ($t:ty,$val:expr) => {{ static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); #[deny(unused_attributes)] let x = STATIC_CELL.uninit().write(($val)); x }}; } const SSID: &str = env!("SSID"); const PASSWORD: &str = env!("PASSWORD"); #[esp_hal_embassy::main] async fn main(spawner: Spawner) { // generator version: 0.3.1 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 = TimerGroup::new(peripherals.TIMG1); esp_hal_embassy::init(timer0.timer0); info!("Embassy initialized!"); let timer1 = TimerGroup::new(peripherals.TIMG0); // let _init = esp_wifi::init( // timer1.timer0, // esp_hal::rng::Rng::new(peripherals.RNG), // peripherals.RADIO_CLK, // ) // .unwrap(); let mut rng = Rng::new(peripherals.RNG); let esp_wifi_ctrl = &*mk_static!( EspWifiController<'static>, esp_wifi::init(timer1.timer0, rng.clone(), peripherals.RADIO_CLK,).unwrap() ); let (controller, interfaces) = esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).unwrap(); let wifi_interface = interfaces.sta; let net_seed = rng.random() as u64 | ((rng.random() as u64) << 32); let tls_seed = rng.random() as u64 | ((rng.random() as u64) << 32); let dhcp_config = DhcpConfig::default(); // dhcp_config.hostname = Some(String::from_str("implRust").unwrap()); let config = embassy_net::Config::dhcpv4(dhcp_config); // Init network stack let (stack, runner) = embassy_net::new( wifi_interface, config, mk_static!(StackResources<3>, StackResources::<3>::new()), net_seed, ); spawner.spawn(connection(controller)).ok(); spawner.spawn(net_task(runner)).ok(); wait_for_connection(stack).await; access_website(stack, tls_seed).await } async fn wait_for_connection(stack: Stack<'_>) { println!("Waiting for link to be up"); loop { if stack.is_link_up() { break; } Timer::after(Duration::from_millis(500)).await; } println!("Waiting to get IP address..."); loop { if let Some(config) = stack.config_v4() { println!("Got IP: {}", config.address); break; } Timer::after(Duration::from_millis(500)).await; } } #[embassy_executor::task] async fn connection(mut controller: WifiController<'static>) { println!("start connection task"); println!("Device capabilities: {:?}", controller.capabilities()); loop { match esp_wifi::wifi::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 = wifi::Configuration::Client(wifi::ClientConfiguration { ssid: SSID.try_into().unwrap(), password: PASSWORD.try_into().unwrap(), ..Default::default() }); controller.set_configuration(&client_config).unwrap(); println!("Starting wifi"); controller.start_async().await.unwrap(); println!("Wifi started!"); } 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 } async fn access_website(stack: Stack<'_>, tls_seed: u64) { let mut rx_buffer = [0; 4096]; let mut tx_buffer = [0; 4096]; let dns = DnsSocket::new(stack); let tcp_state = TcpClientState::<1, 4096, 4096>::new(); let tcp = TcpClient::new(stack, &tcp_state); let tls = TlsConfig::new( tls_seed, &mut rx_buffer, &mut tx_buffer, reqwless::client::TlsVerify::None, ); let mut client = HttpClient::new_with_tls(&tcp, &dns, tls); let mut buffer = [0u8; 4096]; let mut http_req = client .request( reqwless::request::Method::GET, "https://jsonplaceholder.typicode.com/posts/1", ) .await .unwrap(); let response = http_req.send(&mut buffer).await.unwrap(); info!("Got response"); let res = response.body().read_to_end().await.unwrap(); let content = core::str::from_utf8(res).unwrap(); println!("{}", content); }