Bare-Metal Ada on the ESP32-S3 A step-by-step guide to running Ada on the ESP32-S3 with no ESP-IDF, no FreeRTOS, and no Python.

Step 43 of 56

Wi-Fi: pure Ada around three binary blobs

The one place the from-scratch claim has an asterisk — and the asterisk is smaller, and better fenced, than you would expect.

What is a blob and what is not

The radio's MAC and PHY are undocumented, so the driver runs Espressif's closed libraries (libnet80211, libpp, libphy, libcore). Everything around them is Ada written against the embedded (Jorvik) runtime:

Ours, in AdaWhat it does
.OS_Adapter, .RTOS Maps the blobs' RTOS calls onto Jorvik tasks — so FreeRTOS still never runs.
.PHYPHY/RF calibration, and persisting calibration data.
.SupplicantThe WPA2-PSK 4-way handshake.
.IP, .DHCP, .Net_Device A software TCP/IP stack presenting the radio as a Net_Device.
.Interrupt, .Port, .Core_Shim The interrupt, timer and core glue the blobs expect.
.SnifferPromiscuous-mode capture.

The blobs are Apache-2.0 and fetched, not committedtools/fetch-wifi-blobs.sh pins them to exact upstream commits and verifies each by sha256. So the repository still contains no opaque vendor binary for Wi-Fi; you choose to download them.

The supplicant is ours, deliberately

The 4-way handshake is pure Ada: derive the PMK with PBKDF2-HMAC-SHA1, run the handshake (PTK via SHA1-PRF, MIC via HMAC-SHA1, GTK via AES key-unwrap), and reply with message 2 of 4.

That placement is the security-relevant part. Doing the handshake in Ada means your PSK and the derived keys never pass through blob code — the blob is handed an already-established association, not your passphrase. A driver that let the closed library do WPA2 would be trusting it with the one secret that matters.

Using it

type Auth_Mode is ...;
type AP_Record is record ... end record;
type AP_List   is array (Positive range <>) of AP_Record;

procedure Initialize (...);
procedure Scan       (...);
procedure Connect    (...);
function  Connected        return Boolean;
function  Current_Channel  return Natural;

Not re-entrant. This package drives a single radio and one caller — typically the environment task — owns it. Do not call Scan and Connect from two tasks.

Connect returns as soon as the association is started. The association and the 4-way handshake then run to completion on the internal Wi-Fi task, so poll Connected to learn when the link is actually up. Treating Connect's return as "we are on the network" is the mistake to avoid.

The version-locked C structs stay in the body; the spec exposes clean Ada records, so a blob update cannot ripple into your code. Examples run scan, promiscuous sniffing, DNS, HTTP and full HTTPS over the radio.