Air Quality Monitor
July 2020

 

Air Quality Monitor

Monitor  Monitor

 

This project monitors the quantity of fine dust particles in the atmosphere. It measures both PM10 particles (less than 10µm in diameter) and PM2.5 particles (less than 2.5µm in diameter). It uses the SDS011 Laser PM2.5 Sensor which has a particle density resolution of 0.3µg/m3.

Although there are less expensive sensors based on the same light-scattering principle, I chose the SDS011 for a few reasons: It has a built-in fan which greatly simplifies construction and it has built-in logic-level serial communication providing direct readings of the PM2.5 and PM10 values without having to use maths to manipulate PWM values to obtain the results.

The SDS011 documentation claims a service life of up to 8000 hours continuous use but recommends that, where continuous sampling isn't required, the sensor is run for 30 seconds while taking a sample and then switched to standby for a number of minutes.

Although the SDS011 sensor can accept commands on its serial interface to put the sensor into low power mode for a set number of minutes, I chose to simply switch the sensor's main power through a MOSFET such that it powers up for 30 seconds while taking a reading then shuts down completely for 4 minutes 30 seconds. This made it easier to implement a count-down timer on the display.

In addition to displaying the PM2.5 and PM10 data on a local LCD display, I wanted to use an ESP8266 WiFi module to send the data to a Windows PC running a small server application to log the data on a chart to be viewed on the application itself or in a web browser either on the PC or over the internet.

In the current design there is no way to save the data 'locally' in the monitor, but the data can be saved and recalled indefinitely by the PC Application.

The PC needs to have a static IP address set up in its Network Properties settings.

You can view the chart here (if the server application is running).

You can download the Windows application here (v1.3).

(I don't supply source code for my Windows applications so please don't ask.)

 

Circuit Diagram

 

The circuit is fairly straightforward but bear in mind that IO pins on the ESP8266 WeMos D1 Mini are in short supply and many have at least two functions. I recommend that you use the IO pins I've used. One pin is saved by pulling the display's RESET pin high and another is saved by not sending any data to the SDS011 sensor's Rx pin.

 

The 270k resistor connected between 5 volts and the WeMos D1 Mini's A0 pin forms part of a potential divider.

Although it's intended that the air quality monitor will normally be run from a 5v mains adapter, it will run for many hours on a 5v USB power pack so I thought it worthwhile monitoring the supply voltage to make the unit more portable.

The maximum allowable voltage on the analogue input pin on the ESP8266 is only 1 volt so the WeMos D1 Mini provides a potential divider suitable for measuring 3v3 at its A0 pin (R1 and R2 in this diagram). To measure 5 volts, we add the extra 270K resistor (R3) in series with the existing 220K.

With a 5 volt supply, this sets the maximum voltage on the ESP8266 analogue pin to 0.85 volts to provide a bit of a safety margin on the supply voltage.

 

Data is sampled from the SDS011 every 5 minutes so, as there are approximately 300 points across the display, the "histogram plot" represents approximately 24 hours of data.

The data for the histogram is automatically saved to EEPROM once an hour enabling most of the histogram to be recovered after a power cycle. The button connected to the WeMos D1 Mini pin D4, (GPIO2) copies the data into EEPROM at any time. More frequent saving of the data isn't recommended as the life of ESP8266 EEPROM is only around 10,000 cycles or about 2 years when saving once per hour.

Printed Circuit Board

I recommend that you use two 8-way header sockets for the Wemos D1 Mini rather than soldering the module directly to the PCB: I've found one or two modules from dubious Chinese sources have been faulty and using header sockets makes the module much easier to replace.

As I was using a metal enclosure (with plastic ends), I wanted to keep the ESP8266's antenna as close to the end of the enclosure as possible. This meant that I wasn't able to supply power to the project through the WeMos D1 Mini's USB socket so I fitted a micro-USB breakout board to the edge of the PCB.

If you envisage re-programming the Wemos D1 Mini frequently once the monitor is fully assembled, it would be easy enough to make up a short "off-cut" of a USB lead with a micro USB plug at one end (to connect to the WeMos D1 Mini) with the D+ and D- wires connected to the corresponding solder pads on the mico-USB breakout board - ground and +5v already being supplied through the PCB tracks.

Download Actual size PCB Artwork in PDF format.

Download PCB Wizard design files.   (PCB Wizard)

Components

