Supplies
Here's a list of components needed (you can also ship from Aliexpress):
- ESP32 DEVKIT V1 board
- Nova SDS011 PM10/2.5 sensor
- DC/DC step-up converter
- MHZ-19 CO2 sensor
- DHT11 Temperature and humidity sensor
- 3V7 1000 mAh LiPo battery
And some utilities:
- 3D printer (and slicer software)
- Arduino IDE (1.8.19)
- Laser cut (you can also use a printer)
Step 1: Case Development
We began designing the housing for the PCB using a 3D CAD program, paying close attention to the height of the air inlet and outlet of the SDS011. The sensor uses a 5V fan, so we utilized the outlet air to cool the electronics on the right side. Additionally, we designed openings on the right side to keep the MCU and boost converter cool.
Step 2: Schematics and PCB
I began this project using jumper wires, but as the number of sensors increased, it became essential (for the sake of sanity) to design a custom PCB. I chose EasyEDA as software and started with the schematics.
As a PM sensor I've chosen the Nova SDS011 with UART protocol, I connected the sensor to TX2/RX2 of ESP32 through a jack (I use footprint for dimension for this reason isn't connected). The only difficulty is that it needs 5V so we've added a boost converter (XL6009) to step up the 3,3V of the MCU.
We've chosen DHT11 because we didn't need high precision on temperature and it's the most cost-effective one, finally, there's the CO2 sensor (MHZ19) connected to UART1.
Then there's a battery connection called "J2" on the scheme that powers up everything, consider that, in normal cases, you also need a battery management system like TP4056 we chose to omit it because this is a starting project and we monitor the sensor during the measurement.
So I started the PCB design trying to minimize the space occupied, we opted for a rectangular box so the board had to fit it, moreover, the SDS011 air inlet was on the left and we placed the battery above the sensor, MCU and boost converter were in the left part of the case so we leave some opening, now I think they could be reduced a bit.
Check the project repository for Gerber file needed to order the PCB.
Step 3: Assembly & Connection
After the PCB was completed, assembling the device was the easiest part. We only needed to connect the sensors to the electronic board. First, we soldered a female header for the ESP32 to simplify replacement. Next, we soldered the MHZ-19, DHT11, and the boost converter. Then, we aligned the SDS011 inlet with the round hole on the left side of the case and connected its cable to "J1" according to the schematic. Finally, we connected the battery to "J2" (being careful with the polarity) and closed the case with a 4mm laser-cut wood cover.
Step 4: Code Drafting
First, I installed the ESP32 add-on for Arduino IDE. We also needed some libraries to retrieve data from the sensors:
For each one click on <code> and then "Download ZIP", then in Arduino IDE: Sketch/Include library/Add .zip file and select the file downloaded.
The developed code includes a setup where the sensors are initialized, and in the loop, data are acquired and sent to the InfluxDB database.
void loop() { //Print not sended data File file2 = SPIFFS.open("/PM sensor.txt"); if (!file2) { Serial.println("Failed to open file for reading"); return; } Serial.println("File Content:"); while (file2.available()) { Serial.write(file2.read()); } file2.close();
If WiFi isn't available, the MCU stores data in SPIFFS as a .txt file, we chose this system cause we didn't think of a backup system when designing the PCB, now I reckon the best solution is a micro-SD card in combination with a dedicated struct for redundancy. The content of the file is the first thing displayed in the loop as you can see above, in this case, we simply print it in the Serial Monitor, you can also resend data but with TSDB you will have two different data at the same time in the graph, maybe is better to use a different database, this is a starting code that you can adapt as you want!
//retrieve new data sds_011.wakeup(); delay(interval); err = my_sds.read(&p25, &p10); // Request PM values if (!err) { Serial.print("P2.5: " + String(p25)); Serial.println(" P10: " + String(p10)); } else{ Serial.println("Error PM READINGS!!"); } sds_011.sleep(); CO2 = myMHZ19.getCO2(); // Request CO2 (as ppm) Temp = myMHZ19.getTemperature(); // Request Temperature (as Celsius) Serial.print("CO2 (ppm): "); Serial.print(CO2); Serial.print(" Temperature (C): "); Serial.println(Temp); double h = dht.readHumidity(); double t = dht.readTemperature();
In this part, we retrieve new data from PM sensor, then from the CO2 one that returns also the temperature and finally, from DHT11. We had some problems with this sensor but we managed to take the temperature from MHZ19 so we only lost the humidity. If something goes wrong with readings an error message is displayed in the Serial Monitor, in a newer project I will send to InfluxDB a code for each possible failure to find the problem.
//Send data to InfluxDB sensor_readings.clearFields(); sensor_readings.addField("rssi", WiFi.RSSI()); sensor_readings.addField("temperature", t); //Get Data from temperature sensor sensor_readings.addField("co2", CO2); //Get Data from from co2 sensor sensor_readings.addField("humidity", h); //Get Data from humidity sensor sensor_readings.addField("pm2_5", p25); //Get PM2_5 sensor_readings.addField("pm10", p10); //Get PM10 Serial.print("Writing: "); Serial.println(client.pointToLineProtocol(sensor_readings)); // Write point
Here we send data to the database, is quite easy as you can see, you only need to specify a label for the data and the value, and then the values are written.
if ((wifiMulti.run() != WL_CONNECTED) || (!client.writePoint(sensor_readings))) { Serial.print("InfluxDB write failed: "); Serial.println(client.getLastErrorMessage()); //If data aren't sent save in the SPIFFS String measurements = "T: " + String(t) + ", " + "H: " + String(h) + ", " + "PM10: " + String(p10) + ", " + "PM2.5: " + String(p25) + ", " + "CO2: " + String(CO2) + ", " + + "RSSI: " + String( WiFi.RSSI()) + " \n"; File file = SPIFFS.open("/PM sensor.txt", FILE_APPEND); if (!file) { Serial.println("There was an error opening the file for writing"); return; } if (file.print(measurements)) { Serial.println("File was written"); } else { Serial.println("File write failed"); } file.close(); }
If data aren't sent for WiFi or InfluxDB problems the software creates a string with all the measurements and saves it in a dedicated file.
An OTA system is also implemented for software updates in the setup section with sensors and InfluxDB initialisation.
More code including sketches for testing the sensors, updated code and Gerber file for PCB are available in the project repository.
Step 5: InfluxDB Data
In the beginning, I opted for Google Sheets as a database, but as the numbers of data started to increase I discovered InfluxDB, with the team we also incorporated a dashboard to visualize easily all data.
If you are new to it consider this complete guide for setting up your account and start sending data. Now there are some changes, in particular, isn't available a built-in dashboard manager, instead you can use Grafana and I suggest this guide.
Step 6: Conclusion
This is only one of three projects that I will publish in the future about this topic, is quite easy but functional and I think is a great way to start on air quality monitoring if you worked with Arduino or ESP8266.
Huge thanks to Vallauri School which supported us with materials and the prototyping laboratory. Thanks to the team in particular Elena, Kristian, Lorenzo, Alex and Gabriele. Also thanks to my teachers, in particular, Conte Roberto who followed the project since the beginning, teachers from Green Team who allowed me to bring the project to Belgium and last but not least Milanesio Mario for helping with code and sensors.