Room Air Quality Monitor

by Arnov Sharma in Circuits > Raspberry Pi

98 Views, 1 Favorites, 0 Comments

Room Air Quality Monitor

Room AQI Monitor SGP40 and Raspberry Pi PICO W
59.gif
55.gif
45.gif
IMG_7528.JPG

Greetings everyone and welcome back!

Here’s something super fun and informative: the Room Air Quality Meter Project that turns room air quality data into a vibrant visual experience.

This device, which is powered by the Raspberry Pi Pico W and the SGP40 gas sensor, does more than just monitor the air; it visualizes the environment using Conway’s Game of Life on two RGB LED panels.

It is designed to constantly track Total Volatile Organic Compounds (TVOC) and convert the measurements into a dynamic simulation.

Powered by the Raspberry Pi Pico W and the SGP40 gas sensor, this device goes beyond traditional air monitoring.

The display provides immediate, clear feedback as the air quality varies by changing its color from a soothing green to a warning yellow to a bright red. The experience is made more interesting and informative by the availability of live TVOC data via a simple web app, which is made possible by the Pico W's onboard Wi-Fi.

It combines interactive design with environmental awareness in a small, wireless package! and this article presents the simple steps you can take to develop this project. And now let's begin the build process.

Supplies

These are the materials used in this project:

  1. Custom PCBs (provided by PCBWAY)
  2. RGB P3 64x32 Matrix Board
  3. Raspberry Pi PICO W
  4. IP5306 IC
  5. 10 uF SMD Capacitors 1206 Package
  6. USB Type-C Port
  7. 3D-Printed Parts
  8. Li-ion Cell 3.7V 2200mAh 18650
  9. 18650 Cell holder SMD version
  10. Connecting Wires
  11. SGP40 Gas Sensor PG7 Probe

PREVIOUS PROJECT—PORTABLE AIR QUALITY METER

Air Quality Meter using MQ135 ESP32
FC26R5SM56RLGHM.jpg
FJ3PQ6IM56RLJ0D.png

Here's a quick recap of how this project got started:In order to monitor the AQI levels in my locale, I made a very basic version of an air quality meter that uses an MQ135 gas sensor to monitor air data such as the detection of smoke, CO₂, nitrogen oxide, ammonia, etc. in the atmosphere. The data is then displayed on an SSD1306 OLED display. This setup worked, but it only showed the amount of harmful gases suspended in our environment, not the actual AQI readings.

https://www.instructables.com/Portable-Air-Quality-Meter/

After revisiting our approach, we found the SGP40, a top-notch AQI sensor that provides real-time air quality readings and excels at detecting Total Volatile Organic Compounds (TVOC).

DESIGN

untitled.166.png
untitled.168.png
19.gif
untitled.167.png
untitled.169.png
Screenshot 2025-08-11 142041.jpg
Screenshot 2025-08-11 142057.jpg
Screenshot 2025-08-11 142120.jpg

To begin the project, we created a 3D model utilizing the same two Matrix panel arrangement used in our previous Snake game max project.

In that project, we created two frame-like parts that connect two matrix panels to form a single, very long panel by attaching the two panels together side by side.

We have modeled a small driver board on the backside of the matrix, which is fastened to two mounting holes on one of the frames supporting the two displays.

A separate battery board with an SMD lithium cell holder was then added.

Our objective is to wall-mount the setup vertically, so we modeled two frame-like parts. The top frame part will be used to hang the setup on a wall using nails, and the bottom frame part houses the SGP40 Probe. We also added a circular opening where the PG7 connector can be fastened.

Following model completion, we used our Creality K10 Max to print the Top and bottom frames from orange PLA, while the Matrix holder parts were reused from our previous Snake Game Max Project.

PCB DESIGN

SCH_page-0001.jpg
Screenshot 2025-08-11 142719.jpg
Screenshot 2025-08-11 143240.jpg
Screenshot 2025-06-05 114837.png
Screenshot 2025-06-02 141444.png
Screenshot 2025-08-11 142519.jpg
Screenshot 2025-08-11 143114.jpg

