Dhairya ParikhCommon9899Akarsh Agarwal
Published © LGPL

LoRa Powered Air Quality Monitoring System

The project tackles one of the most serious issues of the 21st century, the degrading quality of air around us. We use LoRa to address this.

AdvancedFull instructions provided12 hours4,977
LoRa Powered Air Quality Monitoring System

Things used in this project

Hardware components

Dragino LG01N Lora Gateway
A Single Channel Lora Gateway used to implement the LoRaWAN Protocol in this project
×1
Dragino LoRa Shield v1.4
The Arduino shield we will be using to create our LoRa Node.
×1
Arduino 101
Arduino 101
×1
SparkFun Air Quality Breakout - CCS811
SparkFun Air Quality Breakout - CCS811
×1
Grove - Dust Sensor(PPD42NS)
Seeed Studio Grove - Dust Sensor(PPD42NS)
×1
MICS-5524 Gas Sensor
×1
Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
I am using this to create an interactive Dashboard for visualization.
×1

Software apps and online services

Helium
Raspbian
Raspberry Pi Raspbian
The OS we use for our Pi
The Things Stack
The Things Industries The Things Stack
This will handle all the sensor data it receives from the Gateway and transfer it using Integrations.
ThingSpeak API
ThingSpeak API
Using it as an Intermediate to connect my dashboard and The Things Network.
AWS IoT
Amazon Web Services AWS IoT
We receive the sensor data here too in case we want to use an online dashboard or create an application to view this data.

Story

Read more

Schematics

Node Schematic

Gateway Schematic

Code

node-flow.json