Nova PM Sensor SDS011 with connecting cableBanggood
Wemos ESP8266 D1 Mini Banggood, eBay
2.8 Inch ILI9341 240x320 SPI TFT LCD Display Banggood
Hammond Enclosure. 120 x 103 x 53mm 1455N1202BKCPC
2N7000 MOSFETeBay
8-way long pins (stackable) female header socket (for display),
2 x 8-way female header sockets (for Wemos D1 Mini)
eBay
4 x 15mm x 3M Hex pillars (for spacers between display and PCB)eBay
JST right-angle sockets, resistors, capacitors, plain PCB, 3M nuts and washerseBay

 

Assembly

I used a scrap of Veroboard as a template to drill some air holes in each end of the enclosure. I started with 0.8mm holes and enlarged those directly in line with the SDS011 sensor's inlet pipe to 1.8mm. A piece of foam fitted over the sensor's inlet pipe makes a reasonably close fit to the end plate so that most of the air taken in by the sensor will be from outside the enclosure.. I enlargedl all the holes in the outlet side to 1.8mm.

 

Setting up the Arduino IDE

If you haven't used the ESP8266 in the Arduino environment before, it's necessary to install the ESP8266 Board definitions into the Arduino IDE.

In the Arduino IDE, select File -> Preferences.

In the Additional Boards Manager URLs text box, enter: http://arduino.esp8266.com/stable/package_esp8266com_index.json then click the OK button.

 


Select Tools -> Board -> Board Manager...

Wait for the platforms index to download then scroll down the list to esp2866 by ESP2866 Community and click Install.

 

Close the Arduino IDE.

 


Copy the Arduino/Air Quality Monitor sketch below and paste it into the IDE.

Find the following lines near the top of the sketch, edit it to your own WiFi SSID, WiFi Password and the LAN IP address of the computer that the graph-plotter/server application is running on. Upload the sketch to the Wemos D1 Mini.

const char* ssid = "abcdefg";              // Your WiFi SSID.
const char* password = "******";           // Your WiFi Password.

String serverIP = "192.168.1.3:8802";          // The server IP address and Port number set up in the PC Server/plotter Application.
String deviceId = "air_quality";               // The device ID that the PC server Application will recognize.

 

Air Quality Monitor - Arduino Sketch

The colours used on the display match those used by the UK's Department for Environment Foold & Rural Affairs (Defra).

Other countries use different colours and different boundaries between index values. These can be set in the two blocks of code headed UK air pollution bands for PM2.5 and PM10 Particles in the sketch below.

Additional Arduino Libraries

SDS011 Library
Adafruit Graphics Library
Adafruit 2.8" TFT Library

 

// SDS011 Air Quality Monitor
// --------------------------
// Optionally, used in conjunction with PC Server/plotter application at (c) vwlowen.co.uk
//
// Based on SDS011 Sensor libray by R. Zschiegner (rz@madavi.de).




#include <SDS011.h>                           //  https://platformio.org/lib/show/1563/SDS011%20sensor%20Library

#include <Adafruit_GFX.h>                     //  https://github.com/adafruit/Adafruit-GFX-Library
#include "Adafruit_ILI9341.h"                 //  https://github.com/adafruit/Adafruit_ILI9341
#include <Fonts/FreeSansBold18pt7b.h  >
#include <SPI.h>

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

#include <EEPROM.h>


const char* ssid = "abcdefg";             // Your WiFi SSID.
const char* password = "*****";           // Your WiFi Password.

String serverIP = "192.168.1.3:8802";          // The server IP address and Port number set up in the PC Server/plotter Application.
String deviceId = "air_quality";               // The device ID that the PC server Application will recognize.


//#define TFT_RST                // (Not connected. Pull TFT RST HIGH with 10k resistor)   
//#define TFT_SCLK  D5           // SCLK is explicit and must be connected to D5 (GPIO14)
//#define TFT_MOSI  D7           //  MOSI is explicit and must be connected to D7 (GPIO13)

#define TFT_CS      D3           // GPIO0                
#define TFT_DC      D2           // GPIO4  

#define SAVE_COUNTER 12          // Data is saved to EEPROM every SAVE_COUNTER * SAMPLE_INTERVAL minutes. (1 hour).

#define SAMPLE_INTERVAL  5       // Take air sample every SAMPLE_INTERVAL minutes

#define SAMPLE_SECS  30          // Run fan for SAMPLE_SECONDS, then take air sample

#define SDS_TX       D1          // SDS011 Tx Pin  GPIO 5
#define SDS_RX       D6          // SDS011 Rx Pin  GPIO 12 (Unused IO - **Do Not Use**)