The PCB design process came next, and we began putting together the schematic. There are two main sections in the schematic: the Power section, which included the IP5306 power management circuit, which is a very helpful circuit that I always use in my projects when 5V power is needed. It is dependable, simple to assemble, requires few components, has good efficiency, has battery low- and high-cut features, even has LED fuel indication, and provides stable 5V 2A for driving any 5V device.

The Matrix will be powered by our beloved Raspberry Pi PICO W, which is coupled to a HUB75 connector in the second section.

We connected the matrix's HUB75 pins (CON 16) to the PICO's GPIO pins in the following order: A to GPIO19, B to GPIO16, C to GPIO18, D to GPIO20, E to GPIO22, CLK to GPIO11, LAT/STB to GPIO12, OE to GPIO13, R1 to GPIO2, G1 to GPIO3, B1 to GPIO4, R2 to GPIO5, G2 to GPIO8, and B2 to GPIO9.

Additionally, we created an additional board that would house the SMD lithium cell 18650 holder. A JST wire harness will connect this board to the driver board's battery connector.

We constructed both PCBs using the dimensions from the Cad model, placing components in their proper locations by following the specified dimensions in the Cad file.

PCBWAY SERVICE

01.gif
IMG_6472.JPG
IMG_6473.JPG

We placed two orders for this project: one for the battery board and another for the driver board. A blue solder mask with white silkscreen was ordered for the battery board, and a white solder mask with black silkscreen was ordered for the driver board.

The Driver board was ordered in white solder mask and black silkscreen, while the Battery Board was ordered in blue solder mask and white silkscreen.

After placing the order, the PCBs were received within a week, and the PCB quality was pretty great.

Over the past ten years, PCBWay has distinguished themselves by providing outstanding PCB manufacturing and assembly services, becoming a trusted partner for countless engineers and designers worldwide.

Their commitment to quality and customer satisfaction has been unwavering, leading to significant growth and expansion.

You guys can check out PCBWAY if you want great PCB service at an affordable rate.

DRIVER BOARD ASSEMBLY PROCESS

03.gif
04.gif
05.gif
06.gif
07.gif
08.gif
09.gif
10.gif
11.gif
12.gif
13.gif
  1. Using the solder paste dispensing syringe, solder paste is applied to each SMD component pad to start the driver board's PCB assembly process. Here, 63/37 Sn/Pb solder paste is being used.
  2. We pick all of the SMD components, including the IP5306 IC setup, using our ESD Tweezers and position them correctly in their position.
  3. After that, the PCB is set on a reflow hotplate, which heats it from below to the melting point of solder paste, causing all of the SMD components to be connected to their pads.
  4. The next step is the through-hole component assembly, which starts with the creation of a large HUB75 16 pin connector by aligning two con8 male header pins side by side.
  5. After that, a type C port is installed in its place, and then a push button.
  6. Next, we place two CON2 JST connections, which are used to add a lithium cell to our driver board. In order to increase the battery capacity of our arrangement, we added two connectors so that we could utilize a different battery board.
  7. Finally, two CON20 female header pins are attached to the Raspberry Pi Pico's footprint. We then flip the board over and use a soldering iron to secure all of the through-hole components in place between the pads.
  8. Once the assembly process is finished, we can place the PICO W into the two CON20 header pins to position it.

PCB ASSEMBLY-BATTERY BOARD

14.gif
15.gif
16.gif
17.gif
18.gif
  1. We start the process by applying solder paste to both of the SMD cell holder's pads, much like we do with the driver board assembly.
  2. After positioning the lithium cell holder over its designated spot, the entire board is placed over the reflow hotplate, which melts the solder paste and joins the PCB and lithium cell holder.
  3. We soldered the positive and negative wires of the JST connector to the positive and negative of the battery board to complete the assembly.

