AI-assisted ESP-IDF + LVGL firmware for the JC3636K518C round knob display
A 1.8” round 360×360 IPS display with a rotary knob around it, driven by an ESP32-S3R8 (dual-core 240 MHz, 8 MB octal PSRAM, 16 MB QSPI flash). A second MCU, an ESP32-U4WDH “sidecar”, runs the vendor’s Bluetooth audio firmware. Our firmware targets the S3 only.
Source of truth for every number below: the Waveshare schematic and ESP-IDF demos, cross-checked
on a real unit (I2C scan, SD mount, knob events, touch, mic, backlight, haptics). The full pin
list is in references/pinmap.md; the same values live as macros in
templates/knob-starter/components/board/include/board_jc3636k518c.h, which is the only
place pins may appear in code.
| Subsystem | Part | Bus | Notes |
|---|---|---|---|
| Display | ST77916, 360×360 RGB565 | QSPI on SPI2, 80 MHz | Colours inverted in the init table; TE not wired |
| Touch | CST816D (chip ID 0xB6) | I2C0 @ 0x15 | Shares the bus with the haptic driver |
| Haptics | DRV2605L + LRA | I2C0 @ 0x5A | I2C-only control, ~120 library effects |
| Knob | 2 detector switches (SSCM110100) | 2 GPIO | One switch pair on the S3, the other on the sidecar |
| microSD | TF slot, SDMMC 4-bit | 6 GPIO | Shipped 480 MB card; units vary in quality |
| Microphone | MSM261D4030H1CPM PDM | 2 GPIO | Mono, 16 kHz works |
| Audio out | PCM5100A I2S DAC, line-level | 3 GPIO + mux | 3.5 mm jack only; no amplifier, no speaker |
| Backlight | AO3400A low-side switch | GPIO47 | 10 kΩ pull-down: dark until driven; LEDC PWM at 20 kHz works |
| Battery sense | 10k/10k divider from the 5 V rail | GPIO1 (ADC1_CH0) | Reads the system rail ×2, not the cell |
| USB | One USB-C, D+/D− to both chips | — | Orientation picks the chip (below) |
| GPIO | Why |
|---|---|
| 0 | Triple-booked: BOOT strap, select line of the CH445P I2S mux (high = S3 owns the DAC), and exposed on the 10-pin header. On-board pull-up. Never configure or drive it. |
| 38, 48 | UART to the sidecar (S3 TX 38, RX 48). Reserved. |
| 39, 40, 41 | I2S to the DAC (BCK, WS, DOUT). Unused by the starter; reserved. |
| 45, 46 | Mic clock and data. Both are S3 strapping pins; as inputs they are fine. Never pull 46 up at boot. |
| 19, 20 | Native USB D−/D+. |
The sidecar owns the DAC soft-mute line, so line-out audio from the S3 only works if the stock sidecar firmware leaves the DAC unmuted. Treat the sidecar as opaque.
One USB-C socket, two USB data paths. Flip the plug to switch.
| Enumerates as | Chip | Port | Use |
|---|---|---|---|
303a:1001 Espressif USB JTAG/serial |
ESP32-S3 | /dev/ttyACM0 |
Flash and monitor our firmware here |
1a86:7523 CH340 USB-UART |
ESP32 sidecar | /dev/ttyUSB0 |
Never flash the S3 image here |
If lsusb shows 1a86:7523, flip the plug.
--no-stub)The S3 enumerates as native USB-Serial/JTAG, and the esptool flasher stub stalls on it
(“Packet content transfer stopped”). Baud is ignored over USB CDC. Always pass --no-stub.
Flash from the host, never from inside the Docker build container (it has no serial access).
lsusb | grep 303a && ls /dev/ttyACM0 # confirm the S3 side is up
esptool --chip esp32s3 --port /dev/ttyACM0 --no-stub \
--before default_reset --after hard_reset write_flash \
--flash_mode dio --flash_size 16MB --flash_freq 80m \
0x0 build/bootloader/bootloader.bin \
0x8000 build/partition_table/partition-table.bin \
0x10000 build/knob_starter.bin
Success ends with Hash of data verified. and Hard resetting via RTS pin.... After the first
full flash, an app-only flash (just the 0x10000 image) is enough for code changes.
templates/knob-starter/scripts/flash.sh wraps this.
esptool --chip esp32s3 --port /dev/ttyACM0 --no-stub read_flash 0 0x1000000 stock.bin.idf.py monitor -p /dev/ttyACM0 from the host or any serial terminal at 115200.espressif/knob read zero. Poll both pins every 3 ms and
decode with a small debounced state machine (knob_decode.c in the template). On this board
only one switch pair reaches the S3 (GPIO8/7); that is enough for direction.0x21; do not also call esp_lcd_panel_invert_color(). Byte swapping (swap_bytes) is a
separate axis and is still required for RGB565 over SPI.ui_panel_init_cmds.c in the template is the
proven ST77916 sequence (the ESPHome port also needed an explicit 0x3A 0x55 for RGB565).embedded-constraints skill.swap_bytes on, no extra invert → correct colours.