Start here
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. Fifty-six short steps, one aspect each, from a blank machine to your own Ada application running on both cores.
The ESP32-S3 is normally programmed through Espressif's ESP-IDF: a large C SDK, a Python build front end, and FreeRTOS underneath everything. This guide takes a different route. The runtime here owns both cores — the context switch, the interrupt vectors, the clock tick, the SMP scheduler and the inter-core interrupt are all its own, written in Ada and a little Xtensa assembly. FreeRTOS never runs; its scheduler is not even linked. The toolchain you install is one package manager.
Each page below covers exactly one thing, and each links to the next. Steps 1 to 5 get an LED blinking. Steps 6 to 9 explain what you just did and how to configure it. Steps 10 and 11 are your own project and the driver library, and step 12 catalogues the 96 examples. Steps 13 to 29 are the chip's own peripherals and 30 to 39 the external devices the SDK drives — start with whichever your board actually has. Steps 40 to 45 are the networking stack, from sockets up through TLS and Wi-Fi, and 46 to 52 the storage, filesystems and standalone tools. Steps 53 and 54 are the test harnesses and the runtime itself, and 55 and 56 the debugger and what to do when something goes wrong.
The SDK lives at github.com/rowsail/ada_esp32s3. Grab it as a tagged release archive or clone it — step 3 covers both. The same release page carries Bare-Metal Ada on the ESP32-S3, the book: this guide gets you running and explains each driver, while the book is the long-form design write-up behind it.
Begin with step 01 →Contents
- What you need (and what you don't)
The board, the cable, the one package manager — and the four things you are not installing.
- Installing Alire and the toolchains
Installing Alire and selecting the cross, native and build toolchains.
- Getting the code
Cloning the repository with its two submodules, and what is in the tree.
- Plugging in the board and finding the port
The native USB port, the device node, serial permissions, and forcing download mode.
- Your first blink
One command builds, flashes and monitors a pure-Ada GPIO driver at 2 Hz.
- What happens between reset and Main
Every layer between the mask ROM and your first line of Ada, and why
Maincan be empty. - What a build actually does
The five build steps, and the two Ada host tools that replace esptool.
- Choosing a runtime profile
light-tasking, embedded, full — what each gives you and which to pick.
- Board configuration: board.ads
Flash and PSRAM size in
board.ads, and why PSRAM size rebuilds the bootloader. - Your own project, outside the repo
Scaffolding a standalone project anywhere on disk with
export.shandesp32-ada. - Talking to the hardware: the HAL
Using the peripheral drivers, how they are shaped, and what still needs verifying on your board.
- The examples: all 96 of them
All 96 examples: what each shows, which profile it needs, and where it is explained.
- GPIO in depth
The pin type that rejects a pad which would hang the chip, what is atomic in silicon, and the interrupt callback rule.
- I2C in depth
An RAII session that cannot leak the bus lock, repeated START, and why payload length never reaches your code.
- SPI in depth
Per-device clock and mode on a shared host, chip select three ways, and DMA rules enforced as preconditions.
- UART in depth
No setup call by design, interrupt-driven RX with a buffer Ada makes you declare just so, and a routing trap.
- GDMA: the DMA engine everything else borrows
Five channel pairs claimed at run time, and the buffer rules PSRAM's cache imposes.
- I2S: audio that only moves by DMA
Audio with no CPU FIFO: DMA-only transfers, gapless looping, and capture underneath playback.
- LCD: two very different display modes
A command-driven i8080 bus and a continuously-refreshed RGB panel from one controller.
- TWAI: CAN 2.0, with the bus-off trap
CAN 2.0 with identifier widths the type system keeps apart, and the bus-off trap.
- RMT: an arbitrary pulse generator
Arbitrary {level, duration} pulse trains for IR, WS2812 and 1-Wire.
- LEDC and sigma-delta: the simple outputs
Eight PWM channels for dimming, and eight density-modulated outputs that filter to analog.
- MCPWM: PWM that can shut itself down
Dead-time, a chopper carrier, and fault inputs that force the pins safe in hardware.
- Timers and pulse counting
A 54-bit timer with an alarm, and four edge counters that wrap sooner than you think.
- Analog in: the SAR ADC and capacitive touch
The SAR ADC on fixed pins, and touch channels that count their way to a reading.
- RTC, pad hold and deep sleep
Deep sleep resets the chip; what survives is RTC memory and the pads you held.
- Hardware crypto, and one honest caveat
SHA, AES and RSA acceleration, MD5 for flash verification, and why the RNG is not a CSPRNG here.
- SD cards: two hosts, one API shape
SPI transport versus the native SD bus, and why the faster one runs on the lean runtime.
- Chip identity: die temperature and the eFuse MAC
Die temperature (not ambient) and the four factory MACs in eFuse.
- ST7789 display and GT911 touch
A write-only SPI panel you cannot probe, and a touch chip whose address is set at reset.
- ES8311: the audio codec
Control over I2C, audio over I2S, and the 256x MCLK ratio the codec depends on.
- Sensors: the QMI8658C IMU and SHT41
A register-mapped IMU and a command-based humidity sensor, and how each flags a bad reading.
- PCF85063A: a clock that tells you when not to trust it
A typed BCD calendar, an alarm, and the oscillator-stop flag that says do not trust me.
- Port expanders: TCA9555, CH422G and HC595
Per-pin control, an all-or-nothing direction bit, and a shift register with no readback.
- TX1812: addressable LEDs from RMT symbols
LED timing generated as RMT symbols, with the strip sized at elaboration.
- Off-chip memory: NOR flash, EEPROM and FRAM
NOR flash, the 24C EEPROM catalogue and FRAM — three technologies, three bargains.
- TLV2556: a pipelined external ADC
A pipelined SPI ADC whose result belongs to the previous request.
- GPS: a background service, not a device handle
A background task decoding NMEA into a protected store that timestamps its own staleness.
- W5500: Ethernet with the stack on the chip
Ethernet with the TCP/IP stack in silicon, layered up to a GNAT.Sockets facade.
- The chip-neutral network stack
One GNAT.Sockets subset over several possible NICs, with longest-prefix routing and failover.
- DNS and NTP: portable by construction
DNS and NTP written against the socket facade, so the same source runs on host and board.
- TLS 1.3, in Ada, with no C library
A complete TLS 1.3 client in Ada: ECDHE, chain validation to a pinned root, resumption.
- Wi-Fi: pure Ada around three binary blobs
Pure Ada around three fetched Apache-2.0 blobs, with the WPA2 handshake kept out of them.
- Modbus TCP: master and slave
Industrial master and slave on the socket facade, owning none of your data.
- FTP: client and server
Outbound-only streamed transfers, and an anonymous server over your ext4 volumes.
- Block devices and wear levelling
One vtable the filesystems talk to, and a filter that spreads flash wear.
- ext4: a real filesystem, in Ada
A from-scratch ext2/3/4 with JBD2 replay, on-device mkfs, and Ada exceptions.
- FAT16: the filesystem a PC can read
Read-only, FAT16-only, long filenames — the filesystem a PC can mount.
- Console output, text and fonts
Formatted output with no hosted runtime, and glyph data that knows nothing about panels.
- Esp_Loader: your board as the programmer
Your board as the programmer: the ROM protocol, streamed, with per-chip quirks handled.
- SIMD: the PIE vector unit
128-bit PIE kernels in inline assembly — vendored, and honestly labelled beta.
- Stack usage: measuring what analysis cannot see
Stack painting: the measured counterpart to static analysis, conservative by design.
- Testing and proof: reproducing the claims
Thirty-two harnesses that run on your PC — cross-checked against the host's own tools, and SPARK-proven.
- The runtime: how it is built, ported and proven conformant
Where the three profiles come from, the rebuild trap, porting, and the ACATS grade.
- Debugging: GDB over the same cable
OpenOCD and GDB over the same USB cable, editor integration, and decoding a Guru Meditation.
- Troubleshooting, and where to go next
The failure modes worth recognising on sight, a cheat sheet, and where to read next.