DUAL RGB P3 Matrix Panels

20.gif

For the Display of this project, we are reusing two of our P3 RGB 64x32 matrix Panels that we used in our previous Snake Game project.

These two panels are connected side by side and secured using a special 3D Printed part that resembles a frame that joins displays together. To create a longer 128x32 RGB matrix panel, which will serve as our primary display for the current project, these 64x32 matrix panels are connected together side by side.

we got both of these displays from PCBWAY's GIFTSHOP and below is the link to the wiki page of the display.

https://www.waveshare.com/wiki/RGB-Matrix-P3-64x32

FRAME ASSEMBLY PROCESS

21.gif
22.gif
23.gif
24.gif
  1. Mounting the top-side frame holder part with the display is the first step in the Frame Assembly process. The holder part is then positioned over its mounting location and fastened with two M2.5 bolts using our RGB Matrix.
  2. Likewise, we positioned the bottom side frame holder over its location and fastened it with the RGB Matrix using two M2.5 bolts.

POWER SOURCE

27.gif
28.gif
29.gif
30.gif

A 2200mAh 3.7V 18650 Lithium Ion cell will be used as the project's power source. It will be connected to our Driver board to provide a steady 5V 2A to both panels and our PICO setup.

After positioning the cell in its holder with the proper polarity, we connect the battery board's wire harness to the driver board's JST connector.

The device switches on and provides a steady 5V output when the onboard push button is pressed once. Indicator LED turns ON meaning our setup is functioning.

We only need to double-press the push button to switch the setup off.

MATRIX SETUP & BATTERY BOARD ASSEMBLY

31.gif
32.gif

We begin the battery board assembly and matrix setup by positioning the battery board over the Frame Holder part's mounting hole and using the M2.5 Bolt to secure it in place with the matrix setup.

MATRIX SETUP & DRIVER BOARD ASSEMBLY

33.gif
34.gif
35.gif
36.gif
37.gif
  1. The driver board placement with matrix configuration follows, which starts with the driver board being positioned over its mounting holes and fastened with two M2 screws to the frame holder portion.
  2. After the battery board's JST wire harness is connected to the driver board's JST connector, the driver board's two power connectors are plugged into both matrix power connectors.
  3. Subsequently, we linked our driver board and matrix by using a long ribbon cable connector with a hub75 connector on both ends.
  4. We then place PICO W in its place and the basic assembly is now completed.

TEST SKETCH

38.gif
IMG_7483.JPG

After the assembly process of the Matrix Setup with the Driver board and Battery board, we use the following sketch to display a test sketch on our long RGB Display.

#include <Adafruit_Protomatter.h>
// Panel pin setup
#define R1 2
#define G1 3
#define B1 4
#define R2 5
#define G2 8
#define B2 9
#define A 10
#define B 16
#define C 18
#define D 20
#define CLK 11
#define LAT 12
#define OE 13
#define PANEL_WIDTH 64
#define PANEL_HEIGHT 32
#define NUM_PANELS 2
#define WIDTH (PANEL_WIDTH * NUM_PANELS)
#define HEIGHT PANEL_HEIGHT
uint8_t rgbPins[] = { R1, G1, B1, R2, G2, B2 };
uint8_t addrPins[] = { A, B, C, D };
Adafruit_Protomatter matrix(WIDTH, HEIGHT, 1, rgbPins, 4, addrPins, CLK, LAT, OE, false);
void setup() {
matrix.begin();
matrix.setTextWrap(false);
matrix.setTextColor(matrix.color565(0, 255, 0)); // Bright green text
matrix.setCursor(10, 12); // Vertically centered
matrix.print("THIS DISPLAY WORKS");
matrix.show(); // Show once, no loop refresh
}
void loop() {
// No refresh — static display only
}

Let's have a short breakdown of the test sketch.

First, we are using the Adafruit_Protomatter Library, which handles low-level timing and control for RGB matrix panels.

