Close Menu
  • Articles
    • Learn Electronics
    • Product Review
    • Tech Articles
  • Electronics Circuits
    • 555 Timer Projects
    • Op-Amp Circuits
    • Power Electronics
  • Microcontrollers
    • Arduino Projects
    • STM32 Projects
    • AMB82-Mini IoT AI Camera
    • BLE Projects
  • IoT Projects
    • ESP8266 Projects
    • ESP32 Projects
    • ESP32 MicroPython
    • ESP32-CAM Projects
    • LoRa/LoRaWAN Projects
  • Raspberry Pi
    • Raspberry Pi Projects
    • Raspberry Pi Pico Projects
    • Raspberry Pi Pico W Projects
  • Electronics Calculator
Facebook X (Twitter) Instagram
  • About Us
  • Disclaimer
  • Privacy Policy
  • Contact Us
  • Advertise With Us
Facebook X (Twitter) Instagram Pinterest YouTube LinkedIn
How To Electronics
  • Articles
    • Learn Electronics
    • Product Review
    • Tech Articles
  • Electronics Circuits
    • 555 Timer Projects
    • Op-Amp Circuits
    • Power Electronics
  • Microcontrollers
    • Arduino Projects
    • STM32 Projects
    • AMB82-Mini IoT AI Camera
    • BLE Projects
  • IoT Projects
    • ESP8266 Projects
    • ESP32 Projects
    • ESP32 MicroPython
    • ESP32-CAM Projects
    • LoRa/LoRaWAN Projects
  • Raspberry Pi
    • Raspberry Pi Projects
    • Raspberry Pi Pico Projects
    • Raspberry Pi Pico W Projects
  • Electronics Calculator
How To Electronics
Home » Indoor Environment Monitoring with ESP32 & LCD Screen
ESP32 Projects

Indoor Environment Monitoring with ESP32 & LCD Screen

Mamtaz AlamBy Mamtaz AlamUpdated:August 20, 20223 Comments5 Mins Read
Share Facebook Twitter LinkedIn Telegram Reddit WhatsApp
Indoor Environment Monitoring ESP32
Share
Facebook Twitter LinkedIn Pinterest Email Reddit Telegram WhatsApp

Overview

This is a simple Indoor Environment Monitoring Project with ESP32 & LCD screens. We will utilize the Indoor Environment Expansion board made using DHT11 Humidity & Temperature Sensor and SPG30 Air Quality Sensor. The expansion board is used for measuring CO2, TVOC, Temperature and Humidity. Earlier we used BME680 Sensor the Indoor Air Quality.

The LCD Screen used here is having an ILI9488 driver with a screen size of 3.5″ & a screen resolution of 320×480. It is basically a TFT touch screen but we will not be using the touch function but rather use it as a colorful display.

As a first project, we will display the Temperature, Humidity & CO2 value in a beautiful gauge. The three gauges will show the increase or decrease in circular widgets pattern. Similarly, as a second project, we will display the CO2 & TVOC value in a graphical format. The graphs will rise and fall on the basis of an increase or decrease in CO2 & TVOC value. The ESP32 & TFT LCD Display can be used in Indoor Environment Monitoring.



Bill of Materials

Following is the list of components that you need for making this project. You can purchase all these components online.

S.N.ComponentsDescriptionQuantityPurchase Links
1ESP32+TFT Touch DisplayESP32 3.5" TFT Touch(Capacitive) with Camera1Makerfabs Link
2Indoor Environment Expansion boardSPG30 + DHT11 Sensor + Buzzer1Makerfabs Link
3USB CableType-C USB Cable for Programming1Amazon | AliExpress
3Connecting WiresMale to Female Jumper Wires3Amazon | AliExpress

ESP32 3.5″ TFT Touch(Capacitive) with Camera

This is a beautiful 3.5” touchscreen display, based on ESP32-WROVER chip, with a built-in 2M pixel OV2640 camera. The combination of all these gives a perfect platform for multiple ESP32 Applications & Projects.

ESP32 3.5" TFT Touch with Camera

The TFT LCD driver is basically ILI9488 & has a dimension of 3.5″ with 320×480 screen resolution. The ILI9488 LCD uses SPI for communication with the ESP32 chip. The SPI main clock could be up to 60M~80M, make the display smooth enough for videos.

While the camera is not used, you can freely use all these pins with the breakout connectors. You can then connect the ESP32 display with sensors or modules & use it for any IoT applications. The ESP32 chip support Arduino or MicroPython programming

The board is having a micro SD-Card slot for attaching an external SD-Card. The SD Card can be used for storing files and images. There is a type C USB Port, basically a USB to UART converter for ESP32 programming. You can connect a Type-C data cable to the board & directly upload the code to the Board.

ESP32 TFT Touch Screen Camera

Specification

1. 3.5 inch display, 320×480
2. Capacitive/Resistive Touch
3. ESP32-WROVER Controller
4. WIFI/ BLE Connection.
5. Onboard USB2UART convertor for ESP32 programming
6. 2M pixel OV2640 Camera
7. OV2640 supports output images up to 2 million pixels
8. LCD 3.5-inch Amorphous-TFT-LCD for mobile-phone or handy electrical equipment
9. LCD Driver: ILI9488
10. LCD Resolution: 320*480
11. Micro SD card slot on the board
12. NS2009: A 4-wire resistive touch screen control circuit
13. FT6236: single-chip capacitive touch panel controller Integrated Circuit
14. Power supply: 5V, Type-C USB



Check some of our previous projects using this TFT Touch Screen Display:
1. ESP32 TFT Touch Display Video Games
2. ESP32 TFT Touch & Camera Projects
3. Ultrasonic Range Finder
4.Wind Speed Display
5. Temperature Humidity Monitoring


ESP32 Touch Indoor Environment Expansion

ESP32 Indoor Expansion

This is an Indoor environment expansion board for the ESP32 3.5 Touch Screen. The ESP32 3.5 TFT touch can also be used as a hardware expansion, with the expansion connector.