JSON
[{"id":"19ef90f4.4dc70f","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"1f2c9cd9.64a733","type":"http request","z":"19ef90f4.4dc70f","name":"Get JSON Data","method":"GET","ret":"obj","paytoqs":false,"url":"https://api.thingspeak.com/channels/998094/fields/{{{query}}}}","tls":"","persist":false,"proxy":"","authType":"","x":540,"y":60,"wires":[["25dc7c1.c63c284"]]},{"id":"a34ff103.833dd","type":"inject","z":"19ef90f4.4dc70f","name":"Make Request","topic":"","payload":"1.json?results=1","payloadType":"str","repeat":"10","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":60,"wires":[["95775252.934bd"]]},{"id":"95775252.934bd","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"query","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":60,"wires":[["1f2c9cd9.64a733"]]},{"id":"25dc7c1.c63c284","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.feeds[0].field1","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":120,"y":120,"wires":[["62ff34fa.3b37ec"]]},{"id":"8b775b01.4a6738","type":"http request","z":"19ef90f4.4dc70f","name":"Get JSON Data","method":"GET","ret":"obj","paytoqs":false,"url":"https://api.thingspeak.com/channels/998094/fields/{{{query}}}}","tls":"","persist":false,"proxy":"","authType":"","x":520,"y":260,"wires":[["3c41b531.fed3ea"]]},{"id":"c99c4486.ba6418","type":"inject","z":"19ef90f4.4dc70f","name":"Make Request","topic":"","payload":"2.json?results=1","payloadType":"str","repeat":"10","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":260,"wires":[["e390a943.139e58"]]},{"id":"e390a943.139e58","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"query","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":260,"wires":[["8b775b01.4a6738"]]},{"id":"3c41b531.fed3ea","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.feeds[0].field2","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":120,"y":320,"wires":[["a47032a5.843e9"]]},{"id":"23cb94b2.69e60c","type":"http request","z":"19ef90f4.4dc70f","name":"Get JSON Data","method":"GET","ret":"obj","paytoqs":false,"url":"https://api.thingspeak.com/channels/998094/fields/{{{query}}}}","tls":"","persist":false,"proxy":"","authType":"","x":540,"y":460,"wires":[["4f5cd88.8d35e28"]]},{"id":"dad79d8a.e08fe","type":"inject","z":"19ef90f4.4dc70f","name":"Make Request","topic":"","payload":"3.json?results=1","payloadType":"str","repeat":"10","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":460,"wires":[["1d614dc9.dd0f42"]]},{"id":"1d614dc9.dd0f42","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"query","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":460,"wires":[["23cb94b2.69e60c"]]},{"id":"4f5cd88.8d35e28","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.feeds[0].field3","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":140,"y":520,"wires":[["caccca3.b0a8638"]]},{"id":"a47032a5.843e9","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$number(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":300,"y":340,"wires":[["98d9799.de5d288","91095301.cc1d1","c6793b74.cb49a8"]]},{"id":"62ff34fa.3b37ec","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$number(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":300,"y":140,"wires":[["a76f7b92.d1fa48","4ff26dc9.132bf4","1c6ea65e.aa355a"]]},{"id":"caccca3.b0a8638","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$number(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":540,"wires":[["9bebb23.6aa8f5","30760d52.52dc42","9367636a.a53f5"]]},{"id":"a76f7b92.d1fa48","type":"ui_gauge","z":"19ef90f4.4dc70f","name":"eCO2 Gauge","group":"7e389247.61017c","order":1,"width":0,"height":0,"gtype":"gage","title":"eCO2 Gauge","label":"ppm","format":"{{value}}","min":"400","max":"8192","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":490,"y":120,"wires":[]},{"id":"4ff26dc9.132bf4","type":"ui_chart","z":"19ef90f4.4dc70f","name":"","group":"7e389247.61017c","order":2,"width":0,"height":0,"label":"eCO2 Line Chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":"5","removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"outputs":1,"x":510,"y":160,"wires":[[]]},{"id":"98d9799.de5d288","type":"ui_gauge","z":"19ef90f4.4dc70f","name":"TVOC Gauge","group":"593f5489.70ceac","order":1,"width":0,"height":0,"gtype":"gage","title":"TVOC Gauge","label":"ppb","format":"{{value}}","min":0,"max":"1187","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":500,"y":320,"wires":[]},{"id":"91095301.cc1d1","type":"ui_chart","z":"19ef90f4.4dc70f","name":"TVOC Line Chart","group":"593f5489.70ceac","order":2,"width":0,"height":0,"label":"TVOC Line Chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":"5","removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"outputs":1,"x":510,"y":360,"wires":[[]]},{"id":"9bebb23.6aa8f5","type":"ui_gauge","z":"19ef90f4.4dc70f","name":"MICS Gauge","group":"b37e3329.404eb","order":1,"width":0,"height":0,"gtype":"gage","title":"MICS Gauge","label":"ppm","format":"{{value}}","min":0,"max":"500","colors":["#136fe7","#136fe7","#136fe7"],"seg1":"","seg2":"","x":550,"y":520,"wires":[]},{"id":"30760d52.52dc42","type":"ui_chart","z":"19ef90f4.4dc70f","name":"MICS Line Chart","group":"b37e3329.404eb","order":2,"width":0,"height":0,"label":"MICS Line Chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":"5","removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"outputs":1,"x":560,"y":560,"wires":[[]]},{"id":"9367636a.a53f5","type":"function","z":"19ef90f4.4dc70f","name":"MICS Range Select","func":"\nif(msg.payload < 20)\n{\n    msg.select = 1;\n}\nelse if(21 < msg.payload < 50)\n{\n    msg.select = 2;\n}\nelse if(51 < msg.payload < 80)\n{\n    msg.select = 3;\n}\nelse\n{\n   msg.select = 4;\n}\n\n\nreturn msg;","outputs":1,"noerr":0,"x":390,"y":620,"wires":[["5a19196b.a81bc8"]]},{"id":"b15cc192.ed6c7","type":"ui_led","z":"19ef90f4.4dc70f","group":"b37e3329.404eb","order":5,"width":"3","height":"3","label":"MICS","labelPlacement":"left","labelAlignment":"center","colorForValue":[{"color":"green","value":"1","valueType":"num"},{"color":"yellow","value":"2","valueType":"num"},{"color":"orange","value":"3","valueType":"num"},{"color":"red","value":"4","valueType":"num"}],"allowColorForValueInMessage":false,"name":"MICS Indicator","x":800,"y":640,"wires":[]},{"id":"900e1da7.849be","type":"http request","z":"19ef90f4.4dc70f","name":"Get JSON Data","method":"GET","ret":"obj","paytoqs":false,"url":"https://api.thingspeak.com/channels/998094/fields/{{{query}}}}","tls":"","persist":false,"proxy":"","authType":"","x":540,"y":740,"wires":[["85bc9c3d.0e10d"]]},{"id":"6a7d8154.f8105","type":"inject","z":"19ef90f4.4dc70f","name":"Make Request","topic":"","payload":"4.json?results=1","payloadType":"str","repeat":"10","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":740,"wires":[["f188b618.0526c8"]]},{"id":"f188b618.0526c8","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"query","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":740,"wires":[["900e1da7.849be"]]},{"id":"85bc9c3d.0e10d","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.feeds[0].field4","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":140,"y":800,"wires":[["ae76ea.341f7918"]]},{"id":"ae76ea.341f7918","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$number(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":820,"wires":[["7e8cf1f7.be266","deaadd41.321fc","177a3f6f.a10db1"]]},{"id":"7e8cf1f7.be266","type":"ui_gauge","z":"19ef90f4.4dc70f","name":"Dust Gauge","group":"82e177be.a20708","order":1,"width":0,"height":0,"gtype":"gage","title":"Dust Gauge","label":"ug/m3","format":"{{value}}","min":0,"max":"300","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":550,"y":800,"wires":[]},{"id":"deaadd41.321fc","type":"ui_chart","z":"19ef90f4.4dc70f","name":"Dust Line Chart","group":"82e177be.a20708","order":2,"width":0,"height":0,"label":"Dust Line Chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":"5","removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"outputs":1,"x":560,"y":840,"wires":[[]]},{"id":"177a3f6f.a10db1","type":"function","z":"19ef90f4.4dc70f","name":"Dust Density Range Select","func":"if(msg.payload < 20)\n{\n    msg.select = 1;\n}\nelse if(21 < msg.payload < 50)\n{\n    msg.select = 2;\n}\nelse if(51 < msg.payload < 80)\n{\n    msg.select = 3;\n}\nelse\n{\n   msg.select = 4;\n}\n\n\nreturn msg;","outputs":1,"noerr":0,"x":380,"y":900,"wires":[["84140a5.f339df8"]]},{"id":"9b363446.6f2998","type":"ui_led","z":"19ef90f4.4dc70f","group":"82e177be.a20708","order":5,"width":"3","height":"3","label":"Dust","labelPlacement":"left","labelAlignment":"center","colorForValue":[{"color":"green","value":"1","valueType":"num"},{"color":"yellow","value":"2","valueType":"num"},{"color":"orange","value":"3","valueType":"num"},{"color":"red","value":"4","valueType":"num"}],"allowColorForValueInMessage":false,"name":"Dust Indicator","x":820,"y":900,"wires":[],"inputLabels":["msg.select"]},{"id":"84140a5.f339df8","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"select","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"$number(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":620,"y":900,"wires":[["9b363446.6f2998"]]},{"id":"5a19196b.a81bc8","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"select","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"$number(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":600,"y":640,"wires":[["b15cc192.ed6c7"]]},{"id":"c6793b74.cb49a8","type":"function","z":"19ef90f4.4dc70f","name":"TVOC Range Select","func":"if(msg.payload < 100)\n{\n    msg.select = 1;\n}\nelse if(101 < msg.payload < 300)\n{\n    msg.select = 2;\n}\nelse if(301 < msg.payload < 600)\n{\n    msg.select = 3;\n}\nelse\n{\n   msg.select = 4;\n}\n\n\nreturn msg;","outputs":1,"noerr":0,"x":520,"y":400,"wires":[["98604cde.2d78d"]]},{"id":"30fbe077.ffc8d","type":"ui_led","z":"19ef90f4.4dc70f","group":"593f5489.70ceac","order":4,"width":"3","height":"3","label":"TVOC","labelPlacement":"left","labelAlignment":"center","colorForValue":[{"color":"green","value":"1","valueType":"num"},{"color":"yellow","value":"2","valueType":"num"},{"color":"orange","value":"3","valueType":"num"},{"color":"red","value":"4","valueType":"num"}],"allowColorForValueInMessage":false,"name":"TVOC Indicator","x":920,"y":420,"wires":[]},{"id":"98604cde.2d78d","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"select","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"$number(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":720,"y":420,"wires":[["30fbe077.ffc8d","1216106e.bf53"]]},{"id":"1c6ea65e.aa355a","type":"function","z":"19ef90f4.4dc70f","name":"eCO2 Range Select","func":"if(msg.payload < 1000)\n{\n    msg.select = 1;\n}\nelse if(1001 < msg.payload < 3000)\n{\n    msg.select = 2;\n}\nelse if(3001 < msg.payload < 4000)\n{\n    msg.select = 3;\n}\nelse\n{\n   msg.select = 4;\n}\n\n\nreturn msg;","outputs":1,"noerr":0,"x":520,"y":200,"wires":[["eda83abb.653838"]]},{"id":"ac6fb4f4.997898","type":"ui_led","z":"19ef90f4.4dc70f","group":"7e389247.61017c","order":4,"width":"3","height":"3","label":"eCO2","labelPlacement":"left","labelAlignment":"center","colorForValue":[{"color":"green","value":"1","valueType":"num"},{"color":"yellow","value":"2","valueType":"num"},{"color":"orange","value":"3","valueType":"num"},{"color":"red","value":"4","valueType":"num"}],"allowColorForValueInMessage":false,"name":"eCO2 Indicator","x":920,"y":220,"wires":[]},{"id":"eda83abb.653838","type":"change","z":"19ef90f4.4dc70f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"select","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"$number(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":720,"y":220,"wires":[["ac6fb4f4.997898"]]},{"id":"1216106e.bf53","type":"debug","z":"19ef90f4.4dc70f","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":860,"y":380,"wires":[]},{"id":"7e389247.61017c","type":"ui_group","z":"","name":"eCO2 Data","tab":"1b4092bb.d7c5dd","order":1,"disp":true,"width":"6","collapse":false},{"id":"593f5489.70ceac","type":"ui_group","z":"","name":"TVOC Data","tab":"1b4092bb.d7c5dd","order":2,"disp":true,"width":"6","collapse":false},{"id":"b37e3329.404eb","type":"ui_group","z":"","name":"MICS Data","tab":"1b4092bb.d7c5dd","order":3,"disp":true,"width":"6","collapse":false},{"id":"82e177be.a20708","type":"ui_group","z":"","name":"Dust Data","tab":"1b4092bb.d7c5dd","order":4,"disp":true,"width":"6","collapse":false},{"id":"1b4092bb.d7c5dd","type":"ui_tab","z":"","name":"LoRa Air Quality Dashboard","icon":"dashboard","order":1,"disabled":false,"hidden":false}]

Arduino Node Code

Arduino
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
#include <Wire.h>
#include "SparkFunCCS811.h"
#define USE_AVG

#define CCS811_ADDR 0x5A
CCS811 mySensor(CCS811_ADDR);

int co2,tvoc;
int pin2 = 8;
unsigned long duration2;

const int sharpLEDPin = 7;   // Arduino digital pin 7 connect to sensor LED.
const int sharpVoPin = A1;   // Arduino analog pin 5 connect to sensor Vo.

#ifdef USE_AVG
#define N 100
static unsigned long VoRawTotal = 0;
static int VoRawCount = 0;
#endif // USE_AVG

// Set the typical output voltage in Volts when there is zero dust. 
static float Voc = 0.6;

// Use the typical sensitivity in units of V per 100ug/m3.
const float K = 0.5;

// LoRaWAN NwkSKey, network session key
// This is the default Semtech key, which is used by the prototype TTN
// network initially.
//ttn
static const PROGMEM u1_t NWKSKEY[16] = { 0xE6, 0x54, 0x89, 0xD6, 0x05, 0x57, 0x45, 0x99, 0xAB, 0x18, 0x25, 0x14, 0x34, 0xA1, 0x99, 0x30 };

// LoRaWAN AppSKey, application session key
// This is the default Semtech key, which is used by the early prototype TTN
// network.
static const u1_t PROGMEM APPSKEY[16] = { 0x7A, 0x12, 0xB2, 0x84, 0xCA, 0xED, 0xE2, 0xAD, 0x07, 0xA9, 0x4A, 0xDB, 0xA0, 0x55, 0x05, 0x5D };

// LoRaWAN end-device address (DevAddr)
static const u4_t DEVADDR = 0x26011C08; // <-- Change this address for every node!
byte payload[8];

// These callbacks are only used in over-the-air activation, so they are
// left empty here (we cannot leave them out completely unless
// DISABLE_JOIN is set in config.h, otherwise the linker will complain).
void os_getArtEui (u1_t* buf) { }
void os_getDevEui (u1_t* buf) { }
void os_getDevKey (u1_t* buf) { }


static uint8_t mydata[] = {0x00, 0x00};
static osjob_t initjob,sendjob,blinkjob;

// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 5;

// Pin mapping
const lmic_pinmap lmic_pins = {
    .nss = 10,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 9,
    .dio = {2, 6, 7},
};
void do_send(osjob_t* j){
    // Check if there is not a current TX/RX job running
    if (LMIC.opmode & OP_TXRXPEND) {
        Serial.println("OP_TXRXPEND, not sending");
    } else {
        // Prepare upstream data transmission at the next possible time.
         LMIC_setTxData2(2, payload, sizeof(payload), 0);
         Serial.println(mySensor.getCO2());
         Serial.println(mySensor.getTVOC());
    
        Serial.println("Packet queued");
        Serial.println(LMIC.freq);
    }
    // Next TX is scheduled after TX_COMPLETE event.
}

void onEvent (ev_t ev) {
    Serial.print(os_getTime());
    Serial.print(": ");
    Serial.println(ev);
    switch(ev) {
        case EV_SCAN_TIMEOUT:
            Serial.println("EV_SCAN_TIMEOUT");
            break;
        case EV_BEACON_FOUND:
            Serial.println("EV_BEACON_FOUND");
            break;
        case EV_BEACON_MISSED:
            Serial.println("EV_BEACON_MISSED");
            break;
        case EV_BEACON_TRACKED:
            Serial.println("EV_BEACON_TRACKED");
            break;
        case EV_JOINING:
            Serial.println("EV_JOINING");
            break;
        case EV_JOINED:
            Serial.println("EV_JOINED");
            break;
        case EV_RFU1:
            Serial.println("EV_RFU1");
            break;
        case EV_JOIN_FAILED:
            Serial.println("EV_JOIN_FAILED");
            break;
        case EV_REJOIN_FAILED:
            Serial.println("EV_REJOIN_FAILED");
            break;
        case EV_TXCOMPLETE:
            Serial.println("EV_TXCOMPLETE (includes waiting for RX windows)");
            if(LMIC.dataLen) {
                // data received in rx slot after tx
                Serial.print("Data Received: ");
                Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen);
                Serial.println();
            }
            // Schedule next transmission
            os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
            break;
        case EV_LOST_TSYNC:
            Serial.println("EV_LOST_TSYNC");
            break;
        case EV_RESET:
            Serial.println("EV_RESET");
            break;
        case EV_RXCOMPLETE:
            // data received in ping slot
            Serial.println("EV_RXCOMPLETE");
            break;
        case EV_LINK_DEAD:
            Serial.println("EV_LINK_DEAD");
            break;
        case EV_LINK_ALIVE:
            Serial.println("EV_LINK_ALIVE");
            break;
         default:
            Serial.println("Unknown event");
            break;
    }
}

// Helper functions to print a data value to the serial monitor.
void printValue(String text, unsigned int value, bool isLast = false) {
  Serial.print(text);
  Serial.print("=");
  Serial.print(value);
  if (!isLast) {
    Serial.print(", ");
  }
}
void printFValue(String text, float value, String units, bool isLast = false) {
  Serial.print(text);
  Serial.print("=");
  Serial.print(value);
  Serial.print(units);
  if (!isLast) {
    Serial.print(", ");
  }
}

void setup() 
{
    Serial.begin(115200);
    pinMode(sharpLEDPin, OUTPUT);
    pinMode(pin2,INPUT);
    while(!Serial);
    Wire.begin(); //Inialize I2C Hardware
  
  if (mySensor.begin() == false)
  {
    Serial.print("CCS811 error. Please check wiring. Freezing...");
    while (1);
  }
    
    Serial.println("Starting");
    #ifdef VCC_ENABLE
    // For Pinoccio Scout boards
    pinMode(VCC_ENABLE, OUTPUT);
    digitalWrite(VCC_ENABLE, HIGH);
    delay(1000);
    #endif
    
    
    // LMIC init
    os_init();
    // Reset the MAC state. Session and pending data transfers will be discarded.
    LMIC_reset();
    //LMIC_setClockError(MAX_CLOCK_ERROR * 1/100);
    // Set static session parameters. Instead of dynamically establishing a session
    // by joining the network, precomputed session parameters are be provided.
    #ifdef PROGMEM
    
    uint8_t appskey[sizeof(APPSKEY)];
    uint8_t nwkskey[sizeof(NWKSKEY)];
    memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
    memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
    LMIC_setSession (0x1, DEVADDR, nwkskey, appskey);
    #else
    // If not running an AVR with PROGMEM, just use the arrays directly 
    LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);
    #endif
    
    // Disable link check validation
    LMIC_setLinkCheckMode(0);

    // TTN uses SF9 for its RX2 window.
    LMIC.dn2Dr = DR_SF9;
    
    // Set data rate and transmit power (note: txpow seems to be ignored by the library)
    LMIC_setDrTxpow(DR_SF7,14);

    // Start job
    do_send(&sendjob);

}

