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