The Indoor environment expansion integrates the temperature & humidity sensor DHT11 and SGP30 Air Quality Sensor, to detect the CO2 and TVOC, so you can create an indoor environment detector easily. There is also an onboard buzzer that can be used for the alarm.


Installing Necessary Libraries to Arduino IDE

1. TFT_eSPI Library

This is a TFT graphics library for Arduino processors with performance optimization for STM32, ESP8266 & ESP32. The library is targeted at 32-bit processors. It supports “Four wire” SPI and 8 bit parallel interfaces. The library supports some TFT displays with drivers for ILI9163, ILI9225, ILI9341, ILI9488, ST7735 etc.

You can download the library from the Github directory. But the library requires some modification for our project. So here is an edited version of the library. You can download and add it to the library folder.

Download: TFT_eSPI Library


2. DHT11 Library

To read the temperature and humidity we need any temperature humidity sensor. For that DHT11 is the best and cheap sensor. We need to install DHT11 Library for that. Download the library from below and add to the Arduino IDE.

Download: DHT11 Library


3. Adafruit SPG30 Library

To read the environmental CO2 & TVOC Data, we need SPG30 Sensor. The Adafruit SGP30 Gas/Air Quality I2C sensor library measure equivalent carbondioxide as well as Total Volatile Organic Compound.

Download: Adafruit SPG30 Library




Indoor Environment Monitoring on Gauge

Let us use the ESP32 & LCD Screen for Indoor Environment Monitoring. The LCD Screen will display the value of temperature, humidity, and CO2 level in Gauge format.

The below code uses two files one as a .ino file and the other as a .h file.


Main Code

Copy the following code and paste it on your Arduino IDE and save it as .ino format.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
// Meter colour schemes
#define RED2RED 0
#define GREEN2GREEN 1
#define BLUE2BLUE 2
#define BLUE2RED 3
#define GREEN2RED 4
#define RED2GREEN 5
 
#define TFT_GREY 0x2104 // Dark grey 16 bit colour
 
 
#include "alert.h" // Out of range alert icon
 
#include <TFT_eSPI.h> // Hardware-specific library
#include <SPI.h>
 
#include <Wire.h>
#include "Adafruit_SGP30.h"
#include "DHT.h"
 
 
#define BUZZPIN 5
 
#define LDO_PWR_EN_PIN 19   //AP2112K enable pin
 
 
#define   BUZZER_ON     digitalWrite(BUZZPIN, HIGH)
#define   BUZZER_OFF   digitalWrite(BUZZPIN, LOW)
 
#define   LDO_1V8_ON   digitalWrite(LDO_PWR_EN_PIN, HIGH)
#define   LDO_1V8_OFF  digitalWrite(LDO_PWR_EN_PIN, LOW)
 
#define DHTPIN 18     // Digital pin connected to the DHT sensor
#define DHTTYPE DHT11   // DHT11
DHT dht(DHTPIN, DHTTYPE);
 
 
Adafruit_SGP30 sgp;
#define I2C_SDA 26
#define I2C_SCL 27
 
 
 
 
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library with default width and height
 
uint32_t runTime = -99999; // time for next update
int d = 0;       // Variable used for the sinewave test waveform
boolean range_error = 0;
 
unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
unsigned long debounceDelay = 1000; // the debounce time; increase if the output flickers
int CO2_level = 0;
int Count = 0;
int counter = 0;
float humidity = 0;
float temperature = 0;
 
 
uint32_t getAbsoluteHumidity(float temperature, float humidity) {
    // approximation formula from Sensirion SGP30 Driver Integration chapter 3.15
    const float absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f * exp((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature)); // [g/m^3]
    const uint32_t absoluteHumidityScaled = static_cast<uint32_t>(1000.0f * absoluteHumidity); // [mg/m^3]
    return absoluteHumidityScaled;
}
 
 
 
 
void setup(void)
{
  Serial.begin(115200);
 
dht.begin();
 
    pinMode(BUZZPIN, OUTPUT);
    pinMode(LDO_PWR_EN_PIN, OUTPUT);
 
 
    BUZZER_ON;
    LDO_1V8_ON;
    delay(1000);
    BUZZER_OFF;
 
    
    Wire.begin(I2C_SDA, I2C_SCL);
 
    Serial.println("SGP30 test");
 
  if (! sgp.begin()){
    Serial.println("Sensor not found :(");
    while (1);
  }
 
 
  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(TFT_BLACK);
 
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.drawString("Makerfabs", 10, 10, 2);
  //tft.drawString("CO2 level", 215, 305, 2);
  //tft.drawString("Tem", 100, 95, 2);
  //tft.drawString("Hum", 360, 95, 2);
}
 
void loop()
{
  co2_level_measure();
  if (millis() - runTime >= 0L)
  { // Execute every TBD ms
    runTime = millis();
 
    // Test with a slowly changing value from a Sine function
    //d += 4; if (d >= 360) d = 0;
 
    // Set the the position, gap between meters, and inner radius of the meters
    int xpos = 0, ypos = 5, gap = 4, radius = 52;
 
    // Draw meter and get back x position of next meter
 
    // Test with Sine wave function, normally reading will be from a sensor
    //reading = 250 + 250 * sineWave(d+0);
    //xpos = gap + ringMeter(reading, 0, 500, xpos, ypos, radius, "mA", GREEN2RED); // Draw analogue meter
 
    //reading = 20 + 30 * sineWave(d+60);
    //xpos = gap + ringMeter(reading, -10, 50, xpos, ypos, radius, "degC", BLUE2RED); // Draw analogue meter
 
    //reading = 50 + 50 * sineWave(d + 120);
    //ringMeter(reading, 0, 100, xpos, ypos, radius, "%RH", BLUE2BLUE); // Draw analogue meter
 
    // Draw two more larger meters
    //xpos = 20, ypos = 115, gap = 24, radius = 64;
 
    //reading = 1000 + 150 * sineWave(d + 90);
    //xpos = gap + ringMeter(reading, 850, 1150, xpos, ypos, radius, "mb", BLUE2RED); // Draw analogue meter
 
    //reading = 15 + 15 * sineWave(d + 150);
    //xpos = gap + ringMeter(reading, 0, 30, xpos, ypos, radius, "Volts", GREEN2GREEN); // Draw analogue meter
 
    // Draw a large meter
    xpos = 40, ypos = 30, gap = 15, radius = 70;
    // Comment out above meters, then uncomment the next line to show large meter
    ringMeter(CO2_level, 0, 5000, 160, 140, 80, "ppm", GREEN2RED); // Draw analogue meter
    ringMeter((int)temperature, -10, 50, 40, 30, 70, "C", BLUE2RED);
    ringMeter((int)humidity, 0, 100, 300, 30, 70, "RH%", RED2GREEN);
  }
}
 
