EMC/EMI Checklist Before Your Board Goes to Certification
EMC testing is expensive, and failing it is worse than the test fee — it costs you a respin, a
The ESP32 CYD (Cheap Yellow Display) represents a breakthrough in affordable embedded systems development, combining the powerful ESP32 microcontroller with an integrated 2.8-inch TFT touchscreen. This all-in-one solution eliminates the need for complex wiring between separate components, making it ideal for IoT dashboards, smart home controllers, wearable devices, and industrial HMIs. While many tutorials focus on the Arduino IDE for ESP32 CYD development, this comprehensive guide explores a more professional approach using Visual Studio Code with PlatformIO, offering superior code management, debugging capabilities, and library handling.
This tutorial not only covers basic setup but also dives deep into configuring the essential libraries—TFT_eSPI, XPT2046_Touchscreen, and LVGL—that transform this budget-friendly board into a capable interactive display platform. Whether you're building a weather station interface, a retro gaming console, or a custom control panel, mastering VS Code with the ESP32 CYD will significantly accelerate your development workflow and project sophistication.

Before diving into the programming process, ensure you have the following components and knowledge:
Visual Studio Code (latest stable version)
PlatformIO IDE extension for VS Code
Basic familiarity with C/C++ programming
Git (for library management)
ESP32 CYD board (ESP32-2432S028R) or compatible variant
USB-C cable for programming and power
Computer with available USB port
Basic ESP32 programming concepts
Understanding of SPI communication protocol
Previous experience with Arduino IDE (helpful but not mandatory)
Familiarity with library management in embedded development
Note: This guide also applies if you're using a standard ESP32 development board with a separate 2.8-inch ILI9341 240×320 TFT LCD touchscreen. The configuration steps remain largely identical, with only minor pin assignment differences.
Download and Install VS Code: Visit the official Visual Studio Code website and download the appropriate version for your operating system (Windows, macOS, or Linux). Follow the standard installation procedure.
Install PlatformIO Extension:
Open VS Code and navigate to the Extensions view (Ctrl+Shift+X or Cmd+Shift+X on macOS)
Search for "PlatformIO IDE"
Click "Install" on the official PlatformIO extension by PlatformIO
Wait for the installation to complete (this may take several minutes as PlatformIO downloads necessary toolchains)
Initial PlatformIO Configuration:
After installation, you'll see the PlatformIO icon (an alien head) in the VS Code activity bar
Click this icon to open the PlatformIO Home screen
PlatformIO will automatically set up the ESP32 toolchain on first launch
From PlatformIO Home, select "New Project"
Name your project (e.g., "ESP32_CYD_Test")
Select "Espressif ESP32" as the board
Choose "ESP32 Dev Module" as the specific board (the CYD uses a standard ESP32)
Select "Arduino" as the framework
Choose your project location and click "Finish"
PlatformIO will create a project structure with a src folder (for your code) and a critical platformio.ini file (for project configuration). This initialization process demonstrates the professional workflow advantage of PlatformIO over Arduino IDE, with proper project separation and dependency management.
The ESP32 CYD requires three core libraries for full functionality:
TFT_eSPI: Handles display graphics and text rendering
XPT2046_Touchscreen: Manages touch input from the resistive touch layer
LVGL (optional but recommended): Provides advanced graphical user interface elements
Unlike the Arduino IDE where libraries can be installed via the Library Manager, PlatformIO requires a different approach for the XPT2046_Touchscreen library due to known compatibility issues:
Open your project's platformio.ini file
Add the following line under the [env:esp32dev] section (or your environment name):
lib_deps = https://github.com/PaulStoffregen/XPT2046_Touchscreen.git
Save the file. PlatformIO will automatically download and install this library from GitHub on the next build.
Expert Tip: Adding libraries via GitHub URLs ensures you get the latest version directly from the source, which is particularly important for frequently updated libraries like XPT2046_Touchscreen. This method also provides better version control than the PlatformIO library registry for this specific library.
The TFT_eSPI library can be installed through PlatformIO's Library Manager:
Click the PlatformIO icon in the activity bar
Select "Libraries" from the PlatformIO menu
Search for "TFT_eSPI" in the search bar
Find the library by Bodmer (the official version)
Click "Add to Project" and select your current ESP32 CYD project
Alternatively, you can add it directly to your platformio.ini file:
lib_deps = https://github.com/PaulStoffregen/XPT2046_Touchscreen.git bodmer/TFT_eSPI@^2.5.43
The version specification (@^2.5.43) ensures compatibility while allowing minor updates. After adding, your platformio.ini should also include these essential settings for proper serial monitoring:
monitor_speed = 115200 upload_speed = 921600 board_build.flash_mode = dio
The TFT_eSPI library requires specific configuration for each display type through a User_Setup.h file. Generic configurations available online often won't work correctly with the ESP32 CYD, leading to display issues, incorrect colors, or touch calibration problems.
Locate the TFT_eSPI library in your project:
In VS Code's Explorer panel, navigate to: .pio/libdeps/esp32dev/TFT_eSPI
The esp32dev portion may vary based on your board selection during project creation
Find and open User_Setup.h:
Within the TFT_eSPI folder, locate User_Setup.h
This file contains numerous configuration options for different displays
Replace with ESP32 CYD specific configuration:
Download the pre-configured User_Setup.h file specifically tested for ESP32 CYD
Completely replace the contents of the existing User_Setup.h with the downloaded version
Save the file
Key Configuration Details:
The correct User_Setup.h for ESP32 CYD should include these essential definitions:
#define ILI9341_DRIVER #define TFT_WIDTH 240 #define TFT_HEIGHT 320 #define TFT_MISO 39 #define TFT_MOSI 32 #define TFT_SCLK 25 #define TFT_CS 33 #define TFT_DC 15 #define TFT_RST 32 #define TOUCH_CS 33 #define LOAD_GLCD #define LOAD_FONT2 #define SPI_FREQUENCY 40000000 #define SPI_TOUCH_FREQUENCY 2500000
Critical Note: Using an incorrect
User_Setup.hfile is the most common source of problems when working with TFT displays on ESP32. The configuration must match exactly the pin assignments and display driver of your specific hardware. Random Nerd Tutorials provides a tested configuration that accounts for the ESP32 CYD's unique pin mapping and ILI9341 display controller characteristics.
After installing and configuring the libraries, verify everything works correctly with this comprehensive test code:
#include <SPI.h> #include <TFT_eSPI.h> #include <XPT2046_Touchscreen.h> TFT_eSPI tft = TFT_eSPI(); // ESP32 CYD touchscreen pin definitions #define XPT2046_IRQ 36 // T_IRQ #define XPT2046_MOSI 32 // T_DIN #define XPT2046_MISO 39 // T_OUT #define XPT2046_CLK 25 // T_CLK #define XPT2046_CS 33 // T_CS SPIClass touchscreenSPI = SPIClass(VSPI); XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ); #define SCREEN_WIDTH 320 #define SCREEN_HEIGHT 240 #define FONT_SIZE 2 void setup() { Serial.begin(115200); // Initialize touchscreen SPI touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS); touchscreen.begin(touchscreenSPI); touchscreen.setRotation(1); // Landscape mode // Initialize display tft.init(); tft.setRotation(1); tft.fillScreen(TFT_BLACK); tft.setTextColor(TFT_WHITE, TFT_BLACK); // Display welcome message tft.drawCentreString("ESP32 CYD Ready", SCREEN_WIDTH/2, 50, FONT_SIZE); tft.drawCentreString("Touch to Test", SCREEN_WIDTH/2, 100, FONT_SIZE); tft.drawCentreString("Check Serial Monitor", SCREEN_WIDTH/2, 150, FONT_SIZE); Serial.println("ESP32 CYD Test Initialized"); } void loop() { if (touchscreen.tirqTouched() && touchscreen.touched()) { TS_Point p = touchscreen.getPoint(); // Calibrate raw touch coordinates to screen dimensions int x = map(p.x, 200, 3700, 1, SCREEN_WIDTH); int y = map(p.y, 240, 3800, 1, SCREEN_HEIGHT); int z = p.z; // Display touch coordinates tft.fillRect(0, 180, SCREEN_WIDTH, 60, TFT_BLACK); tft.drawCentreString("X: " + String(x), SCREEN_WIDTH/2, 180, FONT_SIZE); tft.drawCentreString("Y: " + String(y), SCREEN_WIDTH/2, 200, FONT_SIZE); tft.drawCentreString("Pressure: " + String(z), SCREEN_WIDTH/2, 220, FONT_SIZE); // Print to serial monitor Serial.printf("Touch - X: %d, Y: %d, Pressure: %d\n", x, y, z); delay(200); // Debounce delay } }
Build the Project: Click the checkmark icon in the blue bottom status bar (or press Ctrl+Alt+B) to compile the code
Upload to ESP32 CYD: Click the right-arrow icon (or press Ctrl+Alt+U) to upload the compiled code
Open Serial Monitor: Click the plug icon (or press Ctrl+Alt+S) to open the serial monitor at 115200 baud
Expected Results:
The display should show a welcome message
Touching the screen should display coordinates on both the display and serial monitor
The touch pressure value should increase when pressing harder
| Problem | Possible Cause | Solution |
|---|---|---|
| Blank or white screen | Incorrect User_Setup.h | Verify User_Setup.h matches ESP32 CYD exactly |
| Touch coordinates incorrect | Calibration values off | Adjust map() function parameters in test code |
| Display colors distorted | Wrong color mode setting | Ensure TFT_RGB_ORDER is correctly set in User_Setup.h |
| SPI conflicts | Multiple SPI devices | Ensure proper CS pin selection and SPI bus separation |
LVGL (Light and Versatile Graphics Library) is a professional open-source embedded graphics library that enables sophisticated user interfaces on microcontrollers. While the TFT_eSPI library handles basic drawing functions, LVGL provides:
Advanced widgets (buttons, sliders, charts, lists)
Animations and transitions
Multi-language support
Memory-efficient rendering
Touch gesture recognition
For projects requiring more than basic text and shapes, LVGL transforms the ESP32 CYD into a capable platform for modern GUI applications.
Add LVGL to PlatformIO Project:
Open PlatformIO Libraries (PIO Home → Libraries)
Search for "lvgl"
Select version 9.x (latest stable)
Click "Add to Project" and select your ESP32 CYD project
Create lv_conf.h Configuration File:
In your project's root directory, create a new file named lv_conf.h
Copy the LVGL configuration template specifically designed for ESP32 CYD:
#ifndef LV_CONF_H #define LV_CONF_H #define LV_MEM_SIZE (32 * 1024) // Increased memory for ESP32 #define LV_HOR_RES_MAX 320 #define LV_VER_RES_MAX 240 #define LV_COLOR_DEPTH 16 #define LV_USE_GPU_STM32_DMA2D 0 #define LV_USE_LOG 1 #define LV_LOG_PRINTF 1 #define LV_USE_ASSERT_NULL 1 #define LV_USE_ASSERT_MEM 1 #define LV_USE_ASSERT_OBJ 1 #define LV_USE_USER_DATA 1 // Enable required widgets #define LV_USE_LABEL 1 #define LV_USE_BUTTON 1 #define LV_USE_BUTTONMATRIX 1 #define LV_USE_CHECKBOX 1 #define LV_USE_SLIDER 1 #define LV_USE_DROPDOWN 1 // Add other widgets as needed #endif /*LV_CONF_H*/
Update platformio.ini: Ensure your configuration includes sufficient memory allocation:
board_build.partitions = huge_app.csv build_flags = -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue lib_ldf_mode = deep+
Integrate LVGL with your existing TFT_eSPI and touchscreen setup:
#include <lvgl.h> #include <TFT_eSPI.h> #include <XPT2046_Touchscreen.h> TFT_eSPI tft = TFT_eSPI(); XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ); static lv_disp_draw_buf_t draw_buf; static lv_color_t buf[SCREEN_WIDTH * SCREEN_HEIGHT / 10]; void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) { uint32_t w = (area->x2 - area->x1 + 1); uint32_t h = (area->y2 - area->y1 + 1); tft.startWrite(); tft.setAddrWindow(area->x1, area->y1, w, h); tft.pushColors((uint16_t *)&color_p->full, w * h, true); tft.endWrite(); lv_disp_flush_ready(disp); } void touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data) { if (touchscreen.touched()) { TS_Point p = touchscreen.getPoint(); data->point.x = map(p.x, 200, 3700, 0, SCREEN_WIDTH); data->point.y = map(p.y, 240, 3800, 0, SCREEN_HEIGHT); data->state = LV_INDEV_STATE_PR; } else { data->state = LV_INDEV_STATE_REL; } } void setup() { Serial.begin(115200); // Initialize display tft.init(); tft.setRotation(1); // Initialize touchscreen touchscreen.begin(); // Initialize LVGL lv_init(); lv_disp_draw_buf_init(&draw_buf, buf, NULL, SCREEN_WIDTH * SCREEN_HEIGHT / 10); // Initialize display driver static lv_disp_drv_t disp_drv; lv_disp_drv_init(&disp_drv); disp_drv.hor_res = SCREEN_WIDTH; disp_drv.ver_res = SCREEN_HEIGHT; disp_drv.flush_cb = my_disp_flush; disp_drv.draw_buf = &draw_buf; lv_disp_drv_register(&disp_drv); // Initialize input device driver static lv_indev_drv_t indev_drv; lv_indev_drv_init(&indev_drv); indev_drv.type = LV_INDEV_TYPE_POINTER; indev_drv.read_cb = touchpad_read; lv_indev_drv_register(&indev_drv); // Create a simple button as test lv_obj_t *btn = lv_btn_create(lv_scr_act()); lv_obj_set_size(btn, 100, 50); lv_obj_center(btn); lv_obj_t *label = lv_label_create(btn); lv_label_set_text(label, "Click Me!"); lv_obj_center(label); Serial.println("LVGL Initialized with ESP32 CYD"); } void loop() { lv_timer_handler(); delay(5); }
Combine all learned elements into a practical IoT dashboard example:
// ESP32 CYD IoT Dashboard // Displays sensor data with LVGL widgets #include <lvgl.h> #include <TFT_eSPI.h> #include <XPT2046_Touchscreen.h> #include <WiFi.h> #include <HTTPClient.h> // Display and touch objects TFT_eSPI tft; XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ); // LVGL objects lv_obj_t *temperatureLabel; lv_obj_t *humidityLabel; lv_obj_t *pressureLabel; lv_obj_t *chart; lv_chart_series_t *tempSeries; // WiFi credentials const char* ssid = "YOUR_SSID"; const char* password = "YOUR_PASSWORD"; // Weather API const String apiKey = "YOUR_API_KEY"; const String city = "London"; void updateWeatherData() { if (WiFi.status() == WL_CONNECTED) { HTTPClient http; String url = "http://api.openweathermap.org/data/2.5/weather?q=" + city + "&appid=" + apiKey + "&units=metric"; http.begin(url); int httpCode = http.GET(); if (httpCode == 200) { String payload = http.getString(); // Parse JSON response (simplified) float temperature = 22.5; // Extract from JSON float humidity = 65; // Extract from JSON float pressure = 1013; // Extract from JSON // Update LVGL labels lv_label_set_text_fmt(temperatureLabel, "Temp: %.1f°C", temperature); lv_label_set_text_fmt(humidityLabel, "Humidity: %.0f%%", humidity); lv_label_set_text_fmt(pressureLabel, "Pressure: %.0fhPa", pressure); // Update chart lv_chart_set_next_value(chart, tempSeries, temperature); } http.end(); } } void setup() { Serial.begin(115200); // Initialize display tft.init(); tft.setRotation(1); // Initialize touchscreen touchscreen.begin(); // Initialize LVGL lv_init(); // ... LVGL initialization code from previous section // Connect to WiFi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Connecting to WiFi..."); } Serial.println("WiFi connected"); // Create UI lv_obj_t *scr = lv_scr_act(); // Title lv_obj_t *title = lv_label_create(scr); lv_label_set_text(title, "ESP32 CYD Weather Dashboard"); lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 10); // Data labels temperatureLabel = lv_label_create(scr); lv_label_set_text(temperatureLabel, "Temp: -- °C"); lv_obj_align(temperatureLabel, LV_ALIGN_LEFT_MID, 20, -40); humidityLabel = lv_label_create(scr); lv_label_set_text(humidityLabel, "Humidity: --%"); lv_obj_align(humidityLabel, LV_ALIGN_LEFT_MID, 20, -10); pressureLabel = lv_label_create(scr); lv_label_set_text(pressureLabel, "Pressure: -- hPa"); lv_obj_align(pressureLabel, LV_ALIGN_LEFT_MID, 20, 20); // Chart chart = lv_chart_create(scr); lv_obj_set_size(chart, 150, 100); lv_obj_align(chart, LV_ALIGN_RIGHT_MID, -20, 0); lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, 0, 40); lv_chart_set_point_count(chart, 20); tempSeries = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y); // Update button lv_obj_t *btn = lv_btn_create(scr); lv_obj_set_size(btn, 100, 40); lv_obj_align(btn, LV_ALIGN_BOTTOM_MID, 0, -20); lv_obj_t *btnLabel = lv_label_create(btn); lv_label_set_text(btnLabel, "Update"); lv_obj_center(btnLabel); // Button event lv_obj_add_event_cb(btn, [](lv_event_t *e) { updateWeatherData(); }, LV_EVENT_CLICKED, NULL); // Initial data update updateWeatherData(); } void loop() { lv_timer_handler(); // Auto-update every 30 seconds static unsigned long lastUpdate = 0; if (millis() - lastUpdate > 30000) { updateWeatherData(); lastUpdate = millis(); } delay(5); }
Use PSRAM Effectively: The ESP32 CYD includes 8MB of PSRAM. Enable it in platformio.ini:
board_build.partitions = huge_app.csv build_flags = -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue
LVGL Memory Optimization:
Adjust LV_MEM_SIZE in lv_conf.h based on your widget complexity
Use lv_disp_set_draw_buffers() for double buffering
Enable compression for images: #define LV_USE_IMG_COMPRESSED 1
Display Refresh Optimization:
Set appropriate SPI frequency (40MHz works well for ESP32 CYD)
Use tft.setSwapBytes(true) for correct color order
Implement partial updates where possible instead of full screen refreshes
| Advanced Issue | Diagnosis Method | Solution |
|---|---|---|
| LVGL lag or slow response | Monitor frame rate with lv_refr_get_fps_avg() |
Reduce widget count, optimize drawing callbacks, increase LVGL tick rate |
| Memory fragmentation | Monitor heap with ESP.getFreeHeap() |
Use PSRAM for buffers, implement memory pooling for frequently created/destroyed objects |
| SPI conflicts with other peripherals | Check SPI bus usage conflicts | Use different SPI buses (HSPI/VSPI) for display vs other sensors |
| Touch calibration drift | Log raw touch values over time | Implement dynamic calibration, store calibration data in NVS |
Library Version Pinning: Always specify library versions in platformio.ini to ensure consistent builds:
lib_deps = bodmer/[email protected] https://github.com/PaulStoffregen/XPT2046_Touchscreen.git#v1.4.0 lvgl/[email protected]
Configuration File Management: Store your User_Setup.h and lv_conf.h files in a version control system separate from the library installations.
Project Templates: Create a baseline ESP32 CYD project with all configurations pre-set, then duplicate it for new projects rather than reconfiguring each time.
The ESP32 CYD, when combined with VS Code and PlatformIO, becomes a powerful development platform for interactive display applications. By following this comprehensive guide, you've established a professional development environment, configured all necessary libraries, and learned to create both basic and advanced applications with LVGL.
VS Code with PlatformIO offers superior project management compared to Arduino IDE
Proper library configuration is critical—especially the User_Setup.h file for TFT_eSPI
LVGL enables professional-grade user interfaces on the ESP32 CYD
Regular testing at each configuration step prevents compound issues
Explore LVGL Widgets: Experiment with advanced widgets like charts, meters, rollers, and text areas
Add External Sensors: Connect I2C or SPI sensors to create complete monitoring systems
Implement Power Saving: Use ESP32 deep sleep modes with display power management for battery-powered projects
Create Custom Themes: Design unique visual styles with LVGL's theme system
Add Wireless Connectivity: Implement MQTT for IoT dashboards or Bluetooth for peripheral control
With this foundation, you're equipped to tackle increasingly complex projects with the ESP32 CYD. The combination of low-cost hardware and professional development tools democratizes advanced embedded display applications, enabling everything from home automation interfaces to portable diagnostic tools. As you continue developing, remember to share your projects with the community—each project contributes to the collective knowledge base that makes platforms like the ESP32 CYD so valuable to makers and engineers worldwide.
======================================
NE555 Motor Speed Regulation Kit Maker DIY Teaching Electronic Manufacturing Technology Test Assembly Training Welding
$3.90
WM1302 LoRaWAN Gateway Module USB US915MHz Without SX1262
$37.90
28Pin Chip Original TPA3118 60W DC 12V-24V Mono Digital Audio Power Amplifier Board Amp Module
$2.90
IMX219-160 8MP IR-CUT Camera 162 Degree FOV IR-CUT Infrared Applicable For Jetson Nano Compute Module
$29.80
Long-wave IR Thermal Imaging Camera Module, 80 × 62 Pixels IR Array, For Raspberry Pi And Other Development Boards
EMC testing is expensive, and failing it is worse than the test fee — it costs you a respin, a
We review a lot of boards — some designed in-house, some by other contractors, some by founders doing their own
When a battery-powered product dies too soon, the instinct is to blame the firmware. Surely there is a sleep mode
A wearable PCB is not just a small PCB. It is a board where every constraint that normally has some
The STM32 family is the workhorse of embedded hardware, and almost every project that uses one starts the same way:
The nRF52840 is one of the most popular BLE and multiprotocol chips in the world, and for good reason: it
No account yet?
Create an Account