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

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.

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 Mokxi runs one. A sketch written around setup() and loop() never notices. Anything that starts a task on core 1 will not run.

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. Bluetooth is not simulated at all.

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

What is not

A radio of any kind, and Bluetooth. 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 and the watchdogs. 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.

Avoid floating-point arithmetic: this model has no FPU, so a float multiply faults with a message in the serial monitor rather than returning a wrong number. Serial.print(1.5) works. 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.

Elsewhere