// #########################################################################
//  Draw the meter on the screen, returns x coord of righthand side
// #########################################################################
int ringMeter(int value, int vmin, int vmax, int x, int y, int r, const char *units, byte scheme)
{
  // Minimum value of r is about 52 before value text intrudes on ring
  // drawing the text first is an option
 
  x += r;
  y += r; // Calculate coords of centre of ring
 
  int w = r / 3; // Width of outer ring is 1/4 of radius
 
  int angle = 150; // Half the sweep angle of meter (300 degrees)
 
  int v = map(value, vmin, vmax, -angle, angle); // Map the value to an angle v
 
  byte seg = 3; // Segments are 3 degrees wide = 100 segments for 300 degrees
  byte inc = 6; // Draw segments every 3 degrees, increase to 6 for segmented ring
 
  // Variable to save "value" text colour from scheme and set default
  int colour = TFT_BLUE;
 
  // Draw colour blocks every inc degrees
  for (int i = -angle + inc / 2; i < angle - inc / 2; i += inc)
  {
    // Calculate pair of coordinates for segment start
    float sx = cos((i - 90) * 0.0174532925);
    float sy = sin((i - 90) * 0.0174532925);
    uint16_t x0 = sx * (r - w) + x;
    uint16_t y0 = sy * (r - w) + y;
    uint16_t x1 = sx * r + x;
    uint16_t y1 = sy * r + y;
 
    // Calculate pair of coordinates for segment end
    float sx2 = cos((i + seg - 90) * 0.0174532925);
    float sy2 = sin((i + seg - 90) * 0.0174532925);
    int x2 = sx2 * (r - w) + x;
    int y2 = sy2 * (r - w) + y;
    int x3 = sx2 * r + x;
    int y3 = sy2 * r + y;
 
    if (i < v)
    { // Fill in coloured segments with 2 triangles
      switch (scheme)
      {
      case 0:
        colour = TFT_RED;
        break; // Fixed colour
      case 1:
        colour = TFT_GREEN;
        break; // Fixed colour
      case 2:
        colour = TFT_BLUE;
        break; // Fixed colour
      case 3:
        colour = rainbow(map(i, -angle, angle, 0, 127));
        break; // Full spectrum blue to red
      case 4:
        colour = rainbow(map(i, -angle, angle, 70, 127));
        break; // Green to red (high temperature etc)
      case 5:
        colour = rainbow(map(i, -angle, angle, 127, 63));
        break; // Red to green (low battery etc)
      default:
        colour = TFT_BLUE;
        break; // Fixed colour
      }
      tft.fillTriangle(x0, y0, x1, y1, x2, y2, colour);
      tft.fillTriangle(x1, y1, x2, y2, x3, y3, colour);
      //text_colour = colour; // Save the last colour drawn
    }
    else // Fill in blank segments
    {
      tft.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_GREY);
      tft.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_GREY);
    }
  }
  // Convert value to a string
  char buf[10];
  byte len = 3;
  if (value > 999)
    len = 5;
  dtostrf(value, len, 0, buf);
  buf[len] = ' ';
  buf[len + 1] = 0; // Add blanking space and terminator, helps to centre text too!
  // Set the text colour to default
  tft.setTextSize(1);
 
  if (value < vmin || value > vmax)
  {
    drawAlert(x, y + 90, 50, 1);
  }
  else
  {
    drawAlert(x, y + 90, 50, 0);
  }
 
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  // Uncomment next line to set the text colour to the last segment value!
  tft.setTextColor(colour, TFT_BLACK);
  tft.setTextDatum(MC_DATUM);
  // Print value, if the meter is large then use big font 8, othewise use 4
  if (r > 84)
  {
    tft.setTextPadding(55 * 3);   // Allow for 3 digits each 55 pixels wide
    tft.drawString(buf, x, y, 8); // Value in middle
  }
  else
  {
    tft.setTextPadding(3 * 14);   // Allow for 3 digits each 14 pixels wide
    tft.drawString(buf, x, y, 4); // Value in middle
  }
  tft.setTextSize(1);
  tft.setTextPadding(0);
  // Print units, if the meter is large then use big font 4, othewise use 2
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  if (r > 84)
    tft.drawString(units, x, y + 60, 4); // Units display
  else
    tft.drawString(units, x, y + 15, 2); // Units display
 
  // Calculate and return right hand side x coordinate
  return x + r;
}
 
void drawAlert(int x, int y, int side, boolean draw)
{
  if (draw && !range_error)
  {
    drawIcon(alert, x - alertWidth / 2, y - alertHeight / 2, alertWidth, alertHeight);
    range_error = 1;
  }
  else if (!draw)
  {
    tft.fillRect(x - alertWidth / 2, y - alertHeight / 2, alertWidth, alertHeight, TFT_BLACK);
    range_error = 0;
  }
}
 