void loop() 
{
    mySensor.readAlgorithmResults();

    uint32_t co2 = mySensor.getCO2();
    uint32_t tvoc = mySensor.getTVOC();
    uint32_t reading = analogRead(A0);

    digitalWrite(sharpLEDPin, LOW);

  // Wait 0.28ms before taking a reading of the output voltage as per spec.
  delayMicroseconds(280);

  // Record the output voltage. This operation takes around 100 microseconds.
  int VoRaw = analogRead(sharpVoPin);
  
  // Turn the dust sensor LED off by setting digital pin HIGH.
  digitalWrite(sharpLEDPin, HIGH);

  // Wait for remainder of the 10ms cycle = 10000 - 280 - 100 microseconds.
  delayMicroseconds(9620);
  
  // Print raw voltage value (number from 0 to 1023).
  #ifdef PRINT_RAW_DATA
  printValue("VoRaw", VoRaw, true);
  Serial.println("");
  #endif // PRINT_RAW_DATA
  
  // Use averaging if needed.
  float Vo = VoRaw;
  #ifdef USE_AVG
  VoRawTotal += VoRaw;
  VoRawCount++;
  if ( VoRawCount >= N ) {
    Vo = 1.0 * VoRawTotal / N;
    VoRawCount = 0;
    VoRawTotal = 0;
  } else {
    return;
  }
  #endif // USE_AVG

  // Compute the output voltage in Volts.
  Vo = Vo / 1024.0 * 5.0;
  //printFValue("Vo", Vo*1000.0, "mV");

  // Convert to Dust Density in units of ug/m3.
  float dV = Vo - Voc;
  if ( dV < 0 ) {
    dV = 0;
    Voc = Vo;
  }
  float dustDensity = dV / K * 100.0;

    

    uint32_t conc = int(dustDensity);
    payload[0] = highByte(co2);
    payload[1] = lowByte(co2);
    payload[2] = highByte(tvoc);
    payload[3] = lowByte(tvoc);
    payload[4] = highByte(reading);
    payload[5] = lowByte(reading);
    payload[6] = highByte(conc);
    payload[7] = lowByte(conc); 

    os_runloop_once();
}

Credits

Dhairya Parikh

Dhairya Parikh

21 projects • 123 followers
Project Developer | IoT and Machine Learning Enthusiast | Open Source Enthusiast |
Common9899

Common9899

39 projects • 38 followers
Akarsh Agarwal

Akarsh Agarwal

59 projects • 187 followers
Im an electronics engineering student

Comments