#define R1 2 // Red channel for top half
#define G1 3 // Green channel for top half
#define B1 4 // Blue channel for top half
#define R2 5 // Red channel for bottom half
#define G2 8 // Green channel for bottom half
#define B2 9 // Blue channel for bottom half
#define A 10
#define B 16
#define C 18
#define D 20
#define CLK 11 // Clock pin
#define LAT 12 // Latch pin
#define OE 13 // Output Enable pin

Next come the Pin Definitions; these statements assign readable names to GPIO pins used to control the LED matrix. The matrix is split into top and bottom halves, each with its own RGB channels. the address pins are also declared, which are used to select which row of LEDs to update.

Panel Dimensions

#define PANEL_WIDTH 64
#define PANEL_HEIGHT 32
#define NUM_PANELS 2
#define WIDTH (PANEL_WIDTH * NUM_PANELS)
#define HEIGHT PANEL_HEIGHT

We're using two 64×32 panels side-by-side, giving a total resolution of 128×32 pixels.

Matrix Object Initialization

uint8_t rgbPins[] = { R1, G1, B1, R2, G2, B2 };
uint8_t addrPins[] = { A, B, C, D };
Adafruit_Protomatter matrix(WIDTH, HEIGHT, 1, rgbPins, 4, addrPins, CLK, LAT, OE, false);
  1. Here, the WIDTH, HEIGHT are the total display size.
  2. 1 number of bitplanes is used for color depth.
  3. rgbPins are RGB data pins.
  4. 4 is the number of address pins.
  5. addrPins are the row selection pins.
  6. CLK, LAT and OE are timing control pins.
  7. false disables double-buffering (static display only)


Setup Function

void setup() {
matrix.begin(); // Initialize the matrix
matrix.setTextWrap(false); // Prevent text from wrapping
matrix.setTextColor(matrix.color565(0, 255, 0)); // Set text color to bright green
matrix.setCursor(10, 12); // Set text position
matrix.print("THIS DISPLAY WORKS"); // Display message
matrix.show(); // Push buffer to display
}

This Section initializes the matrix and displays a static message in green at a fixed position. we added the "THIS DISPLAY WORKS" message, which will be displayed on the matrix.

Since matrix.show() is called only once in setup(), the display remains static. No animation or updates occur in loop().

Once the setup is complete, we proceed to the second phase of this project, which involves integrating the SGP40 Sensor with our matrix configuration.

SGP40 VOC AQI SENSOR PROBE

IMG_7513.JPG
39.gif
40.gif
IMG_7512.JPG

We utilize a unique Air Sensor Proble with the SGP40 Sensor for air quality monitoring.

The SGP40 sensor was created especially to measure Total Volatile Organic Compounds (TVOC) and evaluate pollution levels in enclosed areas in order to monitor indoor air quality. It works best in regulated interior spaces with consistent ventilation, humidity, and temperature.

Because external environmental factors, including extreme temperature swings, humidity swings, and direct contact with pollutants, can significantly affect the sensor's accuracy and reliability, it is not appropriate for outdoor use.

It's best to use the SGP40 indoors, where air quality testing is crucial, such as in homes, offices, and industrial settings, for optimal results.

Below is the SGP40 Datasheet for more thorough details about the project.

https://evelta.com/content/datasheets/011-SGP40-Datasheet.pdf

Our probe is a PG7 Cable Gland, a specialized connector used to seal and fasten electrical cables when they enter panels, housings, or enclosures. A circuit with the SGP40 is part of the Probe and is located in our PG7 Cable Gland.

MATRIX SETUP & SGP40 PROBE ASSEMBLY

41.gif
42.gif
43.gif
44.gif

In order to secure the entire sensor in its mounting position, we remove the nut of the PG7 connector, insert its wire through the mounting hole, and then replace the plastic nut on the connector. Finally, we tighten this nut.

Following that, we cut off the PG7 connector's extra-long wire and stripped all of the wires that will be soldered to the PICO Board.

