ESP32-2432S028 ESP32-2432S024
ESP32 Wroom with Display and GPIO Extension
For those who wish to do a project with the ESP 32 and an integrated 320*240 pixel screen, there is a family of very interesting circuit boards:
- ESP32-2432S028R
- Display 2.8″
- type ILI9341
- Back Light gpio 27
- ESP32-2432S028
- Display 2.8″
- type ST7789
- Back Light gpio 27
- ESP32-2432S024
- Display 2.4″
- type ILI9341
- Back Light gpio 21
- ESP32-2432S024
- Display 2.4″
- type ST7789
- Back Light gpio 21
Each board includes :
- An ESP32 Wroom (dual-core)
- A 320×240 pixel touchscreen
- An ambient light sensor
- A tri-color LED on the front or rear panel (red: GPIO 4, green and blue: GPIO 16 or 17 depending on the board)
- An XPT2046 resistive touch sensor
These boards do have a weakness for certain applications: the limited number of GPIO pins. Only three are available in the basic version.
In this article, we’ll see how to easily expand the number of GPIO pins, adding four more to allow for the development of applications that require a large number of them.
These boards are also called “ESP32-Cheap-Yellow-Display” or CYD and can be found on Aliexpress.

Display
Several libraries exist for use with the Arduino IDE. Personally, I used “LovyanGFX”. See the example below, which is compatible with the four models shown.
ESP32 Wroom

The ESP32 Wroom is a dual-core processor running at 240MHz with built-in Wi-Fi and Bluetooth, enabling powerful applications such as the F1ATB photovoltaic router.
With the Arduino IDE, you need to configure the “Espressif” board manager (Espressif develops the ESP32) in the preferences. Go to File / Preferences and enter the address: https://dl.espressif.com/dl/package_esp32_index.json


GPIO
The table on the right shows the GPIO pinout of the ESP32 on the 2.8-inch board. There are only 3 GPIO pins available (shown here in green): 22, 27, and 35. Unfortunately, pin 35 only functions as an input, either digital or analog.
To expand the number of GPIO pins, you can easily repurpose those used for the micro-SD memory card if you’re not using it (shown in yellow in the table). You can use a “Card-Sniffer,” available on AliExpress. It’s a dummy memory card that provides the GPIO pins.
The metal casing of the bracket can also be desoldered to access the four GPIO pins used to control the SD card. Then, 2.54mm pitch Dupont pins can be soldered, attaching the assembly to the solder points on the casing on the sides.



Power Supply
It is possible to power the board via the micro-USB port or via the 4-pin connector next to it with a power supply that provides 5V and 500mA minimum.
Speaker
On GPIO26, there is a small amplifier allowing you to connect a small 4 or 8 ohm speaker to send audio signals if needed.
LDR Luminosity