// #########################################################################
// Return a 16 bit rainbow colour
// #########################################################################
unsigned int rainbow(byte value)
{
  // Value is expected to be in range 0-127
  // The value is converted to a spectrum colour from 0 = blue through to 127 = red
 
  byte red = 0;   // Red is the top 5 bits of a 16 bit colour value
  byte green = 0; // Green is the middle 6 bits
  byte blue = 0;  // Blue is the bottom 5 bits
 
  byte quadrant = value / 32;
 
  if (quadrant == 0)
  {
    blue = 31;
    green = 2 * (value % 32);
    red = 0;
  }
  if (quadrant == 1)
  {
    blue = 31 - (value % 32);
    green = 63;
    red = 0;
  }
  if (quadrant == 2)
  {
    blue = 0;
    green = 63;
    red = value % 32;
  }
  if (quadrant == 3)
  {
    blue = 0;
    green = 63 - 2 * (value % 32);
    red = 31;
  }
  return (red << 11) + (green << 5) + blue;
}
 
// #########################################################################
// Return a value in range -1 to +1 for a given phase angle in degrees
// #########################################################################
float sineWave(int phase)
{
  return sin(phase * 0.0174532925);
}
 
//====================================================================================
// This is the function to draw the icon stored as an array in program memory (FLASH)
//====================================================================================
 
// To speed up rendering we use a 64 pixel buffer
#define BUFF_SIZE 64
 
// Draw array "icon" of defined width and height at coordinate x,y
// Maximum icon size is 255x255 pixels to avoid integer overflow
 
void drawIcon(const unsigned short *icon, int16_t x, int16_t y, int8_t width, int8_t height)
{
 
  uint16_t pix_buffer[BUFF_SIZE]; // Pixel buffer (16 bits per pixel)
 
  tft.startWrite();
 
  // Set up a window the right size to stream pixels into
  tft.setAddrWindow(x, y, width, height);
 
  // Work out the number whole buffers to send
  uint16_t nb = ((uint16_t)height * width) / BUFF_SIZE;
 
  // Fill and send "nb" buffers to TFT
  for (int i = 0; i < nb; i++)
  {
    for (int j = 0; j < BUFF_SIZE; j++)
    {
      pix_buffer[j] = pgm_read_word(&icon[i * BUFF_SIZE + j]);
    }
    tft.pushColors(pix_buffer, BUFF_SIZE);
  }
 
  // Work out number of pixels not yet sent
  uint16_t np = ((uint16_t)height * width) % BUFF_SIZE;
 
  // Send any partial buffer left over
  if (np)
  {
    for (int i = 0; i < np; i++)
      pix_buffer[i] = pgm_read_word(&icon[nb * BUFF_SIZE + i]);
    tft.pushColors(pix_buffer, np);
  }
 
  tft.endWrite();
}
 
//wind
void co2_level_measure()
{
  humidity = dht.readHumidity();
temperature = dht.readTemperature();
 
 
    Serial.print("humidity=");
    Serial.println(humidity);
    Serial.print("temperature=");
    Serial.println(temperature);
 
 
  // put your main code here, to run repeatedly:
  // If you have a temperature / humidity sensor, you can set the absolute humidity to enable the humditiy compensation for the air quality signals
  //float temperature = 22.1; // [°C]
  //float humidity = 45.2; // [%RH]
  sgp.setHumidity(getAbsoluteHumidity(temperature, humidity));
 
  if (! sgp.IAQmeasure()) {
    Serial.println("Measurement failed");
    return;
  }
  Serial.print("TVOC "); Serial.print(sgp.TVOC); Serial.print(" ppb\t");
  Serial.print("eCO2 "); Serial.print(sgp.eCO2); Serial.println(" ppm");
  CO2_level= sgp.eCO2;
  
  if (! sgp.IAQmeasureRaw()) {
    Serial.println("Raw Measurement failed");
    return;
  }
  Serial.print("Raw H2 "); Serial.print(sgp.rawH2); Serial.print(" \t");
  Serial.print("Raw Ethanol "); Serial.print(sgp.rawEthanol); Serial.println("");
  counter++;
  delay(500);
  if (counter == 30) {
    counter = 0;
 
    uint16_t TVOC_base, eCO2_base;
    if (! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) {
      Serial.println("Failed to get baseline readings");
      return;
    }
    Serial.print("****Baseline values: eCO2: 0x"); Serial.print(eCO2_base, HEX);
    Serial.print(" & TVOC: 0x"); Serial.println(TVOC_base, HEX);
  }
 
}
 
void onChange()
{
 
}


Alert.h

Open a new tab in Arduino IDE. Copy the following code and save it as an Alert.h file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// We need this header file to use FLASH as storage with PROGMEM directive:
#include <pgmspace.h>
 
// Icon width and height
const uint16_t alertWidth = 32;
const uint16_t alertHeight = 32;
 
