Boards

The ESP32-S3-DevKitC-1

The ESP32-S3 on an Xtensa LX7 at 240 MHz: Espressif's own DevKitC-1, with its RGB LED on the RMT, two USB sockets, LEDC PWM and a twelve-bit ADC.

The overview, with examples running, is on the board page and The ESP32-S3 model (every register).

What it is

The ESP32-S3-DevKitC-1, Espressif's board for the ESP32-S3, running on our own Xtensa core. Firmware is an Xtensa ELF talking to the real ESP32-S3 addresses, and almost every one of those addresses was checked against Espressif's technical reference manual.

The board is 62.7 by 25.4 mm and its two rows of 22 pins are 0.9 inch apart, so it straddles a breadboard's center channel with one row of holes left free on each side, like the classic ESP32 DevKit.

The pins

Twenty-two down each long edge, the two micro-USB sockets at the bottom, the module and its antenna at the top. The silkscreen names each GPIO by its number.

J1, the left column, top to bottom: 3V3, 3V3, RST, 4, 5, 6, 7, 15, 16, 17, 18, 8, 3, 46, 9, 10, 11, 12, 13, 14, 5V, G.

J3, the right column, top to bottom: G, TX, RX, 1, 2, 42, 41, 40, 39, 38, 37, 36, 35, 0, 45, 48, 47, 21, 20, 19, G, G.

TX and RX are GPIO 43 and 44, UART0. RST is the chip enable. 5V is the USB rail. This is a 3.3 volt board and its pins are not 5 V tolerant.

Things the pinout does to you

  • GPIO 35, 36 and 37 belong to the PSRAM on the octal-PSRAM boards (the N8R8 most people buy). They are on the header, and on those boards you must not use them. Mokxi has no PSRAM, so here they work, which a desk might not.
  • GPIO 19 and 20 are the USB port's D- and D+. Drive them and the "USB" socket stops working on a real board. Here the USB serial port keeps going, which is kinder than the silicon.
  • GPIO 0, 3, 45 and 46 are strapping pins, so what is wired to them at power on matters. GPIO 0 is also the BOOT button.
  • Some pins power up with their input switched off. GPIO 4 to 8, 15, 16 and 19 to 21 read 0 until pinMode turns the input buffer on, which is the chip's reset state. pinMode always does it, so a sketch never notices.

The RGB LED

The board has no plain LED. It has an addressable RGB LED, a WS2812-type part, on GPIO 48 on the first boards and on GPIO 38 on v1.1, which Espressif lists as the only difference between the two revisions. The board's revision property picks which one is on your breadboard.

LED_BUILTIN on this board is not a GPIO number: as in the Arduino core, it is the GPIO count plus the LED's pin, and digitalWrite(LED_BUILTIN, HIGH) sends the LED a frame that lights it white at a quarter brightness. For color, rgbLedWrite(RGB_BUILTIN, red, green, blue). Both go out through the chip's RMT peripheral, 24 bits green first, every bit inside the WS2812B datasheet's timing, and the LED on the board lights with whatever color the frame carries.

The examples are built for GPIO 48, as the Arduino core's board definition is. On a v1.1 board the LED is on GPIO 38, so the same sketch leaves it dark, which is what would happen on a desk. The pin is also on the header, so a ws2812 strip wired to 48 sees the same frames.

analogRead: ten channels, and 2.9 V is the top

ADC1's ten channels are GPIO 1 to 10: channel n is GPIO n + 1. Twelve bits. At the default 12 dB attenuation the datasheet's measurement range ends at 2.9 V, and Mokxi reads 4095 from there up, so a potentiometer across the 3.3 V rail reads 4095 over the top eighth of its travel. The knob example prints the raw number so that is not a mystery.

That straight line up to 2.9 V is an approximation: a real S3 keeps a little headroom above the range and bends at both ends, corrected from calibration burned into the chip. ADC2 is not modeled.

analogWrite is LEDC, and it costs the core nothing

analogWrite(pin, 0..255) attaches one of LEDC's eight channels to the pin: eight bits at 1 kHz, on any GPIO, through the GPIO matrix. ledcAttach(pin, frequency, bits) and ledcWrite(pin, duty) are there when the frequency matters. On the S3, LEDC settings only take effect when an update bit is written, and the runtime writes it; a fading LED costs the core nothing.

Serial, and the two USB sockets

A real DevKitC-1 has two micro-USB sockets. "UART" goes through a USB-to-UART bridge to UART0: that is Serial, as in the Arduino core with USB CDC On Boot switched off, its default for this board. "USB" is the chip's own USB Serial/JTAG controller: that is USBSerial, which sends a 64-byte packet at a time, at the end of each line.

