HTTP Client
Next, we'll write a small client that retrieves data over an HTTP connection to the internet.
For demonstration purposes we implement the http client ourselves. Usually you want to use e.g. reqwless
or edge-net
Before jumping to the exercise, let's explore how Wi-Fi works in no_std
Rust for Espressif devices.
Wi-Fi Ecosystem
Wi-Fi support comes in the esp-wifi
crate. The esp-wifi
is home to the Wi-Fi, Bluetooth and ESP-NOW driver implementations for no_std
Rust.
Check the repository README for current support, limitations and usage details.
There are some other relevant crates, on which esp-wifi
depends on:
smol-tcp
: Event-driven TCP/IP stack implementation.- It does not require heap allocation (which is a requirement for some
no_std
projects) - For more information about the crate, see the official documentation
- It does not require heap allocation (which is a requirement for some
Additionally, when using async, embassy-net
is relevant.
Setup
✅ Go to intro/http-client
directory.
✅ Open the prepared project skeleton in intro/http-client
.
✅ Add your network credentials: Set the SSID
and PASSWORD
environment variables.
intro/http-client/examples/http-client.rs
contains the solution. You can run it with the following command:
cargo run --release --example http-client
✅ Read the Optimization Level section of the esp-wifi
README.
Exercise
✅ Bump the clock
frequency at which the target operates to its maximum. Consider using ClockControl::configure
or ClockControl::max
✅ Create a timer
and initialize the Wi-Fi
let timg0 = esp_hal::timer::timg::TimerGroup::new(peripherals.TIMG0);
let init = init(
EspWifiInitFor::Wifi,
timg0.timer0,
Rng::new(peripherals.RNG),
peripherals.RADIO_CLK,
)
.unwrap();
✅ Configure Wi-Fi using Station Mode
let mut wifi = peripherals.WIFI;
let mut socket_set_entries: [SocketStorage; 3] = Default::default();
let (iface, device, mut controller, sockets) =
create_network_interface(&init, &mut wifi, WifiStaDevice, &mut socket_set_entries).unwrap();
✅ Create a Client with your Wi-Fi credentials and default configuration. Look for a suitable constructor in the documentation.
let client_config = Configuration::Client(ClientConfiguration {
....
});
let res = controller.set_configuration(&client_config);
println!("Wi-Fi set_configuration returned {:?}", res);
✅ Start the Wi-Fi controller, scan the available networks, and try to connect to the one we set.
controller.start().unwrap();
println!("Is wifi started: {:?}", controller.is_started());
println!("Start Wifi Scan");
let res: Result<(heapless::Vec<AccessPointInfo, 10>, usize), WifiError> = controller.scan_n();
if let Ok((res, _count)) = res {
for ap in res {
println!("{:?}", ap);
}
}
println!("{:?}", controller.get_capabilities());
println!("Wi-Fi connect: {:?}", controller.connect());
// Wait to get connected
println!("Wait to get connected");
loop {
let res = controller.is_connected();
match res {
Ok(connected) => {
if connected {
break;
}
}
Err(err) => {
println!("{:?}", err);
loop {}
}
}
}
println!("{:?}", controller.is_connected());
✅ Then we obtain the assigned IP
// Wait for getting an ip address
let now = || time::now().duration_since_epoch().to_millis();
let wifi_stack = WifiStack::new(iface, device, sockets, now);
println!("Wait to get an ip address");
loop {
wifi_stack.work();
if wifi_stack.is_iface_up() {
println!("got ip {:?}", wifi_stack.get_ip_info());
break;
}
}
If the connection succeeds, we proceed with the last part, making the HTTP request.
By default, only unencrypted HTTP is available, which limits our options of hosts to connect to. We're going to use www.mobile-j.de/
.
To make an HTTP request, we first need to open a socket, and write to it the GET request,
✅ Open a socket with the following IPv4 address 142.250.185.115
and port 80
. See IpAddress::Ipv4
documentation.
✅ write
the following message to the socket and flush
it: b"GET / HTTP/1.0\r\nHost: www.mobile-j.de\r\n\r\n"
✅ Then we wait for the response and read it out.
let deadline = time::now() + Duration::secs(20);
let mut buffer = [0u8; 512];
while let Ok(len) = socket.read(&mut buffer) {
let to_print = unsafe { core::str::from_utf8_unchecked(&buffer[..len]) };
print!("{}", to_print);
if time::now() > deadline {
println!("Timeout");
break;
}
}
println!();
✅ Finally, we will close the socket and wait
socket.disconnect();
let deadline = time::now() + Duration::secs(5);
while time::now() < deadline {
socket.work();
}
Simulation
This project is available for simulation through two methods:
- Wokwi projects:
- Exercise: Currently not available
- Solution: Currently not available
- Wokwi files are also present in the project folder to simulate it with Wokwi VS Code extension:
- Press F1, select
Wokwi: Select Config File
and chooseintro/http-client/wokwi.toml
- Edit the
wokwi.toml
file to select between exercise and solution simulation
- Edit the
- Build you project
- Press F1 again and select
Wokwi: Start Simulator
- Press F1, select