Augustin De PianteJustin PeetzLuke Sewing
Published

IOT Based Air Monitoring Mesh System

A simple array of a few Particle Argons to monitor and report environmental conditions of a mesh network of several spaces.

IntermediateFull instructions provided920
IOT Based Air Monitoring Mesh System

Things used in this project

Hardware components

Argon
Particle Argon
×3
Grove - Barometer Sensor (BMP280)
Seeed Studio Grove - Barometer Sensor (BMP280)
×2
Grove - Dust Sensor(PPD42NS)
Seeed Studio Grove - Dust Sensor(PPD42NS)
×2
Grove - OLED Display 1.12'' V2
Seeed Studio Grove - OLED Display 1.12'' V2
×2
Adafruit Accessories Grove Shield FeatherWing for Particle Mesh and all Feathers
×2
Seeed Studio Seeed Grove - Universal 4 Pin 20cm Unbuckled Cable (5 PCs Pack)
×2

Software apps and online services

Particle Build Web IDE
Particle Build Web IDE
Discord
Fritzing Circuit Design
Solidworks

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

Box

Enclosure for the system

Box lid

Lid for the enclosure

Schematics

Monitor_Schematic

Monitor Schematic

Physical hookup for Air Monitor.

Code

Air Monitor Code

C/C++
This sketch is uploaded to the Argon connected to the sensors. It sends data both to the other Air monitor and to the Message Hub.
#include "Particle.h"                   //Begin including libraries. This is the library for the Particle itself.
#include "Air_Quality_Sensor.h"         //Library for Air Quality Sensor.
#include "Adafruit_BME280.h"            //Library for BME280.
#include "SeeedOLED.h"                  //Library for OLED Display.

#define AQS_PIN A2                      //Defines Analog Pin 2 for our Air Quality Sensor.
#define DUST_SENSOR_PIN D4              //Defines Digital Pin 4 for our Dust Sensor.
#define SENSOR_READING_INTERVAL 1000    //Defines the Sensor Read Interval.

SYSTEM_THREAD(ENABLED);

int temp = 0;                           //Placeholder for Temperature value.
int humidity = 0;                       //Placeholder for Humidity value.
int pressure = 0;                       //Placeholder for Pressure value.
int brightnessPotVal = 50;
int red = 255;
int blue = 255;
int green = 255;
int did_this_come_in = FALSE;

unsigned long lastInterval;             
unsigned long lowpulseoccupancy = 0;
unsigned long last_lpo = 0;
unsigned long duration;

float ratio = 0;
float concentration = 0;

char pang[1] = {0};

const String key = "LWVHBUBP1V7UB09D";  //thingspeak Write Key

int getBMEValues(int &temp, int &humidity, int &pressure);                                  //
void getDustSensorReadings();                                                               ////
String getAirQuality();                                                                     //////
AirQualitySensor aqSensor(AQS_PIN);                                                         ////////
Adafruit_BME280 bme;                                                                        ////////// Function Prototypes
void updateDisplay(int temp, int humidity, int pressure, String airQuality);                ////////
void discord(int temp, int humidity, int pressure, float concentration);                    //////
void pong( const char *event, const char *data);                                            ////
void write_all_to_ThingSpeak(int temp, int humidity, int pressure, float concentration);    //

void setup()
{
    Serial.begin(9600);
    delay(50);
    pinMode(DUST_SENSOR_PIN, INPUT);
    pinMode(D7, OUTPUT);
    
    if (aqSensor.init())
        {
        Serial.println("Air Quality Sensor ready.");
        }
    else
        {
        Serial.println("Air Quality Sensor ERROR!");
        }

    Wire.begin();
    SeeedOled.init();

    SeeedOled.clearDisplay();
    SeeedOled.setNormalDisplay();
    SeeedOled.setPageMode();

    SeeedOled.setTextXY(2, 0);
    SeeedOled.putString("----------");
    SeeedOled.setTextXY(3, 0);
    SeeedOled.putString("Trump 2024");
    SeeedOled.setTextXY(4, 0);
    SeeedOled.putString("----------");

  if (bme.begin())
  {
    Serial.println("BME280 Sensor ready.");
  }
  else
  {
    Serial.println("BME280 Sensor ERROR!");
  }

  lastInterval = millis();
  
    Particle.subscribe("Echo", pong);
    Particle.subscribe("ack_outbound_luke", ack_received);

}

