An Arduino project that controls a Yeelight on the local network using the circular touchscreen on the Waveshare ESP32-S3-Touch-LCD-1.28. The yeelight-touch-controller.ino sketch sends Yeelight LAN Control commands to a physical light over TCP.
- Tap anywhere to toggle the light.
- Swipe up to increase brightness or down to decrease it, from 1% to 100%. A vertical displacement of at least 24 pixels, no smaller than the horizontal displacement, starts a brightness gesture. Brightness changes by 1% per 2 pixels relative to the gesture's starting position.
- A yellow fill and percentage indicate brightness. When off, the screen shows
OFFand preserves the brightness level as a dark amber fill. Swiping while off does not adjust brightness. - After 5 seconds without touch input, the backlight turns off and the LCD enters sleep. A wake-up tap also toggles the light.
- The ESP32, touch input, and Wi-Fi continue running. This is display sleep, not ESP32 Light Sleep or Deep Sleep. Turning off the display does not turn off the light.
The Waveshare product documentation identifies an ESP32-S3 board with 16 MB Flash, 2 MB PSRAM, and a 1.28-inch, 240 × 240 circular LCD. It uses a GC9A01A display controller and CST816S capacitive touch controller. The onboard QMI8658 IMU and battery features are not used by this sketch. Connect the board to a computer with a USB Type-C data cable for development.
The following assignments come from this sketch and the Waveshare Setup207_GC9A01.h configuration. These are onboard connections; no external display wiring is required.
| Signal | GPIO |
|---|---|
| LCD MOSI / SCLK | 11 / 10 |
| LCD CS / DC / RST | 9 / 8 / 14 |
| LCD backlight | 2, active high |
| MISO defined in the TFT setup | 12 |
| Touch SDA / SCL | 6 / 7 |
| Touch RST / IRQ | 13 / 5 |
The bundled touch driver uses I2C address 0x15 and is initialized as CST816S touch(6, 7, 13, 5).
This project targets the following dependency versions and configuration. The Waveshare Arduino guide provides the board setup and example package:
| Dependency | Version or configuration |
|---|---|
| esp32 by Espressif Systems | 2.0.12 |
| LVGL | 8.3.10 |
| TFT_eSPI | 2.5.34 |
| TFT_eSPI_Setups | Setup207_GC9A01.h from the Waveshare example package |
| CST816S | Bundled .h and .cpp files |
Obtain the matching libraries from the example package linked in the official Arduino guide. The ESP32 core supplies WiFi.h, esp_timer.h, and Wire.h. This project uses LVGL 8 APIs; compatibility with LVGL 9 or ESP32 core 3.x has not been verified.
Place TFT_eSPI and TFT_eSPI_Setups in your Arduino sketchbook's libraries directory. In TFT_eSPI/User_Setup_Select.h, enable the setup for this board:
#include <../TFT_eSPI_Setups/Setup207_GC9A01.h>Disable other setup includes, including the default User_Setup.h. Do not select the similarly numbered Setup207_LilyGo_T_HMI.h. The Waveshare setup uses GC9A01_DRIVER, a 240 × 240 resolution, an 80 MHz SPI clock, and the GPIO assignments above. Recheck the selection after library updates.
Ensure the active lv_conf.h contains these settings:
#define LV_COLOR_DEPTH 16
#define LV_COLOR_16_SWAP 0
#define LV_FONT_MONTSERRAT_28 1
#define LV_TICK_CUSTOM 0The Waveshare library package may place this file at libraries/lvgl/src/lv_conf.h. Follow the configuration layout of the library package you install and edit its active configuration file. The sketch uses esp_timer to call lv_tick_inc() every 2 ms and calls lv_timer_handler() in its main loop.
For compatible bulbs whose Mi Home app does not expose LAN Control, use the following setup procedure. Support depends on the bulb model and firmware.
Pair the bulb in Mi Home first. Use Xiaomi Cloud Tokens Extractor to retrieve its device token. Find the bulb in the results and record its IP address and token.
On a computer connected to the bulb's local network, install Python and python-miio, then run:
python -m pip install python-miio
miiocli yeelight --ip BULB_IP --token BULB_TOKEN set_developer_mode 1Replace BULB_IP and BULB_TOKEN with the retrieved values. A successful response typically looks like:
Setting developer mode to True
['ok']
Set kLightIp in secrets.h to that bulb's current LAN address and leave kYeelightPort at 55443. The token is used for this setup command only; the ESP32 firmware uses Yeelight LAN Control directly and does not need the token or Xiaomi account credentials in secrets.h.
Arduino IDE is optional. Use either the IDE workflow or the Arduino CLI workflow below; both require the same libraries and configuration described under Dependencies.
- Keep the checkout folder named
yeelight-touch-controllerand openyeelight-touch-controller.inoin Arduino IDE. Arduino requires the main sketch filename to match its folder name. Keep the bundled.hand.cppfiles alongside the sketch. - For a fresh checkout, copy
secrets.example.htosecrets.hand setkWifiSsid,kWifiPassword, andkLightIp. Ifsecrets.halready exists, keep your existing settings. Git ignoressecrets.h. - Leaving the SSID empty calls
WiFi.begin()to try credentials already stored in ESP32 NVS. The sketch usesWiFi.persistent(false), so do not assume credentials supplied by this sketch will be saved to NVS. - Set
kLightIpinsecrets.hto the light's address; the template uses a placeholder address.kYeelightPortdefaults to55443. Display timeout (kScreenTimeoutMs) and swipe sensitivity (kSwipeThresholdPixels,kPixelsPerBrightnessPercent) are also configured insecrets.h; keep these values positive. The light must support and have Yeelight LAN Control enabled. Place it and the ESP32 on a mutually reachable local network; a DHCP reservation helps keep its address stable. - Select
ESP32S3 Dev Moduleand the board's COM port. Compare board options with the settings image in the official Arduino guide. Use 16 MB Flash and QSPI PSRAM for this board. For serial logging through its onboard USB-to-UART bridge, set USB CDC On Boot to Disabled. - Select Verify to compile, then Upload. Open Serial Monitor at 115200 baud.
- Check for
[WIFI] Connectedand[LIGHT]messages. Test power toggling, brightness gestures, and waking the display after it becomes idle.
The examples below use Windows PowerShell. Install the standalone executable following the Arduino CLI installation guide, add its directory to PATH, and open a new terminal. Arduino IDE is not required.
Check the CLI and create its configuration file on a fresh installation. If a configuration file already exists, skip config init and keep your existing settings.
arduino-cli version
arduino-cli config init
arduino-cli config add board_manager.additional_urls https://espressif.github.io/arduino-esp32/package_esp32_index.json
arduino-cli core update-index
arduino-cli core install esp32:esp32@2.0.12
arduino-cli core listSkip config add if that URL is already configured. The package index is the official Espressif stable index. Keep the core at 2.0.12 for this project.
Run arduino-cli config dump and check directories.user, the CLI's sketchbook directory. Place the Waveshare libraries from Dependencies in its libraries subdirectory: lvgl, TFT_eSPI, and TFT_eSPI_Setups. Apply the TFT_eSPI and LVGL settings above before compiling. If reusing an IDE installation, ensure the CLI points to the same sketchbook; for example, arduino-cli config set directories.user "C:\Users\YOUR_USER_NAME\Documents\Arduino" (replace the example path with your actual sketchbook).
The libraries are not supplied by installing the ESP32 core. The bundled CST816S.h and CST816S.cpp stay in the project folder and need no separate installation.
Change into your checkout, keeping its folder named yeelight-touch-controller. Replace the example path below. Create secrets.h only if it does not already exist:
Set-Location "C:\path\to\yeelight-touch-controller"
if (-not (Test-Path .\secrets.h)) {
Copy-Item .\secrets.example.h .\secrets.h
}Edit secrets.h with your editor and set kWifiSsid, kWifiPassword, and kLightIp as described in the IDE workflow. Enable Yeelight LAN Control before testing the firmware.
Connect the board with a USB data cable, then inspect available ports and board options:
arduino-cli board list
arduino-cli board details --fqbn esp32:esp32:esp32s3
$fqbn = "esp32:esp32:esp32s3:FlashSize=16M,PSRAM=enabled,CDCOnBoot=default"
arduino-cli compile --fqbn $fqbn --output-dir .\build .This FQBN selects ESP32S3 Dev Module, 16 MB Flash, QSPI PSRAM, and USB CDC On Boot: Disabled for logging through the onboard USB-to-UART bridge. Other board options retain the core's defaults; flash size does not automatically select a 16 MB partition layout. These option names match ESP32 core 2.0.12. The build files go into the Git-ignored build directory.
Continue in the same PowerShell session so $fqbn remains defined. Replace COM3 with the board's port from board list; an Unknown board name is acceptable when the port is correct and the FQBN is supplied explicitly. Close any serial monitor using that port first.
Run upload only after compilation succeeds:
$port = "COM3"
arduino-cli upload --port $port --fqbn $fqbn --input-dir .\build .Upload does not compile the sketch. After changing source code, libraries, board options, or secrets.h, rerun the compile command before uploading. After a successful upload, open the serial monitor:
arduino-cli monitor --port $port --config baudrate=115200Press the board's RESET button if you need to see startup messages. Check for [WIFI] Connected and [LIGHT] messages, then test the gestures and display timeout. Press Ctrl+C to close the monitor before the next upload. For missing ports or upload errors, see Troubleshooting.
See the Arduino CLI getting started guide for general CLI usage.
At startup, the sketch attempts one get_prop request for power and bright if a connection can be established. If it fails, the local UI starts at off and 50% brightness; these fallback values are not automatically sent to the light. Touch input updates the local UI first, then queues a set_power or set_bright command. Brightness commands are spaced at least 100 ms apart.
Wi-Fi reconnects after a disconnect. A TCP connection is established when a command needs to be sent, with connection attempts spaced at least 1.5 seconds apart. While disconnected, the sketch retains the latest pending state rather than a history of every gesture. Sending an off command clears pending brightness changes. State is held in RAM; rebooting triggers a new startup query.
Normal command responses and light notifications are printed to Serial Monitor but are not continuously parsed to update the UI. Reconnecting does not automatically repeat get_prop. Changes made through another app may therefore leave the display out of sync, and a local UI update is not confirmation that the light executed a command. There is no automatic device discovery, Wi-Fi provisioning page, or cloud integration.
| File | Purpose |
|---|---|
yeelight-touch-controller.ino |
LVGL UI, gestures, display sleep, Wi-Fi, and light control |
CST816S.h / CST816S.cpp |
Bundled touch driver |
secrets.example.h |
Configuration template with example IP and default preferences |
secrets.h |
Local Wi-Fi credentials, light address, and preferences; excluded from Git |
.gitignore |
Excludes credentials, build output, and a local utility |
| Symptom | What to check |
|---|---|
Missing secrets.h |
Create the local configuration file from the template |
Undefined TFT_BL, blank screen, or incorrect display output |
Confirm TFT_eSPI selects only the correct Waveshare setup |
Undefined lv_font_montserrat_28 |
Enable the font in the active lv_conf.h |
| LVGL type or API compilation errors | Check that LVGL 8.3.10 and matching libraries are selected |
| Upload failure or missing COM port | Check the data cable, CH343 USB-to-UART driver, and selected port; use the documented BOOT/RESET procedure if download mode is needed |
Repeated [WIFI] reconnection messages |
Check credentials, 2.4 GHz Wi-Fi availability, and signal strength |
[LIGHT] Cannot connect |
Check the light's IP, LAN Control, TCP port 55443, and router client isolation |
| Screen becomes black after 5 seconds | This is the default timeout; change kScreenTimeoutMs in secrets.h to adjust it |
CST816S.h and CST816S.cpp retain the MIT license notice from Felix Biego (2021). No separate license has been declared for the remaining project code. Third-party libraries retain their own licenses.
- Waveshare ESP32-S3-Touch-LCD-1.28 product documentation
- Waveshare Arduino setup and example downloads
This README is based on the official documentation and the project source. Changes to hardware configuration or dependency versions should be verified on the target board.