// The icon file can be created with the "UTFT ImageConverter 565" bitmap to .c file creation utility, more can be pasted in here
const unsigned short  alert[1024] PROGMEM={
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0840,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 0, 32 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1080,0xAC66,0xEDE8,0xFE69,0xC4C6,0x2901,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 1, 64 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xBCC6,0xFE68,0xFE68,0xFE6A,0xFE68,0xEDE8,0x18A1,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 2, 96 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x8344,0xFE48,0xFE8C,0xFFDD,0xFFFF,0xFEF0,0xFE48,0xB466,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 3, 128 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1880,0xEDC7,0xFE48,0xFF99,0xFFBC,0xFF9B,0xFFBD,0xFE6A,0xFE48,0x5A23,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 4, 160 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x9BE5,0xFE28,0xFED0,0xFFBC,0xFF7A,0xFF9A,0xFF9B,0xFF35,0xFE28,0xBCA6,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 5, 192 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3962,0xFE28,0xFE28,0xFF9A,0xFF79,0xFF9A,0xFF9B,0xFF9A,0xFFBD,0xFE6B,0xFE28,0x72E3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 6, 224 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xB465,0xFE28,0xFEF2,0xFF7A,0xFF79,0xFF7A,0xFF9A,0xFF7A,0xFF7A,0xFF78,0xFE28,0xDD67,0x0860,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 7, 256 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x5A22,0xFE07,0xFE29,0xFF9B,0xFF37,0xFF58,0xFF79,0xFF79,0xFF79,0xFF58,0xFF9B,0xFEAE,0xFE07,0x93A4,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 8, 288 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xC4A5,0xFE07,0xFF15,0xFF37,0xFF36,0xAD11,0x2965,0x2965,0xCDF4,0xFF37,0xFF37,0xFF79,0xFE07,0xFE07,0x2901,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 9, 320 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x7B03,0xFDE7,0xFE4B,0xFF79,0xFEF4,0xFF15,0xB552,0x2945,0x2945,0xDE55,0xFF16,0xFF15,0xFF58,0xFED1,0xFDE7,0xAC25,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 10, 352 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0840,0xDD26,0xFDE7,0xFF57,0xFED3,0xFED2,0xFEF4,0xBD93,0x2124,0x2124,0xDE75,0xFF14,0xFED3,0xFED3,0xFF7A,0xFE08,0xFDE7,0x49A2,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 11, 384 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x9BA4,0xFDC6,0xFE6E,0xFF36,0xFE90,0xFEB1,0xFED3,0xC592,0x2124,0x2124,0xE675,0xFED3,0xFEB2,0xFEB1,0xFEF3,0xFEF3,0xFDC6,0xBC45,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 12, 416 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3141,0xF5C6,0xF5C7,0xFF58,0xFE90,0xFE6F,0xFE8F,0xFEB1,0xCDB2,0x2104,0x2104,0xF6B4,0xFEB1,0xFE90,0xFE8F,0xFE90,0xFF58,0xFE0A,0xF5C6,0x72A3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 13, 448 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xABE4,0xF5A6,0xFEB1,0xFED3,0xFE4E,0xFE6E,0xFE6F,0xFE90,0xD5F2,0x18E3,0x18E3,0xFED4,0xFE90,0xFE6F,0xFE6F,0xFE6E,0xFE91,0xFF36,0xF5A6,0xCCA5,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 14, 480 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0x5202,0xF5A6,0xF5C7,0xFF58,0xFE4D,0xFE4D,0xFE4D,0xFE4E,0xFE6F,0xDE11,0x18C3,0x18C3,0xFED3,0xFE6F,0xFE6E,0xFE4E,0xFE4D,0xFE4D,0xFF16,0xFE2C,0xF5A6,0x9363,0x0000,0x0000,0x0000,0x0000,0x0000, // row 15, 512 pixels
0x0000,0x0000,0x0000,0x0000,0x0000,0xBC44,0xF585,0xFED3,0xFE6F,0xFE2C,0xFE2C,0xFE2D,0xFE4D,0xFE4E,0xE630,0x10A2,0x2104,0xFED1,0xFE4E,0xFE4D,0xFE4D,0xFE2D,0xFE2C,0xFE4D,0xFF37,0xF586,0xF585,0x28E1,0x0000,0x0000,0x0000,0x0000, // row 16, 544 pixels
0x0000,0x0000,0x0000,0x0000,0x7282,0xF565,0xF5EA,0xFF16,0xFE0B,0xFE0B,0xFE0B,0xFE2C,0xFE2C,0xFE4D,0xF670,0x1082,0x2924,0xFEB0,0xFE2D,0xFE2C,0xFE2C,0xFE2C,0xFE0B,0xFE0B,0xFEB2,0xFE6F,0xF565,0xA383,0x0000,0x0000,0x0000,0x0000, // row 17, 576 pixels
0x0000,0x0000,0x0000,0x0840,0xD4C4,0xF565,0xFEF5,0xFE0C,0xFDE9,0xFDEA,0xFE0A,0xFE0B,0xFE0B,0xFE2C,0xFE8F,0x0861,0x2964,0xFE8F,0xFE2C,0xFE0B,0xFE0B,0xFE0B,0xFE0A,0xFDEA,0xFE0B,0xFF37,0xF586,0xF565,0x4181,0x0000,0x0000,0x0000, // row 18, 608 pixels
0x0000,0x0000,0x0000,0x9343,0xF545,0xF60C,0xFED3,0xFDC8,0xFDC8,0xFDC9,0xFDE9,0xFDEA,0xFDEA,0xFE0B,0xFE8E,0x0861,0x3184,0xFE6D,0xFE0B,0xFE0A,0xFDEA,0xFDEA,0xFDE9,0xFDC9,0xFDC9,0xFE4E,0xFEB2,0xF545,0xB3E3,0x0000,0x0000,0x0000, // row 19, 640 pixels
0x0000,0x0000,0x28E0,0xF544,0xF545,0xFF17,0xFDC8,0xFDA7,0xFDA7,0xFDC8,0xFDC8,0xFDC9,0xFDC9,0xFDE9,0xFE6C,0x10A2,0x39C4,0xFE4C,0xFDEA,0xFDE9,0xFDC9,0xFDC9,0xFDC8,0xFDC8,0xFDA7,0xFDA8,0xFF16,0xF588,0xF544,0x6222,0x0000,0x0000, // row 20, 672 pixels
0x0000,0x0000,0xA383,0xF524,0xF64E,0xFE4E,0xFD86,0xFD86,0xFD87,0xFDA7,0xFDA7,0xFDA8,0xFDC8,0xFDC8,0xFE2A,0xA469,0xB4EA,0xFE2A,0xFDC9,0xFDC8,0xFDC8,0xFDA8,0xFDA7,0xFDA7,0xFD87,0xFD86,0xFDEA,0xFED3,0xF524,0xC443,0x0000,0x0000, // row 21, 704 pixels
0x0000,0x51C1,0xF504,0xF546,0xFF16,0xF565,0xFD65,0xFD65,0xFD86,0xFD86,0xFD86,0xFDA7,0xFDA7,0xFDA7,0xFDE8,0xFE6A,0xFE4A,0xFDE8,0xFDA7,0xFDA7,0xFDA7,0xFDA7,0xFD86,0xFD86,0xFD86,0xFD65,0xFD65,0xFEB2,0xF5CA,0xF504,0x8AE2,0x0000, // row 22, 736 pixels
0x0000,0xB3A2,0xED03,0xFE92,0xFDC9,0xF543,0xF544,0xFD44,0xFD65,0xFD65,0xFD65,0xFD86,0xFD86,0xFD86,0xFDA7,0xFDC7,0xFDC7,0xFDA7,0xFD86,0xFD86,0xFD86,0xFD86,0xFD65,0xFD65,0xFD65,0xFD44,0xF544,0xFD86,0xFEF5,0xED03,0xE4C3,0x1880, // row 23, 768 pixels
0x7241,0xECE3,0xF567,0xFED3,0xF523,0xF523,0xF523,0xF543,0xF544,0xF544,0xFD65,0xFD65,0xFD65,0xFD65,0xD4E6,0x39C5,0x39A5,0xD4E6,0xFD86,0xFD65,0xFD65,0xFD65,0xFD65,0xF544,0xF544,0xF543,0xF523,0xF523,0xFE2E,0xF5EC,0xECE3,0x9B42, // row 24, 800 pixels
0xD443,0xECE3,0xFED4,0xF565,0xF502,0xF502,0xF522,0xF523,0xF523,0xF543,0xF544,0xF544,0xF544,0xFD65,0x8B64,0x18C3,0x18C3,0x8344,0xFD85,0xFD44,0xF544,0xF544,0xF544,0xF543,0xF523,0xF523,0xF522,0xF502,0xF523,0xFEF5,0xED04,0xECE3, // row 25, 832 pixels
0xECC3,0xF5AB,0xFE6F,0xF501,0xF4E1,0xF501,0xF502,0xF502,0xF522,0xF522,0xF523,0xF523,0xF523,0xFD84,0xC504,0x20E1,0x18E1,0xC4E4,0xFD84,0xF543,0xF523,0xF523,0xF523,0xF522,0xF522,0xF502,0xF502,0xF501,0xF501,0xFDC9,0xF62F,0xECC3, // row 26, 864 pixels
0xECC2,0xFE92,0xF523,0xF4E0,0xF4E0,0xF4E1,0xF4E1,0xF501,0xF501,0xF502,0xF502,0xF522,0xF522,0xF543,0xFDE3,0xFEA5,0xF6A4,0xFE04,0xF543,0xF522,0xF522,0xF522,0xF502,0xF502,0xF501,0xF501,0xF4E1,0xF4E1,0xF4E0,0xF4E1,0xFED4,0xECC2, // row 27, 896 pixels
0xECA2,0xF5EC,0xF4E0,0xF4C0,0xF4E0,0xF4E0,0xF4E0,0xF4E1,0xF4E1,0xF501,0xF501,0xF501,0xF502,0xF502,0xF542,0xFDA2,0xFDA2,0xF542,0xF502,0xF502,0xF502,0xF501,0xF501,0xF501,0xF4E1,0xF4E1,0xF4E0,0xF4E0,0xF4E0,0xF4C0,0xF5A9,0xECA2, // row 28, 928 pixels
0xECA2,0xECA2,0xECC2,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4E1,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xECC2,0xECC3,0xECA2, // row 29, 960 pixels
0x8AC1,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0x9B01, // row 30, 992 pixels
0x0000,0x1880,0x51A0,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x61E0,0x28E0,0x0000}; // row 31, 1024 pixels