void loop()
{
    duration = pulseIn(DUST_SENSOR_PIN, LOW);
    lowpulseoccupancy = lowpulseoccupancy + duration;

    if ((millis() - lastInterval) > SENSOR_READING_INTERVAL)
    {
    String quality = getAirQuality();
    Serial.printlnf("Air Quality: %s", quality.c_str());

    getBMEValues(temp, pressure, humidity);
    Serial.printlnf("Temp: %d", temp);
    Serial.printlnf("Pressure: %d", pressure);
    Serial.printlnf("Humidity: %d", humidity);

    getDustSensorReadings();

    updateDisplay(temp, humidity, pressure, quality);

    lowpulseoccupancy = 0;
    lastInterval = millis();
    }
    
    int ack_from_gus = 0;
    delay(15000);
    if (did_this_come_in = (TRUE))
    {
        ack_from_gus = 1;
        RGB.control(TRUE);
        RGB.color(0, 0, green);
        RGB.brightness(brightnessPotVal);
        Particle.publish("ack_outbound_gus", String(ack_from_gus), PRIVATE);
        delay(15000);
    }
    
    ack_from_gus = 0;
    RGB.control(TRUE);
    RGB.color(red, 0, 0);
    RGB.brightness(brightnessPotVal);
    Particle.publish("ack_outbound_gus", String(ack_from_gus), PRIVATE);

    
    
    if (pang != 0)
    {
        digitalWrite(D7, HIGH);
        delay(1000);
        digitalWrite(D7, LOW);
        delay(1000);
    }
    write_all_to_ThingSpeak(temp, humidity, pressure, concentration);
    discord(temp, humidity, pressure, concentration);
    delay(1800000);



}

String getAirQuality()
{
  int quality = aqSensor.slope();
  String qual = "None";

  if (quality == AirQualitySensor::FORCE_SIGNAL)
  {
    qual = "Danger";
  }
  else if (quality == AirQualitySensor::HIGH_POLLUTION)
  {
    qual = "High Pollution";
  }
  else if (quality == AirQualitySensor::LOW_POLLUTION)
  {
    qual = "Low Pollution";
  }
  else if (quality == AirQualitySensor::FRESH_AIR)
  {
    qual = "Fresh Air";
  }

  return qual;
}

int getBMEValues(int &temp, int &pressure, int &humidity)
{
  temp = (int)bme.readTemperature();
  pressure = (int)(bme.readPressure() / 100.0F);
  humidity = (int)bme.readHumidity();

  return 1;
}

void getDustSensorReadings()
{
  // This particular dust sensor returns 0s often, so let's filter them out by making sure we only
  // capture and use non-zero LPO values for our calculations once we get a good reading.
  if (lowpulseoccupancy == 0)
  {
    lowpulseoccupancy = last_lpo;
  }
  else
  {
    last_lpo = lowpulseoccupancy;
  }

  ratio = lowpulseoccupancy / (SENSOR_READING_INTERVAL * 10.0);                   // Integer percentage 0=>100
  concentration = 1.1 * pow(ratio, 3) - 3.8 * pow(ratio, 2) + 520 * ratio + 0.62; // using spec sheet curve

  Serial.printlnf("LPO: %lu", lowpulseoccupancy);
  Serial.printlnf("Ratio: %f%%", ratio);
  Serial.printlnf("Concentration: %f pcs/L", concentration);
}