The serial monitor here listens to both at once, so either one prints in the same place, and what you type goes to both.

One core, not two

A real S3 has two Xtensa LX7 cores, and an Arduino sketch here runs on one. A sketch written around setup() and loop() never notices. Anything that starts a task on core 1 will not run.

A firmware you built yourself is different: MicroPython and an ESP-IDF build run on a model of the whole chip, where both cores do run (below).

What is modeled

The Xtensa LX7, which for everything a compiler emits from C is the classic ESP32's LX6 instruction set: the same windowed ABI, and the S3 compiler's output was checked byte for byte against the ESP32 compiler's. Around it: 416 KB of SRAM seen through both buses, the GPIO matrix and IO MUX with the manual's reset pulls on every pin, UART0, the USB Serial/JTAG serial port, SYSTIMER behind millis() and micros(), ADC1, LEDC, the RMT's four transmit channels, the clock gates, and as much of the interrupt matrix as four sources need.

WiFi, simulated

There is no radio. What there is is Mokxi's simulated network, the same one the classic ESP32 and the ESP32-C3 have, behind a block that is Mokxi's own rather than the chip's, at an address the S3 leaves unmapped. WiFi.h, HTTPClient.h and WebServer.h work the way a tutorial expects: the board joins an access point whose name and password are properties of the board, fetches a page from a made-up address under .mokxi, or serves one to the Web panel. There is no 802.11, no TCP, no TLS and no real internet. The one exception is the Real internet switch in the Cloud panel, which sends requests for other names out through Mokxi's gateway. Bluetooth LE is simulated the same way, with a phone in the editor's Bluetooth pane (Bluetooth, simulated).

WiFi, simulated is how to use it; Simulated WiFi is the contract.

What is not

A radio of any kind: WiFi and Bluetooth LE are simulated, with no radio under them. The second core. The FPU and the S3's vector instructions: both fault rather than returning a wrong number, and the runtime is soft float. SRAM0, the cache, flash and PSRAM. The RTC, the ULP coprocessors and deep sleep. ADC2, the touch sensor and the temperature sensor. The timer groups' timers (their watchdogs are modeled, for the task watchdog). UART1 and UART2, the I2C and SPI controllers, I2S, the camera interface, TWAI and USB OTG. Wire and SPI in the runtime are software on ordinary pins, as on every board here: SDA on 8, SCL on 9, and SPI on 10 to 13.

Vendor ESP-IDF and Arduino-core binaries will not run here: the runtime is ours.

Compiling your sketch

Edit the sketch and press Run: it compiles in the tab with Espressif's clang for Xtensa, the same toolchain as the classic ESP32 and the ESP8266 (clang-v3, about 80 MB, downloaded once). Nothing is uploaded. An untouched example runs its prebuilt program, built with Espressif's GCC for the S3; nothing of GCC is linked into it.

Floating-point arithmetic works: this model has no FPU, so the build does float and double sums in software, as on the boards with no FPU at all. Compiling in the browser has the full account.

The examples

Blink, Rainbow, Button, Serial, USB serial, Fade, Knob, and the four WiFi ones: WiFi: join the network, WiFi: fetch the weather, API lab: fetch JSON and WiFi: a lamp on a web page.

A firmware you built yourself

An Arduino sketch is built here, and it runs on one core. A program built somewhere else, with ESP-IDF or in Rust (esp-hal, esp-idf-hal), runs too, on a model of the whole chip with both cores: upload its .bin or .elf in the Firmware panel, and the board boots it as after idf.py flash. Wi-Fi and Bluetooth are the gap, since their drivers are Espressif's closed libraries: a firmware that starts either one stops with a line saying so. The steps and the whole list are in Run a prebuilt firmware.

MicroPython and CircuitPython

Pick MicroPython (main.py) in the board's Firmware panel and Run boots the official MicroPython v1.29.0 firmware for the ESP32-S3 with your main.py, on a model of the whole chip booted from Espressif's own ROM. machine.Pin, PWM, ADC, I2C, SPI, UART(1) and neopixel work, and network.WLAN joins the simulated network. See MicroPython on the ESP32.

Pick CircuitPython (code.py) instead and Run boots the official CircuitPython 10.3.1 firmware for this board, with its CIRCUITPY drive and its REPL on the S3's own USB port. WiFi is not simulated under CircuitPython here. See CircuitPython on the Pico, which covers this board too.

Elsewhere