From the tool menu select the ESP32 Wrover Module and following other options.

Connect the ESP32 Board with Type-C USB Cable and then upload the code.

After uploading the code, press the reset button. You will then see three different widgets/gauges displaying the data of temperature humidity and CO2.

Indoor Air Quality Monitor with SPG30 & DHT11 on ESP32 LCD Screen


CO2 & TVOC Historical Data Monitoring in Graph

Let us again use the ESP32 & LCD Screen for CO2 & TVOC Monitoring. The LCD Screen will display the value of CO2 and TVOC level in Graphical format. So that you can monitor the historical data.

Copy the following code and upload it to the ESP32 Board.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#include "SPI.h"
#include "TFT_eSPI.h"
#include <Wire.h>
#include "Adafruit_SGP30.h"
#include "DHT.h"
 
#define BUZZER_ON digitalWrite(BUZZPIN, HIGH)
#define BUZZER_OFF digitalWrite(BUZZPIN, LOW)
#define LDO_1V8_ON digitalWrite(LDO_PWR_EN_PIN, HIGH)
#define LDO_1V8_OFF digitalWrite(LDO_PWR_EN_PIN, LOW)
 
#define DHTPIN 18     // Digital pin connected to the DHT sensor
#define DHTTYPE DHT11 // DHT11
#define BUZZPIN 5
#define LDO_PWR_EN_PIN 19 //AP2112K enable pin
#define I2C_SDA 26
#define I2C_SCL 27
 
TFT_eSPI tft = TFT_eSPI();
DHT dht(DHTPIN, DHTTYPE);
Adafruit_SGP30 sgp;
 
int list_1[50] = {0};
int list_1_length = 20;
int list_2[50] = {0};
int list_2_length = 20;
int interval = 3000;
 
void setup()
{
    Serial.begin(115200);
    Serial.println("Enviroment Test!");
 
    dht.begin();
 
    pinMode(BUZZPIN, OUTPUT);
    pinMode(LDO_PWR_EN_PIN, OUTPUT);
 
    BUZZER_ON;
    LDO_1V8_ON;
    delay(1000);
    BUZZER_OFF;
 
    Wire.begin(I2C_SDA, I2C_SCL);
 
    Serial.println("SGP30 test");
 
    if (!sgp.begin())
    {
        Serial.println("Sensor not found :(");
        while (1)
            ;
    }
 
    //Draw frame
    tft.init();
    tft.fillScreen(TFT_BLACK);
 
    tft.setTextColor(TFT_WHITE);
    tft.setTextSize(2);
 
    tft.setCursor(10, 40);
    tft.println("Makerfabs line chart");
 
    delay(1000);
    tft.fillScreen(TFT_BLACK);
    draw_line_chart_window("TVOC Line Chart ( Unit : ppb ) ", (String) "Interval : " + interval + " ms", list_1_length, 0, 1200, TFT_WHITE);
    draw_line_chart_window2("ECO2 Line Chart ( Unit : ppm ) ", (String) "Interval : " + interval + " ms", list_2_length, 0, 5000, TFT_WHITE);
}
 
