The board used in this project, identified as
ESP32-2432S028R and also called CYD (Cheap Yellow Display), is very interesting
because, in addition to a powerful 32-bit MCU like the ESP32, it has a
relatively large 2.8” TFT touch display with a resolution of 240 x 320 pixels.
It was designed to develop in IOT using languages such as Arduino IDE,
ESP-IDF, Micro/CircuitPython and Scratc, given the large availability of
libraries, I used the Arduino IDE.
There are numerous IOT and gaming entertainment
applications on the net, in this project I did not use the WiFi that the ESP32
is equipped with but I created a small weather station capable of recording the
ambient temperature, atmospheric pressure and relative humidity on SD.
To make this small weather station you don't
need any soldering or manual skills, you just need to load the Aduino IDE
development system with the ESP32 package and the various libraries used for
the display, the touch controller, the sensor and the SD on your PC.
This article is mainly used to learn how to use
the touch display and the SD together with the I2C interface that allows you to
connect countless devices. For the I2C interface, you just need to take into
account that the external devices must be powered at 3.3V and not at 5V. This
is because, if the device is powered at 5V, even the two SDA and SCL signals
will have similar levels that could damage the ESP32 which has levels of 3.3V.
The ESP32-2432S028R board (CYD)
This board is well built but has significant
hardware design flaws, in particular it does not have an acceptable serial
UART: UART0 is already used by the USB adapter, and it is better not to use it
as an RX even if there is a connector (P1) on the board. It has a useless
tricolor LED on the back that could be replaced by a more useful UART2. It has
only three I/O pins available on the connectors and also duplicated. Another absurd
thing is that the two SPI buses have been used for the display and for the
touch controller while the SD slot has other pins. In my project with ESP32 and
separate displays, I had used the same bus for the display and the SD. If I
want to use the SD with hardware SPI I have to give up the touch, unless you
use a software SPI, as I did in this project. I used the mySD.h library that
seems to also be contained in the ESP32 package because the compiler gives
repetition warnings, but it works anyway.
I2C Interface
The CYD board has two I2C bus interfaces, but
no dedicated I2C pins. Instead, it allows flexible pin assignment, meaning any
GPIO pin can be configured as I2C SDA (data line) and SCL (clock line).
However, GPIO21 (SDA) and GPIO22 (SCL) are commonly
used as the default I2C pins to simplify things for people using existing
Arduino code, libraries and sketches.
For I2C peripherals, use the CN1 connector and
set GPIO27 as SDA. The connections with the BME280 sensor are as shown in the
figure 1. For the connection, use the connector included in the package with
the ends headed with single-pin female connectors that are inserted into the
male pin strips according to the order shown.
 |