We connect the SDA and SCL Pins of the SGP40 to GPIO26 and GPIO27 of the PICO W since the SGP40 Sensor needs an I2C connection for data transfer. The VCC pin is connected to the PICO W's 5V pin, and GND is connected to GND.

We have finished assembling our room air quality monitor.

MAIN CODE

This is the main code we used in this project, and it is fairly straightforward.

#include <Adafruit_Protomatter.h>
#include <Adafruit_SGP40.h>
#include <Wire.h>
#include <WiFi.h>
#include <WebServer.h>
// Wi-Fi credentials
const char* ssid = "SSID";
const char* password = "PASSWORD";
// Web server
WebServer server(80);
// Display pin definitions
#define R1 2
#define G1 3
#define B1 4
#define R2 5
#define G2 8
#define B2 9
#define A 10
#define B 16
#define C 18
#define D 20
#define CLK 11
#define LAT 12
#define OE 13
#define PANEL_WIDTH 64
#define PANEL_HEIGHT 32
#define NUM_PANELS 2
#define WIDTH (PANEL_WIDTH * NUM_PANELS)
#define HEIGHT PANEL_HEIGHT
uint8_t rgbPins[] = { R1, G1, B1, R2, G2, B2 };
uint8_t addrPins[] = { A, B, C, D };
Adafruit_Protomatter matrix(WIDTH, HEIGHT, 1, rgbPins, 4, addrPins, CLK, LAT, OE, false);
Adafruit_SGP40 sgp;
#define SDA_PIN 26
#define SCL_PIN 27
bool grid[WIDTH][HEIGHT];
bool newGrid[WIDTH][HEIGHT];
uint16_t vocIndex = 0;
void setup() {
Serial.begin(115200);
// I2C setup
Wire1.setSDA(SDA_PIN);
Wire1.setSCL(SCL_PIN);
Wire1.begin();
// Matrix setup
matrix.begin();
// SGP40 setup
if (!sgp.begin(&Wire1)) {
matrix.setTextColor(matrix.color565(255, 0, 0));
matrix.setCursor(10, HEIGHT / 2 - 4);
matrix.print("SGP40 Error");
matrix.show();
while (1);
}
// Wi-Fi setup
WiFi.begin(ssid, password);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected. IP: " + WiFi.localIP().toString());
// Web server routes
server.on("/", handleRoot);
server.on("/voc", handleVOC);
server.begin();
// Initialize grid
randomSeed(analogRead(0));
for (int x = 0; x < WIDTH; x++) {
for (int y = 0; y < HEIGHT; y++) {
grid[x][y] = random(2);
}
}
}
void loop() {
vocIndex = sgp.measureVocIndex();
// Determine cell color based on air quality
uint16_t cellColor;
if (vocIndex <= 100) {
cellColor = matrix.color565(0, 255, 0); // Green
} else if (vocIndex <= 200) {
cellColor = matrix.color565(255, 255, 0); // Yellow
} else {
cellColor = matrix.color565(255, 0, 0); // Red
}
matrix.fillScreen(0); // Clear screen
// Update grid based on Game of Life rules
for (int x = 0; x < WIDTH; x++) {
for (int y = 0; y < HEIGHT; y++) {
int aliveNeighbors = countAliveNeighbors(x, y);
if (grid[x][y]) {
newGrid[x][y] = (aliveNeighbors == 2 || aliveNeighbors == 3);
} else {
newGrid[x][y] = (aliveNeighbors == 3);
}
if (newGrid[x][y]) {
matrix.drawPixel(x, y, cellColor);
}
}
}
memcpy(grid, newGrid, sizeof(grid));
matrix.show();
delay(100);
if (isGridEmpty()) {
resetGrid();
}
server.handleClient();
}
int countAliveNeighbors(int x, int y) {
int count = 0;
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -1; dy <= 1; dy++) {
if (dx == 0 && dy == 0) continue;
int nx = (x + dx + WIDTH) % WIDTH;
int ny = (y + dy + HEIGHT) % HEIGHT;
if (grid[nx][ny]) count++;
}
}
return count;
}
bool isGridEmpty() {
for (int x = 0; x < WIDTH; x++) {
for (int y = 0; y < HEIGHT; y++) {
if (grid[x][y]) return false;
}
}
return true;
}
void resetGrid() {
for (int x = 0; x < WIDTH; x++) {
for (int y = 0; y < HEIGHT; y++) {
grid[x][y] = random(2);
}
}
}
void handleRoot() {
String html = "<!DOCTYPE html><html><head><meta http-equiv='refresh' content='2'>";
html += "<title>Air Quality Dashboard</title></head><body>";
html += "<h1>Room Air Quality</h1>";
html += "<p><strong>VOC Index:</strong> " + String(vocIndex) + "</p>";
html += "<p><strong>Status:</strong> " + interpretVOC(vocIndex) + "</p>";
html += "</body></html>";
server.send(200, "text/html", html);
}
void handleVOC() {
server.send(200, "text/plain", String(vocIndex));
}
String interpretVOC(uint16_t voc) {
if (voc <= 100) return "Excellent";
if (voc <= 200) return "Good";
if (voc <= 400) return "Moderate";
if (voc <= 600) return "Poor";
return "Unhealthy";
}

