Introduction: System for Monitoring Air Quality for Particulates Pollution
INTRO :
1 In this project I show how to build a particle detector with data display, data backup on SD card and IOT. Visually a neopixels ring display indicates the air quality.
2 Air quality is an increasingly important concern today. there are systems to measure the dust rate but they are very expensive. There are low-cost, high-quality particle detectors on the market, as shown by some studies.
for example :
https://www.atmos-meas-tech.net/11/4823/2018/amt-1...
3 I therefore decided to build a device capable of measuring the number of particles by size classes (0.5µm to 10 µm), visually with a simple display of the result (neo pixel ring), a more detailed display on a TFT screen and a time-stamped backup on an SD card.
4 In addition I have added a bluetooth communication module to be able to communicate with an android application and thus publish the results on an IOT server.
5 The overall cost of the whole does not exceed 60 €
Supplies
-Arduino uno R3
-Arduino proto shield
-TFT screen ST7735
-Neopixel ring 24 led
-Plantower PMS5003
-HC-06 bluetooth module
Step 1: Connecting the Components
the different components are connected according to the diagram above
Step 2: Library and Arduino Program
1 the library
for the TFT screen
https://github.com/adafruit/Adafruit-GFX-Library
for the neo pixel ring
https://github.com/adafruit/Adafruit_NeoPixel
for the sd card
https://github.com/arduino-libraries/SD
2 the arduino sketch
#include <softwareserial.h><br>#include <wire.h> // Bibliothèque pour l'I2C
#include "RTClib.h" // Bibliothèque pour le module RTC
RTC_DS1307 RTC;
#include <adafruit_neopixel.h></adafruit_neopixel.h></wire.h></softwareserial.h></p><p>// Which pin on the Arduino is connected to the NeoPixels?
#define PIN 6 // On Trinket or Gemma, suggest changing this to 1</p><p>// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS 24 // Popular NeoPixel ring size
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
uint32_t vert = pixels.Color(0, 250, 0);
uint32_t orange = pixels.Color(250, 250, 0);
uint32_t rouge = pixels.Color(255, 0, 0);</p><p>SoftwareSerial pmsSerial(2, 3);
#define cs 10
#define dc 9
#define rst 8 // you can also connect this to the Arduino reset</p><p>#include <adafruit_gfx.h> // Core graphics library
#include <adafruit_st7735.h> // Hardware-specific library
#include <spi.h>
#include<sd.h>
const int cs_sd=4;
int temps; // temps d'acquisition
double tempsInit; // initialisation du timer au démarrage du loop()</sd.h></spi.h></adafruit_st7735.h></adafruit_gfx.h></p><p>#if defined(__SAM3X8E__)
#undef __FlashStringHelper::F(string_literal)
#define F(string_literal) string_literal
#endif</p><p>// Option 1: use any pins but a little slower
//Adafruit_ST7735 tft = Adafruit_ST7735(cs, dc, mosi, sclk, rst);</p><p>// Option 2: must use the hardware SPI pins
// (for UNO thats sclk = 13 and sid = 11) and pin 10 must be
// an output. This is much faster - also required if you want
// to use the microSD card (see the image drawing example)
Adafruit_ST7735 tft = Adafruit_ST7735(cs, dc, rst);
float nombre_leds=0;
void setup() {
Serial.begin(9600);
// Initialise la liaison I2C
Wire.begin();
// Initialise le module RTC
RTC.begin();
Serial.print("init SD");
delay(1000);
if(!SD.begin(cs_sd)) //Condition vérifiant si la carte SD est présente dans l'appareil
{
Serial.print("Defaut SD");
return;
}
Serial.print("Carte SD OK");</p><p> File data = SD.open("donnees.txt",FILE_WRITE); // Ouvre le fichier "donnees.txt"
data.println(""); data.println("Démarrage acquisition"); // Ecrit dans ce fichier
data.close();
tft.initR(INITR_GREENTAB); // initialize a ST7735S chip, black tab
Serial.println("init");
// our debugging output
tft.fillScreen(ST7735_BLACK);
// sensor baud rate is 9600
pmsSerial.begin(9600);</p><p> pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
pixels.setBrightness(2);</p><p>
}</p><p>struct pms5003data {
uint16_t framelen;
uint16_t pm10_standard, pm25_standard, pm100_standard;
uint16_t pm10_env, pm25_env, pm100_env;
uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um;
uint16_t unused;
uint16_t checksum;
};</p><p>
struct pms5003data data;
void loop()
{
pixels.clear(); // Set all pixel colors to 'off'
DateTime now=RTC.now(); //Récupère l'heure et le date courante
//affiche_date_heure(now);</p><p>
temps = ((millis() - tempsInit))/1000 ; // Démarrage du chrono </p><p>
if (readPMSdata(&pmsSerial)) {
// tft.fillScreen(ST7735_BLACK);
tft.setCursor(10, 5);
tft.setTextColor(ST7735_WHITE);
tft.println(" nbre parts/ 0.1 l");</p><p>
tft.setCursor(10, 17);
tft.setTextColor(ST7735_GREEN,ST7735_BLACK);
tft.setTextSize(1);
tft.setCursor(10, 17);
tft.print("0.3 um ");tft.print(data.particles_03um);tft.print(" ");</p><p>tft.setCursor(10, 29);
tft.setTextColor(ST7735_GREEN,ST7735_BLACK);
tft.setTextSize(1);
tft.print("0.5 um ");tft.print(data.particles_05um);tft.print(" ");</p><p>tft.setCursor(10, 41);
tft.setTextColor(ST7735_GREEN,ST7735_BLACK);
tft.setTextSize(1);
tft.print("1.0 um ");tft.print(data.particles_10um);tft.print(" ");</p><p>tft.setCursor(10, 53);
tft.setTextColor(ST7735_GREEN,ST7735_BLACK);
tft.setTextSize(1);
tft.print("2.5 um ");tft.print(data.particles_25um);tft.print(" ");</p><p>tft.setCursor(10, 65);
tft.setTextColor(ST7735_GREEN,ST7735_BLACK);
tft.setTextSize(1);
tft.print("5.0 um ");tft.print(data.particles_50um);tft.print(" ");</p><p>tft.setCursor(10, 77);
tft.setTextColor(ST7735_GREEN,ST7735_BLACK);
tft.setTextSize(1);
tft.print("10 um ");tft.print(data.particles_100um);tft.print(" ");</p><p>tft.setCursor(2, 89);
tft.setTextColor(ST7735_GREEN,ST7735_BLACK);
tft.setTextSize(1);
tft.print("PM 1.0 ");tft.setTextColor(ST7735_YELLOW,ST7735_BLACK);tft.print(data.pm10_standard);tft.print(" ");tft.setTextColor(ST7735_GREEN,ST7735_BLACK);tft.print(" microg/m3 ");
</p><p>
tft.setCursor(2, 100);
tft.setTextColor(ST7735_GREEN,ST7735_BLACK);
tft.setTextSize(1);
tft.print("PM 2.5 ");tft.setTextColor(ST7735_YELLOW,ST7735_BLACK);tft.print(data.pm25_standard);tft.setTextColor(ST7735_GREEN,ST7735_BLACK);tft.print(" microg/m3 ");</p><p> tft.setCursor(2, 110);
tft.setTextColor(ST7735_GREEN,ST7735_BLACK);
tft.setTextSize(1);
tft.print("PM 10 ");tft.setTextColor(ST7735_YELLOW,ST7735_BLACK);tft.print(data.pm100_standard);tft.setTextColor(ST7735_GREEN,ST7735_BLACK);tft.print(" microg/m3 ");
</p><p> tft.setCursor(10, 5);
tft.setTextColor(ST7735_WHITE,ST7735_BLACK);
tft.setTextSize(1);
tft.println(" nbre parts/ 0.1 l");</p><p> // Serial.print(temps);
// Serial.print (" ");
Serial.print ("#");
Serial.print ("03µm ");
Serial.print(data.particles_03um);
Serial.print (" ");
Serial.print ("05µm ");
Serial.print(data.particles_05um);
Serial.print (" ");
Serial.print ("1µm ");
Serial.print(data.particles_10um);
Serial.print (" ");
Serial.print ("25µm ");
Serial.print(data.particles_25um);
Serial.print (" ");
Serial.print ("50µm ");
Serial.print(data.particles_50um);
Serial.print (" ");
Serial.print ("100µm ");
Serial.print(data.particles_100um);
Serial.println (" ");
nombre_leds =int (((float (data.particles_03um)/65535)*24));
//nombre_leds =(8);
Serial.println (nombre_leds);</p><p> if ((nombre_leds<=8) and (nombre_leds>=1)){
pixels.fill(vert , 0, nombre_leds);
}
else if ((nombre_leds<=16) and (nombre_leds>=8)) {
pixels.fill(vert , 0, 8);
pixels.fill(orange , 8, ((nombre_leds)-8));
}
else if (nombre_leds>16) {</p><p>
pixels.fill(vert , 0, 8);
pixels.fill(orange , 8, 8);
pixels.fill(rouge , 16, ((nombre_leds)-16));
}
else if (nombre_leds<=1) {
pixels.fill(vert , 0, 1);
}
pixels.show(); // Send the updated pixel colors to the hardware.</p><p>
// Définition données
String PM03=String(data.particles_03um);
String PM05=String(data.particles_05um);
String PM10=String(data.particles_10um);
String PM25=String(data.particles_25um);
String PM50=String(data.particles_50um);
String PM100=String(data.particles_100um);
String PMS10=String(data.pm10_standard);
String PMS25=String(data.pm25_standard);
String PMS100=String(data.pm100_standard);
String Temps=String(temps);</p><p> //Ecriture des données dans le fichier texte
File data=SD.open("donnees.txt",FILE_WRITE);
data.println( Temps + " " + PM03+ " " + PM05 +" " +PM10+" " +PM25+" "+PM50+" " +PM100+" "+PMS10+" "+PMS25+" "+PMS100+" ");
data.close();
}</p><p>
}</p><p>boolean readPMSdata(Stream *s) {
if (! s->available()) {
return false;
}
// Read a byte at a time until we get to the special '0x42' start-byte
if (s->peek() != 0x42) {
s->read();
return false;
}</p><p> // Now read all 32 bytes
if (s->available() < 32) {
return false;
}
uint8_t buffer[32];
uint16_t sum = 0;
s->readBytes(buffer, 32);</p><p> // get checksum ready
for (uint8_t i=0; i<30; i++) {
sum += buffer[i];
}</p><p> /* debugging
for (uint8_t i=2; i<32; i++) {
Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", ");
}
Serial.println();
*/
// The data comes in endian'd, this solves it so it works on all platforms
uint16_t buffer_u16[15];
for (uint8_t i=0; i<15; i++) {
buffer_u16[i] = buffer[2 + i*2 + 1];
buffer_u16[i] += (buffer[2 + i*2] << 8);
}</p><p> // put it into a nice struct :)
memcpy((void *)&data, (void *)buffer_u16, 30);</p><p> if (sum != data.checksum) {
Serial.println("Checksum failure");
return false;
}
// success!
return true;
}</p><p>//Converti le numéro de jour en jour /!\ la semaine commence un dimanche
String donne_jour_semaine(uint8_t j){
switch(j){
case 0: return "DIM";
case 1: return "LUN";
case 2: return "MAR";
case 3: return "MER";
case 4: return "JEU";
case 5: return "VEN";
case 6: return "SAM";
default: return " ";
}
}</p><p>// affiche la date et l'heure sur l'écran
void affiche_date_heure(DateTime datetime){
// Date
String jour = donne_jour_semaine(datetime.dayOfTheWeek()) + " " +
Vers2Chiffres(datetime.day())+ "/" +
Vers2Chiffres(datetime.month())+ "/" +
String(datetime.year(),DEC);
// heure
String heure = "";
heure = Vers2Chiffres(datetime.hour())+ ":" +
Vers2Chiffres(datetime.minute())+ ":" +
Vers2Chiffres(datetime.second());</p><p>
Serial.print(jour);
Serial.print(" ");
Serial.print(heure);
//Serial.print(" ");
File data=SD.open("donnees.txt",FILE_WRITE);
data.print(jour + " " + heure+" " );
data.close();</p><p> tft.setCursor(2, 120);
tft.setTextColor(ST7735_GREEN);
tft.setTextSize(1);
tft.print("date ");tft.setTextColor(ST7735_YELLOW);tft.print(jour);tft.setTextColor(ST7735_GREEN);tft.setCursor(2, 130);tft.print(" heure");tft.setTextColor(ST7735_YELLOW);tft.print(heure);</p><p> delay(500);
}</p><p>//permet d'afficher les nombres sur deux chiffres
String Vers2Chiffres(byte nombre) {
String resultat = "";
if(nombre < 10)
resultat = "0";
return resultat += String(nombre,DEC);
}</p>Step 3: MIT App Inventor 2 Program
this is the MIT app inventor code block
Step 4: THE RESULT
here is the video of the result