void loop()
{
    draw_line_chart(list_1, list_1_length, 0, 1200, TFT_WHITE);
    draw_line_chart2(list_2, list_2_length, 0, 5000, TFT_WHITE);
    delay(interval);
 
    int tvoc = 0;
    int eco2 = 0;
    weather_read(&tvoc, &eco2);
 
    draw_line_chart(list_1, list_1_length, 0, 1200, TFT_BLACK);
    draw_line_chart2(list_2, list_2_length, 0, 5000, TFT_BLACK);
    add_list(list_1, list_1_length, tvoc);
    add_list(list_2, list_2_length, eco2);
}
 
// Add a num to the beginning of the list.
void add_list(int *list, int length, int num)
{
    for (int i = length - 2; i >= 0; i--)
    {
        *(list + i + 1) = *(list + i);
    }
    *list = num;
}
 
void draw_line_chart_window(String text1, String text2, int length, int low, int high, int color)
{
    //draw rect and unit
    //tft.drawRect(20, 20, 280, 200, color);
    tft.drawLine(30, 20, 30, 220, color);
    tft.drawLine(30, 220, 300, 220, color);
 
    tft.drawLine(20, 20, 30, 20, color);
    tft.drawLine(20, 120, 30, 120, color);
 
    tft.setTextColor(color);
    tft.setTextSize(1);
 
    tft.setCursor(0, 10);
    tft.println(high);
 
    tft.setCursor(80, 10);
    tft.println(text1);
 
    tft.setCursor(0, 110);
    tft.println((high + low) / 2);
 
    tft.setCursor(0, 210);
    tft.println(low);
 
    tft.setCursor(80, 230);
    tft.println(text2);
 
    int x_start = 32;
    int x_unit = 280 / (length - 1);
    for (int i = 0; i < length; i++)
    {
        int x = x_start + x_unit * i;
        if (i != 0 && i != length - 1)
            tft.drawLine(x, 220, x, 225, color);
    }
}
 
void draw_line_chart(int *list, int length, int low, int high, int color)
{
    //list to position
    int pos[50][2] = {0};
    int detail = 50;
    int x_start = 32;
    int y_start = 218;
    int x_unit = 280 / (length - 1);
    int y_unit = -200 / (detail - 1);
    for (int i = 0; i < length; i++)
    {
        pos[i][0] = x_start + i * x_unit;
        int y = map(*(list + i), low, high, 0, detail);
        if (y > detail)
            y = detail;
        pos[i][1] = y_start + y_unit * y;
    }
 
    //draw line chart
    for (int i = 0; i < length - 1; i++)
    {
        tft.drawLine(pos[i][0], pos[i][1], pos[i + 1][0], pos[i + 1][1], color);
    }
}
 
uint32_t getAbsoluteHumidity(float temperature, float humidity)
{
    // approximation formula from Sensirion SGP30 Driver Integration chapter 3.15
    const float absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f * exp((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature)); // [g/m^3]
    const uint32_t absoluteHumidityScaled = static_cast<uint32_t>(1000.0f * absoluteHumidity);                                                                // [mg/m^3]
    return absoluteHumidityScaled;
}
 
void weather_read(int *tvoc, int *eco2)
{
 
    float humidity = dht.readHumidity();
    float temperature = dht.readTemperature();
 
    Serial.print("humidity=");
    Serial.println(humidity);
    Serial.print("temperature=");
    Serial.println(temperature);
 
    sgp.setHumidity(getAbsoluteHumidity(temperature, humidity));
 
    if (!sgp.IAQmeasure())
    {
        Serial.println("Measurement failed");
        return;
    }
    Serial.print("TVOC ");
    Serial.print(*tvoc = sgp.TVOC);
    Serial.print(" ppb\t");
    Serial.print("eCO2 ");
    Serial.print(*eco2 = sgp.eCO2);
    Serial.println(" ppm");
}
 
void draw_line_chart_window2(String text1, String text2, int length, int low, int high, int color)
{
    //draw rect and unit
    //tft.drawRect(20, 20, 280, 200, color);
    tft.drawLine(30, 260, 30, 460, color);
    tft.drawLine(30, 460, 300, 460, color);
 
    tft.drawLine(20, 260, 30, 260, color);
    tft.drawLine(20, 360, 30, 360, color);
 
    tft.setTextColor(color);
    tft.setTextSize(1);
 
    tft.setCursor(0, 250);
    tft.println(high);
 
    tft.setCursor(80, 250);
    tft.println(text1);
 
    tft.setCursor(0, 350);
    tft.println((high + low) / 2);
 
    tft.setCursor(0, 450);
    tft.println(low);
 
    tft.setCursor(80, 470);
    tft.println(text2);
 
    int x_start = 32;
    int x_unit = 280 / (length - 1);
    for (int i = 0; i < length; i++)
    {
        int x = x_start + x_unit * i;
        if (i != 0 && i != length - 1)
            tft.drawLine(x, 460, x, 465, color);
    }
}
 
void draw_line_chart2(int *list, int length, int low, int high, int color)
{
    //list to position
    int pos[50][2] = {0};
    int detail = 50;
    int x_start = 32;
    int y_start = 458;
    int x_unit = 280 / (length - 1);
    int y_unit = -200 / (detail - 1);
    for (int i = 0; i < length; i++)
    {
        pos[i][0] = x_start + i * x_unit;
        int y = map(*(list + i), low, high, 0, detail);
        if (y > detail)
            y = detail;
        pos[i][1] = y_start + y_unit * y;
    }
 
    //draw line chart
    for (int i = 0; i < length - 1; i++)
    {
        tft.drawLine(pos[i][0], pos[i][1], pos[i + 1][0], pos[i + 1][1], color);
    }
}

Upload the code again. After uploading the code, press the reset button. You will then see graphical display data of CO2 & TVOC.

Indoor Air Quality Monitoring ESP32