Let's have a brief code breakdown.

Libraries & Setup

#include <Adafruit_Protomatter.h> // Controls RGB LED matrix
#include <Adafruit_SGP40.h> // Interfaces with the SGP40
#include <Wire.h> // I2C communication
#include <WiFi.h> // Wi-Fi connectivity
#include <WebServer.h> // Hosts a web server

We first added the following libraries to enable hardware control, sensor communication, and networking.

Wi-Fi & Web Server

const char* ssid = "SSID";
const char* password = "PASSWORD";
WebServer server(80);

Using the above section, we connect to a Wi-Fi network using provided credentials and sets up a web server on port 80.

LED Matrix Configuration

#define PANEL_WIDTH 64
#define PANEL_HEIGHT 32
#define NUM_PANELS 2
#define WIDTH (PANEL_WIDTH * NUM_PANELS)
#define HEIGHT PANEL_HEIGHT

This section defines a dual-panel RGB matrix (128×32 pixels). The Pin mappings are set for color channels and address lines.

Adafruit_Protomatter matrix(WIDTH, HEIGHT, 1, rgbPins, 4, addrPins, CLK, LAT, OE, false);

this Initializes the matrix with specified dimensions and control pins.

Sensor Setup

Adafruit_SGP40 sgp;
#define SDA_PIN 26
#define SCL_PIN 27

We use GPIO26 and GPIO27 as I2C pins to communicate with the SGP40 sensor.

Game of Life Grid

bool grid[WIDTH][HEIGHT];
bool newGrid[WIDTH][HEIGHT];

Here, the grid stores current cell states and the newGrid holds the next generation based on Conway’s rules.

setup() Function

Wire1.setSDA(SDA_PIN);
Wire1.setSCL(SCL_PIN);
Wire1.begin();
matrix.begin();
sgp.begin(&Wire1);
WiFi.begin(ssid, password);
server.on("/", handleRoot);
server.on("/voc", handleVOC);
server.begin();

This Section Initializes I2C, the matrix, the sensor, Wi-Fi, and the web server and also randomly seeds the Game of Life grid.

loop() Function

vocIndex = sgp.measureVocIndex();

This Part Reads the current VOC index.

if (vocIndex <= 100) cellColor = green;
else if (vocIndex <= 200) cellColor = yellow;
else cellColor = red;

This section chooses display color based on air quality.

for (int x = 0; x < WIDTH; x++) {
for (int y = 0; y < HEIGHT; y++) {
int aliveNeighbors = countAliveNeighbors(x, y);
newGrid[x][y] = Game of Life logic;
if (newGrid[x][y]) matrix.drawPixel(x, y, cellColor);
}
}

