A working PlatformIO firmware for the Waveshare ESP32-S3-Knob-Touch-LCD-1.8 that demonstrates concurrent touch and rotary-encoder input with LVGL.
- 360×360 SH8601 AMOLED display via QSPI (Arduino_GFX)
- CST816 capacitive touch — drag the arc with your finger
- Rotary encoder — turn the knob to change the arc value
- LVGL 8.x rendering with correct tick handling
This board has two USB-C ports — one for the ESP32-S3 (programming) and one for power only. More importantly:
If flashing fails (no port found / upload error), flip your USB-C cable 180° and try again.
The USB-C connector on the ESP32-S3 side is not orientation-symmetric for data on this board. One orientation targets the ESP32-S3 for programming; the other does not.
The rotary knob uses two independent micro-switches, not a standard quadrature encoder. Each switch fires independently:
- Switch A closes → clockwise step
- Switch B closes → counter-clockwise step
Standard quadrature decoding algorithms will not work with this hardware. See src/main.cpp for the correct polling algorithm.
| Peripheral | Pin |
|---|---|
| LCD QSPI CS | 14 |
| LCD QSPI CLK | 13 |
| LCD QSPI D0–D3 | 15, 16, 17, 18 |
| LCD RST | 21 |
| LCD Backlight | 47 |
| Touch SDA | 11 |
| Touch SCL | 12 |
| Touch RST | 10 |
| Touch INT | 9 |
| Encoder CW (A) | 8 |
| Encoder CCW (B) | 7 |
├── src/
│ ├── main.cpp # Main firmware (display + touch + encoder + LVGL)
│ └── waveshare_init.h # SH8601 proprietary init sequence
├── include/
│ └── pins.h # All GPIO definitions
└── platformio.ini # Build configuration
- PlatformIO (VS Code extension or CLI)
- USB-C cable that supports data
# Build only
pio run
# Build and upload
pio run -t upload
# Serial monitor (115200 baud)
pio device monitorIf upload fails: flip the USB-C cable and try again.
| Symptom | Root Cause | Fix |
|---|---|---|
| Upload fails | Wrong USB-C orientation | Flip cable |
| Touch not responding | CST816 needs hardware reset before I2C works | Reset pulse in touch_init() |
| Arc snaps back when encoder is turned | Wrong quadrature algorithm used | Two-switch polling in poll_encoder() |
| UI frozen at first frame | lv_tick_inc() missing from loop |
Call lv_tick_inc(5) every loop iteration |
| Encoder breaks after touching screen | LVGL pointer device steals group focus | Drive arc directly via lv_arc_set_value() in loop |
| Library | Version |
|---|---|
| GFX Library for Arduino | ^1.4.0 |
| LVGL | ^8.3.9 |
MIT