Imagine having multiple ESP32 sensor nodes scattered around your home or workshop, all wirelessly sending temperature and humidity data to a single, sleek touchscreen display that shows everything in real-time. With the ESP32 Cheap Yellow Display (CYD) and ESP-NOW communication protocol, you can build exactly that—no Wi-Fi router required.
In this project, you’ll learn how to receive and display data from multiple ESP32 sender boards on your CYD, organizing the information in separate tabs for each device. This is the perfect foundation for creating wireless environmental monitoring systems, remote sensors networks, or any multi-point data logging project.

Project Overview: How It Works
Before diving into code, let’s understand the system architecture:
┌─────────────────┐ ┌─────────────────────────────────────────┐
│ ESP32 Sender 1 │────▶│ │
│ (with BME280) │ │ ESP32 CYD │
└─────────────────┘ │ (ESP-NOW Receiver) │
│ ┌─────────────────────────────────┐ │
┌─────────────────┐ │ │ Tab 1: Board 1 Data │ │
│ ESP32 Sender 2 │────▶│ │ Temperature: 23.5°C │ │
│ (with BME280) │ │ │ Humidity: 65% │ │
└─────────────────┘ │ ├─────────────────────────────────┤ │
│ │ Tab 2: Board 2 Data │ │
│ │ Temperature: 24.1°C │ │
│ │ Humidity: 58% │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
-
Two ESP32 sender boards each read data from a BME280 environmental sensor
-
They send this data via ESP-NOW—a connectionless protocol that requires no Wi-Fi network
-
The ESP32 CYD board acts as the receiver, listening for incoming data packets
-
The CYD displays the data in a tabbed interface, with separate tables for each sender
-
Data updates automatically whenever new readings arrive
ESP-NOW is ideal for this application because it’s fast, low-power, and works even when Wi-Fi networks are unavailable. Each sender can be placed anywhere within range (typically 30-50 meters indoors) and send data directly to the CYD.
What You’ll Need: Complete Parts List
To build this project, gather the following components:
👉 Find all these parts at the best prices here
Prerequisites: Setting Up Your Environment
Before starting, ensure you’ve completed these essential setup steps. Missing any will cause compilation errors.
1. ESP32 Board Support in Arduino IDE
If you haven’t already, install ESP32 board support in Arduino IDE. Follow our detailed guide: Installing ESP32 Board in Arduino IDE.
2. Familiarize Yourself with the CYD
The ESP32 Cheap Yellow Display (ESP32-2432S028R) is the heart of this project. If this is your first time using it, complete our Getting Started with ESP32 CYD guide first. You’ll need to:
-
Install the TFT_eSPI library
-
Configure the User_Setup.h file correctly
-
Test basic display and touch functionality
3. Install LVGL Library for the CYD
We’ll use LVGL (Light and Versatile Graphics Library) to create the tabbed interface. Follow our dedicated tutorial to install and configure LVGL for the CYD:
👉 LVGL with ESP32 Cheap Yellow Display
4. Install BME280 Libraries
For the sender boards, install the Adafruit BME280 library:
-
In Arduino IDE, go to Sketch > Include Library > Manage Libraries
-
Search for “Adafruit BME280” and install the latest version
-
Install any required dependencies (Adafruit Bus IO, Adafruit Unified Sensor)
5. Understand ESP-NOW Basics
ESP-NOW is a proprietary protocol from Espressif that allows multiple devices to communicate without Wi-Fi. If you’re new to it, read our Getting Started with ESP-NOW tutorial.
Step 1: Get Your CYD Board’s MAC Address
ESP-NOW uses MAC addresses to identify devices. First, you need the MAC address of your CYD receiver so the sender boards know where to send data.
Upload this simple sketch to your CYD:
#include <WiFi.h>
#include <esp_wifi.h>
void readMacAddress() {
uint8_t baseMac[6];
esp_err_t ret = esp_wifi_get_mac(WIFI_IF_STA, baseMac);
if (ret == ESP_OK) {
Serial.printf("CYD MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
baseMac[0], baseMac[1], baseMac[2],
baseMac[3], baseMac[4], baseMac[5]);
} else {
Serial.println("Failed to read MAC address");
}
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.STA.begin();
delay(100);
readMacAddress();
}
void loop() {}
After uploading, open the Serial Monitor at 115200 baud and press the reset button. You’ll see output like:
CYD MAC Address: 24:dc:c3:49:6a:14
Write this address down—you’ll need it for each sender board.
Step 2: Prepare the Sender Boards
Now, let’s set up the two ESP32 boards that will send sensor data. Each board gets a unique ID and a BME280 sensor.
Wiring the BME280 to ESP32
Connect the BME280 sensor to your ESP32 sender board using the default I2C pins:
(If your ESP32 board uses different default I2C pins, adjust accordingly.)
Sender Board Code
Each sender board runs identical code, except for the BOARD_ID and the receiver MAC address. Here’s the complete sender sketch:
ESP32 ESP-NOW Sender with BME280
Sends temperature and humidity to CYD receiver
Complete project: https://RandomNerdTutorials.com/esp32-cyd-esp-now-receive-data/
*********/
#include <esp_now.h>
#include <WiFi.h>
#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>
#define BOARD_ID 1
Adafruit_BME280 bme;
uint8_t broadcastAddress[] = {0x24, 0xDC, 0xC3, 0x49, 0x6A, 0x14};
typedef struct struct_message {
int id;
float temp;
float hum;
int readingId;
} struct_message;
struct_message myData;
unsigned long previousMillis = 0;
const long interval = 10000;
unsigned int readingId = 0;
void initBME() {
if (!bme.begin(0x76)) {
Serial.println("BME280 not found! Check wiring.");
while (1);
}
Serial.println("BME280 initialized");
}
float readTemperature() {
return bme.readTemperature();
}
float readHumidity() {
return bme.readHumidity();
}
esp_now_peer_info_t peerInfo;
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
Serial.print("Send status: ");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Success" : "Fail");
}
void setup() {
Serial.begin(115200);
initBME();
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW init failed");
return;
}
esp_now_register_send_cb(OnDataSent);
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
Serial.println("Failed to add peer");
return;
}
Serial.println("ESP-NOW ready. Sending data every 10 seconds...");
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
myData.id = BOARD_ID;
myData.temp = readTemperature();
myData.hum = readHumidity();
myData.readingId = readingId++;
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *)&myData, sizeof(myData));
Serial.print("Sending [ID:");
Serial.print(myData.id);
Serial.print("] Temp:");
Serial.print(myData.temp);
Serial.print("C Hum:");
Serial.print(myData.hum);
Serial.print("% Reading:");
Serial.print(myData.readingId);
Serial.print(" - ");
Serial.println(result == ESP_OK ? "OK" : "FAILED");
}
}
Important modifications for each sender:
-
Board 1: Set #define BOARD_ID 1
-
Board 2: Set #define BOARD_ID 2
-
Both: Replace the broadcastAddress array with your CYD‘s actual MAC address
Upload this code to both ESP32 sender boards after wiring the BME280 sensors.
Step 3: The CYD Receiver Code (LVGL GUI)
Now for the star of the show—the CYD receiver that displays all incoming data in a beautiful tabbed interface. This code uses LVGL 9.2 to create two tabs (one for each sender) with tables showing temperature and humidity.
Setting Up LVGL for CYD
Before uploading, ensure you’ve:
-
Installed LVGL version 9.2 via Library Manager
-
Placed the correct lv_conf.h file in your Arduino libraries folder (as per the LVGL CYD tutorial)
-
Configured the TFT_eSPI library with the proper User_Setup.h
Complete CYD Receiver Code
ESP32 CYD ESP-NOW Receiver with LVGL Tabbed Display
Receives data from multiple ESP32 senders and displays in tabs
Complete project: https://RandomNerdTutorials.com/esp32-cyd-esp-now-receive-data/
*********/
#include <esp_now.h>
#include <WiFi.h>
#include <lvgl.h>
#include <TFT_eSPI.h>
#include <XPT2046_Touchscreen.h>
#define XPT2046_IRQ 36
#define XPT2046_MOSI 32
#define XPT2046_MISO 39
#define XPT2046_CLK 25
#define XPT2046_CS 33
SPIClass touchscreenSPI = SPIClass(VSPI);
XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);
#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 320
#define DRAW_BUF_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT / 10 * (LV_COLOR_DEPTH / 8))
uint32_t draw_buf[DRAW_BUF_SIZE / 4];
typedef struct struct_message {
int id;
float temp;
float hum;
int readingId;
} struct_message;
struct_message incomingData;
float latestTemp[4] = {0};
float latestHum[4] = {0};
int latestReadingId[4] = {0};
bool dataReceived[4] = {false};
lv_obj_t *tabview;
lv_obj_t *tabs[4];
lv_obj_t *tab_containers[4];
lv_obj_t *temp_label[4];
lv_obj_t *hum_label[4];
lv_obj_t *reading_label[4];
lv_obj_t *status_label;
void log_print(lv_log_level_t level, const char *buf) {
LV_UNUSED(level);
Serial.println(buf);
Serial.flush();
}
void touchscreen_read(lv_indev_t *indev, lv_indev_data_t *data) {
if (touchscreen.tirqTouched() && touchscreen.touched()) {
TS_Point p = touchscreen.getPoint();
int x = map(p.x, 200, 3700, 1, SCREEN_WIDTH);
int y = map(p.y, 240, 3800, 1, SCREEN_HEIGHT);
data->state = LV_INDEV_STATE_PRESSED;
data->point.x = x;
data->point.y = y;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) {
memcpy(&incomingData, incomingData, sizeof(incomingData));
int id = incomingData.id;
if (id >= 1 && id <= 4) {
latestTemp[id-1] = incomingData.temp;
latestHum[id-1] = incomingData.hum;
latestReadingId[id-1] = incomingData.readingId;
dataReceived[id-1] = true;
Serial.printf("Received from Board %d: Temp=%.2fC Hum=%.2f%% (Reading %d)\n",
id, incomingData.temp, incomingData.hum, incomingData.readingId);
updateTabDisplay(id-1);
}
}
void updateTabDisplay(int boardIndex) {
char buf[32];
if (temp_label[boardIndex]) {
snprintf(buf, sizeof(buf), "Temp: %.1f°C", latestTemp[boardIndex]);
lv_label_set_text(temp_label[boardIndex], buf);
}
if (hum_label[boardIndex]) {
snprintf(buf, sizeof(buf), "Humidity: %.0f%%", latestHum[boardIndex]);
lv_label_set_text(hum_label[boardIndex], buf);
}
if (reading_label[boardIndex]) {
snprintf(buf, sizeof(buf), "Reading #%d", latestReadingId[boardIndex]);
lv_label_set_text(reading_label[boardIndex], buf);
}
}
void create_tab_ui() {
tabview = lv_tabview_create(lv_scr_act());
const char *tab_names[] = {"Board 1", "Board 2", "Board 3", "Board 4"};
for (int i = 0; i < 4; i++) {
tabs[i] = lv_tabview_add_tab(tabview, tab_names[i]);
lv_obj_t *cont = tabs[i];
temp_label[i] = lv_label_create(cont);
lv_label_set_text(temp_label[i], "Temp: --°C");
lv_obj_set_pos(temp_label[i], 20, 40);
lv_obj_set_style_text_font(temp_label[i], &lv_font_montserrat_20, 0);
hum_label[i] = lv_label_create(cont);
lv_label_set_text(hum_label[i], "Humidity: --%");
lv_obj_set_pos(hum_label[i], 20, 80);
lv_obj_set_style_text_font(hum_label[i], &lv_font_montserrat_20, 0);
reading_label[i] = lv_label_create(cont);
lv_label_set_text(reading_label[i], "Reading #--");
lv_obj_set_pos(reading_label[i], 20, 130);
lv_obj_set_style_text_font(reading_label[i], &lv_font_montserrat_14, 0);
lv_obj_t *update_label = lv_label_create(cont);
lv_label_set_text(update_label, dataReceived[i] ? "✓ Active" : "⏳ Waiting...");
lv_obj_set_pos(update_label, 20, 180);
lv_obj_set_style_text_color(update_label, dataReceived[i] ? lv_palette_main(LV_PALETTE_GREEN) : lv_palette_main(LV_PALETTE_GREY), 0);
}
}
void setup() {
Serial.begin(115200);
Serial.println("ESP32 CYD ESP-NOW Receiver Starting...");
touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
touchscreen.begin(touchscreenSPI);
touchscreen.setRotation(1);
lv_init();
lv_log_register_print_cb(log_print);
lv_display_t *disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));
lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);
lv_indev_t *indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(indev, touchscreen_read);
create_tab_ui();
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW init failed");
return;
}
esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));
Serial.println("ESP-NOW ready. Waiting for data...");
}
void loop() {
lv_task_handler();
lv_tick_inc(5);
delay(5);
}
How the Receiver Code Works
-
ESP-NOW Setup: The CYD initializes ESP-NOW and registers a receive callback function OnDataRecv() that triggers whenever data arrives.
-
Data Parsing: Incoming data packets are parsed into the struct_message structure, which contains board ID, temperature, humidity, and reading ID.
-
Tabbed UI Creation: The create_tab_ui() function builds four tabs (though we only use first two), each containing labels for temperature, humidity, and reading number.
-
Dynamic Updates: When new data arrives, updateTabDisplay() refreshes the labels for the corresponding board’s tab.
-
Touch Support: The touchscreen is fully integrated—users can tap between tabs to view different boards’ data.
Testing Your Wireless Sensor Network
-
Power all three boards (two senders + CYD receiver) via USB
-
Open Serial Monitor on the CYD (115200 baud) to see received data
-
Watch the CYD screen—within 10 seconds, you should see:
-
Tab 1 showing data from Board 1
-
Tab 2 showing data from Board 2
-
Data updating every 10 seconds
-
Touch the screen to switch between tabs
If data doesn’t appear:
-
Verify MAC addresses in sender code
-
Check BME280 wiring on both senders
-
Ensure all boards are powered and within range (try moving closer)
Troubleshooting Common Issues
Taking It Further: Project Enhancements
This foundation opens up many possibilities:
1. Add More Senders
Modify the code to support up to 4 boards. Simply assign BOARD_ID 3 and 4 to additional ESP32s with sensors.
2. Log Data to SD Card
The CYD has a microSD slot. Add code to log all received readings with timestamps.
3. Add Alarms
Trigger an on-screen alert if temperature exceeds a threshold, or flash the RGB LED.
4. Create Historical Graphs
Use LVGL’s chart widget to display temperature trends over time.
5. Add Wi-Fi Upload
Forward received data to a web server or MQTT broker for remote monitoring.
Where to Buy Components
Ready to build your wireless sensor network? Here are the parts you’ll need:
👉 Check all parts and best prices here
Conclusion
You’ve just built a wireless multi-sensor monitoring system using ESP-NOW and the ESP32 Cheap Yellow Display. This project demonstrates:
-
ESP-NOW communication for direct device-to-device data transfer
-
LVGL tabbed interfaces for organizing information from multiple sources
-
Real-time data updates on a touchscreen display
This same architecture can monitor temperatures in different rooms, track inventory levels, or collect data from remote weather stations—all displayed beautifully on your CYD.
Get your components today and start building your own wireless sensor network!
Contact Us