This Part Applies Game of Life rules and draws live cells in the chosen color.

memcpy(grid, newGrid, sizeof(grid));
matrix.show();
delay(100);

Using the above section, we update the grid and refresh the display.

if (isGridEmpty()) resetGrid();
server.handleClient();

This part resets the grid if all cells die and handles incoming web requests.

countAliveNeighbors(x,y):This Counts surrounding live cells.

isGridEmpty(): This Checks if all cells are dead.

resetGrid(): This Randomizes the grid again.

Web Interface

void handleRoot() {
String html = "<html>...VOC Index: " + String(vocIndex) + "</html>";
server.send(200, "text/html", html);
}

This Section Serves as a basic HTML page showing VOC index and status.

void handleVOC() {
server.send(200, "text/plain", String(vocIndex));
}

This Part Returns the raw VOC value for API or external use.

String interpretVOC(uint16_t voc) {
if (voc <= 100) return "Excellent";
if (voc <= 200) return "Good";
if (voc <= 400) return "Moderate";
if (voc <= 600) return "Poor";
return "Unhealthy";
}

This part Converts the VOC index into a human-readable air quality label.

RESULT

46.gif
47.gif
59.gif
50.gif
51.gif
52.gif
53.gif
54.gif

The result of this straightforward but incredibly useful build is the ROOM AIR QUALITY MONITOR, a device that uses Connoways' game of life in a clever way to offer real-time TVOC readings. When air quality is great, simulation runs green; when air quality is low and poor, simulation becomes yellow and red.

After mounting the Air Qualit monitor on a wall, we ignited an incense.

A mixture of chemicals and particles, including volatile organic compounds like benzene, toluene, and formaldehyde, particulate debris like ash, and even carbon monoxide, is released into the air when the incense burns.

VOCs linger and amass in the absence of airflow, leading the sensor to record higher readings. The Game of Life simulation changed from green to yellow and later red as a result; Our Device was correctly capturing the deteriorating air quality.

The story's lesson is to avoid burning incense in a restricted space. This demonstration shows how our air quality monitor is a useful environmental monitor that responds to actual changes in the environment.

WEB INTERFACE

56.gif
57.gif

Let's now examine the project's webapp, which functions as a sleek and responsive dashboard for monitoring VOC levels in real time.

It displays the data in an aesthetically appealing way and is hosted directly on the Raspberry Pi Pico W. It automatically refreshes every two seconds.

The interface shows the current VOC index, and we added a gradient background and centered layout. The readings are highlighted by a bordered card-style design that makes them easily accessible on both desktop and mobile devices.

After the PICO has been connected to our local network, we can access the web application that will display the room VOC data by using the IP address we obtained during the code upload process.

CONCLUSION

49.gif
55.gif
Room AQI Monitor SGP40 and Raspberry Pi PICO W

This project successfully demonstrates the fusion of environmental sensing with dynamic visual feedback, transforming abstract air quality data into an intuitive and engaging display. By integrating the SGP40 VOC sensor with a dual-panel RGB matrix and a web-based dashboard, the system offers real-time insights into indoor air conditions—making invisible pollutants visibly impactful.

The use of Conway’s Game of Life as a visual metaphor adds a layer of depth: as air quality deteriorates, the simulation shifts in color, subtly reminding users of the delicate balance between life and environment. Real-world tests, such as burning incense in a closed room, validated the system’s sensitivity and responsiveness, highlighting its potential as both a functional monitor and an educational tool.

This project is finished and requires no further revision; all project-related information has been included in this article. comment If you need any additional help with this project, check out my previous Air Quality meter project if you're interested in whole air monitoring.

In addition, we appreciate PCBWAY's support of this project. Visit them for a variety of PCB-related services, such as stencil and PCB assembly services, as well as 3D printing services

Thanks for reaching this far, and I will be back with a new project pretty soon.

Peace.