Figure 1
|
Here's how to set them up in the program:
#include <Wire.h>
#define SDA 27
#define SCL 22
And in setup():
Wire.begin (SDA, SCL);// assign the I2C
pins
The BME280 sensor
The BME280 is a Bosch temperature, barometric
pressure and relative humidity sensor in a tiny metal case measuring just
2.5x2.5x0.93 mm. It also consumes very little power, making it particularly
suitable for cell phones, GPS receivers or watches. It has a pressure accuracy
of 0.12 hPa, which is equivalent to about 1 m of altitude difference with a
thermal drift of only 12.6 cm/°C. The temperature sensor has a resolution of
0.01 °C and an accuracy of 0.5 °C at 25 °C. The relative humidity sensor has
an accuracy of 3 %RH at 25 °C.
The sensor has the same pinout as the BMP280,
which however lacks the humidity sensor.
It can be powered from 1.7V to 3.6V, in this
case it is powered at 3.3V by the LDO (low drop-out) regulator inside the
module.
Modules with this sensor are commercially
available with SPI and/or I2C interfaces, the latter should be chosen for our
project.
As libraries, in addition to Wire.h which
manages the I2C bus, you need the two libraries Adafruit_Sensor.h and
Adafruit_BME280.h, which can be downloaded from the Arduino IDE.
SPI Interfaces
The ESP32 has four SPI peripherals, called
SPI0, SPI1, SPI2 (HSPI), and SPI3 (VSPI). SPI0 is entirely dedicated to the
flash cache that the ESP32 uses to map the SPI flash device it is connected to
into memory. SPI1 is connected to the same hardware lines as SPI0 and is used
to write to the flash chip. HSPI and VSPI are general purpose and available to
the user.
Similar to I2C, the ESP32 allows flexible pin
assignments for SPI. This means that any GPIO pin can be configured as an SPI
pin. The CYD board uses VSPI for the touch controller, and SPI for the TFT
display.
The TFT display
The display is a 2.8-inch 320x240 pixel TFT
touch with 18-bit for 262k colors with 6-bit encoding per color. The control chip
used for this screen is an ILI9341
while the Touch Screen Controller is an XPT2046.
The TFT_eSPI.h library was used, specifically dedicated to 32-bit MCUs, when
you insert it with the Arduino IDE, you need to go to the folder
…\Documents\Arduino\libraries\TFT_eSPI\ and replace the configuration file
“User_Setup.h” according to the I/O of the CYD board, I loaded the one by Rui
and Sara Santos from their site https://randomnerdtutorials.com/cheap-yellow-display-esp32-2432s028r/.
The Touch Controller
The TFT_eSPI.h library also has XPT2046
support, but the examples provided do not work. Maybe because it only works
with SPI bus shared by ILI9341 and XPT2046 controllers, in our case they are
connected to different SPI buses, so we need to use the XPT2046_Touchscreen.h
library by Paul Stoffregen. And also modify the examples, if we need them.
SD Interface
It uses a 1-bit SPI bus, so you only need 4
pins to use it:
SD
CLK GPIO18
SD
MISO GPIO19
SD MOSI GPIO23
SD CS GPIO5
As already written, the two available SPI buses
are used by the display and the touch controller, so you need to use a slower
software interface. I have successfully used the mySD.h library.
This project does not require a high writing
speed as one line of text is written every few tens of seconds.
The program
To develop the program I used the Arduino IDE,
version 2.3.3 with the ESP32 package. I set “ESP32-WROOM-DA Mod” as the board.
The sensor and display libraries must be loaded using the same IDE with “Manage
libraries...”. The mySD.h library from SparkFun Electronics can be downloaded
from the site https://github.com/nhatuan84/esp32-micro-sdcard/tree/master.
Every “period” [s], the program measures the
three variables and prints them on the display, then transforms them into
integers, multiplying the temperature and pressure by 10, and puts them in the “Gbuff”
graphics buffer (3x320).
The graph starts from left to right and, once
the buffer is filled, it scrolls the measurements from right to left and
inserts the last measurements on the right. To have a graph and measurements of
the last sampling, a graph of only one variable is executed, but by touching
the display, the variable changes according to the sequence T, P, H.
To save energy, the display turns on for only
20 seconds, but by touching it turns on. The touch is used only for this
function.
Initially it takes a measurement to evaluate
the minimum and maximum values to have graphs with good precision.
With period = 60 seconds, on the display, you
have a graph of 60 * 320 = 19200 seconds equal to 5h 20'.
If you insert a micro SD into the slot, the
program initially writes a line (header) with the names of the variables and
the sampling period and saves the measurements in the file "TPH.TXT".
If the file is already present, it appends a new header and the measurements.
If it acquires measurements, the number of
samples saved on the SD card will appear on the display next to the
temperature.
The Arduino code
/* program CYD_PTH
uses ESP32-2432S028 board with BME280
sensor on CN1
save data on SD (with software SPI)
Giovanni Carrera, rev.171124 */
#include <WiFi.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <Wire.h>
#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme;//
I2C
#define SDA 27
#define SCL 22
#include <TFT_eSPI.h> //
Graphics and font library for TFT driver chip
#include <SPI.h>
TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in
User_Setup.h
#include <XPT2046_Touchscreen.h>
// pins used for XPT2046 on CYD board
#define XPT2046_MOSI 32
#define XPT2046_MISO 39
#define XPT2046_CLK 25
#define XPT2046_CS 33
#define XPT2046_IRQ 36
SPIClass tsSPI = SPIClass(VSPI);
XPT2046_Touchscreen ts(XPT2046_CS, XPT2046_IRQ);// interrupt enabled polling
#include <mySD.h>
ext::File myFile;
//define pins of SD card slot
#define SD_CS 5
#define SD_MOSI 23
#define SD_MISO 19
#define SD_SCK 18
float temperature,
humidity, pressure, altitude;
int16_t GBuff [3][320];// data buffer for graphics
int16_t BuffInd= 0;// data buffer index
int16_t GVal [3];
int16_t ValMin [3];
int16_t ValMax [3];
const char ValName[4] = {'T', 'P', 'H'};
uint32_t time_now = 0;
bool first_buffer = true;// first buffer fill flag
int period = 60;// sample period in seconds, for 5h20' plot (if
= 90, 8 hours plot)
uint32_t secperiod = 1000; // 1 second
uint8_t sec, mp = 0;
int8_t BLsec = 20;
bool SDok = false;
bool ftime = true;
uint32_t vep = 0;
void setup() {
WiFi.disconnect(true);//
turn OFF WiFi to save power
WiFi.mode(WIFI_OFF);
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
tft.drawString("TPH monitor by GCAR", 0, 0, 4);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
Wire.begin(SDA, SCL);
if (!bme.begin(0x76, &Wire)){
tft.drawString("BME280 NOT OK", 0, 26, 4);
while (1); // sketch halts in an endless loop
}
GetMeasures();// initial values to calculate ValMin and ValMax
ValMin [0] = GVal[0]-50;// T-5 C°
ValMin [1] = GVal[1]-50;// P-5 hPa
ValMin [2] = 20;// 20 %
ValMax [0] = GVal[0]+50;// T+5 C°
ValMax [1] = GVal[1]+50;// P+5 hPa
ValMax [2] = 100;// 100 %
tsSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI,
XPT2046_CS);//
define SPI pins for touchscreen
ts.begin(tsSPI);
ts.setRotation(1); //
display in landscape aspect
tft.drawString("Initializing SD card...", 0, 26, 4);
if (!SD.begin(SD_CS, SD_MOSI, SD_MISO, SD_SCK)) {//initialise
SD card
tft.setTextColor(TFT_RED, TFT_BLACK);// red color
tft.drawString("SD card failed !", 0, 52, 4);
tft.setTextColor(TFT_WHITE, TFT_BLACK);// white
} else {
SDok = true;
tft.drawString("SD card installed", 0, 52, 4);
}
delay(5000);
}
void loop() {
if(millis() - time_now > secperiod) {
time_now = millis();
sec ++;
if (ts.tirqTouched() && ts.touched()) { // display is touched
digitalWrite(TFT_BL, HIGH); // TFT backlight ON
mp++;
if (mp == 3) mp = 0;
PrintMeasures();
PlotMeasures(319, mp);// plot measure
BLsec = 20;
}
BLsec --;
if (BLsec <= 0){
digitalWrite(TFT_BL, LOW); // TFT backlight OFF
}
if (sec == period){
sec = 0;
GetMeasures();
if (SDok) SaveMeasures();
if (first_buffer){//
store them in the buffer
for (int j=0; j <= 2; j++){
GBuff[j][BuffInd]= GVal[j];
}
BuffInd++;
if (BuffInd == 320) first_buffer = false;
} else {
for (int j=0; j <= 2; j++){
for (int i=0; i <= 238; i++){
GBuff[j][i]= GBuff [j][i+1];// shift left previous values
}
}
for (int j=0; j <= 2; j++){
GBuff[j][319]= GVal[j];// update
buffer with last values
}
}
}
}
}
/************************** MY FUNCTIONS
**************************/
void PlotMeasures(int16_t k,uint8_t m) {
//plot k graph points of all measures
const int yp [3] = {2,106,210};// upper left corners of the plots
//digitalWrite(TFTLed, LOW);// turn ON display
leds
tft.fillRect(0,110,320,130,TFT_BLACK);// clear only the plot window
tft.drawRect(0,110,320,130,TFT_WHITE);
tft.setTextColor(TFT_CYAN);
tft.drawString(String(ValName[m]), 5, 112, 2);
for (int j=0; j <= k; j++){
int val = map(GBuff[m][j], ValMin[m], ValMax[m], 130, 0);
tft.drawPixel(j, val+110, TFT_CYAN);// draw the graph
}
}
void GetMeasures(){
temperature = bme.readTemperature();
pressure = bme.readPressure()/100.0F;// pressure in hPascal
humidity = bme.readHumidity()+13;// corrected in comparison with a precision hygrometer
GVal[0] = int(temperature*10);//stores
temperature in tenths of a degree
GVal[1] = int(pressure*10);//stores
pressure in tenths of a millibar
GVal[2] = int(humidity);//stores relative humidity in %
}
void PrintMeasures(){
tft.fillRect(0,26,300,78,TFT_BLACK);// clear only the print window
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.drawString("T = "+String(temperature,1)+" [C]", 0, 26, 4);
if (SDok) tft.drawString("Ns= "+String(vep), 180, 26, 4);
tft.drawString("P = "+String(pressure,1)+" [hPa]", 0, 52, 4);
tft.drawString("H = "+ String(humidity,1)+" [%]", 0, 78, 4);
}
void SaveMeasures(){
myFile = SD.open("TPH.TXT" , FILE_WRITE);
if (myFile) {
// if the file is available, write to it:
if (ftime){ // at the beginning write the header line
myFile.println("T[C], P[hPa], H [%], Period = "+String(period)+" [s]");
ftime = false;
}
myFile.print(temperature,1);
myFile.print(',');
myFile.print(pressure,1);
myFile.print(',');
myFile.println(humidity);
vep++;
myFile.close();
}
}