#define SDS011_PWR   D8          // Power control to SDS011 Sensor.
#define SAVE_DATA    D4          //

int loopCount = 0;

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);  // Adafruit TFT library. Create an instance.

SDS011 sdsSensor;                                         // Sensor library - create an instance of the sensor.

String quality;                                           // Define PM2.5 value as LOW, MEDIUM etc (UK Defra scale).
int colour;                                               // Define PM2.5 value as colour (UK Defra scale)
short pm25Array[320];                                     // Array to hold sensor values for the histogram.

float p10, p25;                                           // Variabled for PM10 and MP2.5 data from sensor.
int error;                                                // Confirms valid data from sensor. 0 = error.

short arrayPointer = 15;                                  // Array element currently being written to (16-bit integer)
int yPos;                                                 // Vertical marker for bar chart.

int sleepSeconds;

float volts = 0.0;
const float Vmax = 5.75;                                 // Max voltage can be set here. Depends on resistor tolerances.

void saveData() {
  
  EEPROM.put(0, arrayPointer);
  
  for (int i= 15; i<=319; i++ ) {
     EEPROM.put(i*2, pm25Array[i]);
  }  
  EEPROM.commit();
  
  tft.setCursor(245, 10);
  tft.setTextColor(ILI9341_BLUE, ILI9341_BLACK);
  tft.print("SAVED");
  while (digitalRead(SAVE_DATA) == LOW);
  delay(250);
} 


void plotHistogram() {                                    // Function to re-draw the histogram.
   tft.fillRect(15,120, 319, 90, ILI9341_BLACK);          // Clear plotting area

   byte line;
   for (int i = 15; i <= 319;  i++){
      getTextData25(pm25Array[i] / 10);                   // Get colour corrsponding to each air quality level. Value is stored
                                                          // multiplied by 10 so divide by 10 here to get true value.                        
      line = constrain(sqrt(pm25Array[i]*40), 0, 105);    // Calculate length of line to plot. sqrt compresses higher values.
     tft.drawFastVLine(i, 225 - line, line,  colour);     // Draw vertical line in chosen colour.
   }
}


#define LIGHT_GREEN  0x9FF3                               // Define colours used by UK Defra to specify pollutant bands.
#define MID_GREEN    0x37E0
#define DARK_GREEN   0x3660
#define LIGHT_YELLOW 0xFFE0
#define MID_YELLOW   0xFE60
#define ORANGE       0xFCC0
#define LIGHT_RED    0xFB2C
#define MID_RED      0xF800
#define DARK_RED     0x9800
#define PURPLE       0xC99F


// UK air pollution bands for PM2.5 and PM10 Particles.  
// https://uk-air.defra.gov.uk/air-pollution/daqi?view=more-info&pollutant=pm25#pollutant

int getTextData25(int value) {                                                         // Function sets three global variables: 'Ypos'
  switch (value) {                                                                     // (vertical cursor position), 'colour' &
    case 0 ... 11 : yPos = 100; colour = LIGHT_GREEN; quality = "1 LOW"; break;        // 'quality' and returns half the length of the
    case 12 ... 23 : yPos = 90; colour = MID_GREEN; quality = "2 LOW"; break;          // text string 'quality' whose value is used to
    case 24 ... 35 : yPos = 80; colour = DARK_GREEN; quality = "3 LOW"; break;         // centre justify the text on the display.
    case 36 ... 41 : yPos = 70; colour = LIGHT_YELLOW; quality = "4 MODERATE"; break;
    case 42 ... 47 : yPos = 60; colour = MID_YELLOW; quality = "5 MODERATE"; break;
    case 48 ... 53 : yPos = 50; colour = ORANGE; quality = "6 MODERATE";  break;
    case 54 ... 58 : yPos = 40; colour = LIGHT_RED;  quality = "7 HIGH"; break;
    case 59 ... 64 : yPos = 30; colour = MID_RED;  quality = "8 HIGH"; break;
    case 65 ... 70 : yPos = 20; colour = DARK_RED;  quality = "9 HIGH"; break;  
    case 71 ... 9999: yPos = 10; colour = PURPLE;  quality = "10 VERY HIGH"; break;   
    default: yPos = 10; colour = ILI9341_MAGENTA;  quality = "HAZARDOUS"; break;
  }  
  return (quality.length() / 2) * 6;
}                                         