void updateDisplay(int temp, int humidity, int pressure, String airQuality)
{
  SeeedOled.clearDisplay();

  SeeedOled.setTextXY(0, 0);
  SeeedOled.putString(airQuality);

  SeeedOled.setTextXY(2, 0);
  SeeedOled.putString("Temp: ");
  SeeedOled.putNumber(temp);
  SeeedOled.putString("C");

  SeeedOled.setTextXY(3, 0);
  SeeedOled.putString("Humidity: ");
  SeeedOled.putNumber(humidity);
  SeeedOled.putString("%");

  SeeedOled.setTextXY(4, 0);
  SeeedOled.putString("Press: ");
  SeeedOled.putNumber(pressure);
  SeeedOled.putString(" hPa");

    if (concentration > 1)
      {
        SeeedOled.setTextXY(5, 0);
        SeeedOled.putString("Dust: ");
        SeeedOled.putNumber(concentration); // Will cast our float to an int to make it more compact
        SeeedOled.putString(" pcs/L");
      }
}

void discord(int temp, int humidity, int pressure, float concentration)
{
    digitalWrite(D7, HIGH);                                                                                             //Turns on onboard LED 
    delay(1000);                                                                                                        //For 1000ms
    digitalWrite(D7, LOW);                                                                                              //Turns off onboard LED 
    delay(1000);                                                                                                        //For 1000ms
    String discord_data = String::format(
        "**__Gus's House__**\n```Temperature: %d°C\nHumidity: %d\%\nPressure: %dhPa\nDust Concentration: %fpcs/L```", 
        temp, humidity, pressure, concentration);
    Particle.publish("net-data", discord_data, PRIVATE);
}

void pong( const char *event, const char *data)
{
            
    for(int i = 0; i < 1; i++)
        {
        pang[i] = data[i];      //Assigns the value of data to pang 
        }
}

void write_all_to_ThingSpeak(int temp, int humidity, int pressure, float concentration) //
{
    Particle.publish("thingspeak", +
     "{ \"1\": \"" + String(temp) + "\"," +                 // Change values 1-4 to 5-8 for second monitor.
        "\"2\": \"" + String(humidity) + "\"," +
        "\"3\": \"" + String(pressure) + "\"," +
        "\"4\": \"" + String(concentration) + "\"," +
        "\"k\": \"" + key + "\" }", PRIVATE);
}

void ack_received(const char *event, const char *data)
{
    if ( (int)data == 1)
    {
    did_this_come_in = TRUE;
    }
}

Message Hub

C/C++
Communicates with both Air Monitor modules (sends and receives data), and then publishes Monitor Information to Discord and Thingspeak.
char discord_data[256] = {0};   //Sets an initial value of 0 to the char that will hold all our data
char ping[1] = {0};             //Sets an initial value of 0 to the char that will hold the temporary value of a conditional variable


void setup() {
    pinMode(D7, OUTPUT);
    Particle.subscribe("net-data", discord_subscribe);  //Subscribe to the particle function that passes data rom the monitors.
    Serial.begin(9600);                                 // Open serial communication for debugging.
}

void loop() {
    
    if (discord_data != 0)                              //Check if the incoming data has a value
    {
        digitalWrite(D7, HIGH);                         //Turn on and off the LED to indicate proper function
        delay(1000);
        digitalWrite(D7, LOW);                          
        delay(1000);
        Particle.publish("Information", discord_data, PRIVATE);         // Publish data to Discord
        delay(60000);
        char ping[1] = {1};                                             // Rewrite the value of ping to true, allowing the two way ccommunication to function.
        Particle.publish("Echo", ping, PRIVATE);                        // Publish data back to Air Monitor
        
    }
}

char discord_subscribe( const char *event, const char *data) {          // Take in data from air monitor and rewrite it to the discord_data buffer.
        for(int i = 0; i < 256; i++){
            discord_data[i] = data[i];
        }
        Serial.print("-------\n");
        Serial.print(data);
        Serial.println("");
        Serial.print(discord_data);
        Serial.println("");
        return 0;
}

Credits

Augustin De Piante

Augustin De Piante

1 project • 2 followers
Justin Peetz

Justin Peetz

1 project • 3 followers
Luke Sewing

Luke Sewing

1 project • 3 followers

Comments