Networking (WiFi & HTTP)
WiFi and HTTP are thin classes: new WiFi(...) carries the whole link policy (credentials, security, timeout), and new Request(...) carries the whole request (method, URL, body, TLS policy). Every method lowers onto Zephyr’s own networking — wifi_mgmt for the link, sockets and http_client for requests. https:// requests are encrypted.
The same calls work three ways — blocking at the top level, fire-and-forget with callbacks, or async/await for programs that need to keep doing other things while the link comes up.
WiFi and HTTP require a networked target — an ESP32 board on the Zephyr framework (esp32_devkitc, esp32s3_devkitc). They compile-time-error on boards without a radio, including the nRF52840-based XIAO.
Connecting to WiFi
Basic connect
Construct the link policy, then join(). The constructor carries the credentials; join() associates and waits — bounded by timeoutMs (default 15 s) — until the board has an IP address.
import { WiFi } from '@typecad/hal';
import { UART0 } from '@typecad/board';
const wifi = new WiFi('MyNetwork', { psk: 'correct-horse-battery-staple' });
if (wifi.join()) {
UART0.writeLine(wifi.ip()); // the IP address as a string
}Omit psk for an open network. Security defaults to WPA2 when a psk is present and OPEN otherwise; override it with the WiFi.WPA3 / WiFi.WPA2_WPA3 tokens.
Static IP and radio tuning
Both are construction facts, set at the same time as the credentials:
const lab = new WiFi('LabNet', {
psk: 'correct-horse-battery-staple',
timeoutMs: 30000, // 30 s deadline
ipv4: { addr: '10.0.0.5', gateway: '10.0.0.1', netmask: '255.255.255.0' },
powerSave: WiFi.PS_OFF, // disable modem sleep for lower latency
channel: 6, // 0 / omitted = any
});With static ipv4 facts the link comes up without DHCP — join() returns as soon as the address is applied.
Async connect
Inside an async function, the join splits into a non-blocking start plus a poll, so other tasks keep running while the station associates:
const wifi = new WiFi('MyNetwork', { psk: 'hunter22' });
async function network() {
wifi.joinStart(); // fire-and-forget associate
while (!wifi.linked()) { await Time.sleep(100); }
UART0.writeLine(wifi.ip());
}Link events
wifi.onUp(() => UART0.writeLine('online at ' + wifi.ip()));
wifi.onDrop(() => UART0.writeLine('link lost')); // safe to call join() from here
wifi.join();Scanning
scan() performs one blocking scan and returns a read-only handle over a fixed pool of 16 results:
const results = wifi.scan();
for (let i = 0; i < results.count(); i++) {
UART0.writeLine(results.ssid(i) + ' ' + results.rssi(i) + ' dBm ch' + results.channel(i));
}Access point mode
Bring the radio up as an access point for provisioning:
import { WiFiAP } from '@typecad/hal';
const ap = new WiFiAP('cuttlefish-setup', { psk: 'config123', channel: 6 });
ap.start();
// …provisioning…
ap.stop();HTTP — Request
Request is the HTTP/S client. Construction carries method, URL, timeout, body, and TLS policy; header() chains request headers; send() performs the request and leaves the response in the client until the next send.
import { Request } from '@typecad/hal';
const req = new Request('GET', 'http://192.168.2.184:8080/health');
req.header('X-Device', 'cuttlefish');
if (req.send()) {
UART0.writeLine(req.status()); // 200
UART0.writeLine(req.ok()); // true for any 2xx
UART0.writeLine(req.text()); // the body, until the next send
UART0.writeLine(req.responseHeader('Content-Type'));
}Method tokens and chaining:
new Request(Request.POST, 'http://api.local/telemetry', {
body: '{"temp":21.5}',
json: true, // sets Content-Type: application/json
timeoutMs: 5000,
}).header('Authorization', 'Bearer x').send();HTTPS and certificate pinning
HTTPS is native. The CA policy is a construction fact:
const pinned = new Request(Request.GET, 'https://internal.corp/api', { caCert: CORP_CA_PEM });
const lab = new Request(Request.GET, 'https://lab-server.local', { insecure: true });caCert pins a specific CA (a PEM string — it is converted to binary form at build time, so no PEM parser ships on the board). insecure: true encrypts but skips verification; do not ship it in production.
Async requests
Inside an async function, await req.send() splits into send-start plus done-polling — a slow request never stalls the rest of the program:
async function pollCloud() {
while (true) {
const req = new Request(Request.GET, 'http://api.local/health');
await req.send();
UART0.writeLine(req.status());
await Time.sleep(5000);
}
}Put the await on its own statement. A value-position await (const ok = await req.send()) cannot suspend mid-expression and falls back to the blocking form.
⚙️ If the board reboots right after joining
Some ESP32-S3 dev kits reboot the moment the radio transmits, with E BOD: Brownout detector was triggered in the serial log. The power amplifier draws a brief current spike on the first probe frame, and on a marginal USB supply (a long cable, a weak charger, a cheap regulator) that spike droops the 3.3 V rail below the brownout threshold.
The workaround is a lower transmit power while diagnosing. The root cause is the power supply, not firmware — for production, fix the rail (shorter cable, beefier 5 V source, bulk capacitance near the module).
Build-time checks
Two checks run before you flash. Using WiFi, Request, or Mqtt on a board with no WiFi radio (the nRF52840-based XIAO, for example) is a build error naming an ESP32 target. And a program that issues HTTP requests without ever bringing a WiFi link up (join() or an AP) is flagged with a build-time warning — every request would fail at runtime.
API Reference
WiFi
| Member | Description |
|---|---|
new WiFi(ssid, opts?) | Link policy: psk, security, channel, band, timeoutMs (default 15 s), powerSave, ipv4 { addr, gateway, netmask }. |
join() | Associate and wait for IP connectivity. Awaitable. |
joinStart() | Fire-and-forget associate — poll linked(). |
leave() | Disassociate. |
linked() / rssi() / ip() / mac() | Link state and info. |
onUp(fn) / onDrop(fn) | Link-up / link-down callbacks. |
scan() | One blocking scan; results via the returned handle. Awaitable. |
Request
| Member | Description |
|---|---|
new Request(method, url, opts?) | timeoutMs (default 10 s), body, json, insecure, caCert. |
Request.GET / POST / PUT / DELETE / HEAD / PATCH | Method tokens. |
header(name, value) | Attach a request header; chainable. |
send() | Perform the request. Awaitable. |
status() / ok() / text() | Response status, 2xx check, body (until the next send). |
contentLength() / responseHeader(name) | Response metadata. |
On This Page