int getTextDataPM10(int value) {
  switch (value) {
    case 0 ... 16 : colour = LIGHT_GREEN; quality = "1 LOW"; break;
    case 17 ... 33 : colour = MID_GREEN; quality = "2 LOW"; break;
    case 34 ... 50 :  colour = DARK_GREEN; quality = "3 LOW"; break;
    case 51 ... 58 : colour = LIGHT_YELLOW; quality = "4 MODERATE"; break;
    case 59 ... 66 : colour = MID_YELLOW; quality = "5 MODERATE"; break;
    case 67 ... 75 : colour = ORANGE; quality = "6 MODERATE";  break;
    case 76 ... 83 : colour = LIGHT_RED;  quality = "7 HIGH"; break;
    case 84 ... 91 : colour = MID_RED;  quality = "8 HIGH"; break;
    case 92 ... 100 : colour = DARK_RED;  quality = "9 HIGH"; break;  
    case 101 ... 9999: colour = PURPLE;  quality = "10 VERY HIGH"; break;   
    default: colour = ILI9341_MAGENTA;  quality = "HAZARDOUS"; break;
  }
   return (quality.length() / 2) * 6;
}


void setup() {

  pinMode(SDS011_PWR, OUTPUT); 
  pinMode(SAVE_DATA, INPUT_PULLUP);
  pinMode(A0, INPUT); 

  EEPROM.begin(1000);
  

  //-- The following block of code tests if the EEPROM has been 'prepared'  ----
  //-- with all zeroes and clears it if necessary.------------------------------ 
   
  bool eraseFlag = false;
  for (int i = 2; i< 30; i++ ) {
    if (EEPROM.read(i) != 0) {
      eraseFlag = true;
      break;
    }
  }
      
   if (eraseFlag) {
     for (int i=0; i< 640; i++){
       EEPROM.write(i, (byte) 0);                      // Reset EEPROM addresses to zero
     }
     EEPROM.put(0, (short) 15);                         // Reset array Pointer to start address (15)
     EEPROM.commit();
   }

 //-------------------------------------------------------------------------------

   

  EEPROM.get(0, arrayPointer);
  for (int i=15; i<319; i++) {
    EEPROM.get(i*2, pm25Array[i]);
  }
  
  tft.begin(); 

  tft.setRotation(1); 
  tft.setTextWrap(true);

  tft.fillScreen(ILI9341_BLACK);
  tft.setTextSize(1);
 

  sleepSeconds = (SAMPLE_INTERVAL * 60) - SAMPLE_SECS;          // Calculate sleep time in seconds.

  WiFi.begin(ssid, password);                                   // Connect WiFi to server running on PC to
                                                                // plot PM10 and PM2.5 values.
 
  int wifi_timeout = 0;
  tft.setCursor(0, 10);
  tft.print("Connecting to WiFi...");
  while (WiFi.status() != WL_CONNECTED)  {
    delay(10);
    tft.setCursor(0, 20);
    tft.fillRect(0, 20, 100, 10, ILI9341_BLACK);
    tft.print(wifi_timeout);
    wifi_timeout++;
    if(wifi_timeout > 1000) {
      tft.setCursor(0, 35);
      tft.println("Failed to connect to Wifi");
      break;
    }
  }

  if (WiFi.status() == WL_CONNECTED) {
    tft.setCursor(0, 20);
    tft.print("Connected to ");
    tft.println(WiFi.SSID());
    tft.print("IP address: ");
    tft.println(WiFi.localIP());
    delay(5000);
  }
 
  tft.fillScreen(ILI9341_BLACK);
 
  tft.setCursor(15, tft.height() -20);
 
  tft.setTextSize(1);                                         // Print static labels and headers on display.
  tft.setCursor(150, 10);
  tft.println("PM 2.5");
  tft.setCursor(150, 17);
  tft.print("ug/m3");

  tft.setCursor(5, 10);
  tft.print("PM 10"); 
  tft.setCursor(5, 17);
  tft.print("ug/m3"); 

  tft.fillRect(312, 10, 6, 10, PURPLE);                        // Print a colour key for the Defra pollutant bands.
  tft.fillRect(312, 20, 6, 10, DARK_RED);
  tft.fillRect(312, 30, 6, 10, MID_RED);
  tft.fillRect(312, 40, 6, 10, LIGHT_RED);
  tft.fillRect(312, 50, 6, 10, ORANGE);
  tft.fillRect(312, 60, 6, 10, MID_YELLOW);
  tft.fillRect(312, 70, 6, 10, LIGHT_YELLOW);
  tft.fillRect(312, 80, 6, 10, DARK_GREEN);
  tft.fillRect(312, 90, 6, 10, MID_GREEN);
  tft.fillRect(312, 100, 6, 10, LIGHT_GREEN);
    
  tft.drawFastVLine(13, 120, 108, ILI9341_BLUE);               // Draw histogram vertical axis
  tft.setTextColor(ILI9341_BLUE);
 
  tft.setCursor(0, 120);
  tft.print(" ^");
  tft.setCursor(0, 130);
  tft.print("50"); 
  tft.setCursor(0, 170);
  tft.print("10"); 
  tft.setCursor(0, 200);
  tft.print(" 1");

  tft.drawFastHLine(12, tft.height() - 14, tft.width()-1, ILI9341_BLUE);     // Draw histogram horizontal axis

  for (int x = 319; x > 15; x-=12) {
     tft.drawFastVLine(x, 227, 3, ILI9341_BLUE);                             // Draw 1-hour ticks on horizontal axis.
  }
  tft.setTextColor(ILI9341_BLUE);
  tft.setCursor(105, 232); 
  tft.print("Air Quality Monitor");

   int rssi = WiFi.RSSI();

   tft.setCursor(0,  232); 
   tft.fillRect(0, 232, 50, 8, ILI9341_BLACK);
   tft.setTextSize(1);

   if (WiFi.status() == WL_CONNECTED) {
     tft.setTextColor(ILI9341_BLUE);
     tft.print("RSSI " + String(rssi) + " dB");
   } else {
     tft.setTextColor(ILI9341_RED);
     tft.print("No WiFi");    
   }
   
  tft.setTextColor(ILI9341_GREEN);
 
  tft.setTextSize(3);
  
  Serial.begin(9600);
  sdsSensor.begin(SDS_TX, SDS_RX);    // Begin sensor and define Tx and Rx pins.

  plotHistogram();
                                    
}