Video Tutorial & Demo

Indoor Environment Monitoring with ESP32 & TFT LCD Screen on Gauge & Line Chart
Watch this video on YouTube.

Share. Facebook Twitter Pinterest LinkedIn Tumblr Email Reddit Telegram WhatsApp
Previous ArticleGSM & Arduino Communication with Firebase or Thingspeak
Next Article How to use TM1637 4-digit 7-segment LED display with Arduino

Related Posts

IoT Based PM & Air Quality Monitoring System using ESP32

IoT Based PM & Air Quality Monitoring System using ESP32

DIY ESP32 MLX90640 IR Thermal Camera with Live Web Display

DIY ESP32 MLX90640 IR Thermal Camera with Live Web Display

Updated:May 10, 20261K
IoT Activity Tracker with ESP32 & Accelerometer Gyroscope

IoT Activity Tracker with ESP32 & Accelerometer/Gyroscope

Updated:May 2, 2026

ESP32 IoT Vehicle Motion Analyzer with MPU6050 & LIS3MDL

Updated:April 27, 20261K
High-Accuracy Pitch, Roll, Yaw with ESP32 & BNO08x IMU

High-Accuracy Pitch, Roll, Yaw with ESP32 & BNO08x IMU

Updated:April 27, 20262K
DIY Colorimeter using AS7265x Spectroscopy Sensor & ESP32

DIY Colorimeter using AS7265x Spectroscopy Sensor & ESP32

Updated:February 1, 20261K
View 3 Comments

3 Comments

  1. Chris on September 28, 2021 1:22 AM

    Is there a way to add soil monitoring sensors to this? NPK, EC, and moisture. If so what are the extra parts one would need?

    Reply
    • Admin on September 28, 2021 10:16 AM

      There will be a video coming on this, couple of month later.

      Reply
  2. Chris on September 28, 2021 11:23 AM

    Awesome, I will keep an eye out for it. Thank you.

    Reply

CommentsCancel reply

Latest Posts
IoT Based PM & Air Quality Monitoring System using ESP32

IoT Based PM & Air Quality Monitoring System using ESP32

May 31, 2026
DIY ESP32 MLX90640 IR Thermal Camera with Live Web Display

DIY ESP32 MLX90640 IR Thermal Camera with Live Web Display

May 10, 2026
IoT Activity Tracker with ESP32 & Accelerometer Gyroscope

IoT Activity Tracker with ESP32 & Accelerometer/Gyroscope

May 2, 2026
A Guide to Sourcing Obsolete ICs for Vintage Projects

Beyond AliExpress: A Guide to Sourcing Obsolete ICs for Vintage Projects

April 21, 2026

ESP32 IoT Vehicle Motion Analyzer with MPU6050 & LIS3MDL

April 27, 2026
Building a Smart Sensor Node with a BLE Microcontroller

Building a Smart Sensor Node with a BLE Microcontroller

February 26, 2026
High-Accuracy Pitch, Roll, Yaw with ESP32 & BNO08x IMU

High-Accuracy Pitch, Roll, Yaw with ESP32 & BNO08x IMU

April 27, 2026
DIY Colorimeter using AS7265x Spectroscopy Sensor & ESP32

DIY Colorimeter using AS7265x Spectroscopy Sensor & ESP32

February 1, 2026
Top Posts & Pages
  • IoT Based PM & Air Quality Monitoring System using ESP32
    IoT Based PM & Air Quality Monitoring System using ESP32
  • 12V DC to 220V AC Inverter Circuit & PCB
    12V DC to 220V AC Inverter Circuit & PCB
  • IoT AC Energy Meter with PZEM-004T & ESP32 WebServer
    IoT AC Energy Meter with PZEM-004T & ESP32 WebServer
  • Buck Converter: Basics, Working, Design & Application
    Buck Converter: Basics, Working, Design & Application
  • How to use INA219 DC Current Sensor Module with Arduino
    How to use INA219 DC Current Sensor Module with Arduino
  • ECG Graph Monitoring with AD8232 ECG Sensor & Arduino
    ECG Graph Monitoring with AD8232 ECG Sensor & Arduino
  • IoT Based Electricity Energy Meter using ESP32 & Blynk
    IoT Based Electricity Energy Meter using ESP32 & Blynk
  • How to use INA226 DC Current Sensor with Arduino
    How to use INA226 DC Current Sensor with Arduino
Categories
  • Arduino Projects (197)
  • Articles (60)
    • Learn Electronics (19)
    • Product Review (15)
    • Tech Articles (28)
  • Electronics Circuits (46)
    • 555 Timer Projects (21)
    • Op-Amp Circuits (7)
    • Power Electronics (13)
  • IoT Projects (204)
    • ESP32 MicroPython (7)
    • ESP32 Projects (81)
    • ESP32-CAM Projects (15)
    • ESP8266 Projects (76)
    • LoRa/LoRaWAN Projects (22)
  • Microcontrollers (38)
    • AMB82-Mini IoT AI Camera (4)
    • BLE Projects (18)
    • STM32 Projects (19)
  • Raspberry Pi (93)
    • Raspberry Pi Pico Projects (57)
    • Raspberry Pi Pico W Projects (12)
    • Raspberry Pi Projects (24)
Follow Us
  • Facebook
  • Twitter
  • Pinterest
  • Instagram
  • YouTube
About Us

“‘How to Electronics’ is a vibrant community for electronics enthusiasts and professionals. We deliver latest insights in areas such as Embedded Systems, Power Electronics, AI, IoT, and Robotics. Our goal is to stimulate innovation and provide practical solutions for students, organizations, and industries. Join us to transform learning into a joyful journey of discovery and innovation.

Copyright © How To Electronics. All rights reserved.
  • About Us
  • Disclaimer
  • Privacy Policy
  • Contact Us
  • Advertise With Us

Type above and press Enter to search. Press Esc to cancel.

Ad Blocker Enabled!
Ad Blocker Enabled!
Looks like you're using an ad blocker. Please allow ads on our site. We rely on advertising to help fund our site.