Skip to content
On this page

Lab: blink the ESP32-S3-DevKitC-1 RGB LED

This lab blinks the onboard addressable RGB LED of the ESP32-S3-DevKitC-1 through the Adafruit NeoPixel driver, with every version pinned so the compile is reproducible. Read the board identification guide first if you do not know your board revision: the LED GPIO depends on it.

What this experiment exercises

  • The addressable RGB LED wiring facts (GPIO38 on v1.1 / GPIO48 on v1.0) from the board record — a digitalWrite() cannot produce colors on this LED, see why the LED is on GPIO38 or GPIO48.
  • The pinned toolchain (Arduino core + driver library versions below) compiling the sketch for the ESP32-S3.
  • The first-flash path: flash via the USB-to-UART port following the first connection guide.

Pinned versions

ComponentVersionWhy pinned
BoardESP32-S3-DevKitC-1The lab reads its LED GPIO facts from the board record
Board revisionv1.1 (LED on GPIO38); BOARD_REV=10 selects the v1.0 wiring (GPIO48)The revision moved the LED; both constants are in the sketch
Compile targetFQBN esp32:esp32:esp32s3 (core default menu values; the blink sketch uses no PSRAM or non-default flash settings, so the binary is not tied to one ordering SKU)Stated exactly, not claimed for every SKU
arduino-cli1.5.1Toolchain entry point
arduino-esp32 core3.3.12SDK version the sketch compiles against
Adafruit_NeoPixel1.15.5LED driver, version-locked

The sketch

Source in the repository: site/labs/esp32-s3-blink-led/esp32-s3-blink-led.ino.

#include <Adafruit_NeoPixel.h>

// The RGB LED GPIO moved between board revisions; both constants come from
// the official Espressif user guides pinned on the board page.
constexpr int LED_PIN_V1_0 = 48;  // v1.0: GPIO48, J3 contact 16
constexpr int LED_PIN_V1_1 = 38;  // v1.1: GPIO38, J3 contact 10

// Select your board revision: 10 for v1.0, 11 for v1.1.
#ifndef BOARD_REV
#define BOARD_REV 11
#endif

#if BOARD_REV == 10
constexpr int LED_PIN = LED_PIN_V1_0;
#elif BOARD_REV == 11
constexpr int LED_PIN = LED_PIN_V1_1;
#else
#error "BOARD_REV must be 10 (v1.0, GPIO48) or 11 (v1.1, GPIO38)"
#endif

constexpr int LED_COUNT = 1;
constexpr unsigned long BLINK_INTERVAL_MS = 800;
constexpr uint32_t COLOR_ORDER = NEO_GRB + NEO_KHZ800;

Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, COLOR_ORDER);

const uint32_t COLORS[] = {
  Adafruit_NeoPixel::Color(20, 0, 0),   // dim red
  Adafruit_NeoPixel::Color(0, 20, 0),   // dim green
  Adafruit_NeoPixel::Color(0, 0, 20),   // dim blue
};
constexpr int COLOR_COUNT = sizeof(COLORS) / sizeof(COLORS[0]);

int colorIndex = 0;
bool ledOn = false;
unsigned long lastToggle = 0;

void setup() {
  strip.begin();
  strip.setBrightness(32);
  strip.show();  // start dark: init state must be explicit, not assumed
}

void loop() {
  unsigned long now = millis();
  if (now - lastToggle < BLINK_INTERVAL_MS) {
    return;
  }
  lastToggle = now;
  ledOn = !ledOn;
  if (ledOn) {
    strip.setPixelColor(0, COLORS[colorIndex % COLOR_COUNT]);
    colorIndex++;
  } else {
    strip.setPixelColor(0, 0);  // explicit off
  }
  strip.show();
}

The full sketch in the repository additionally documents the identification guide link, the toolchain table and the WS2812/digitalWrite caveat in its header comments.

Compile and flash

arduino-cli compile --fqbn esp32:esp32:esp32s3 site/labs/esp32-s3-blink-led
arduino-cli upload  --fqbn esp32:esp32:esp32s3 -p /dev/<your-usb-serial-port> site/labs/esp32-s3-blink-led

Compile receipt

Compiled on 2026-11-09 00:49 CST with the pinned toolchain below; both board revisions compiled in separate clean build paths, and a negative build proves the BOARD_REV macro actually reaches the sketch.

Toolchain actually used (versions read back from the build, not assumed):

ComponentVersionEvidence
arduino-cli1.5.1arduino-cli entry point
arduino-esp32 core3.3.12Used platform line: esp32:esp32 3.3.12
xtensa-esp-elf-gcc14.2.0 (crosstool-NG esp-14.2.0_20260121)xtensa-esp32s3-elf-gcc --version
Adafruit NeoPixel1.15.5Used library line
builtin ctags5.8-arduino11platform build dependency

Commands (one per board revision, separate build paths):

arduino-cli compile --fqbn esp32:esp32:esp32s3 \
  --build-property "compiler.cpp.extra_flags=-DBOARD_REV=11" \
  --build-path build/r11 site/labs/esp32-s3-blink-led

arduino-cli compile --fqbn esp32:esp32:esp32s3 \
  --build-property "compiler.cpp.extra_flags=-DBOARD_REV=10" \
  --build-path build/r10 site/labs/esp32-s3-blink-led

Results:

BuildExitSketch sizeRAMesp32-s3-blink-led.ino.bin SHA-256 (first 16)
BOARD_REV=11 (v1.1, LED on GPIO38)0289858 / 1310720 bytes (22%)22208 / 327680 (6%)3eb838864030eb06…
BOARD_REV=10 (v1.0, LED on GPIO48)0289858 / 1310720 bytes (22%)22208 / 327680 (6%)89d1e4bac804483f…
BOARD_REV=99 (negative control)1——no output

Macro evidence (the revision constant genuinely drives the binary, not just the source):

  • The two revisions produce different binaries (3eb83886… vs 89d1e4ba…) and different .elf files — the LED pin constant (GPIO38 vs GPIO48) differs in the compiled output.
  • The negative control -DBOARD_REV=99 fails the build at the sketch’s own guard: esp32-s3-blink-led.ino:37: error: #error "BOARD_REV must be 10 (v1.0, GPIO48) or 11 (v1.1, GPIO38)" — proving the macro reaches the translation unit.

Status: compiled (pinned toolchain), not flashed to hardware

Both board revisions compile cleanly with the pinned versions above — that is the verified claim. No hardware flashing or on-board observation happened: the blink result on a physical board has not been seen in this environment, and the upload command above remains the reader’s step. The LED GPIO facts, the addressable-LED driver choice and the revision caveats all come from the fixed official sources on the board page.