void loop() {

  digitalWrite(SDS011_PWR, HIGH);                         // Turn on SDS011 Sensor power


  tft.setTextSize(1);                                     //
  tft.setCursor(248, 232);   
  tft.setTextColor(ILI9341_GREEN, ILI9341_BLACK);
  tft.print("SAMPLING ");  

  for (int i = SAMPLE_SECS; i>=0; i--) {                // Run fan for 30 seconds to ensure new air
    
    tft.setCursor(300,  232);                         
     if (i < 10) {
       tft.print("0");
     }
     tft.print(i);

     if (digitalRead(SAVE_DATA) == LOW) {
        saveData();
     }   
          
     delay(1000);
  }

  int raw = 0;                                             // Get supply voltage. Useful when battery operated.
  for (byte i=0;i<10;i++) {
    raw += analogRead(A0);
    delay(10);
  }
  raw = raw / 10;
  float volts = (raw / 1023.0) * Vmax;

  
  tft.fillRect(248, 232, 70, 10, ILI9341_BLACK);
 
  tft.fillRect(85, 105, 65, 8, ILI9341_BLACK);
    
	error = sdsSensor.read(&p25,&p10);                   // Read PM2.5 and PM10 values from sensor.

	if (! error) {
   Serial.print("P2.5: ");
   Serial.println(p25);
	 Serial.print("P10:  ");
   Serial.println(p10);
    

   int x = getTextData25(p25);                                          // Function retuns (width of text)/2 so we can
                                                                        // centre-justify it on the display. It also sets
   tft.setTextColor(colour);                                            // the text colour appropriate to the PM2.5 value as
                                                                        // defined by the UK Defra documentation.
                                                                        
   tft.fillRect(305, 10, 5, 105, ILI9341_BLACK);                        // Clear old triangle
   tft.fillTriangle(305, yPos, 308, yPos+5, 305, yPos+10, colour);      // Plot new position of triangle on colour scale
   
   tft.fillRect(100, 40, 110, 8, ILI9341_BLACK);                        // Clear display areas where new text will                
                                                                        // be drawn. (Graphical fonts don't overwrite
   tft.fillRect(0, 36, 285, 77, ILI9341_BLACK);                         // previous text.
   
   tft.setCursor(165 - x, 40);                                          // Set cursor to centre of display area.
   tft.print(quality);

   tft.setTextSize(2);
   tft.setFont(&FreeSansBold18pt7b);                                    // Change to new font.

   String sp25 = String(p25);                                           // Convert PM2.5 value to text because the
                                                                        // 'getTextBounds' function needs text.
   int16_t x1, y1;
   uint16_t w, h;
   tft.getTextBounds(sp25, 0,0, &x1, &y1, &w, &h);                      // We mainly want the width of the text that
                                                                        // we're about to print so we can centre-justify it.   
   tft.setCursor(183-(w/2), 110);
   tft.print(p25, 1);
   
   tft.setFont();                                                       // Revert to standard font.
   tft.setTextSize(1);
   
   tft.setTextColor(colour, ILI9341_BLACK);                             // PM10 data is less-used so just print it
   tft.fillRect(0, 30, 100, 10, ILI9341_BLACK);                         // in the top left corner of the display.
   tft.setCursor(0, 40);
   tft.print(quality);
   tft.setTextSize(2);
   tft.fillRect(0, 55, 60, 20, ILI9341_BLACK);
 
   tft.setCursor(2, 55);
   tft.print(p10, 1);

   tft.setTextSize(1); 
   tft.setTextColor(ILI9341_GREEN);
   tft.fillRect(80, 10, 50, 10, ILI9341_BLACK);
   tft.setCursor(80, 10);
   tft.print(volts);
   tft.print("v");

   tft.fillRect(245, 10, 30, 10, ILI9341_BLACK);


// ====== plot histogram (bar graph) ==============

   if (arrayPointer >= 319) {                              // If array has been filled, move all values down one.
     for (int i = 15; i <= 319; i++) {
       pm25Array[i] = pm25Array[i+1];
     }
   }
 
   pm25Array[arrayPointer] = (short) (p25 * 10);           // Multiply float value by 10 to make short integer.

   plotHistogram();
   
   if (arrayPointer < 319) arrayPointer++;                 // Increment the pointer to store the next value.



   delay(100);

 
 //======= end plot ====================


   int rssi = WiFi.RSSI();                                   // Get the WiFi signal strength and print on the display.

   tft.setCursor(0,  232); 
   tft.fillRect(0, 232, 50, 8, ILI9341_BLACK);
   tft.setTextSize(1);
   
   if (WiFi.status() == WL_CONNECTED) {
     tft.setTextColor(ILI9341_BLUE);
     tft.print(" RSSI " + String(rssi) + " dB");
   } else {
     tft.setTextColor(ILI9341_RED);
     tft.print(" No WiFi");    
   }
 

   if (WiFi.status() == WL_CONNECTED)  { 
     HTTPClient http;
 
    // Specify request destination, including your GET variables 
     
     String http_request = "";

     http_request = "http://" + serverIP  + "/apage?";      // Build the text string for the HTTP GET request to the PC server.
     http_request += "id=" + deviceId;
     http_request += "&leftaxis=" + sp25;
     http_request += "&rightaxis=" + String(p10);
     http_request += "&rssi=" + String(rssi);
     http_request += "&volts=" + String(volts);
    
     Serial.println("Making HTTP request...");
     Serial.println(http_request);

     http.begin(http_request);  
     
    // Send the request
     int httpCode = http.GET();

    // Check the returning HTTP code
     if (httpCode > 0) {
      // Get a response back from the server
       String payload = http.getString();
      // Print the response
       Serial.println("HTTP Response: ");
       Serial.println(payload);
     }

    // Close the HTTP connection
      http.end(); 

    } 
	}
 
	digitalWrite(SDS011_PWR, LOW);                        // Turn off SDS011 Power.

  delay(1000);
  tft.setTextSize(1);

  tft.setCursor(248, 232); 
  tft.setTextColor(ILI9341_BLUE, ILI9341_BLACK);
  tft.print("SLEEP   ");  

  int secs;
  int mins; 

 for (int i = sleepSeconds; i>0; i--) {                 // Sleep for the sample interval (less the 30 seconds warmup time)
     tft.setCursor(285,  232);                          //
     secs = i;
     tft.print(secs / 60);                              // Print minutes remaining.
     tft.print(":");
     
     if (secs % 60 < 10) {
       tft.print("0");
     }
     tft.print(secs % 60);                              // Print seconds remaining.
     tft.print(" ");

     if (digitalRead(SAVE_DATA) == LOW) {               // Manually save histogram data to EEPROM - don't wait for the
        saveData();                                     // auto-save after one hour to expire.
     }   

     delay(1000);
  }

  loopCount++;
  if (loopCount >= SAVE_COUNTER) {                      // Each loop takes 5 minutes. 12 loops = 5 * 12 = 60 minutes
    loopCount = 0;
    saveData();
  }

  Serial.print("Heap size at end of loop ");
  Serial.println(system_get_free_heap_size() );         // Free  memory check!

  
}

 

Back to Index

 


This site and its contents are © Copyright 2005 - All Rights Reserved.