On the front of the board, there’s a variable resistor that adjusts with brightness. Its supply resistance is too high. It needs to be reduced to achieve good analog measurement dynamics on GPIO 34. Solder a 20 to 47k ohm resistor in parallel with R15. This way, by measuring the voltage on GPIO 34, you can integrate a brightness measurement into your program.
Code Example
Attached is a code example for the ESP32-2432S028R and ESP32-2432S028RESP32-2432S024 boards. Install the LovyanGFX library in the Arduino IDE. A Google search will provide the instruction set for controlling the graphics generator.
This code consists of two files to be placed in the same folder:
TFT_ESP32_CYD.ino: Main file for screen management and display
Ecran.h: Definition of parameters for the four screen models. GPIO assignment, etc.
TFT_ESP32_CYD.ino
// ESP32 with screen 320*240px CYD (Cheap-Yellow-Display )
// Resistive touch panel
// F1ATB - 2026
#include "Ecran.h"
String SerialIn = "";
uint16_t Calibre[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
int numEcran = -1;
// commandes via Serial port or Telnet
const char* helpSerial = R"====(
**************************************
Commands for configuration via serial port or Telnet (respect capitalization, punctuation and end with the 'Enter' key):
Commandes pour configuration par port série ou Telnet (respect majuscules, ponctuation et terminer par touche 'Enter'):
E:0 | S028 ST7789 BL21
E:1 | S024 ST7789 BL27
E:2 | S028 ILI9341 BL21
E:3 | S024 ILI9341 BL27
R:X | Rotation X=0,1,2 ou 3
restart | Pour redémarrer l'ESP32 sans appui sur le bouton EN
************************************
)====";
int rotation = 1;
int16_t ClickX = 0, ClickY = 0;
byte LEDs[] = { 4, 16,17}; //Pour les S024 RGB ILI9341. 4,17,16 pour ST7789
int cpt = 0;
LGFX* lcd = nullptr;
void setup() {
//Ports Série ESP
Serial.begin(115200);
Serial.println(helpSerial);
}
void lcd_init(ScreenType type) {
lcd = new LGFX(type);
lcd->init();
lcd->setRotation(rotation);
lcd->fillScreen(TFT_NAVY);
lcd->setTextColor(TFT_WHITE, TFT_NAVY);
lcd->setTextSize(3);
int W = lcd->textWidth("F1ATB") / 2;
int X = lcd->width() / 2;
lcd->setCursor(X - W, lcd->height() / 2);
lcd->println("F1ATB");
lcd->drawRect(10, 10, lcd->width() - 20, lcd->height() - 20, TFT_YELLOW);
}
void loop() {
// put your main code here, to run repeatedly:
LireSerial();
delay(100);
if (numEcran >= 0) {
if (lcd->getTouch(&ClickX, &ClickY)) {
Serial.println("touche");
lcd->setBrightness(255);
if (Calibre[7] == 0 ) { //Not yet calibrated.Pas encore calibré les résistifs
TraceCalibr();
} else {
lcd->fillScreen(TFT_NAVY);
PrintCentre(String(ClickX) + " , " + String(ClickY), -1, lcd->height() / 2 + 50, 2);
}
}
}
cpt++;
cpt = cpt % 30;
if ((numEcran == 1 || numEcran == 3) && (cpt % 10) == 0) { //S024
for (int i = 0; i < 3; i++) {
pinMode(LEDs[i], OUTPUT);
digitalWrite(LEDs[i], HIGH);
}
if (cpt == 0) digitalWrite(LEDs[0], LOW);
if (cpt == 10) digitalWrite(LEDs[1], LOW);
if (cpt == 20) digitalWrite(LEDs[2], LOW);
Serial.println(cpt);
}
}
void LireSerial() {
int inbyte;
//Port Serie
while (Serial.available() > 0) {
inbyte = Serial.read();
if ((inbyte == 10) || (inbyte == 13)) {
DecodeSerial();
} else {
SerialIn += String(char(inbyte));
}
}
}
void DecodeSerial() {
String sw;
String valeur = "";
int p;
SerialIn.trim();
p = SerialIn.indexOf(":");
if (p > 0) {
sw = SerialIn.substring(0, p + 1);
valeur = SerialIn.substring(p + 1);
sw.trim();
valeur.trim();
} else {
sw = SerialIn;
}
if (sw.indexOf("restart") >= 0) {
ESP.restart();
}
if (sw.indexOf("E:") >= 0) {
numEcran = valeur.toInt();
Serial.println(numEcran);
}
if ((sw.indexOf("H") >= 0 || sw.indexOf("?") >= 0) && p == -1) {
Serial.println(helpSerial);
}
if (sw.indexOf("R:") >= 0) {
rotation = valeur.toInt();
Serial.println(rotation);
}
SerialIn = "";
if (numEcran >= 0) {
lcd_init((ScreenType)numEcran);
}
}
void TraceCalibr() {
lcd->fillScreen(TFT_NAVY);
lcd->setTextColor(TFT_WHITE, TFT_NAVY);
PrintCentre("Calibration", -1, lcd->height() / 2 -50, 2.5);
PrintCentre("Click in the corner", -1, lcd->height() / 2 , 2);
PrintCentre("Cliquez dans le coin", -1, lcd->height() / 2 + 50, 2);
delay(1000);
lcd->calibrateTouch(Calibre, TFT_MAGENTA, TFT_WHITE, 30); // Runs a test that has you touch the corners of the screen
lcd->setTouchCalibrate(Calibre); // setTouch actually implements the data form calibrateTouch
lcd->fillScreen(TFT_NAVY);
}
void PrintCentre(String S, int X, int Y, float Sz) {
if (X < 0) X = lcd->width() / 2;
lcd->setTextSize(Sz);
int W = lcd->textWidth(S);
lcd->setCursor(X - W / 2, Y + 3);
lcd->print(S);
}
Ecran.h
// ESP32 with screen 320*240px CYD (Cheap-Yellow-Display )
// Resistive touch panel
// F1ATB - 2026
#include "Ecran.h"
String SerialIn = "";
uint16_t Calibre[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
int numEcran = -1;
// commandes via Serial port or Telnet
const char* helpSerial = R"====(
**************************************
Commands for configuration via serial port or Telnet (respect capitalization, punctuation and end with the 'Enter' key):
Commandes pour configuration par port série ou Telnet (respect majuscules, ponctuation et terminer par touche 'Enter'):
E:0 | S028 ST7789 BL21
E:1 | S024 ST7789 BL27
E:2 | S028 ILI9341 BL21
E:3 | S024 ILI9341 BL27
R:X | Rotation X=0,1,2 ou 3
restart | Pour redémarrer l'ESP32 sans appui sur le bouton EN
************************************
)====";
int rotation = 1;
int16_t ClickX = 0, ClickY = 0;
byte LEDs[] = { 4, 16,17}; //Pour les S024 RGB ILI9341. 4,17,16 pour ST7789
int cpt = 0;
LGFX* lcd = nullptr;
void setup() {
//Ports Série ESP
Serial.begin(115200);
Serial.println(helpSerial);
}
void lcd_init(ScreenType type) {
lcd = new LGFX(type);
lcd->init();
lcd->setRotation(rotation);
lcd->fillScreen(TFT_NAVY);
lcd->setTextColor(TFT_WHITE, TFT_NAVY);
lcd->setTextSize(3);
int W = lcd->textWidth("F1ATB") / 2;
int X = lcd->width() / 2;
lcd->setCursor(X - W, lcd->height() / 2);
lcd->println("F1ATB");
lcd->drawRect(10, 10, lcd->width() - 20, lcd->height() - 20, TFT_YELLOW);
}
void loop() {
// put your main code here, to run repeatedly:
LireSerial();
delay(100);
if (numEcran >= 0) {
if (lcd->getTouch(&ClickX, &ClickY)) {
Serial.println("touche");
lcd->setBrightness(255);
if (Calibre[7] == 0 ) { //Not yet calibrated.Pas encore calibré les résistifs
TraceCalibr();
} else {
lcd->fillScreen(TFT_NAVY);
PrintCentre(String(ClickX) + " , " + String(ClickY), -1, lcd->height() / 2 + 50, 2);
}
}
}
cpt++;
cpt = cpt % 30;
if ((numEcran == 1 || numEcran == 3) && (cpt % 10) == 0) { //S024
for (int i = 0; i < 3; i++) {
pinMode(LEDs[i], OUTPUT);
digitalWrite(LEDs[i], HIGH);
}
if (cpt == 0) digitalWrite(LEDs[0], LOW);
if (cpt == 10) digitalWrite(LEDs[1], LOW);
if (cpt == 20) digitalWrite(LEDs[2], LOW);
Serial.println(cpt);
}
}
void LireSerial() {
int inbyte;
//Port Serie
while (Serial.available() > 0) {
inbyte = Serial.read();
if ((inbyte == 10) || (inbyte == 13)) {
DecodeSerial();
} else {
SerialIn += String(char(inbyte));
}
}
}
void DecodeSerial() {
String sw;
String valeur = "";
int p;
SerialIn.trim();
p = SerialIn.indexOf(":");
if (p > 0) {
sw = SerialIn.substring(0, p + 1);
valeur = SerialIn.substring(p + 1);
sw.trim();
valeur.trim();
} else {
sw = SerialIn;
}
if (sw.indexOf("restart") >= 0) {
ESP.restart();
}
if (sw.indexOf("E:") >= 0) {
numEcran = valeur.toInt();
Serial.println(numEcran);
}
if ((sw.indexOf("H") >= 0 || sw.indexOf("?") >= 0) && p == -1) {
Serial.println(helpSerial);
}
if (sw.indexOf("R:") >= 0) {
rotation = valeur.toInt();
Serial.println(rotation);
}
SerialIn = "";
if (numEcran >= 0) {
lcd_init((ScreenType)numEcran);
}
}
void TraceCalibr() {
lcd->fillScreen(TFT_NAVY);
lcd->setTextColor(TFT_WHITE, TFT_NAVY);
PrintCentre("Calibration", -1, lcd->height() / 2 -50, 2.5);
PrintCentre("Click in the corner", -1, lcd->height() / 2 , 2);
PrintCentre("Cliquez dans le coin", -1, lcd->height() / 2 + 50, 2);
delay(1000);
lcd->calibrateTouch(Calibre, TFT_MAGENTA, TFT_WHITE, 30); // Runs a test that has you touch the corners of the screen
lcd->setTouchCalibrate(Calibre); // setTouch actually implements the data form calibrateTouch
lcd->fillScreen(TFT_NAVY);
}
void PrintCentre(String S, int X, int Y, float Sz) {
if (X < 0) X = lcd->width() / 2;
lcd->setTextSize(Sz);
int W = lcd->textWidth(S);
lcd->setCursor(X - W / 2, Y + 3);
lcd->print(S);
}
Using the 115000 baud serial port, sending “H” or “?” will return a summary of commands for selecting the card model and display rotation. Since these cards have a resistive touchscreen, touching the screen will initiate a calibration phase.
Assembly

For assembly, I suggest a 3D-printable box that includes the lid on the back. The hole in the bottom right corner allows you to house a PIR sensor (GPIO 35, for example) which, through programming, can turn on the screen if someone approaches it. The lower section can accommodate the 230V/5V 700mA power supply described earlier.

Présentation vidéo
/








Recent Comments