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.

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

  1. What you need (and what you don't)

    The board, the cable, the one package manager — and the four things you are not installing.

  2. Installing Alire and the toolchains

    Installing Alire and selecting the cross, native and build toolchains.

  3. Getting the code

    Cloning the repository with its two submodules, and what is in the tree.

  4. Plugging in the board and finding the port

    The native USB port, the device node, serial permissions, and forcing download mode.

  5. Your first blink

    One command builds, flashes and monitors a pure-Ada GPIO driver at 2 Hz.

  6. What happens between reset and Main

    Every layer between the mask ROM and your first line of Ada, and why Main can be empty.

  7. What a build actually does

    The five build steps, and the two Ada host tools that replace esptool.

  8. Choosing a runtime profile

    light-tasking, embedded, full — what each gives you and which to pick.

  9. Board configuration: board.ads

    Flash and PSRAM size in board.ads, and why PSRAM size rebuilds the bootloader.

  10. Your own project, outside the repo

    Scaffolding a standalone project anywhere on disk with export.sh and esp32-ada.

  11. Talking to the hardware: the HAL

    Using the peripheral drivers, how they are shaped, and what still needs verifying on your board.

  12. The examples: all 96 of them

    All 96 examples: what each shows, which profile it needs, and where it is explained.

  13. 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.

  14. I2C in depth

    An RAII session that cannot leak the bus lock, repeated START, and why payload length never reaches your code.

  15. SPI in depth

    Per-device clock and mode on a shared host, chip select three ways, and DMA rules enforced as preconditions.

  16. UART in depth

    No setup call by design, interrupt-driven RX with a buffer Ada makes you declare just so, and a routing trap.

  17. GDMA: the DMA engine everything else borrows

    Five channel pairs claimed at run time, and the buffer rules PSRAM's cache imposes.

  18. I2S: audio that only moves by DMA

    Audio with no CPU FIFO: DMA-only transfers, gapless looping, and capture underneath playback.

  19. LCD: two very different display modes

    A command-driven i8080 bus and a continuously-refreshed RGB panel from one controller.

  20. 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.

  21. RMT: an arbitrary pulse generator

    Arbitrary {level, duration} pulse trains for IR, WS2812 and 1-Wire.

  22. LEDC and sigma-delta: the simple outputs

    Eight PWM channels for dimming, and eight density-modulated outputs that filter to analog.

  23. MCPWM: PWM that can shut itself down

    Dead-time, a chopper carrier, and fault inputs that force the pins safe in hardware.

  24. Timers and pulse counting

    A 54-bit timer with an alarm, and four edge counters that wrap sooner than you think.

  25. Analog in: the SAR ADC and capacitive touch

    The SAR ADC on fixed pins, and touch channels that count their way to a reading.

  26. RTC, pad hold and deep sleep

    Deep sleep resets the chip; what survives is RTC memory and the pads you held.

  27. Hardware crypto, and one honest caveat

    SHA, AES and RSA acceleration, MD5 for flash verification, and why the RNG is not a CSPRNG here.

  28. SD cards: two hosts, one API shape

    SPI transport versus the native SD bus, and why the faster one runs on the lean runtime.

  29. Chip identity: die temperature and the eFuse MAC

    Die temperature (not ambient) and the four factory MACs in eFuse.

  30. ST7789 display and GT911 touch

    A write-only SPI panel you cannot probe, and a touch chip whose address is set at reset.

  31. ES8311: the audio codec

    Control over I2C, audio over I2S, and the 256x MCLK ratio the codec depends on.

  32. Sensors: the QMI8658C IMU and SHT41

    A register-mapped IMU and a command-based humidity sensor, and how each flags a bad reading.

  33. 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.

  34. Port expanders: TCA9555, CH422G and HC595

    Per-pin control, an all-or-nothing direction bit, and a shift register with no readback.

  35. TX1812: addressable LEDs from RMT symbols

    LED timing generated as RMT symbols, with the strip sized at elaboration.

  36. Off-chip memory: NOR flash, EEPROM and FRAM

    NOR flash, the 24C EEPROM catalogue and FRAM — three technologies, three bargains.

  37. TLV2556: a pipelined external ADC

    A pipelined SPI ADC whose result belongs to the previous request.

  38. GPS: a background service, not a device handle

    A background task decoding NMEA into a protected store that timestamps its own staleness.

  39. W5500: Ethernet with the stack on the chip

    Ethernet with the TCP/IP stack in silicon, layered up to a GNAT.Sockets facade.

  40. The chip-neutral network stack

    One GNAT.Sockets subset over several possible NICs, with longest-prefix routing and failover.

  41. DNS and NTP: portable by construction

    DNS and NTP written against the socket facade, so the same source runs on host and board.

  42. 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.

  43. 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.

  44. Modbus TCP: master and slave

    Industrial master and slave on the socket facade, owning none of your data.

  45. FTP: client and server

    Outbound-only streamed transfers, and an anonymous server over your ext4 volumes.

  46. Block devices and wear levelling

    One vtable the filesystems talk to, and a filter that spreads flash wear.

  47. ext4: a real filesystem, in Ada

    A from-scratch ext2/3/4 with JBD2 replay, on-device mkfs, and Ada exceptions.

  48. FAT16: the filesystem a PC can read

    Read-only, FAT16-only, long filenames — the filesystem a PC can mount.

  49. Console output, text and fonts

    Formatted output with no hosted runtime, and glyph data that knows nothing about panels.

  50. Esp_Loader: your board as the programmer

    Your board as the programmer: the ROM protocol, streamed, with per-chip quirks handled.

  51. SIMD: the PIE vector unit

    128-bit PIE kernels in inline assembly — vendored, and honestly labelled beta.

  52. Stack usage: measuring what analysis cannot see

    Stack painting: the measured counterpart to static analysis, conservative by design.

  53. 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.

  54. The runtime: how it is built, ported and proven conformant

    Where the three profiles come from, the rebuild trap, porting, and the ACATS grade.

  55. Debugging: GDB over the same cable

    OpenOCD and GDB over the same USB cable, editor integration, and decoding a Guru Meditation.

  56. Troubleshooting, and where to go next

    The failure modes worth recognising on sight, a cheat sheet, and where to read next.

Start here · Bare-Metal Ada on the ESP32-S3
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.

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

  1. What you need (and what you don't)

    The board, the cable, the one package manager — and the four things you are not installing.

  2. Installing Alire and the toolchains

    Installing Alire and selecting the cross, native and build toolchains.

  3. Getting the code

    Cloning the repository with its two submodules, and what is in the tree.

  4. Plugging in the board and finding the port

    The native USB port, the device node, serial permissions, and forcing download mode.

  5. Your first blink

    One command builds, flashes and monitors a pure-Ada GPIO driver at 2 Hz.

  6. What happens between reset and Main

    Every layer between the mask ROM and your first line of Ada, and why Main can be empty.

  7. What a build actually does

    The five build steps, and the two Ada host tools that replace esptool.

  8. Choosing a runtime profile

    light-tasking, embedded, full — what each gives you and which to pick.

  9. Board configuration: board.ads

    Flash and PSRAM size in board.ads, and why PSRAM size rebuilds the bootloader.

  10. Your own project, outside the repo

    Scaffolding a standalone project anywhere on disk with export.sh and esp32-ada.

  11. Talking to the hardware: the HAL

    Using the peripheral drivers, how they are shaped, and what still needs verifying on your board.

  12. The examples: all 96 of them

    All 96 examples: what each shows, which profile it needs, and where it is explained.

  13. 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.

  14. I2C in depth

    An RAII session that cannot leak the bus lock, repeated START, and why payload length never reaches your code.

  15. SPI in depth

    Per-device clock and mode on a shared host, chip select three ways, and DMA rules enforced as preconditions.

  16. UART in depth

    No setup call by design, interrupt-driven RX with a buffer Ada makes you declare just so, and a routing trap.

  17. GDMA: the DMA engine everything else borrows

    Five channel pairs claimed at run time, and the buffer rules PSRAM's cache imposes.

  18. I2S: audio that only moves by DMA

    Audio with no CPU FIFO: DMA-only transfers, gapless looping, and capture underneath playback.

  19. LCD: two very different display modes

    A command-driven i8080 bus and a continuously-refreshed RGB panel from one controller.

  20. 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.

  21. RMT: an arbitrary pulse generator

    Arbitrary {level, duration} pulse trains for IR, WS2812 and 1-Wire.

  22. LEDC and sigma-delta: the simple outputs

    Eight PWM channels for dimming, and eight density-modulated outputs that filter to analog.

  23. MCPWM: PWM that can shut itself down

    Dead-time, a chopper carrier, and fault inputs that force the pins safe in hardware.

  24. Timers and pulse counting

    A 54-bit timer with an alarm, and four edge counters that wrap sooner than you think.

  25. Analog in: the SAR ADC and capacitive touch

    The SAR ADC on fixed pins, and touch channels that count their way to a reading.

  26. RTC, pad hold and deep sleep

    Deep sleep resets the chip; what survives is RTC memory and the pads you held.

  27. Hardware crypto, and one honest caveat

    SHA, AES and RSA acceleration, MD5 for flash verification, and why the RNG is not a CSPRNG here.

  28. SD cards: two hosts, one API shape

    SPI transport versus the native SD bus, and why the faster one runs on the lean runtime.

  29. Chip identity: die temperature and the eFuse MAC

    Die temperature (not ambient) and the four factory MACs in eFuse.

  30. ST7789 display and GT911 touch

    A write-only SPI panel you cannot probe, and a touch chip whose address is set at reset.

  31. ES8311: the audio codec

    Control over I2C, audio over I2S, and the 256x MCLK ratio the codec depends on.

  32. Sensors: the QMI8658C IMU and SHT41

    A register-mapped IMU and a command-based humidity sensor, and how each flags a bad reading.

  33. 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.

  34. Port expanders: TCA9555, CH422G and HC595

    Per-pin control, an all-or-nothing direction bit, and a shift register with no readback.

  35. TX1812: addressable LEDs from RMT symbols

    LED timing generated as RMT symbols, with the strip sized at elaboration.

  36. Off-chip memory: NOR flash, EEPROM and FRAM

    NOR flash, the 24C EEPROM catalogue and FRAM — three technologies, three bargains.

  37. TLV2556: a pipelined external ADC

    A pipelined SPI ADC whose result belongs to the previous request.

  38. GPS: a background service, not a device handle

    A background task decoding NMEA into a protected store that timestamps its own staleness.

  39. W5500: Ethernet with the stack on the chip

    Ethernet with the TCP/IP stack in silicon, layered up to a GNAT.Sockets facade.

  40. The chip-neutral network stack

    One GNAT.Sockets subset over several possible NICs, with longest-prefix routing and failover.

  41. DNS and NTP: portable by construction

    DNS and NTP written against the socket facade, so the same source runs on host and board.

  42. 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.

  43. 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.

  44. Modbus TCP: master and slave

    Industrial master and slave on the socket facade, owning none of your data.

  45. FTP: client and server

    Outbound-only streamed transfers, and an anonymous server over your ext4 volumes.

  46. Block devices and wear levelling

    One vtable the filesystems talk to, and a filter that spreads flash wear.

  47. ext4: a real filesystem, in Ada

    A from-scratch ext2/3/4 with JBD2 replay, on-device mkfs, and Ada exceptions.

  48. FAT16: the filesystem a PC can read

    Read-only, FAT16-only, long filenames — the filesystem a PC can mount.

  49. Console output, text and fonts

    Formatted output with no hosted runtime, and glyph data that knows nothing about panels.

  50. Esp_Loader: your board as the programmer

    Your board as the programmer: the ROM protocol, streamed, with per-chip quirks handled.

  51. SIMD: the PIE vector unit

    128-bit PIE kernels in inline assembly — vendored, and honestly labelled beta.

  52. Stack usage: measuring what analysis cannot see

    Stack painting: the measured counterpart to static analysis, conservative by design.

  53. 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.

  54. The runtime: how it is built, ported and proven conformant

    Where the three profiles come from, the rebuild trap, porting, and the ACATS grade.

  55. Debugging: GDB over the same cable

    OpenOCD and GDB over the same USB cable, editor integration, and decoding a Guru Meditation.

  56. Troubleshooting, and where to go next

    The failure modes worth recognising on sight, a cheat sheet, and where to read next.

Start here · Bare-Metal Ada on the ESP32-S3
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.

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

  1. What you need (and what you don't)

    The board, the cable, the one package manager — and the four things you are not installing.

  2. Installing Alire and the toolchains

    Installing Alire and selecting the cross, native and build toolchains.

  3. Getting the code

    Cloning the repository with its two submodules, and what is in the tree.

  4. Plugging in the board and finding the port

    The native USB port, the device node, serial permissions, and forcing download mode.

  5. Your first blink

    One command builds, flashes and monitors a pure-Ada GPIO driver at 2 Hz.

  6. What happens between reset and Main

    Every layer between the mask ROM and your first line of Ada, and why Main can be empty.

  7. What a build actually does

    The five build steps, and the two Ada host tools that replace esptool.

  8. Choosing a runtime profile

    light-tasking, embedded, full — what each gives you and which to pick.

  9. Board configuration: board.ads

    Flash and PSRAM size in board.ads, and why PSRAM size rebuilds the bootloader.

  10. Your own project, outside the repo

    Scaffolding a standalone project anywhere on disk with export.sh and esp32-ada.

  11. Talking to the hardware: the HAL

    Using the peripheral drivers, how they are shaped, and what still needs verifying on your board.

  12. The examples: all 96 of them

    All 96 examples: what each shows, which profile it needs, and where it is explained.

  13. 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.

  14. I2C in depth

    An RAII session that cannot leak the bus lock, repeated START, and why payload length never reaches your code.

  15. SPI in depth

    Per-device clock and mode on a shared host, chip select three ways, and DMA rules enforced as preconditions.

  16. UART in depth

    No setup call by design, interrupt-driven RX with a buffer Ada makes you declare just so, and a routing trap.

  17. GDMA: the DMA engine everything else borrows

    Five channel pairs claimed at run time, and the buffer rules PSRAM's cache imposes.

  18. I2S: audio that only moves by DMA

    Audio with no CPU FIFO: DMA-only transfers, gapless looping, and capture underneath playback.

  19. LCD: two very different display modes

    A command-driven i8080 bus and a continuously-refreshed RGB panel from one controller.

  20. 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.

  21. RMT: an arbitrary pulse generator

    Arbitrary {level, duration} pulse trains for IR, WS2812 and 1-Wire.

  22. LEDC and sigma-delta: the simple outputs

    Eight PWM channels for dimming, and eight density-modulated outputs that filter to analog.

  23. MCPWM: PWM that can shut itself down

    Dead-time, a chopper carrier, and fault inputs that force the pins safe in hardware.

  24. Timers and pulse counting

    A 54-bit timer with an alarm, and four edge counters that wrap sooner than you think.

  25. Analog in: the SAR ADC and capacitive touch

    The SAR ADC on fixed pins, and touch channels that count their way to a reading.

  26. RTC, pad hold and deep sleep

    Deep sleep resets the chip; what survives is RTC memory and the pads you held.

  27. Hardware crypto, and one honest caveat

    SHA, AES and RSA acceleration, MD5 for flash verification, and why the RNG is not a CSPRNG here.

  28. SD cards: two hosts, one API shape

    SPI transport versus the native SD bus, and why the faster one runs on the lean runtime.

  29. Chip identity: die temperature and the eFuse MAC

    Die temperature (not ambient) and the four factory MACs in eFuse.

  30. ST7789 display and GT911 touch

    A write-only SPI panel you cannot probe, and a touch chip whose address is set at reset.

  31. ES8311: the audio codec

    Control over I2C, audio over I2S, and the 256x MCLK ratio the codec depends on.

  32. Sensors: the QMI8658C IMU and SHT41

    A register-mapped IMU and a command-based humidity sensor, and how each flags a bad reading.

  33. 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.

  34. Port expanders: TCA9555, CH422G and HC595

    Per-pin control, an all-or-nothing direction bit, and a shift register with no readback.

  35. TX1812: addressable LEDs from RMT symbols

    LED timing generated as RMT symbols, with the strip sized at elaboration.

  36. Off-chip memory: NOR flash, EEPROM and FRAM

    NOR flash, the 24C EEPROM catalogue and FRAM — three technologies, three bargains.

  37. TLV2556: a pipelined external ADC

    A pipelined SPI ADC whose result belongs to the previous request.

  38. GPS: a background service, not a device handle

    A background task decoding NMEA into a protected store that timestamps its own staleness.

  39. W5500: Ethernet with the stack on the chip

    Ethernet with the TCP/IP stack in silicon, layered up to a GNAT.Sockets facade.

  40. The chip-neutral network stack

    One GNAT.Sockets subset over several possible NICs, with longest-prefix routing and failover.

  41. DNS and NTP: portable by construction

    DNS and NTP written against the socket facade, so the same source runs on host and board.

  42. 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.

  43. 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.

  44. Modbus TCP: master and slave

    Industrial master and slave on the socket facade, owning none of your data.

  45. FTP: client and server

    Outbound-only streamed transfers, and an anonymous server over your ext4 volumes.

  46. Block devices and wear levelling

    One vtable the filesystems talk to, and a filter that spreads flash wear.

  47. ext4: a real filesystem, in Ada

    A from-scratch ext2/3/4 with JBD2 replay, on-device mkfs, and Ada exceptions.

  48. FAT16: the filesystem a PC can read

    Read-only, FAT16-only, long filenames — the filesystem a PC can mount.

  49. Console output, text and fonts

    Formatted output with no hosted runtime, and glyph data that knows nothing about panels.

  50. Esp_Loader: your board as the programmer

    Your board as the programmer: the ROM protocol, streamed, with per-chip quirks handled.

  51. SIMD: the PIE vector unit

    128-bit PIE kernels in inline assembly — vendored, and honestly labelled beta.

  52. Stack usage: measuring what analysis cannot see

    Stack painting: the measured counterpart to static analysis, conservative by design.

  53. 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.

  54. The runtime: how it is built, ported and proven conformant

    Where the three profiles come from, the rebuild trap, porting, and the ACATS grade.

  55. Debugging: GDB over the same cable

    OpenOCD and GDB over the same USB cable, editor integration, and decoding a Guru Meditation.

  56. Troubleshooting, and where to go next

    The failure modes worth recognising on sight, a cheat sheet, and where to read next.

Start here · Bare-Metal Ada on the ESP32-S3
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.

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

  1. What you need (and what you don't)

    The board, the cable, the one package manager — and the four things you are not installing.

  2. Installing Alire and the toolchains

    Installing Alire and selecting the cross, native and build toolchains.

  3. Getting the code

    Cloning the repository with its two submodules, and what is in the tree.

  4. Plugging in the board and finding the port

    The native USB port, the device node, serial permissions, and forcing download mode.

  5. Your first blink

    One command builds, flashes and monitors a pure-Ada GPIO driver at 2 Hz.

  6. What happens between reset and Main

    Every layer between the mask ROM and your first line of Ada, and why Main can be empty.

  7. What a build actually does

    The five build steps, and the two Ada host tools that replace esptool.

  8. Choosing a runtime profile

    light-tasking, embedded, full — what each gives you and which to pick.

  9. Board configuration: board.ads

    Flash and PSRAM size in board.ads, and why PSRAM size rebuilds the bootloader.

  10. Your own project, outside the repo

    Scaffolding a standalone project anywhere on disk with export.sh and esp32-ada.

  11. Talking to the hardware: the HAL

    Using the peripheral drivers, how they are shaped, and what still needs verifying on your board.

  12. The examples: all 96 of them

    All 96 examples: what each shows, which profile it needs, and where it is explained.

  13. 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.

  14. I2C in depth

    An RAII session that cannot leak the bus lock, repeated START, and why payload length never reaches your code.

  15. SPI in depth

    Per-device clock and mode on a shared host, chip select three ways, and DMA rules enforced as preconditions.

  16. UART in depth

    No setup call by design, interrupt-driven RX with a buffer Ada makes you declare just so, and a routing trap.

  17. GDMA: the DMA engine everything else borrows

    Five channel pairs claimed at run time, and the buffer rules PSRAM's cache imposes.

  18. I2S: audio that only moves by DMA

    Audio with no CPU FIFO: DMA-only transfers, gapless looping, and capture underneath playback.

  19. LCD: two very different display modes

    A command-driven i8080 bus and a continuously-refreshed RGB panel from one controller.

  20. 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.

  21. RMT: an arbitrary pulse generator

    Arbitrary {level, duration} pulse trains for IR, WS2812 and 1-Wire.

  22. LEDC and sigma-delta: the simple outputs

    Eight PWM channels for dimming, and eight density-modulated outputs that filter to analog.

  23. MCPWM: PWM that can shut itself down

    Dead-time, a chopper carrier, and fault inputs that force the pins safe in hardware.

  24. Timers and pulse counting

    A 54-bit timer with an alarm, and four edge counters that wrap sooner than you think.

  25. Analog in: the SAR ADC and capacitive touch

    The SAR ADC on fixed pins, and touch channels that count their way to a reading.

  26. RTC, pad hold and deep sleep

    Deep sleep resets the chip; what survives is RTC memory and the pads you held.

  27. Hardware crypto, and one honest caveat

    SHA, AES and RSA acceleration, MD5 for flash verification, and why the RNG is not a CSPRNG here.

  28. SD cards: two hosts, one API shape

    SPI transport versus the native SD bus, and why the faster one runs on the lean runtime.

  29. Chip identity: die temperature and the eFuse MAC

    Die temperature (not ambient) and the four factory MACs in eFuse.

  30. ST7789 display and GT911 touch

    A write-only SPI panel you cannot probe, and a touch chip whose address is set at reset.

  31. ES8311: the audio codec

    Control over I2C, audio over I2S, and the 256x MCLK ratio the codec depends on.

  32. Sensors: the QMI8658C IMU and SHT41

    A register-mapped IMU and a command-based humidity sensor, and how each flags a bad reading.

  33. 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.

  34. Port expanders: TCA9555, CH422G and HC595

    Per-pin control, an all-or-nothing direction bit, and a shift register with no readback.

  35. TX1812: addressable LEDs from RMT symbols

    LED timing generated as RMT symbols, with the strip sized at elaboration.

  36. Off-chip memory: NOR flash, EEPROM and FRAM

    NOR flash, the 24C EEPROM catalogue and FRAM — three technologies, three bargains.

  37. TLV2556: a pipelined external ADC

    A pipelined SPI ADC whose result belongs to the previous request.

  38. GPS: a background service, not a device handle

    A background task decoding NMEA into a protected store that timestamps its own staleness.

  39. W5500: Ethernet with the stack on the chip

    Ethernet with the TCP/IP stack in silicon, layered up to a GNAT.Sockets facade.

  40. The chip-neutral network stack

    One GNAT.Sockets subset over several possible NICs, with longest-prefix routing and failover.

  41. DNS and NTP: portable by construction

    DNS and NTP written against the socket facade, so the same source runs on host and board.

  42. 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.

  43. 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.

  44. Modbus TCP: master and slave

    Industrial master and slave on the socket facade, owning none of your data.

  45. FTP: client and server

    Outbound-only streamed transfers, and an anonymous server over your ext4 volumes.

  46. Block devices and wear levelling

    One vtable the filesystems talk to, and a filter that spreads flash wear.

  47. ext4: a real filesystem, in Ada

    A from-scratch ext2/3/4 with JBD2 replay, on-device mkfs, and Ada exceptions.

  48. FAT16: the filesystem a PC can read

    Read-only, FAT16-only, long filenames — the filesystem a PC can mount.

  49. Console output, text and fonts

    Formatted output with no hosted runtime, and glyph data that knows nothing about panels.

  50. Esp_Loader: your board as the programmer

    Your board as the programmer: the ROM protocol, streamed, with per-chip quirks handled.

  51. SIMD: the PIE vector unit

    128-bit PIE kernels in inline assembly — vendored, and honestly labelled beta.

  52. Stack usage: measuring what analysis cannot see

    Stack painting: the measured counterpart to static analysis, conservative by design.

  53. 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.

  54. The runtime: how it is built, ported and proven conformant

    Where the three profiles come from, the rebuild trap, porting, and the ACATS grade.

  55. Debugging: GDB over the same cable

    OpenOCD and GDB over the same USB cable, editor integration, and decoding a Guru Meditation.

  56. Troubleshooting, and where to go next

    The failure modes worth recognising on sight, a cheat sheet, and where to read next.

Start here · Bare-Metal Ada on the ESP32-S3
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.

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

  1. What you need (and what you don't)

    The board, the cable, the one package manager — and the four things you are not installing.

  2. Installing Alire and the toolchains

    Installing Alire and selecting the cross, native and build toolchains.

  3. Getting the code

    Cloning the repository with its two submodules, and what is in the tree.

  4. Plugging in the board and finding the port

    The native USB port, the device node, serial permissions, and forcing download mode.

  5. Your first blink

    One command builds, flashes and monitors a pure-Ada GPIO driver at 2 Hz.

  6. What happens between reset and Main

    Every layer between the mask ROM and your first line of Ada, and why Main can be empty.

  7. What a build actually does

    The five build steps, and the two Ada host tools that replace esptool.

  8. Choosing a runtime profile

    light-tasking, embedded, full — what each gives you and which to pick.

  9. Board configuration: board.ads

    Flash and PSRAM size in board.ads, and why PSRAM size rebuilds the bootloader.

  10. Your own project, outside the repo

    Scaffolding a standalone project anywhere on disk with export.sh and esp32-ada.

  11. Talking to the hardware: the HAL

    Using the peripheral drivers, how they are shaped, and what still needs verifying on your board.

  12. The examples: all 96 of them

    All 96 examples: what each shows, which profile it needs, and where it is explained.

  13. 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.

  14. I2C in depth

    An RAII session that cannot leak the bus lock, repeated START, and why payload length never reaches your code.

  15. SPI in depth

    Per-device clock and mode on a shared host, chip select three ways, and DMA rules enforced as preconditions.

  16. UART in depth

    No setup call by design, interrupt-driven RX with a buffer Ada makes you declare just so, and a routing trap.

  17. GDMA: the DMA engine everything else borrows

    Five channel pairs claimed at run time, and the buffer rules PSRAM's cache imposes.

  18. I2S: audio that only moves by DMA

    Audio with no CPU FIFO: DMA-only transfers, gapless looping, and capture underneath playback.

  19. LCD: two very different display modes

    A command-driven i8080 bus and a continuously-refreshed RGB panel from one controller.

  20. 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.

  21. RMT: an arbitrary pulse generator

    Arbitrary {level, duration} pulse trains for IR, WS2812 and 1-Wire.

  22. LEDC and sigma-delta: the simple outputs

    Eight PWM channels for dimming, and eight density-modulated outputs that filter to analog.

  23. MCPWM: PWM that can shut itself down

    Dead-time, a chopper carrier, and fault inputs that force the pins safe in hardware.

  24. Timers and pulse counting

    A 54-bit timer with an alarm, and four edge counters that wrap sooner than you think.

  25. Analog in: the SAR ADC and capacitive touch

    The SAR ADC on fixed pins, and touch channels that count their way to a reading.

  26. RTC, pad hold and deep sleep

    Deep sleep resets the chip; what survives is RTC memory and the pads you held.

  27. Hardware crypto, and one honest caveat

    SHA, AES and RSA acceleration, MD5 for flash verification, and why the RNG is not a CSPRNG here.

  28. SD cards: two hosts, one API shape

    SPI transport versus the native SD bus, and why the faster one runs on the lean runtime.

  29. Chip identity: die temperature and the eFuse MAC

    Die temperature (not ambient) and the four factory MACs in eFuse.

  30. ST7789 display and GT911 touch

    A write-only SPI panel you cannot probe, and a touch chip whose address is set at reset.

  31. ES8311: the audio codec

    Control over I2C, audio over I2S, and the 256x MCLK ratio the codec depends on.

  32. Sensors: the QMI8658C IMU and SHT41

    A register-mapped IMU and a command-based humidity sensor, and how each flags a bad reading.

  33. 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.

  34. Port expanders: TCA9555, CH422G and HC595

    Per-pin control, an all-or-nothing direction bit, and a shift register with no readback.

  35. TX1812: addressable LEDs from RMT symbols

    LED timing generated as RMT symbols, with the strip sized at elaboration.

  36. Off-chip memory: NOR flash, EEPROM and FRAM

    NOR flash, the 24C EEPROM catalogue and FRAM — three technologies, three bargains.

  37. TLV2556: a pipelined external ADC

    A pipelined SPI ADC whose result belongs to the previous request.

  38. GPS: a background service, not a device handle

    A background task decoding NMEA into a protected store that timestamps its own staleness.

  39. W5500: Ethernet with the stack on the chip

    Ethernet with the TCP/IP stack in silicon, layered up to a GNAT.Sockets facade.

  40. The chip-neutral network stack

    One GNAT.Sockets subset over several possible NICs, with longest-prefix routing and failover.

  41. DNS and NTP: portable by construction

    DNS and NTP written against the socket facade, so the same source runs on host and board.

  42. 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.

  43. 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.

  44. Modbus TCP: master and slave

    Industrial master and slave on the socket facade, owning none of your data.

  45. FTP: client and server

    Outbound-only streamed transfers, and an anonymous server over your ext4 volumes.

  46. Block devices and wear levelling

    One vtable the filesystems talk to, and a filter that spreads flash wear.

  47. ext4: a real filesystem, in Ada

    A from-scratch ext2/3/4 with JBD2 replay, on-device mkfs, and Ada exceptions.

  48. FAT16: the filesystem a PC can read

    Read-only, FAT16-only, long filenames — the filesystem a PC can mount.

  49. Console output, text and fonts

    Formatted output with no hosted runtime, and glyph data that knows nothing about panels.

  50. Esp_Loader: your board as the programmer

    Your board as the programmer: the ROM protocol, streamed, with per-chip quirks handled.

  51. SIMD: the PIE vector unit

    128-bit PIE kernels in inline assembly — vendored, and honestly labelled beta.

  52. Stack usage: measuring what analysis cannot see

    Stack painting: the measured counterpart to static analysis, conservative by design.

  53. 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.

  54. The runtime: how it is built, ported and proven conformant

    Where the three profiles come from, the rebuild trap, porting, and the ACATS grade.

  55. Debugging: GDB over the same cable

    OpenOCD and GDB over the same USB cable, editor integration, and decoding a Guru Meditation.

  56. Troubleshooting, and where to go next

    The failure modes worth recognising on sight, a cheat sheet, and where to read next.