mirror of
https://github.com/airgradienthq/arduino.git
synced 2025-06-26 00:01:32 +02:00
Compare commits
71 Commits
Author | SHA1 | Date | |
---|---|---|---|
91563ef836 | |||
feec04f42b | |||
36fd7774f6 | |||
e0d0bb6e3b | |||
9b0d6dbcdd | |||
d38f9e4d31 | |||
3b54adb094 | |||
336cfddacb | |||
f999ebe17c | |||
89758f92fa | |||
dee5b77d2a | |||
d4225896fe | |||
d5432630fe | |||
356e76f10e | |||
92f665d9ee | |||
a42ceb3b77 | |||
1f364a3b74 | |||
37d1140eda | |||
63e1bcdc30 | |||
c53517cadf | |||
6314e52770 | |||
43f599a0a7 | |||
cfc37d2d96 | |||
693d1f78aa | |||
ef802593a6 | |||
8ec543e9c1 | |||
9722eda5fa | |||
253d8a6810 | |||
6101429d30 | |||
47c55ae0dd | |||
0511f5430f | |||
5481bd51c8 | |||
7d717cc01c | |||
05b47e6b3d | |||
77ee1de9c1 | |||
183f62daf7 | |||
5dd8a8be32 | |||
98ec0c4d10 | |||
724d041684 | |||
afd4d16866 | |||
dc1a070ee3 | |||
84fe9c2575 | |||
90eee5d17f | |||
d5c8af68a0 | |||
3c7180e642 | |||
ee40b60aad | |||
b9a968ef7a | |||
4bb434562f | |||
ab1f6c1ae7 | |||
63c7e9ca00 | |||
0b8408ef13 | |||
ff34d2c143 | |||
73498894e4 | |||
ed3b0ff53f | |||
07e1b5e97a | |||
348ddba048 | |||
a3c8054839 | |||
555693cda8 | |||
0a9f184946 | |||
7c02564ed4 | |||
c270cda600 | |||
0abc5629fc | |||
a40399d082 | |||
0753a4c9dd | |||
1a5ac64aa1 | |||
f170404c03 | |||
16a29b4646 | |||
bb3c57297e | |||
89e1d35a49 | |||
4f8bd0dbee | |||
ccfe271f0d |
255
AirGradient.cpp
255
AirGradient.cpp
@ -11,7 +11,7 @@
|
||||
#include <SoftwareSerial.h>
|
||||
#include "Arduino.h"
|
||||
#include <Wire.h>
|
||||
#include <Math.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
// Constructor /////////////////////////////////////////////////////////////////
|
||||
@ -105,7 +105,128 @@ int AirGradient::getPM2_Raw(){
|
||||
pm02 = data.PM_AE_UG_2_5;
|
||||
return pm02;
|
||||
} else {
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int AirGradient::getPM1_Raw(){
|
||||
int pm02;
|
||||
DATA data;
|
||||
requestRead();
|
||||
if (readUntil(data)) {
|
||||
pm02 = data.PM_AE_UG_1_0;
|
||||
return pm02;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int AirGradient::getPM10_Raw(){
|
||||
int pm02;
|
||||
DATA data;
|
||||
requestRead();
|
||||
if (readUntil(data)) {
|
||||
pm02 = data.PM_AE_UG_10_0;
|
||||
return pm02;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int AirGradient::getPM0_3Count(){
|
||||
int count;
|
||||
DATA data;
|
||||
requestRead();
|
||||
if (readUntil(data)) {
|
||||
count = data.PM_RAW_0_3;
|
||||
return count;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int AirGradient::getPM10_0Count(){
|
||||
int count;
|
||||
DATA data;
|
||||
requestRead();
|
||||
if (readUntil(data)) {
|
||||
count = data.PM_RAW_10_0;
|
||||
return count;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int AirGradient::getPM5_0Count(){
|
||||
int count;
|
||||
DATA data;
|
||||
requestRead();
|
||||
if (readUntil(data)) {
|
||||
count = data.PM_RAW_5_0;
|
||||
return count;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int AirGradient::getPM2_5Count(){
|
||||
int count;
|
||||
DATA data;
|
||||
requestRead();
|
||||
if (readUntil(data)) {
|
||||
count = data.PM_RAW_2_5;
|
||||
return count;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int AirGradient::getPM1_0Count(){
|
||||
int count;
|
||||
DATA data;
|
||||
requestRead();
|
||||
if (readUntil(data)) {
|
||||
count = data.PM_RAW_1_0;
|
||||
return count;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int AirGradient::getPM0_5Count(){
|
||||
int count;
|
||||
DATA data;
|
||||
requestRead();
|
||||
if (readUntil(data)) {
|
||||
count = data.PM_RAW_0_5;
|
||||
return count;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int AirGradient::getAMB_TMP(){
|
||||
int count;
|
||||
DATA data;
|
||||
requestRead();
|
||||
if (readUntil(data)) {
|
||||
count = data.PM_TMP;
|
||||
return count;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int AirGradient::getAMB_HUM(){
|
||||
int count;
|
||||
DATA data;
|
||||
requestRead();
|
||||
if (readUntil(data)) {
|
||||
count = data.PM_HUM;
|
||||
return count;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,6 +369,21 @@ void AirGradient::loop()
|
||||
_data->PM_AE_UG_1_0 = makeWord(_payload[6], _payload[7]);
|
||||
_data->PM_AE_UG_2_5 = makeWord(_payload[8], _payload[9]);
|
||||
_data->PM_AE_UG_10_0 = makeWord(_payload[10], _payload[11]);
|
||||
|
||||
// Total particles count per 100ml air
|
||||
_data->PM_RAW_0_3 = makeWord(_payload[12], _payload[13]);
|
||||
_data->PM_RAW_0_5 = makeWord(_payload[14], _payload[15]);
|
||||
_data->PM_RAW_1_0 = makeWord(_payload[16], _payload[17]);
|
||||
_data->PM_RAW_2_5 = makeWord(_payload[18], _payload[19]);
|
||||
_data->PM_RAW_5_0 = makeWord(_payload[20], _payload[21]);
|
||||
_data->PM_RAW_10_0 = makeWord(_payload[22], _payload[23]);
|
||||
|
||||
// Formaldehyde concentration (PMSxxxxST units only)
|
||||
_data->AMB_HCHO = makeWord(_payload[24], _payload[25]) / 1000;
|
||||
|
||||
// Temperature & humidity (PMSxxxxST units only)
|
||||
_data->PM_TMP = makeWord(_payload[20], _payload[21]) / 10;
|
||||
_data->PM_HUM = makeWord(_payload[22], _payload[23]) / 10;
|
||||
}
|
||||
|
||||
_index = 0;
|
||||
@ -303,7 +439,7 @@ TMP_RH AirGradient::periodicFetchData() //
|
||||
return result;
|
||||
}
|
||||
else
|
||||
returnError(error);
|
||||
return returnError(error);
|
||||
}
|
||||
|
||||
TMP_RH_ErrorCode AirGradient::periodicStop() {
|
||||
@ -589,65 +725,74 @@ void AirGradient::CO2_Init(int rx_pin,int tx_pin,int baudRate){
|
||||
delay(10000);
|
||||
}
|
||||
}
|
||||
const char* AirGradient::getCO2(int retryLimit) {
|
||||
int ctr = 0;
|
||||
int result_CO2 = getCO2_Raw();
|
||||
while(result_CO2 == -1){
|
||||
result_CO2 = getCO2_Raw();
|
||||
if((ctr == retryLimit) || (result_CO2 == -1)){
|
||||
Char_CO2[0] = 'N';
|
||||
Char_CO2[1] = 'U';
|
||||
Char_CO2[2] = 'L';
|
||||
Char_CO2[3] = 'L';
|
||||
return Char_CO2;
|
||||
|
||||
int AirGradient::getCO2(int numberOfSamplesToTake) {
|
||||
int successfulSamplesCounter = 0;
|
||||
int co2AsPpmSum = 0;
|
||||
for (int sample = 0; sample < numberOfSamplesToTake; sample++) {
|
||||
int co2AsPpm = getCO2_Raw();
|
||||
if (co2AsPpm > 300 && co2AsPpm < 10000) {
|
||||
Serial.println("CO2 read success " + String(co2AsPpm));
|
||||
successfulSamplesCounter++;
|
||||
co2AsPpmSum += co2AsPpm;
|
||||
} else {
|
||||
Serial.println("CO2 read failed with " + String(co2AsPpm));
|
||||
}
|
||||
ctr++;
|
||||
|
||||
// without delay we get a few 10ms spacing, add some more
|
||||
delay(250);
|
||||
}
|
||||
sprintf(Char_CO2,"%d", result_CO2);
|
||||
return Char_CO2;
|
||||
|
||||
if (successfulSamplesCounter <= 0) {
|
||||
// total failure
|
||||
return -5;
|
||||
}
|
||||
Serial.println("# of CO2 reads that worked: " + String(successfulSamplesCounter));
|
||||
Serial.println("CO2 reads sum " + String(co2AsPpmSum));
|
||||
return co2AsPpmSum / successfulSamplesCounter;
|
||||
}
|
||||
int AirGradient::getCO2_Raw(){
|
||||
int retry = 0;
|
||||
CO2_READ_RESULT result;
|
||||
const byte CO2Command[] = {0xFE, 0X44, 0X00, 0X08, 0X02, 0X9F, 0X25};
|
||||
byte CO2Response[] = {0,0,0,0,0,0,0};
|
||||
|
||||
while(!(_SoftSerial_CO2->available())) {
|
||||
retry++;
|
||||
// keep sending request until we start to get a response
|
||||
_SoftSerial_CO2->write(CO2Command, 7);
|
||||
delay(50);
|
||||
if (retry > 10) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// <<>>
|
||||
int AirGradient::getCO2_Raw() {
|
||||
|
||||
int timeout = 0;
|
||||
|
||||
while (_SoftSerial_CO2->available() < 7) {
|
||||
timeout++;
|
||||
if (timeout > 10) {
|
||||
while(_SoftSerial_CO2->available())
|
||||
_SoftSerial_CO2->read();
|
||||
break;
|
||||
}
|
||||
delay(50);
|
||||
}
|
||||
while(_SoftSerial_CO2->available()) // flush whatever we might have
|
||||
_SoftSerial_CO2->read();
|
||||
|
||||
for (int i=0; i < 7; i++) {
|
||||
int byte = _SoftSerial_CO2->read();
|
||||
if (byte == -1) {
|
||||
result.success = false;
|
||||
return -1;
|
||||
}
|
||||
CO2Response[i] = byte;
|
||||
}
|
||||
int valMultiplier = 1;
|
||||
int high = CO2Response[3];
|
||||
int low = CO2Response[4];
|
||||
unsigned long val = high*256 + low;
|
||||
const byte CO2Command[] = {0XFE, 0X04, 0X00, 0X03, 0X00, 0X01, 0XD5, 0XC5};
|
||||
byte CO2Response[] = {0,0,0,0,0,0,0};
|
||||
int datapos = -1;
|
||||
|
||||
return val;
|
||||
const int commandSize = 8;
|
||||
const int responseSize = 7;
|
||||
|
||||
int numberOfBytesWritten = _SoftSerial_CO2->write(CO2Command, commandSize);
|
||||
|
||||
if (numberOfBytesWritten != commandSize) {
|
||||
// failed to write request
|
||||
return -2;
|
||||
}
|
||||
|
||||
// attempt to read response
|
||||
int timeoutCounter = 0;
|
||||
while (_SoftSerial_CO2->available() < responseSize) {
|
||||
timeoutCounter++;
|
||||
if (timeoutCounter > 10) {
|
||||
// timeout when reading response
|
||||
return -3;
|
||||
}
|
||||
delay(50);
|
||||
}
|
||||
|
||||
// we have 7 bytes ready to be read
|
||||
for (int i=0; i < responseSize; i++) {
|
||||
CO2Response[i] = _SoftSerial_CO2->read();
|
||||
if ((CO2Response[i] == 0xFE) && (datapos == -1)){
|
||||
datapos = i;
|
||||
}
|
||||
Serial.print (CO2Response[i],HEX);
|
||||
Serial.print (":");
|
||||
}
|
||||
return CO2Response[datapos + 3]*256 + CO2Response[datapos + 4];
|
||||
}
|
||||
|
||||
//END CO2 FUNCTIONS //
|
||||
|
@ -186,6 +186,21 @@ class AirGradient
|
||||
uint16_t PM_AE_UG_1_0;
|
||||
uint16_t PM_AE_UG_2_5;
|
||||
uint16_t PM_AE_UG_10_0;
|
||||
|
||||
// Raw particles count (number of particles in 0.1l of air
|
||||
uint16_t PM_RAW_0_3;
|
||||
uint16_t PM_RAW_0_5;
|
||||
uint16_t PM_RAW_1_0;
|
||||
uint16_t PM_RAW_2_5;
|
||||
uint16_t PM_RAW_5_0;
|
||||
uint16_t PM_RAW_10_0;
|
||||
|
||||
// Formaldehyde (HCHO) concentration in mg/m^3 - PMSxxxxST units only
|
||||
uint16_t AMB_HCHO;
|
||||
|
||||
// Temperature & humidity - PMSxxxxST units only
|
||||
int16_t PM_TMP;
|
||||
uint16_t PM_HUM;
|
||||
};
|
||||
|
||||
void PMS(Stream&);
|
||||
@ -197,8 +212,22 @@ class AirGradient
|
||||
void requestRead();
|
||||
bool read_PMS(DATA& data);
|
||||
bool readUntil(DATA& data, uint16_t timeout = SINGLE_RESPONSE_TIME);
|
||||
|
||||
|
||||
const char* getPM2();
|
||||
int getPM2_Raw();
|
||||
int getPM1_Raw();
|
||||
int getPM10_Raw();
|
||||
|
||||
int getPM0_3Count();
|
||||
int getPM0_5Count();
|
||||
int getPM1_0Count();
|
||||
int getPM2_5Count();
|
||||
int getPM5_0Count();
|
||||
int getPM10_0Count();
|
||||
|
||||
int getAMB_TMP();
|
||||
int getAMB_HUM();
|
||||
|
||||
//PMS VARIABLES PUBLIC_END
|
||||
|
||||
@ -223,7 +252,7 @@ class AirGradient
|
||||
void CO2_Init();
|
||||
void CO2_Init(int,int);
|
||||
void CO2_Init(int,int,int);
|
||||
const char* getCO2(int retryLimit = 5);
|
||||
int getCO2(int numberOfSamplesToTake = 5);
|
||||
int getCO2_Raw();
|
||||
SoftwareSerial *_SoftSerial_CO2;
|
||||
|
||||
@ -252,7 +281,7 @@ class AirGradient
|
||||
enum STATUS { STATUS_WAITING, STATUS_OK };
|
||||
enum MODE { MODE_ACTIVE, MODE_PASSIVE };
|
||||
|
||||
uint8_t _payload[12];
|
||||
uint8_t _payload[32];
|
||||
Stream* _stream;
|
||||
DATA* _data;
|
||||
STATUS _PMSstatus;
|
||||
@ -264,7 +293,9 @@ class AirGradient
|
||||
uint16_t _calculatedChecksum;
|
||||
SoftwareSerial *_SoftSerial_PMS;
|
||||
void loop();
|
||||
char Char_PM2[10];
|
||||
char Char_PM1[10];
|
||||
char Char_PM2[10];
|
||||
char Char_PM10[10];
|
||||
//PMS VARIABLES PRIVATE END
|
||||
|
||||
//TMP_RH VARIABLES PRIVATE START
|
||||
|
BIN
AirGradient.zip
BIN
AirGradient.zip
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2020 AirGradient
|
||||
Copyright (c) 2022 AirGradient
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -3,6 +3,6 @@ AirGradient Arduino Library for ESP8266 (Wemos D1 MINI)
|
||||
|
||||
Build your own low cost air quality sensor with optional display measuring PM2.5, CO2, Temperature and Humidity.
|
||||
|
||||
This library makes it easy to read the sensor data from the Plantower PMS5003 PM2.5 sensor, the Senseair S8 and the SHT30/31 Temperature and Humidity sensor. Visit our blog for detailed build instructions and PCB layout.
|
||||
This library makes it easy to read the sensor data from the Plantower PMS5003 PM2.5 sensor, the Senseair S8 and the SHT30/31 Temperature and Humidity sensor. Visit our DIY section for detailed build instructions and PCB layout.
|
||||
|
||||
https://www.airgradient.com/blog/
|
||||
https://www.airgradient.com/open-airgradient/instructions/
|
||||
|
127
examples/C02_CALIBRATION/C02_CALIBRATION.ino
Normal file
127
examples/C02_CALIBRATION/C02_CALIBRATION.ino
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
This is the code for forced calibration of the SenseAir S8 sensor. The sensor also has a one-week automatic baseline calibration that should calibrate the sensor latest after one week.0
|
||||
However if you need a faster calibration please proceed as following:
|
||||
|
||||
1. Flash this code
|
||||
2. Bring the sensor outside into fresh air and leave it there for at least 10 minutes
|
||||
3. Power on the sensor
|
||||
4. Follow the instructions on the display
|
||||
5. After the calibration has been done, flash back the previous code for AQ measurements
|
||||
|
||||
The codes needs the following libraries installed:
|
||||
“S8_UART” by Josep Comas tested with version 1.0.1
|
||||
“U8g2” by oliver tested with version 2.32.15
|
||||
|
||||
Many thanks to Josep Comas of the S8_UART library from which parts of below code are borrowed.
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "s8_uart.h"
|
||||
#include <U8g2lib.h>
|
||||
|
||||
/* BEGIN CONFIGURATION */
|
||||
|
||||
// Display
|
||||
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);//for DIY PRO
|
||||
U8G2_SSD1306_64X48_ER_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); //for DIY BASIC
|
||||
|
||||
#define DEBUG_BAUDRATE 115200
|
||||
|
||||
#if (defined USE_SOFTWARE_SERIAL || defined ARDUINO_ARCH_RP2040)
|
||||
#define S8_RX_PIN 2
|
||||
#define S8_TX_PIN 0
|
||||
#else
|
||||
#define S8_UART_PORT 1
|
||||
#endif
|
||||
|
||||
#define COUNTDOWN (60) //seconds
|
||||
|
||||
/* END CONFIGURATION */
|
||||
|
||||
#ifdef USE_SOFTWARE_SERIAL
|
||||
SoftwareSerial S8_serial(S8_RX_PIN, S8_TX_PIN);
|
||||
#else
|
||||
#if defined(ARDUINO_ARCH_RP2040)
|
||||
REDIRECT_STDOUT_TO(Serial)
|
||||
UART S8_serial(S8_TX_PIN, S8_RX_PIN, NC, NC);
|
||||
#else
|
||||
HardwareSerial S8_serial(S8_UART_PORT);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
S8_UART *sensor_S8;
|
||||
S8_sensor sensor;
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(DEBUG_BAUDRATE);
|
||||
u8g2.begin();
|
||||
int i = 0;
|
||||
while (!Serial && i < 50) {
|
||||
delay(10);
|
||||
i++;
|
||||
}
|
||||
S8_serial.begin(S8_BAUDRATE);
|
||||
sensor_S8 = new S8_UART(S8_serial);
|
||||
sensor_S8->get_firmware_version(sensor.firm_version);
|
||||
int len = strlen(sensor.firm_version);
|
||||
if (len == 0) {
|
||||
Serial.println("SenseAir S8 CO2 sensor not found!");
|
||||
updateOLED2("SenseAir", "not", "found");
|
||||
while (1) { delay(1); };
|
||||
}
|
||||
Serial.println(">>> SenseAir S8 NDIR CO2 sensor <<<");
|
||||
printf("Firmware version: %s\n", sensor.firm_version);
|
||||
sensor.sensor_id = sensor_S8->get_sensor_ID();
|
||||
Serial.print("Sensor ID: 0x"); printIntToHex(sensor.sensor_id, 4); Serial.println("");
|
||||
Serial.println("Now, you put the sensor outside and wait.");
|
||||
Serial.println("Countdown begins...");
|
||||
unsigned int seconds = COUNTDOWN;
|
||||
while (seconds > 0) {
|
||||
printf("Time remaining: %d minutes %d seconds\n", seconds / 60, seconds % 60);
|
||||
updateOLED2("Wait", "for", String(seconds) + " Sec");
|
||||
delay(1000);
|
||||
seconds--;
|
||||
}
|
||||
Serial.println("Time reamining: 0 minutes 0 seconds");
|
||||
// Start manual calibration
|
||||
Serial.println("Starting manual calibration...");
|
||||
updateOLED2("Starting", "Manual", "Calibration");
|
||||
delay(2000);
|
||||
if (!sensor_S8->manual_calibration()) {
|
||||
Serial.println("Error setting manual calibration!");
|
||||
updateOLED2("Error", "Manual", "Calibration");
|
||||
while (1) { delay(10); }
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static unsigned int elapsed = 0;
|
||||
delay(2000);
|
||||
elapsed += 2;
|
||||
|
||||
// Check if background calibration is finished
|
||||
sensor.ack = sensor_S8->get_acknowledgement();
|
||||
if (sensor.ack & S8_MASK_CO2_BACKGROUND_CALIBRATION) {
|
||||
printf("Manual calibration is finished. Elapsed: %u seconds\n", elapsed);
|
||||
updateOLED2("Calibration", "finished", "");
|
||||
while (1) { delay(10); }
|
||||
} else {
|
||||
Serial.println("Doing manual calibration...");
|
||||
updateOLED2("Doing", "manual", "calibration");
|
||||
}
|
||||
}
|
||||
|
||||
void updateOLED2(String ln1, String ln2, String ln3) {
|
||||
char buf[9];
|
||||
u8g2.firstPage();
|
||||
u8g2.firstPage();
|
||||
do {
|
||||
u8g2.setFont(u8g2_font_t0_16_tf);
|
||||
u8g2.drawStr(1, 10, String(ln1).c_str());
|
||||
u8g2.drawStr(1, 28, String(ln2).c_str());
|
||||
u8g2.drawStr(1, 46, String(ln3).c_str());
|
||||
} while ( u8g2.nextPage() );
|
||||
}
|
BIN
examples/C02_PM_SHT/.DS_Store
vendored
BIN
examples/C02_PM_SHT/.DS_Store
vendored
Binary file not shown.
@ -1,26 +0,0 @@
|
||||
#include <AirGradient.h>
|
||||
AirGradient ag = AirGradient();
|
||||
|
||||
void setup(){
|
||||
Serial.begin(9600);
|
||||
ag.PMS_Init();
|
||||
ag.CO2_Init();
|
||||
ag.TMP_RH_Init(0x44); //check for SHT sensor with address 0x44
|
||||
}
|
||||
|
||||
void loop(){
|
||||
|
||||
Serial.print("PM2: ");
|
||||
Serial.println(ag.getPM2());
|
||||
|
||||
|
||||
Serial.print("CO2: ");
|
||||
Serial.println(ag.getCO2());
|
||||
|
||||
TMP_RH result = ag.periodicFetchData();
|
||||
Serial.print("Humidity: ");
|
||||
Serial.print(result.rh);
|
||||
Serial.print(" Temperature: ");
|
||||
Serial.println(result.t);
|
||||
delay(5000);
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
#include <AirGradient.h>
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Adafruit_SSD1306.h>
|
||||
|
||||
AirGradient ag = AirGradient();
|
||||
#define OLED_RESET 0
|
||||
Adafruit_SSD1306 display(OLED_RESET);
|
||||
|
||||
void setup(){
|
||||
Serial.begin(9600);
|
||||
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
|
||||
ag.PMS_Init();
|
||||
ag.CO2_Init();
|
||||
ag.TMP_RH_Init(0x44); //check for SHT sensor with address 0x44
|
||||
showTextRectangle("Init", String(ESP.getChipId(),HEX),"AirGradient");
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
void loop(){
|
||||
int PM2 = ag.getPM2_Raw();
|
||||
int CO2 = ag.getCO2_Raw();
|
||||
TMP_RH result = ag.periodicFetchData();
|
||||
showTextRectangle(String(result.t)+"c "+String(result.rh)+"%", "PM2: "+ String(ag.getPM2()), "CO2: "+String(ag.getCO2())+"");
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
// DISPLAY
|
||||
void showTextRectangle(String ln1, String ln2, String ln3) {
|
||||
display.clearDisplay();
|
||||
display.setTextColor(WHITE);
|
||||
display.setTextSize(1);
|
||||
display.setCursor(32,8);
|
||||
display.println(ln1);
|
||||
display.setTextSize(1);
|
||||
display.setCursor(32,16);
|
||||
display.println(ln2);
|
||||
display.setTextSize(1);
|
||||
display.setCursor(32,24);
|
||||
display.println(ln3);
|
||||
display.display();
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
#include <AirGradient.h>
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Adafruit_SSD1306.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
|
||||
AirGradient ag = AirGradient();
|
||||
#define OLED_RESET 0
|
||||
Adafruit_SSD1306 display(OLED_RESET);
|
||||
|
||||
String APIROOT = "http://hw.airgradient.com/";
|
||||
|
||||
void setup(){
|
||||
Serial.begin(9600);
|
||||
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
|
||||
ag.PMS_Init();
|
||||
ag.CO2_Init();
|
||||
ag.TMP_RH_Init(0x44); //check for SHT sensor with address 0x44
|
||||
showTextRectangle("Init", String(ESP.getChipId(),HEX),"AirGradient");
|
||||
connectToWifi();
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
void loop(){
|
||||
int PM2 = ag.getPM2_Raw();
|
||||
int CO2 = ag.getCO2_Raw();
|
||||
TMP_RH result = ag.periodicFetchData();
|
||||
showTextRectangle(String(result.t)+"c "+String(result.rh)+"%", "PM2: "+ String(PM2), "CO2: "+String(CO2)+"");
|
||||
|
||||
// send payload
|
||||
String payload = "{\"pm02\":" + String(ag.getPM2()) + ",\"wifi\":" + String(WiFi.RSSI()) + ",\"rco2\":" + String(ag.getCO2()) + ",\"atmp\":" + String(result.t) + ",\"rhum\":" + String(result.rh) + "}";
|
||||
Serial.println(payload);
|
||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId(),HEX) + "/measures";
|
||||
Serial.println(POSTURL);
|
||||
HTTPClient http;
|
||||
http.begin(POSTURL);
|
||||
http.addHeader("content-type", "application/json");
|
||||
int httpCode = http.POST(payload);
|
||||
String response = http.getString();
|
||||
Serial.println(httpCode);
|
||||
Serial.println(response);
|
||||
http.end();
|
||||
|
||||
delay(15000);
|
||||
}
|
||||
|
||||
// DISPLAY
|
||||
void showTextRectangle(String ln1, String ln2, String ln3) {
|
||||
display.clearDisplay();
|
||||
display.setTextColor(WHITE);
|
||||
display.setTextSize(1);
|
||||
display.setCursor(32,8);
|
||||
display.println(ln1);
|
||||
display.setTextSize(1);
|
||||
display.setCursor(32,16);
|
||||
display.println(ln2);
|
||||
display.setTextSize(1);
|
||||
display.setCursor(32,24);
|
||||
display.println(ln3);
|
||||
display.display();
|
||||
}
|
||||
|
||||
// Wifi Manager
|
||||
void connectToWifi(){
|
||||
WiFiManager wifiManager;
|
||||
//chWiFi.disconnect(); //to delete previous saved hotspot
|
||||
String HOTSPOT = "AIRGRADIENT-"+String(ESP.getChipId(),HEX);
|
||||
wifiManager.setTimeout(120);
|
||||
if(!wifiManager.autoConnect((const char*)HOTSPOT.c_str())) {
|
||||
//Serial.println("failed to connect and hit timeout");
|
||||
delay(3000);
|
||||
ESP.restart();
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
}
|
39
examples/C02_SIMPLE/C02_SIMPLE.ino
Normal file
39
examples/C02_SIMPLE/C02_SIMPLE.ino
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
This is the code for the AirGradient DIY Air Quality Sensor with an ESP8266 Microcontroller.
|
||||
|
||||
It is a high quality sensor showing PM2.5, CO2, Temperature and Humidity on a small display and can send data over Wifi.
|
||||
|
||||
For build instructions please visit https://www.airgradient.com/open-airgradient/instructions/
|
||||
|
||||
Compatible with the following sensors:
|
||||
SenseAir S8 (CO2 Sensor)
|
||||
|
||||
Please install ESP8266 board manager (tested with version 3.0.0)
|
||||
|
||||
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||
|
||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||
https://www.airgradient.com/
|
||||
|
||||
Kits with all required components are available at https://www.airgradient.com/open-airgradient/shop/
|
||||
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
|
||||
*/
|
||||
|
||||
#include <AirGradient.h>
|
||||
AirGradient ag = AirGradient();
|
||||
|
||||
void setup(){
|
||||
Serial.begin(115200);
|
||||
ag.CO2_Init();
|
||||
}
|
||||
|
||||
void loop(){
|
||||
|
||||
int CO2 = ag.getCO2_Raw();
|
||||
Serial.print("C02: ");
|
||||
Serial.println(ag.getCO2());
|
||||
|
||||
delay(5000);
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#include <AirGradient.h>
|
||||
AirGradient ag = AirGradient();
|
||||
|
||||
void setup(){
|
||||
Serial.begin(9600);
|
||||
ag.CO2_Init();
|
||||
}
|
||||
|
||||
void loop(){
|
||||
|
||||
int CO2 = ag.getCO2_Raw();
|
||||
Serial.print("C02: ");
|
||||
Serial.println(ag.getCO2());
|
||||
|
||||
delay(5000);
|
||||
}
|
132
examples/CO2_TRAFFIC_LIGHT/CO2_TRAFFIC_LIGHT_SIMPLE.ino
Normal file
132
examples/CO2_TRAFFIC_LIGHT/CO2_TRAFFIC_LIGHT_SIMPLE.ino
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
This is the code for the AirGradient DIY CO2 Traffic light with an ESP8266 Microcontroller.
|
||||
|
||||
For build instructions please visit:
|
||||
https://www.airgradient.com/open-airgradient/instructions/diy-co2-traffic-light/
|
||||
|
||||
Kits (including a pre-soldered version) are available:
|
||||
https://www.airgradient.com/open-airgradient/kits/
|
||||
|
||||
Compatible with the following sensors:
|
||||
SenseAir S8 (CO2 Sensor)
|
||||
|
||||
Please install ESP8266 board manager (tested with version 3.0.0)
|
||||
|
||||
Please install the following libraries:
|
||||
"Adafruit NeoMatrix" Library (tested with 1.2.0)
|
||||
"WifiManager by tzapu, tablatronix" tested with Version 2.0.3-alpha
|
||||
|
||||
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||
|
||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||
https://www.airgradient.com/
|
||||
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
|
||||
*/
|
||||
|
||||
#include <AirGradient.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Adafruit_NeoMatrix.h>
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
#ifndef PSTR
|
||||
#define PSTR // Make Arduino Due happy
|
||||
#endif
|
||||
|
||||
AirGradient ag = AirGradient();
|
||||
#define PIN D8
|
||||
|
||||
int co2 = 0;
|
||||
String text = "AirGradient CO2";
|
||||
// set to true if you want to connect to wifi. The display will show values only when the sensor has wifi connection
|
||||
boolean connectWIFI=true;
|
||||
int greenToOrange = 800;
|
||||
int orangeToRed = 1200;
|
||||
|
||||
// change if you want to send the data to another server
|
||||
String APIROOT = "http://hw.airgradient.com/";
|
||||
|
||||
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(8, 8, PIN,
|
||||
NEO_MATRIX_TOP + NEO_MATRIX_RIGHT +
|
||||
NEO_MATRIX_COLUMNS + NEO_MATRIX_PROGRESSIVE,
|
||||
NEO_GRB + NEO_KHZ800);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
ag.CO2_Init();
|
||||
matrix.begin();
|
||||
matrix.setRotation(1); // change rotation
|
||||
matrix.setTextWrap(false);
|
||||
matrix.setBrightness(40);
|
||||
matrix.setTextColor(matrix.Color(70,130,180));
|
||||
|
||||
Serial.println("Chip ID: "+String(ESP.getChipId(),HEX));
|
||||
if (connectWIFI) connectToWifi();
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
showText();
|
||||
}
|
||||
|
||||
int x = matrix.width();
|
||||
|
||||
void showText() {
|
||||
Serial.println("in loop");
|
||||
matrix.fillScreen(0);
|
||||
matrix.setCursor(x, 0);
|
||||
matrix.print(String(text));
|
||||
if(--x < -100) {
|
||||
x = matrix.width();
|
||||
Serial.println("end text");
|
||||
co2 = ag.getCO2_Raw();
|
||||
text = String(co2)+"ppm";
|
||||
if (co2>350) matrix.setTextColor(matrix.Color(0, 255, 0));
|
||||
if (co2>greenToOrange) matrix.setTextColor(matrix.Color(255, 90, 0));
|
||||
if (co2>orangeToRed) matrix.setTextColor(matrix.Color(255,0, 0));
|
||||
|
||||
// send payload
|
||||
String payload = "{\"wifi\":" + String(WiFi.RSSI()) + ",";
|
||||
payload = payload + "\"rco2\":" + String(co2);
|
||||
payload = payload + "}";
|
||||
|
||||
if (connectWIFI) {
|
||||
Serial.println(payload);
|
||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId(), HEX) + "/measures";
|
||||
Serial.println(POSTURL);
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
http.begin(client, POSTURL);
|
||||
http.addHeader("content-type", "application/json");
|
||||
int httpCode = http.POST(payload);
|
||||
String response = http.getString();
|
||||
Serial.println(httpCode);
|
||||
Serial.println(response);
|
||||
http.end();
|
||||
delay(20000);
|
||||
}
|
||||
|
||||
delay(10000);
|
||||
}
|
||||
|
||||
matrix.show();
|
||||
delay(100);
|
||||
}
|
||||
|
||||
// Wifi Manager
|
||||
void connectToWifi(){
|
||||
WiFiManager wifiManager;
|
||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||
String HOTSPOT = "AIRGRADIENT-"+String(ESP.getChipId(),HEX);
|
||||
wifiManager.setTimeout(120);
|
||||
if(!wifiManager.autoConnect((const char*)HOTSPOT.c_str())) {
|
||||
Serial.println("failed to connect and hit timeout");
|
||||
delay(3000);
|
||||
ESP.restart();
|
||||
delay(5000);
|
||||
}
|
||||
}
|
243
examples/DIY_BASIC/DIY_BASIC.ino
Normal file
243
examples/DIY_BASIC/DIY_BASIC.ino
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
This is the code for the AirGradient DIY BASIC Air Quality Sensor with an ESP8266 Microcontroller.
|
||||
|
||||
It is a high quality sensor showing PM2.5, CO2, Temperature and Humidity on a small display and can send data over Wifi.
|
||||
|
||||
Build Instructions: https://www.airgradient.com/open-airgradient/instructions/diy/
|
||||
|
||||
Kits (including a pre-soldered version) are available: https://www.airgradient.com/open-airgradient/kits/
|
||||
|
||||
The codes needs the following libraries installed:
|
||||
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
|
||||
“U8g2” by oliver tested with version 2.32.15
|
||||
"Arduino-SHT" by Johannes Winkelmann Version 1.2.2
|
||||
|
||||
Configuration:
|
||||
Please set in the code below the configuration parameters.
|
||||
|
||||
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||
|
||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||
https://www.airgradient.com/
|
||||
|
||||
MIT License
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <AirGradient.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <U8g2lib.h>
|
||||
#include "SHTSensor.h"
|
||||
|
||||
AirGradient ag = AirGradient();
|
||||
SHTSensor sht;
|
||||
|
||||
U8G2_SSD1306_64X48_ER_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); //for DIY BASIC
|
||||
|
||||
|
||||
// CONFIGURATION START
|
||||
|
||||
//set to the endpoint you would like to use
|
||||
String APIROOT = "http://hw.airgradient.com/";
|
||||
|
||||
// set to true to switch from Celcius to Fahrenheit
|
||||
boolean inF = false;
|
||||
|
||||
// PM2.5 in US AQI (default ug/m3)
|
||||
boolean inUSAQI = false;
|
||||
|
||||
// set to true if you want to connect to wifi. You have 60 seconds to connect. Then it will go into an offline mode.
|
||||
boolean connectWIFI=true;
|
||||
|
||||
// CONFIGURATION END
|
||||
|
||||
|
||||
unsigned long currentMillis = 0;
|
||||
|
||||
const int oledInterval = 5000;
|
||||
unsigned long previousOled = 0;
|
||||
|
||||
const int sendToServerInterval = 10000;
|
||||
unsigned long previoussendToServer = 0;
|
||||
|
||||
const int co2Interval = 5000;
|
||||
unsigned long previousCo2 = 0;
|
||||
int Co2 = 0;
|
||||
|
||||
const int pm25Interval = 5000;
|
||||
unsigned long previousPm25 = 0;
|
||||
int pm25 = 0;
|
||||
|
||||
const int tempHumInterval = 2500;
|
||||
unsigned long previousTempHum = 0;
|
||||
float temp = 0;
|
||||
int hum = 0;
|
||||
long val;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
sht.init();
|
||||
sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM);
|
||||
u8g2.setBusClock(100000);
|
||||
u8g2.begin();
|
||||
updateOLED();
|
||||
|
||||
if (connectWIFI) {
|
||||
connectToWifi();
|
||||
}
|
||||
updateOLED2("Warm Up", "Serial#", String(ESP.getChipId(), HEX));
|
||||
ag.CO2_Init();
|
||||
ag.PMS_Init();
|
||||
//ag.TMP_RH_Init(0x44);
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
currentMillis = millis();
|
||||
updateOLED();
|
||||
updateCo2();
|
||||
updatePm25();
|
||||
updateTempHum();
|
||||
sendToServer();
|
||||
}
|
||||
|
||||
void updateCo2()
|
||||
{
|
||||
if (currentMillis - previousCo2 >= co2Interval) {
|
||||
previousCo2 += co2Interval;
|
||||
Co2 = ag.getCO2_Raw();
|
||||
Serial.println(String(Co2));
|
||||
}
|
||||
}
|
||||
|
||||
void updatePm25()
|
||||
{
|
||||
if (currentMillis - previousPm25 >= pm25Interval) {
|
||||
previousPm25 += pm25Interval;
|
||||
pm25 = ag.getPM2_Raw();
|
||||
Serial.println(String(pm25));
|
||||
}
|
||||
}
|
||||
|
||||
void updateTempHum()
|
||||
{
|
||||
if (currentMillis - previousTempHum >= tempHumInterval) {
|
||||
previousTempHum += tempHumInterval;
|
||||
if (sht.readSample()) {
|
||||
Serial.print("SHT:\n");
|
||||
Serial.print(" RH: ");
|
||||
Serial.print(sht.getHumidity(), 2);
|
||||
Serial.print("\n");
|
||||
Serial.print(" T: ");
|
||||
Serial.print(sht.getTemperature(), 2);
|
||||
Serial.print("\n");
|
||||
temp = sht.getTemperature();
|
||||
hum = sht.getHumidity();
|
||||
} else {
|
||||
Serial.print("Error in readSample()\n");
|
||||
}
|
||||
Serial.println(String(temp));
|
||||
}
|
||||
}
|
||||
|
||||
void updateOLED() {
|
||||
if (currentMillis - previousOled >= oledInterval) {
|
||||
previousOled += oledInterval;
|
||||
|
||||
String ln1;
|
||||
String ln2;
|
||||
String ln3;
|
||||
|
||||
|
||||
if (inUSAQI){
|
||||
ln1 = "AQI:" + String(PM_TO_AQI_US(pm25)) ;
|
||||
} else {
|
||||
ln1 = "PM: " + String(pm25) +"ug" ;
|
||||
}
|
||||
|
||||
ln2 = "CO2:" + String(Co2);
|
||||
|
||||
if (inF) {
|
||||
ln3 = String((temp* 9 / 5) + 32).substring(0,4) + " " + String(hum)+"%";
|
||||
} else {
|
||||
ln3 = String(temp).substring(0,4) + " " + String(hum)+"%";
|
||||
}
|
||||
updateOLED2(ln1, ln2, ln3);
|
||||
}
|
||||
}
|
||||
|
||||
void updateOLED2(String ln1, String ln2, String ln3) {
|
||||
char buf[9];
|
||||
u8g2.firstPage();
|
||||
u8g2.firstPage();
|
||||
do {
|
||||
u8g2.setFont(u8g2_font_t0_16_tf);
|
||||
u8g2.drawStr(1, 10, String(ln1).c_str());
|
||||
u8g2.drawStr(1, 28, String(ln2).c_str());
|
||||
u8g2.drawStr(1, 46, String(ln3).c_str());
|
||||
} while ( u8g2.nextPage() );
|
||||
}
|
||||
|
||||
void sendToServer() {
|
||||
if (currentMillis - previoussendToServer >= sendToServerInterval) {
|
||||
previoussendToServer += sendToServerInterval;
|
||||
|
||||
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
||||
+ (Co2 < 0 ? "" : ", \"rco2\":" + String(Co2))
|
||||
+ (pm25 < 0 ? "" : ", \"pm02\":" + String(pm25))
|
||||
+ ", \"atmp\":" + String(temp)
|
||||
+ (hum < 0 ? "" : ", \"rhum\":" + String(hum))
|
||||
+ "}";
|
||||
|
||||
if(WiFi.status()== WL_CONNECTED){
|
||||
Serial.println(payload);
|
||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId(), HEX) + "/measures";
|
||||
Serial.println(POSTURL);
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
http.begin(client, POSTURL);
|
||||
http.addHeader("content-type", "application/json");
|
||||
int httpCode = http.POST(payload);
|
||||
String response = http.getString();
|
||||
Serial.println(httpCode);
|
||||
Serial.println(response);
|
||||
http.end();
|
||||
}
|
||||
else {
|
||||
Serial.println("WiFi Disconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wifi Manager
|
||||
void connectToWifi() {
|
||||
WiFiManager wifiManager;
|
||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||
String HOTSPOT = "AG-" + String(ESP.getChipId(), HEX);
|
||||
updateOLED2("Connect", "Wifi AG-", String(ESP.getChipId(), HEX));
|
||||
delay(2000);
|
||||
wifiManager.setTimeout(90);
|
||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||
updateOLED2("Booting", "offline", "mode");
|
||||
Serial.println("failed to connect and hit timeout");
|
||||
delay(6000);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate PM2.5 US AQI
|
||||
int PM_TO_AQI_US(int pm02) {
|
||||
if (pm02 <= 12.0) return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0);
|
||||
else if (pm02 <= 35.4) return ((100 - 50) / (35.4 - 12.0) * (pm02 - 12.0) + 50);
|
||||
else if (pm02 <= 55.4) return ((150 - 100) / (55.4 - 35.4) * (pm02 - 35.4) + 100);
|
||||
else if (pm02 <= 150.4) return ((200 - 150) / (150.4 - 55.4) * (pm02 - 55.4) + 150);
|
||||
else if (pm02 <= 250.4) return ((300 - 200) / (250.4 - 150.4) * (pm02 - 150.4) + 200);
|
||||
else if (pm02 <= 350.4) return ((400 - 300) / (350.4 - 250.4) * (pm02 - 250.4) + 300);
|
||||
else if (pm02 <= 500.4) return ((500 - 400) / (500.4 - 350.4) * (pm02 - 350.4) + 400);
|
||||
else return 500;
|
||||
};
|
212
examples/DIY_OUTDOOR/DIY_OUTDOOR.ino
Normal file
212
examples/DIY_OUTDOOR/DIY_OUTDOOR.ino
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
Important: This code is only for the AirGradient DIY OUTDOOR.
|
||||
|
||||
Build Instructions: https://www.airgradient.com/open-airgradient/instructions/diy-outdoor/
|
||||
|
||||
Kits (including a pre-soldered version) are available: https://www.airgradient.com/open-airgradient/kits/
|
||||
|
||||
Configuration:
|
||||
Install required libraries
|
||||
Patch PMS library to accept temperature and humidity from PMS5003T
|
||||
|
||||
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||
|
||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||
https://www.airgradient.com/
|
||||
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
*/
|
||||
|
||||
#include "PMS.h"
|
||||
#include "SoftwareSerial.h"
|
||||
#include <Wire.h>
|
||||
|
||||
#include <WiFiManager.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <WiFiClient.h>
|
||||
|
||||
SoftwareSerial softSerial(D6, D5);
|
||||
SoftwareSerial soft2(D3, D7);
|
||||
|
||||
|
||||
PMS pms(softSerial);
|
||||
PMS pms2(soft2);
|
||||
PMS::DATA data;
|
||||
PMS::DATA data2;
|
||||
|
||||
float pm1Value=0;
|
||||
int pm1Position = 0;
|
||||
|
||||
float pm2Value=0;
|
||||
int pm2Position = 0;
|
||||
|
||||
float temp_pm1 = 0;
|
||||
float hum_pm1 = 0;
|
||||
|
||||
float temp_pm2 = 0;
|
||||
float hum_pm2 = 0;
|
||||
|
||||
unsigned long currentMillis = 0;
|
||||
|
||||
const int pm1Interval = 5000;
|
||||
unsigned long previousPm1 = 0;
|
||||
|
||||
const int pm2Interval = 5000;
|
||||
unsigned long previousPm2 = 0;
|
||||
|
||||
String APIROOT = "http://hw.airgradient.com/";
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println("Chip ID: "+String(ESP.getChipId()));
|
||||
softSerial.begin(9600);
|
||||
soft2.begin(9600);
|
||||
Wire.begin();
|
||||
pinMode(D7, OUTPUT);
|
||||
connectToWifi();
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
currentMillis = millis();
|
||||
updatePm1();
|
||||
updatePm2();
|
||||
}
|
||||
|
||||
|
||||
void updatePm1()
|
||||
{
|
||||
if (currentMillis - previousPm1 >= pm1Interval) {
|
||||
digitalWrite(D7, HIGH);
|
||||
delay(400);
|
||||
digitalWrite(D7, LOW);
|
||||
Serial.println("updatePm1: "+String(pm1Position));
|
||||
previousPm1 += pm1Interval;
|
||||
pms.requestRead();
|
||||
if (pms.readUntil(data)){
|
||||
Serial.println("success read");
|
||||
int pm1 = data.PM_AE_UG_2_5;
|
||||
temp_pm1 = data.AMB_TMP;
|
||||
hum_pm1 = data.AMB_HUM;
|
||||
Serial.print("PMS 1: PM 2.5 (ug/m3): ");
|
||||
Serial.println(pm1);
|
||||
Serial.print("PMS 1: Temp: ");
|
||||
Serial.println(temp_pm1);
|
||||
Serial.print("PMS 1: Hum: ");
|
||||
Serial.println(hum_pm1);
|
||||
Serial.println();
|
||||
delay(1000);
|
||||
pm1Value=pm1Value+pm1;
|
||||
pm1Position++;
|
||||
if (pm1Position==20) {
|
||||
sendToServerPM1(pm1Value);
|
||||
pm1Position=0;
|
||||
pm1Value=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updatePm2()
|
||||
{
|
||||
if (currentMillis - previousPm2 >= pm2Interval) {
|
||||
Serial.println("updatePm2: "+String(pm2Position));
|
||||
previousPm2 += pm2Interval;
|
||||
pms2.requestRead();
|
||||
if (pms2.readUntil(data2)){
|
||||
int pm2 = data2.PM_AE_UG_2_5;
|
||||
temp_pm2 = data2.AMB_TMP ;
|
||||
hum_pm2 = data2.AMB_HUM;
|
||||
Serial.print("PMS 2: PM 2.5 (ug/m3): ");
|
||||
Serial.println(pm2);
|
||||
Serial.print("PMS 2: Temp: ");
|
||||
Serial.println(temp_pm2);
|
||||
Serial.print("PMS 2: Hum: ");
|
||||
Serial.println(hum_pm2);
|
||||
Serial.println();
|
||||
delay(1000);
|
||||
pm2Value=pm2Value+pm2;
|
||||
pm2Position++;
|
||||
if (pm2Position==20) {
|
||||
sendToServerPM2(pm2Value);
|
||||
pm2Position=0;
|
||||
pm2Value=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sendToServerPM1(float pm1Value) {
|
||||
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
||||
+ ", \"pm02\":" + String(pm1Value/20)
|
||||
+ ", \"atmp\":" + String(temp_pm1/10)
|
||||
+ ", \"rhum\":" + String(hum_pm1/10)
|
||||
+ "}";
|
||||
|
||||
if(WiFi.status()== WL_CONNECTED){
|
||||
digitalWrite(D7, HIGH);
|
||||
delay(300);
|
||||
Serial.println(payload);
|
||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId()) + "-1/measures";
|
||||
Serial.println(POSTURL);
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
http.begin(client, POSTURL);
|
||||
http.addHeader("content-type", "application/json");
|
||||
int httpCode = http.POST(payload);
|
||||
String response = http.getString();
|
||||
Serial.println(httpCode);
|
||||
Serial.println(response);
|
||||
http.end();
|
||||
digitalWrite(D7, LOW);
|
||||
}
|
||||
else {
|
||||
Serial.println("WiFi Disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void sendToServerPM2(float pm2Value) {
|
||||
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
||||
+ ", \"pm02\":" + String(pm2Value/20)
|
||||
+ ", \"atmp\":" + String(temp_pm2/10)
|
||||
+ ", \"rhum\":" + String(hum_pm2/10)
|
||||
+ "}";
|
||||
|
||||
if(WiFi.status()== WL_CONNECTED){
|
||||
digitalWrite(D7, HIGH);
|
||||
delay(300);
|
||||
Serial.println(payload);
|
||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId()) + "-2/measures";
|
||||
Serial.println(POSTURL);
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
http.begin(client, POSTURL);
|
||||
http.addHeader("content-type", "application/json");
|
||||
int httpCode = http.POST(payload);
|
||||
String response = http.getString();
|
||||
Serial.println(httpCode);
|
||||
Serial.println(response);
|
||||
http.end();
|
||||
digitalWrite(D7, LOW);
|
||||
}
|
||||
else {
|
||||
Serial.println("WiFi Disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Wifi Manager
|
||||
void connectToWifi() {
|
||||
WiFiManager wifiManager;
|
||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||
String HOTSPOT = "AIRGRADIENT-" + String(ESP.getChipId());
|
||||
wifiManager.setTimeout(60);
|
||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||
Serial.println("failed to connect and hit timeout");
|
||||
delay(6000);
|
||||
}
|
||||
}
|
277
examples/DIY_OUTDOOR_C3_1PP/DIY_OUTDOOR_C3_1PP.ino
Normal file
277
examples/DIY_OUTDOOR_C3_1PP/DIY_OUTDOOR_C3_1PP.ino
Normal file
@ -0,0 +1,277 @@
|
||||
/*
|
||||
Important: This code is only for the DIY OUTDOOR OPEN AIR Presoldered Kit with the ESP-C3.
|
||||
|
||||
It is a high quality outdoor air quality sensor with dual PM2.5 modules and can send data over Wifi.
|
||||
|
||||
Kits are available: https://www.airgradient.com/open-airgradient/kits/
|
||||
|
||||
The codes needs the following libraries installed:
|
||||
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
|
||||
“pms” by Markusz Kakl version 1.1.0 (needs to be patched for 5003T model)
|
||||
|
||||
For built instructions and how to patch the PMS library: https://www.airgradient.com/open-airgradient/instructions/diy-open-air-presoldered-v11/
|
||||
|
||||
Note that below code only works with both PM sensor modules connected.
|
||||
|
||||
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
|
||||
*/
|
||||
|
||||
#include "PMS.h"
|
||||
#include <HardwareSerial.h>
|
||||
#include <Wire.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <WiFiManager.h>
|
||||
|
||||
#define DEBUG true
|
||||
|
||||
HTTPClient client;
|
||||
|
||||
PMS pms1(Serial0);
|
||||
PMS::DATA data1;
|
||||
|
||||
float pm1Value01=0;
|
||||
float pm1Value25=0;
|
||||
float pm1Value10=0;
|
||||
float pm1PCount=0;
|
||||
float pm1temp = 0;
|
||||
float pm1hum = 0;
|
||||
|
||||
PMS pms2(Serial1);
|
||||
PMS::DATA data2;
|
||||
|
||||
float pm2Value01=0;
|
||||
float pm2Value25=0;
|
||||
float pm2Value10=0;
|
||||
float pm2PCount=0;
|
||||
float pm2temp = 0;
|
||||
float pm2hum = 0;
|
||||
|
||||
int countPosition = 0;
|
||||
int targetCount = 20;
|
||||
|
||||
|
||||
String APIROOT = "http://hw.airgradient.com/";
|
||||
|
||||
int loopCount = 0;
|
||||
|
||||
|
||||
void IRAM_ATTR isr() {
|
||||
debugln("pushed");
|
||||
}
|
||||
|
||||
// select board LOLIN C3 mini to flash
|
||||
void setup() {
|
||||
if (DEBUG) {
|
||||
Serial.begin(115200);
|
||||
// see https://github.com/espressif/arduino-esp32/issues/6983
|
||||
Serial.setTxTimeoutMs(0); // <<<====== solves the delay issue
|
||||
}
|
||||
|
||||
debug("starting ...");
|
||||
debug("Serial Number: "+ getNormalizedMac());
|
||||
|
||||
// default hardware serial, PMS connector on the right side of the C3 mini on the Open Air
|
||||
Serial0.begin(9600);
|
||||
|
||||
// second hardware serial, PMS connector on the left side of the C3 mini on the Open Air
|
||||
Serial1.begin(9600, SERIAL_8N1, 0, 1);
|
||||
|
||||
// led
|
||||
pinMode(10, OUTPUT);
|
||||
|
||||
// push button
|
||||
pinMode(9, INPUT_PULLUP);
|
||||
attachInterrupt(9, isr, FALLING);
|
||||
|
||||
pinMode(2, OUTPUT);
|
||||
digitalWrite(2, LOW);
|
||||
|
||||
// give the PMSs some time to start
|
||||
countdown(3);
|
||||
|
||||
connectToWifi();
|
||||
sendPing();
|
||||
switchLED(false);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if(WiFi.status()== WL_CONNECTED) {
|
||||
if (pms1.readUntil(data1, 2000) && pms2.readUntil(data2, 2000)) {
|
||||
pm1Value01=pm1Value01+data1.PM_AE_UG_1_0;
|
||||
pm1Value25=pm1Value25+data1.PM_AE_UG_2_5;
|
||||
pm1Value10=pm1Value10+data1.PM_AE_UG_10_0;
|
||||
pm1PCount=pm1PCount+data1.PM_RAW_0_3;
|
||||
pm1temp=pm1temp+data1.AMB_TMP;
|
||||
pm1hum=pm1hum+data1.AMB_HUM;
|
||||
pm2Value01=pm2Value01+data2.PM_AE_UG_1_0;
|
||||
pm2Value25=pm2Value25+data2.PM_AE_UG_2_5;
|
||||
pm2Value10=pm2Value10+data2.PM_AE_UG_10_0;
|
||||
pm2PCount=pm2PCount+data2.PM_RAW_0_3;
|
||||
pm2temp=pm2temp+data2.AMB_TMP;
|
||||
pm2hum=pm2hum+data2.AMB_HUM;
|
||||
countPosition++;
|
||||
if (countPosition==targetCount) {
|
||||
pm1Value01 = pm1Value01 / targetCount;
|
||||
pm1Value25 = pm1Value25 / targetCount;
|
||||
pm1Value10 = pm1Value10 / targetCount;
|
||||
pm1PCount = pm1PCount / targetCount;
|
||||
pm1temp = pm1temp / targetCount;
|
||||
pm1hum = pm1hum / targetCount;
|
||||
pm2Value01 = pm2Value01 / targetCount;
|
||||
pm2Value25 = pm2Value25 / targetCount;
|
||||
pm2Value10 = pm2Value10 / targetCount;
|
||||
pm2PCount = pm2PCount / targetCount;
|
||||
pm2temp = pm2temp / targetCount;
|
||||
pm2hum = pm2hum / targetCount;
|
||||
postToServer(pm1Value01, pm1Value25,pm1Value10,pm1PCount, pm1temp,pm1hum,pm2Value01, pm2Value25,pm2Value10,pm2PCount, pm2temp,pm2hum);
|
||||
|
||||
countPosition=0;
|
||||
pm1Value01=0;
|
||||
pm1Value25=0;
|
||||
pm1Value10=0;
|
||||
pm1PCount=0;
|
||||
pm1temp=0;
|
||||
pm1hum=0;
|
||||
pm2Value01=0;
|
||||
pm2Value25=0;
|
||||
pm2Value10=0;
|
||||
pm2PCount=0;
|
||||
pm2temp=0;
|
||||
pm2hum=0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
countdown(2);
|
||||
}
|
||||
|
||||
void debug(String msg) {
|
||||
if (DEBUG)
|
||||
Serial.print(msg);
|
||||
}
|
||||
|
||||
void debug(int msg) {
|
||||
if (DEBUG)
|
||||
Serial.print(msg);
|
||||
}
|
||||
|
||||
void debugln(String msg) {
|
||||
if (DEBUG)
|
||||
Serial.println(msg);
|
||||
}
|
||||
|
||||
void debugln(int msg) {
|
||||
if (DEBUG)
|
||||
Serial.println(msg);
|
||||
}
|
||||
|
||||
void switchLED(boolean ledON) {
|
||||
if (ledON) {
|
||||
digitalWrite(10, HIGH);
|
||||
} else {
|
||||
digitalWrite(10, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
void sendPing(){
|
||||
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
||||
+ ", \"boot\":" + loopCount
|
||||
+ "}";
|
||||
sendPayload(payload);
|
||||
}
|
||||
|
||||
void postToServer(int pm1Value01, int pm1Value25, int pm1Value10, int pm1PCount, float pm1temp, float pm1hum,int pm2Value01, int pm2Value25, int pm2Value10, int pm2PCount, float pm2temp, float pm2hum) {
|
||||
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
||||
+ ", \"pm01\":" + String((pm1Value01+pm2Value01)/2)
|
||||
+ ", \"pm02\":" + String((pm1Value25+pm2Value25)/2)
|
||||
+ ", \"pm10\":" + String((pm1Value10+pm2Value10)/2)
|
||||
+ ", \"pm003_count\":" + String((pm1PCount+pm2PCount)/2)
|
||||
+ ", \"atmp\":" + String((pm1temp+pm2temp)/20)
|
||||
+ ", \"rhum\":" + String((pm1hum+pm2hum)/20)
|
||||
+ ", \"boot\":" + loopCount
|
||||
+ ", \"channels\": {"
|
||||
+ "\"1\":{"
|
||||
+ "\"pm01\":" + String(pm1Value01)
|
||||
+ ", \"pm02\":" + String(pm1Value25)
|
||||
+ ", \"pm10\":" + String(pm1Value10)
|
||||
+ ", \"pm003_count\":" + String(pm1PCount)
|
||||
+ ", \"atmp\":" + String(pm1temp/10)
|
||||
+ ", \"rhum\":" + String(pm1hum/10)
|
||||
+ "}"
|
||||
+ ", \"2\":{"
|
||||
+ " \"pm01\":" + String(pm1Value01)
|
||||
+ ", \"pm02\":" + String(pm2Value25)
|
||||
+ ", \"pm10\":" + String(pm2Value10)
|
||||
+ ", \"pm003_count\":" + String(pm2PCount)
|
||||
+ ", \"atmp\":" + String(pm2temp/10)
|
||||
+ ", \"rhum\":" + String(pm2hum/10)
|
||||
+ "}"
|
||||
+ "}"
|
||||
+ "}";
|
||||
sendPayload(payload);
|
||||
}
|
||||
|
||||
void sendPayload(String payload) {
|
||||
if(WiFi.status()== WL_CONNECTED){
|
||||
switchLED(true);
|
||||
String url = APIROOT + "sensors/airgradient:" + getNormalizedMac() + "/measures";
|
||||
debugln(url);
|
||||
debugln(payload);
|
||||
client.setConnectTimeout(5 * 1000);
|
||||
client.begin(url);
|
||||
client.addHeader("content-type", "application/json");
|
||||
int httpCode = client.POST(payload);
|
||||
debugln(httpCode);
|
||||
client.end();
|
||||
resetWatchdog();
|
||||
switchLED(false);
|
||||
}
|
||||
else {
|
||||
debug("post skipped, not network connection");
|
||||
}
|
||||
loopCount++;
|
||||
}
|
||||
|
||||
void countdown(int from) {
|
||||
debug("\n");
|
||||
while (from > 0) {
|
||||
debug(String(from--));
|
||||
debug(" ");
|
||||
delay(1000);
|
||||
}
|
||||
debug("\n");
|
||||
}
|
||||
|
||||
void resetWatchdog() {
|
||||
digitalWrite(2, HIGH);
|
||||
delay(20);
|
||||
digitalWrite(2, LOW);
|
||||
}
|
||||
|
||||
// Wifi Manager
|
||||
void connectToWifi() {
|
||||
WiFiManager wifiManager;
|
||||
switchLED(true);
|
||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||
String HOTSPOT = "AG-" + String(getNormalizedMac());
|
||||
wifiManager.setTimeout(180);
|
||||
|
||||
|
||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||
switchLED(false);
|
||||
Serial.println("failed to connect and hit timeout");
|
||||
delay(6000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
String getNormalizedMac() {
|
||||
String mac = WiFi.macAddress();
|
||||
mac.replace(":", "");
|
||||
mac.toLowerCase();
|
||||
return mac;
|
||||
}
|
339
examples/DIY_OUTDOOR_C3_1PST/DIY_OUTDOOR_C3_1PST.ino
Normal file
339
examples/DIY_OUTDOOR_C3_1PST/DIY_OUTDOOR_C3_1PST.ino
Normal file
@ -0,0 +1,339 @@
|
||||
/*
|
||||
Important: This code is only for the AirGradient ONE Open Air Version with TVOC and CO2 sensor.
|
||||
|
||||
It is a high quality sensor measuring PM2.5, CO2, TVOC, NOx, Temperature and Humidity and can send data over Wifi.
|
||||
|
||||
Build Instructions: https://www.airgradient.com/open-airgradient/instructions/
|
||||
|
||||
Kits (including a pre-soldered version) are available: https://www.airgradient.com/
|
||||
|
||||
The codes needs the following libraries installed:
|
||||
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
|
||||
"Sensirion I2C SGP41" by Sensation Version 0.1.0
|
||||
"Sensirion Gas Index Algorithm" by Sensation Version 3.2.1
|
||||
"Arduino-SHT" by Johannes Winkelmann Version 1.2.2
|
||||
“pms” by Markusz Kakl version 1.1.0 (needs to be patched for 5003T model)
|
||||
|
||||
For built instructions and how to patch the PMS library: https://www.airgradient.com/open-airgradient/instructions/diy-open-air-presoldered-v11/
|
||||
|
||||
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||
|
||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||
https://www.airgradient.com/
|
||||
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
|
||||
*/
|
||||
|
||||
#include "PMS.h"
|
||||
#include <HardwareSerial.h>
|
||||
#include <Wire.h>
|
||||
#include "s8_uart.h"
|
||||
#include <HTTPClient.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <SensirionI2CSgp41.h>
|
||||
#include <NOxGasIndexAlgorithm.h>
|
||||
#include <VOCGasIndexAlgorithm.h>
|
||||
|
||||
#define DEBUG true
|
||||
|
||||
#define I2C_SDA 7
|
||||
#define I2C_SCL 6
|
||||
|
||||
HTTPClient client;
|
||||
|
||||
SensirionI2CSgp41 sgp41;
|
||||
VOCGasIndexAlgorithm voc_algorithm;
|
||||
NOxGasIndexAlgorithm nox_algorithm;
|
||||
|
||||
PMS pms1(Serial0);
|
||||
|
||||
PMS::DATA data1;
|
||||
|
||||
S8_UART * sensor_S8;
|
||||
S8_sensor sensor;
|
||||
|
||||
// time in seconds needed for NOx conditioning
|
||||
uint16_t conditioning_s = 10;
|
||||
|
||||
String APIROOT = "http://hw.airgradient.com/";
|
||||
|
||||
// set to true to switch from Celcius to Fahrenheit
|
||||
//boolean inF = false;
|
||||
|
||||
// PM2.5 in US AQI (default ug/m3)
|
||||
//boolean inUSAQI = false;
|
||||
|
||||
// Display Position
|
||||
//boolean displayTop = true;
|
||||
|
||||
// use RGB LED Bar
|
||||
//boolean useRGBledBar = true;
|
||||
|
||||
// set to true if you want to connect to wifi. You have 60 seconds to connect. Then it will go into an offline mode.
|
||||
boolean connectWIFI = true;
|
||||
|
||||
int loopCount = 0;
|
||||
|
||||
unsigned long currentMillis = 0;
|
||||
|
||||
const int oledInterval = 5000;
|
||||
unsigned long previousOled = 0;
|
||||
|
||||
const int sendToServerInterval = 10000;
|
||||
unsigned long previoussendToServer = 0;
|
||||
|
||||
const int tvocInterval = 1000;
|
||||
unsigned long previousTVOC = 0;
|
||||
int TVOC = -1;
|
||||
int NOX = -1;
|
||||
|
||||
const int co2Interval = 5000;
|
||||
unsigned long previousCo2 = 0;
|
||||
int Co2 = 0;
|
||||
|
||||
const int pmInterval = 5000;
|
||||
unsigned long previousPm = 0;
|
||||
int pm25 = -1;
|
||||
int pm01 = -1;
|
||||
int pm10 = -1;
|
||||
int pm03PCount = -1;
|
||||
float temp;
|
||||
int hum;
|
||||
|
||||
//const int tempHumInterval = 2500;
|
||||
//unsigned long previousTempHum = 0;
|
||||
|
||||
|
||||
|
||||
void setup() {
|
||||
if (DEBUG) {
|
||||
Serial.begin(115200);
|
||||
// see https://github.com/espressif/arduino-esp32/issues/6983
|
||||
Serial.setTxTimeoutMs(0); // <<<====== solves the delay issue
|
||||
}
|
||||
|
||||
Wire.begin(I2C_SDA, I2C_SCL);
|
||||
|
||||
Serial1.begin(9600, SERIAL_8N1, 0, 1);
|
||||
Serial0.begin(9600);
|
||||
|
||||
sgp41.begin(Wire);
|
||||
|
||||
//init Watchdog
|
||||
pinMode(2, OUTPUT);
|
||||
digitalWrite(2, LOW);
|
||||
|
||||
sensor_S8 = new S8_UART(Serial1);
|
||||
|
||||
delay(500);
|
||||
|
||||
// push button
|
||||
pinMode(9, INPUT_PULLUP);
|
||||
|
||||
countdown(3);
|
||||
|
||||
if (connectWIFI) {
|
||||
WiFi.begin("airgradient", "cleanair");
|
||||
int retries = 0;
|
||||
while ((WiFi.status() != WL_CONNECTED) && (retries < 15)) {
|
||||
retries++;
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
if (retries > 14) {
|
||||
Serial.println(F("WiFi connection FAILED"));
|
||||
connectToWifi();
|
||||
}
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
sendPing();
|
||||
Serial.println(F("WiFi connected!"));
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
currentMillis = millis();
|
||||
updateTVOC();
|
||||
updateCo2();
|
||||
updatePm();
|
||||
sendToServer();
|
||||
}
|
||||
|
||||
void updateTVOC() {
|
||||
uint16_t error;
|
||||
char errorMessage[256];
|
||||
uint16_t defaultRh = 0x8000;
|
||||
uint16_t defaultT = 0x6666;
|
||||
uint16_t srawVoc = 0;
|
||||
uint16_t srawNox = 0;
|
||||
uint16_t defaultCompenstaionRh = 0x8000; // in ticks as defined by SGP41
|
||||
uint16_t defaultCompenstaionT = 0x6666; // in ticks as defined by SGP41
|
||||
uint16_t compensationRh = 0; // in ticks as defined by SGP41
|
||||
uint16_t compensationT = 0; // in ticks as defined by SGP41
|
||||
|
||||
delay(1000);
|
||||
|
||||
compensationT = static_cast < uint16_t > ((temp + 45) * 65535 / 175);
|
||||
compensationRh = static_cast < uint16_t > (hum * 65535 / 100);
|
||||
|
||||
if (conditioning_s > 0) {
|
||||
error = sgp41.executeConditioning(compensationRh, compensationT, srawVoc);
|
||||
conditioning_s--;
|
||||
} else {
|
||||
error = sgp41.measureRawSignals(compensationRh, compensationT, srawVoc,
|
||||
srawNox);
|
||||
}
|
||||
|
||||
if (currentMillis - previousTVOC >= tvocInterval) {
|
||||
previousTVOC += tvocInterval;
|
||||
if (error) {
|
||||
TVOC = -1;
|
||||
NOX = -1;
|
||||
//Serial.println(String(TVOC));
|
||||
} else {
|
||||
TVOC = voc_algorithm.process(srawVoc);
|
||||
NOX = nox_algorithm.process(srawNox);
|
||||
//Serial.println(String(TVOC));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateCo2() {
|
||||
if (currentMillis - previousCo2 >= co2Interval) {
|
||||
previousCo2 += co2Interval;
|
||||
Co2 = sensor_S8 -> get_co2();
|
||||
//Serial.println(String(Co2));
|
||||
}
|
||||
}
|
||||
|
||||
void updatePm() {
|
||||
if (currentMillis - previousPm >= pmInterval) {
|
||||
previousPm += pmInterval;
|
||||
if (pms1.readUntil(data1, 2000)) {
|
||||
pm01 = data1.PM_AE_UG_1_0;
|
||||
pm25 = data1.PM_AE_UG_2_5;
|
||||
pm10 = data1.PM_AE_UG_10_0;
|
||||
pm03PCount = data1.PM_RAW_0_3;
|
||||
temp = data1.AMB_TMP;
|
||||
hum = data1.AMB_HUM;
|
||||
} else {
|
||||
pm01 = -1;
|
||||
pm25 = -1;
|
||||
pm10 = -1;
|
||||
pm03PCount = -1;
|
||||
temp = -10001;
|
||||
hum = -10001;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void sendPing() {
|
||||
String payload = "{\"wifi\":" + String(WiFi.RSSI()) +
|
||||
", \"boot\":" + loopCount +
|
||||
"}";
|
||||
}
|
||||
|
||||
|
||||
void sendToServer() {
|
||||
if (currentMillis - previoussendToServer >= sendToServerInterval) {
|
||||
previoussendToServer += sendToServerInterval;
|
||||
String payload = "{\"wifi\":" + String(WiFi.RSSI()) +
|
||||
(Co2 < 0 ? "" : ", \"rco2\":" + String(Co2)) +
|
||||
(pm01 < 0 ? "" : ", \"pm01\":" + String(pm01)) +
|
||||
(pm25 < 0 ? "" : ", \"pm02\":" + String(pm25)) +
|
||||
(pm10 < 0 ? "" : ", \"pm10\":" + String(pm10)) +
|
||||
(pm03PCount < 0 ? "" : ", \"pm003_count\":" + String(pm03PCount)) +
|
||||
(TVOC < 0 ? "" : ", \"tvoc_index\":" + String(TVOC)) +
|
||||
(NOX < 0 ? "" : ", \"nox_index\":" + String(NOX)) +
|
||||
", \"atmp\":" + String(temp/10) +
|
||||
(hum < 0 ? "" : ", \"rhum\":" + String(hum/10)) +
|
||||
", \"boot\":" + loopCount +
|
||||
"}";
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
Serial.println(payload);
|
||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(getNormalizedMac()) + "/measures";
|
||||
Serial.println(POSTURL);
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
http.begin(client, POSTURL);
|
||||
http.addHeader("content-type", "application/json");
|
||||
int httpCode = http.POST(payload);
|
||||
String response = http.getString();
|
||||
Serial.println(httpCode);
|
||||
//Serial.println(response);
|
||||
http.end();
|
||||
resetWatchdog();
|
||||
loopCount++;
|
||||
} else {
|
||||
Serial.println("WiFi Disconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void countdown(int from) {
|
||||
debug("\n");
|
||||
while (from > 0) {
|
||||
debug(String(from--));
|
||||
debug(" ");
|
||||
delay(1000);
|
||||
}
|
||||
debug("\n");
|
||||
}
|
||||
|
||||
void resetWatchdog() {
|
||||
Serial.println("Watchdog reset");
|
||||
digitalWrite(2, HIGH);
|
||||
delay(20);
|
||||
digitalWrite(2, LOW);
|
||||
}
|
||||
|
||||
// Wifi Manager
|
||||
void connectToWifi() {
|
||||
WiFiManager wifiManager;
|
||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||
String HOTSPOT = "AG-" + String(getNormalizedMac());
|
||||
wifiManager.setTimeout(180);
|
||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||
Serial.println("failed to connect and hit timeout");
|
||||
delay(6000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void debug(String msg) {
|
||||
if (DEBUG)
|
||||
Serial.print(msg);
|
||||
}
|
||||
|
||||
void debug(int msg) {
|
||||
if (DEBUG)
|
||||
Serial.print(msg);
|
||||
}
|
||||
|
||||
void debugln(String msg) {
|
||||
if (DEBUG)
|
||||
Serial.println(msg);
|
||||
}
|
||||
|
||||
void debugln(int msg) {
|
||||
if (DEBUG)
|
||||
Serial.println(msg);
|
||||
}
|
||||
|
||||
String getNormalizedMac() {
|
||||
String mac = WiFi.macAddress();
|
||||
mac.replace(":", "");
|
||||
mac.toLowerCase();
|
||||
return mac;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
220
examples/DIY_PRO/DIY_PRO.ino
Normal file
220
examples/DIY_PRO/DIY_PRO.ino
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
This is the code for the AirGradient DIY PRO Air Quality Sensor with an ESP8266 Microcontroller.
|
||||
|
||||
It is a high quality sensor showing PM2.5, CO2, Temperature and Humidity on a small display and can send data over Wifi.
|
||||
|
||||
Build Instructions: https://www.airgradient.com/open-airgradient/instructions/diy-pro/
|
||||
|
||||
Kits (including a pre-soldered version) are available: https://www.airgradient.com/open-airgradient/kits/
|
||||
|
||||
The codes needs the following libraries installed:
|
||||
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
|
||||
“U8g2” by oliver tested with version 2.32.15
|
||||
|
||||
Configuration:
|
||||
Please set in the code below the configuration parameters.
|
||||
|
||||
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||
|
||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||
https://www.airgradient.com/
|
||||
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <AirGradient.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <U8g2lib.h>
|
||||
|
||||
AirGradient ag = AirGradient();
|
||||
|
||||
// Display bottom right
|
||||
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
|
||||
|
||||
// Replace above if you have display on top left
|
||||
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R2, /* reset=*/ U8X8_PIN_NONE);
|
||||
|
||||
|
||||
// CONFIGURATION START
|
||||
|
||||
//set to the endpoint you would like to use
|
||||
String APIROOT = "http://hw.airgradient.com/";
|
||||
|
||||
// set to true to switch from Celcius to Fahrenheit
|
||||
boolean inF = false;
|
||||
|
||||
// set to true if you want to connect to wifi. You have 60 seconds to connect. Then it will go into an offline mode.
|
||||
boolean connectWIFI=true;
|
||||
|
||||
// CONFIGURATION END
|
||||
|
||||
|
||||
unsigned long currentMillis = 0;
|
||||
|
||||
const int oledInterval = 5000;
|
||||
unsigned long previousOled = 0;
|
||||
|
||||
const int sendToServerInterval = 10000;
|
||||
unsigned long previoussendToServer = 0;
|
||||
|
||||
const int co2Interval = 5000;
|
||||
unsigned long previousCo2 = 0;
|
||||
int Co2 = 0;
|
||||
|
||||
const int pm25Interval = 5000;
|
||||
unsigned long previousPm25 = 0;
|
||||
int pm25 = 0;
|
||||
|
||||
const int tempHumInterval = 2500;
|
||||
unsigned long previousTempHum = 0;
|
||||
float temp = 0;
|
||||
int hum = 0;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
u8g2.setBusClock(100000);
|
||||
u8g2.begin();
|
||||
updateOLED();
|
||||
|
||||
if (connectWIFI) {
|
||||
connectToWifi();
|
||||
}
|
||||
|
||||
updateOLED2("Warming up the", "sensors.", "");
|
||||
|
||||
ag.CO2_Init();
|
||||
ag.PMS_Init();
|
||||
ag.TMP_RH_Init(0x44);
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
currentMillis = millis();
|
||||
updateOLED();
|
||||
updateCo2();
|
||||
updatePm25();
|
||||
updateTempHum();
|
||||
sendToServer();
|
||||
}
|
||||
|
||||
void updateCo2()
|
||||
{
|
||||
if (currentMillis - previousCo2 >= co2Interval) {
|
||||
previousCo2 += co2Interval;
|
||||
Co2 = ag.getCO2_Raw();
|
||||
Serial.println(String(Co2));
|
||||
}
|
||||
}
|
||||
|
||||
void updatePm25()
|
||||
{
|
||||
if (currentMillis - previousPm25 >= pm25Interval) {
|
||||
previousPm25 += pm25Interval;
|
||||
pm25 = ag.getPM2_Raw();
|
||||
Serial.println(String(pm25));
|
||||
}
|
||||
}
|
||||
|
||||
void updateTempHum()
|
||||
{
|
||||
if (currentMillis - previousTempHum >= tempHumInterval) {
|
||||
previousTempHum += tempHumInterval;
|
||||
TMP_RH result = ag.periodicFetchData();
|
||||
temp = result.t;
|
||||
hum = result.rh;
|
||||
Serial.println(String(temp));
|
||||
}
|
||||
}
|
||||
|
||||
void updateOLED() {
|
||||
if (currentMillis - previousOled >= oledInterval) {
|
||||
previousOled += oledInterval;
|
||||
|
||||
String ln3;
|
||||
String ln1 = "PM:" + String(pm25) + " AQI:" + String(PM_TO_AQI_US(pm25)) ;
|
||||
String ln2 = "CO2:" + String(Co2);
|
||||
|
||||
if (inF) {
|
||||
ln3 = "F:" + String((temp* 9 / 5) + 32) + " H:" + String(hum)+"%";
|
||||
} else {
|
||||
ln3 = "C:" + String(temp) + " H:" + String(hum)+"%";
|
||||
}
|
||||
updateOLED2(ln1, ln2, ln3);
|
||||
}
|
||||
}
|
||||
|
||||
void updateOLED2(String ln1, String ln2, String ln3) {
|
||||
char buf[9];
|
||||
u8g2.firstPage();
|
||||
u8g2.firstPage();
|
||||
do {
|
||||
u8g2.setFont(u8g2_font_t0_16_tf);
|
||||
u8g2.drawStr(1, 10, String(ln1).c_str());
|
||||
u8g2.drawStr(1, 30, String(ln2).c_str());
|
||||
u8g2.drawStr(1, 50, String(ln3).c_str());
|
||||
} while ( u8g2.nextPage() );
|
||||
}
|
||||
|
||||
void sendToServer() {
|
||||
if (currentMillis - previoussendToServer >= sendToServerInterval) {
|
||||
previoussendToServer += sendToServerInterval;
|
||||
|
||||
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
||||
+ (Co2 < 0 ? "" : ", \"rco2\":" + String(Co2))
|
||||
+ (pm25 < 0 ? "" : ", \"pm02\":" + String(pm25))
|
||||
+ ", \"atmp\":" + String(temp)
|
||||
+ (hum < 0 ? "" : ", \"rhum\":" + String(hum))
|
||||
+ "}";
|
||||
|
||||
if(WiFi.status()== WL_CONNECTED){
|
||||
Serial.println(payload);
|
||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId(), HEX) + "/measures";
|
||||
Serial.println(POSTURL);
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
http.begin(client, POSTURL);
|
||||
http.addHeader("content-type", "application/json");
|
||||
int httpCode = http.POST(payload);
|
||||
String response = http.getString();
|
||||
Serial.println(httpCode);
|
||||
Serial.println(response);
|
||||
http.end();
|
||||
}
|
||||
else {
|
||||
Serial.println("WiFi Disconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wifi Manager
|
||||
void connectToWifi() {
|
||||
WiFiManager wifiManager;
|
||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||
String HOTSPOT = "AG-" + String(ESP.getChipId(), HEX);
|
||||
updateOLED2("60s to connect", "to Wifi Hotspot", HOTSPOT);
|
||||
wifiManager.setTimeout(60);
|
||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||
updateOLED2("booting into", "offline mode", "");
|
||||
Serial.println("failed to connect and hit timeout");
|
||||
delay(6000);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate PM2.5 US AQI
|
||||
int PM_TO_AQI_US(int pm02) {
|
||||
if (pm02 <= 12.0) return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0);
|
||||
else if (pm02 <= 35.4) return ((100 - 50) / (35.4 - 12.0) * (pm02 - 12.0) + 50);
|
||||
else if (pm02 <= 55.4) return ((150 - 100) / (55.4 - 35.4) * (pm02 - 35.4) + 100);
|
||||
else if (pm02 <= 150.4) return ((200 - 150) / (150.4 - 55.4) * (pm02 - 55.4) + 150);
|
||||
else if (pm02 <= 250.4) return ((300 - 200) / (250.4 - 150.4) * (pm02 - 150.4) + 200);
|
||||
else if (pm02 <= 350.4) return ((400 - 300) / (350.4 - 250.4) * (pm02 - 250.4) + 300);
|
||||
else if (pm02 <= 500.4) return ((500 - 400) / (500.4 - 350.4) * (pm02 - 350.4) + 400);
|
||||
else return 500;
|
||||
};
|
424
examples/DIY_PRO_V3_7/DIY_PRO_V3_7.ino
Normal file
424
examples/DIY_PRO_V3_7/DIY_PRO_V3_7.ino
Normal file
@ -0,0 +1,424 @@
|
||||
/*
|
||||
Important: This code is only for the DIY PRO PCB Version 3.7 that has a push button mounted.
|
||||
|
||||
This is the code for the AirGradient DIY PRO Air Quality Sensor with an ESP8266 Microcontroller with the SGP40 TVOC module from AirGradient.
|
||||
|
||||
It is a high quality sensor showing PM2.5, CO2, Temperature and Humidity on a small display and can send data over Wifi.
|
||||
|
||||
Build Instructions: https://www.airgradient.com/open-airgradient/instructions/diy-pro-v37/
|
||||
|
||||
Kits (including a pre-soldered version) are available: https://www.airgradient.com/open-airgradient/kits/
|
||||
|
||||
The codes needs the following libraries installed:
|
||||
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
|
||||
“U8g2” by oliver tested with version 2.32.15
|
||||
"Sensirion I2C SGP41" by Sensation Version 0.1.0
|
||||
"Sensirion Gas Index Algorithm" by Sensation Version 3.2.1
|
||||
|
||||
Configuration:
|
||||
Please set in the code below the configuration parameters.
|
||||
|
||||
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||
|
||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||
https://www.airgradient.com/
|
||||
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <AirGradient.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <WiFiClient.h>
|
||||
|
||||
#include <EEPROM.h>
|
||||
|
||||
//#include "SGP30.h"
|
||||
#include <SensirionI2CSgp41.h>
|
||||
#include <NOxGasIndexAlgorithm.h>
|
||||
#include <VOCGasIndexAlgorithm.h>
|
||||
|
||||
|
||||
#include <U8g2lib.h>
|
||||
|
||||
AirGradient ag = AirGradient();
|
||||
SensirionI2CSgp41 sgp41;
|
||||
VOCGasIndexAlgorithm voc_algorithm;
|
||||
NOxGasIndexAlgorithm nox_algorithm;
|
||||
// time in seconds needed for NOx conditioning
|
||||
uint16_t conditioning_s = 10;
|
||||
|
||||
// for peristent saving and loading
|
||||
int addr = 4;
|
||||
byte value;
|
||||
|
||||
// Display bottom right
|
||||
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
|
||||
|
||||
// Replace above if you have display on top left
|
||||
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R2, /* reset=*/ U8X8_PIN_NONE);
|
||||
|
||||
|
||||
// CONFIGURATION START
|
||||
|
||||
//set to the endpoint you would like to use
|
||||
String APIROOT = "http://hw.airgradient.com/";
|
||||
|
||||
// set to true to switch from Celcius to Fahrenheit
|
||||
boolean inF = false;
|
||||
|
||||
// PM2.5 in US AQI (default ug/m3)
|
||||
boolean inUSAQI = false;
|
||||
|
||||
// Display Position
|
||||
boolean displayTop = true;
|
||||
|
||||
// set to true if you want to connect to wifi. You have 60 seconds to connect. Then it will go into an offline mode.
|
||||
boolean connectWIFI=true;
|
||||
|
||||
// CONFIGURATION END
|
||||
|
||||
|
||||
unsigned long currentMillis = 0;
|
||||
|
||||
const int oledInterval = 5000;
|
||||
unsigned long previousOled = 0;
|
||||
|
||||
const int sendToServerInterval = 10000;
|
||||
unsigned long previoussendToServer = 0;
|
||||
|
||||
const int tvocInterval = 1000;
|
||||
unsigned long previousTVOC = 0;
|
||||
int TVOC = 0;
|
||||
int NOX = 0;
|
||||
|
||||
const int co2Interval = 5000;
|
||||
unsigned long previousCo2 = 0;
|
||||
int Co2 = 0;
|
||||
|
||||
const int pm25Interval = 5000;
|
||||
unsigned long previousPm25 = 0;
|
||||
int pm25 = 0;
|
||||
|
||||
const int tempHumInterval = 2500;
|
||||
unsigned long previousTempHum = 0;
|
||||
float temp = 0;
|
||||
int hum = 0;
|
||||
|
||||
int buttonConfig=4;
|
||||
int lastState = LOW;
|
||||
int currentState;
|
||||
unsigned long pressedTime = 0;
|
||||
unsigned long releasedTime = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Hello");
|
||||
u8g2.begin();
|
||||
//u8g2.setDisplayRotation(U8G2_R0);
|
||||
|
||||
EEPROM.begin(512);
|
||||
delay(500);
|
||||
|
||||
buttonConfig = String(EEPROM.read(addr)).toInt();
|
||||
setConfig();
|
||||
|
||||
updateOLED2("Press Button", "Now for", "Config Menu");
|
||||
delay(2000);
|
||||
|
||||
currentState = digitalRead(D7);
|
||||
if (currentState == HIGH)
|
||||
{
|
||||
updateOLED2("Entering", "Config Menu", "");
|
||||
delay(3000);
|
||||
lastState = LOW;
|
||||
inConf();
|
||||
}
|
||||
|
||||
if (connectWIFI)
|
||||
{
|
||||
connectToWifi();
|
||||
}
|
||||
|
||||
updateOLED2("Warming Up", "Serial Number:", String(ESP.getChipId(), HEX));
|
||||
sgp41.begin(Wire);
|
||||
ag.CO2_Init();
|
||||
ag.PMS_Init();
|
||||
ag.TMP_RH_Init(0x44);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
currentMillis = millis();
|
||||
updateTVOC();
|
||||
updateOLED();
|
||||
updateCo2();
|
||||
updatePm25();
|
||||
updateTempHum();
|
||||
sendToServer();
|
||||
}
|
||||
|
||||
void inConf(){
|
||||
setConfig();
|
||||
currentState = digitalRead(D7);
|
||||
|
||||
if(lastState == LOW && currentState == HIGH) {
|
||||
pressedTime = millis();
|
||||
}
|
||||
|
||||
else if(lastState == HIGH && currentState == LOW) {
|
||||
releasedTime = millis();
|
||||
long pressDuration = releasedTime - pressedTime;
|
||||
if( pressDuration < 1000 ) {
|
||||
buttonConfig=buttonConfig+1;
|
||||
if (buttonConfig>7) buttonConfig=0;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastState == HIGH && currentState == HIGH){
|
||||
long passedDuration = millis() - pressedTime;
|
||||
if( passedDuration > 4000 ) {
|
||||
// to do
|
||||
// if (buttonConfig==4) {
|
||||
// updateOLED2("Saved", "Release", "Button Now");
|
||||
// delay(1000);
|
||||
// updateOLED2("Starting", "CO2", "Calibration");
|
||||
// delay(1000);
|
||||
// Co2Calibration();
|
||||
// } else {
|
||||
updateOLED2("Saved", "Release", "Button Now");
|
||||
delay(1000);
|
||||
updateOLED2("Rebooting", "in", "5 seconds");
|
||||
delay(5000);
|
||||
EEPROM.write(addr, char(buttonConfig));
|
||||
EEPROM.commit();
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
lastState = currentState;
|
||||
delay(100);
|
||||
inConf();
|
||||
}
|
||||
|
||||
|
||||
void setConfig() {
|
||||
if (buttonConfig == 0) {
|
||||
updateOLED2("Temp. in C", "PM in ug/m3", "Display Top");
|
||||
u8g2.setDisplayRotation(U8G2_R2);
|
||||
inF = false;
|
||||
inUSAQI = false;
|
||||
}
|
||||
if (buttonConfig == 1) {
|
||||
updateOLED2("Temp. in C", "PM in US AQI", "Display Top");
|
||||
u8g2.setDisplayRotation(U8G2_R2);
|
||||
inF = false;
|
||||
inUSAQI = true;
|
||||
}
|
||||
if (buttonConfig == 2) {
|
||||
updateOLED2("Temp. in F", "PM in ug/m3", "Display Top");
|
||||
u8g2.setDisplayRotation(U8G2_R2);
|
||||
inF = true;
|
||||
inUSAQI = false;
|
||||
}
|
||||
if (buttonConfig == 3) {
|
||||
updateOLED2("Temp. in F", "PM in US AQI", "Display Top");
|
||||
u8g2.setDisplayRotation(U8G2_R2);
|
||||
inF = true;
|
||||
inUSAQI = true;
|
||||
}
|
||||
if (buttonConfig == 4) {
|
||||
updateOLED2("Temp. in C", "PM in ug/m3", "Display Bottom");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = false;
|
||||
inUSAQI = false;
|
||||
}
|
||||
if (buttonConfig == 5) {
|
||||
updateOLED2("Temp. in C", "PM in US AQI", "Display Bottom");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = false;
|
||||
inUSAQI = true;
|
||||
}
|
||||
if (buttonConfig == 6) {
|
||||
updateOLED2("Temp. in F", "PM in ug/m3", "Display Bottom");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = true;
|
||||
inUSAQI = false;
|
||||
}
|
||||
if (buttonConfig == 7) {
|
||||
updateOLED2("Temp. in F", "PM in US AQI", "Display Bottom");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = true;
|
||||
inUSAQI = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// to do
|
||||
// if (buttonConfig == 8) {
|
||||
// updateOLED2("CO2", "Manual", "Calibration");
|
||||
// }
|
||||
}
|
||||
|
||||
void updateTVOC()
|
||||
{
|
||||
uint16_t error;
|
||||
char errorMessage[256];
|
||||
uint16_t defaultRh = 0x8000;
|
||||
uint16_t defaultT = 0x6666;
|
||||
uint16_t srawVoc = 0;
|
||||
uint16_t srawNox = 0;
|
||||
uint16_t defaultCompenstaionRh = 0x8000; // in ticks as defined by SGP41
|
||||
uint16_t defaultCompenstaionT = 0x6666; // in ticks as defined by SGP41
|
||||
uint16_t compensationRh = 0; // in ticks as defined by SGP41
|
||||
uint16_t compensationT = 0; // in ticks as defined by SGP41
|
||||
|
||||
delay(1000);
|
||||
|
||||
compensationT = static_cast<uint16_t>((temp + 45) * 65535 / 175);
|
||||
compensationRh = static_cast<uint16_t>(hum * 65535 / 100);
|
||||
|
||||
if (conditioning_s > 0) {
|
||||
error = sgp41.executeConditioning(compensationRh, compensationT, srawVoc);
|
||||
conditioning_s--;
|
||||
} else {
|
||||
error = sgp41.measureRawSignals(compensationRh, compensationT, srawVoc,
|
||||
srawNox);
|
||||
}
|
||||
|
||||
if (currentMillis - previousTVOC >= tvocInterval) {
|
||||
previousTVOC += tvocInterval;
|
||||
TVOC = voc_algorithm.process(srawVoc);
|
||||
NOX = nox_algorithm.process(srawNox);
|
||||
Serial.println(String(TVOC));
|
||||
}
|
||||
}
|
||||
|
||||
void updateCo2()
|
||||
{
|
||||
if (currentMillis - previousCo2 >= co2Interval) {
|
||||
previousCo2 += co2Interval;
|
||||
Co2 = ag.getCO2_Raw();
|
||||
Serial.println(String(Co2));
|
||||
}
|
||||
}
|
||||
|
||||
void updatePm25()
|
||||
{
|
||||
if (currentMillis - previousPm25 >= pm25Interval) {
|
||||
previousPm25 += pm25Interval;
|
||||
pm25 = ag.getPM2_Raw();
|
||||
Serial.println(String(pm25));
|
||||
}
|
||||
}
|
||||
|
||||
void updateTempHum()
|
||||
{
|
||||
if (currentMillis - previousTempHum >= tempHumInterval) {
|
||||
previousTempHum += tempHumInterval;
|
||||
TMP_RH result = ag.periodicFetchData();
|
||||
temp = result.t;
|
||||
hum = result.rh;
|
||||
Serial.println(String(temp));
|
||||
}
|
||||
}
|
||||
|
||||
void updateOLED() {
|
||||
if (currentMillis - previousOled >= oledInterval) {
|
||||
previousOled += oledInterval;
|
||||
|
||||
String ln3;
|
||||
String ln1;
|
||||
|
||||
if (inUSAQI) {
|
||||
ln1 = "AQI:" + String(PM_TO_AQI_US(pm25)) + " CO2:" + String(Co2);
|
||||
} else {
|
||||
ln1 = "PM:" + String(pm25) + " CO2:" + String(Co2);
|
||||
}
|
||||
|
||||
String ln2 = "TVOC:" + String(TVOC) + " NOX:" + String(NOX);
|
||||
|
||||
if (inF) {
|
||||
ln3 = "F:" + String((temp* 9 / 5) + 32) + " H:" + String(hum)+"%";
|
||||
} else {
|
||||
ln3 = "C:" + String(temp) + " H:" + String(hum)+"%";
|
||||
}
|
||||
updateOLED2(ln1, ln2, ln3);
|
||||
}
|
||||
}
|
||||
|
||||
void updateOLED2(String ln1, String ln2, String ln3) {
|
||||
char buf[9];
|
||||
u8g2.firstPage();
|
||||
u8g2.firstPage();
|
||||
do {
|
||||
u8g2.setFont(u8g2_font_t0_16_tf);
|
||||
u8g2.drawStr(1, 10, String(ln1).c_str());
|
||||
u8g2.drawStr(1, 30, String(ln2).c_str());
|
||||
u8g2.drawStr(1, 50, String(ln3).c_str());
|
||||
} while ( u8g2.nextPage() );
|
||||
}
|
||||
|
||||
void sendToServer() {
|
||||
if (currentMillis - previoussendToServer >= sendToServerInterval) {
|
||||
previoussendToServer += sendToServerInterval;
|
||||
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
||||
+ (Co2 < 0 ? "" : ", \"rco2\":" + String(Co2))
|
||||
+ (pm25 < 0 ? "" : ", \"pm02\":" + String(pm25))
|
||||
+ (TVOC < 0 ? "" : ", \"tvoc_index\":" + String(TVOC))
|
||||
+ (NOX < 0 ? "" : ", \"nox_index\":" + String(NOX))
|
||||
+ ", \"atmp\":" + String(temp)
|
||||
+ (hum < 0 ? "" : ", \"rhum\":" + String(hum))
|
||||
+ "}";
|
||||
|
||||
if(WiFi.status()== WL_CONNECTED){
|
||||
Serial.println(payload);
|
||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId(), HEX) + "/measures";
|
||||
Serial.println(POSTURL);
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
http.begin(client, POSTURL);
|
||||
http.addHeader("content-type", "application/json");
|
||||
int httpCode = http.POST(payload);
|
||||
String response = http.getString();
|
||||
Serial.println(httpCode);
|
||||
Serial.println(response);
|
||||
http.end();
|
||||
}
|
||||
else {
|
||||
Serial.println("WiFi Disconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wifi Manager
|
||||
void connectToWifi() {
|
||||
WiFiManager wifiManager;
|
||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||
String HOTSPOT = "AG-" + String(ESP.getChipId(), HEX);
|
||||
updateOLED2("90s to connect", "to Wifi Hotspot", HOTSPOT);
|
||||
wifiManager.setTimeout(90);
|
||||
|
||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||
updateOLED2("booting into", "offline mode", "");
|
||||
Serial.println("failed to connect and hit timeout");
|
||||
delay(6000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Calculate PM2.5 US AQI
|
||||
int PM_TO_AQI_US(int pm02) {
|
||||
if (pm02 <= 12.0) return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0);
|
||||
else if (pm02 <= 35.4) return ((100 - 50) / (35.4 - 12.0) * (pm02 - 12.0) + 50);
|
||||
else if (pm02 <= 55.4) return ((150 - 100) / (55.4 - 35.4) * (pm02 - 35.4) + 100);
|
||||
else if (pm02 <= 150.4) return ((200 - 150) / (150.4 - 55.4) * (pm02 - 55.4) + 150);
|
||||
else if (pm02 <= 250.4) return ((300 - 200) / (250.4 - 150.4) * (pm02 - 150.4) + 200);
|
||||
else if (pm02 <= 350.4) return ((400 - 300) / (350.4 - 250.4) * (pm02 - 250.4) + 300);
|
||||
else if (pm02 <= 500.4) return ((500 - 400) / (500.4 - 350.4) * (pm02 - 350.4) + 400);
|
||||
else return 500;
|
||||
};
|
433
examples/DIY_PRO_V4_2/DIY_PRO_V4_2.ino
Normal file
433
examples/DIY_PRO_V4_2/DIY_PRO_V4_2.ino
Normal file
@ -0,0 +1,433 @@
|
||||
/*
|
||||
Important: This code is only for the DIY PRO PCB Version 4.2 that has a push button mounted.
|
||||
|
||||
This is the code for the AirGradient DIY PRO Air Quality Sensor with an ESP8266 Microcontroller with the SGP40 TVOC module from AirGradient.
|
||||
|
||||
It is a high quality sensor showing PM2.5, CO2, Temperature and Humidity on a small display and can send data over Wifi.
|
||||
|
||||
Build Instructions: https://www.airgradient.com/open-airgradient/instructions/diy-pro-v42/
|
||||
|
||||
Kits (including a pre-soldered version) are available: https://www.airgradient.com/open-airgradient/kits/
|
||||
|
||||
The codes needs the following libraries installed:
|
||||
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
|
||||
“U8g2” by oliver tested with version 2.32.15
|
||||
"Sensirion I2C SGP41" by Sensation Version 0.1.0
|
||||
"Sensirion Gas Index Algorithm" by Sensation Version 3.2.1
|
||||
"Arduino-SHT" by Johannes Winkelmann Version 1.2.2
|
||||
|
||||
Configuration:
|
||||
Please set in the code below the configuration parameters.
|
||||
|
||||
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||
|
||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||
https://www.airgradient.com/
|
||||
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <AirGradient.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <WiFiClient.h>
|
||||
|
||||
#include <EEPROM.h>
|
||||
#include "SHTSensor.h"
|
||||
|
||||
//#include "SGP30.h"
|
||||
#include <SensirionI2CSgp41.h>
|
||||
#include <NOxGasIndexAlgorithm.h>
|
||||
#include <VOCGasIndexAlgorithm.h>
|
||||
|
||||
|
||||
#include <U8g2lib.h>
|
||||
|
||||
AirGradient ag = AirGradient();
|
||||
SensirionI2CSgp41 sgp41;
|
||||
VOCGasIndexAlgorithm voc_algorithm;
|
||||
NOxGasIndexAlgorithm nox_algorithm;
|
||||
SHTSensor sht;
|
||||
|
||||
// time in seconds needed for NOx conditioning
|
||||
uint16_t conditioning_s = 10;
|
||||
|
||||
// for peristent saving and loading
|
||||
int addr = 4;
|
||||
byte value;
|
||||
|
||||
// Display bottom right
|
||||
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
|
||||
|
||||
// Replace above if you have display on top left
|
||||
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R2, /* reset=*/ U8X8_PIN_NONE);
|
||||
|
||||
|
||||
// CONFIGURATION START
|
||||
|
||||
//set to the endpoint you would like to use
|
||||
String APIROOT = "http://hw.airgradient.com/";
|
||||
|
||||
// set to true to switch from Celcius to Fahrenheit
|
||||
boolean inF = false;
|
||||
|
||||
// PM2.5 in US AQI (default ug/m3)
|
||||
boolean inUSAQI = false;
|
||||
|
||||
// Display Position
|
||||
boolean displayTop = true;
|
||||
|
||||
// set to true if you want to connect to wifi. You have 60 seconds to connect. Then it will go into an offline mode.
|
||||
boolean connectWIFI=true;
|
||||
|
||||
// CONFIGURATION END
|
||||
|
||||
|
||||
unsigned long currentMillis = 0;
|
||||
|
||||
const int oledInterval = 5000;
|
||||
unsigned long previousOled = 0;
|
||||
|
||||
const int sendToServerInterval = 10000;
|
||||
unsigned long previoussendToServer = 0;
|
||||
|
||||
const int tvocInterval = 1000;
|
||||
unsigned long previousTVOC = 0;
|
||||
int TVOC = 0;
|
||||
int NOX = 0;
|
||||
|
||||
const int co2Interval = 5000;
|
||||
unsigned long previousCo2 = 0;
|
||||
int Co2 = 0;
|
||||
|
||||
const int pmInterval = 5000;
|
||||
unsigned long previousPm = 0;
|
||||
int pm25 = 0;
|
||||
int pm01 = 0;
|
||||
int pm10 = 0;
|
||||
int pm03PCount = 0;
|
||||
|
||||
const int tempHumInterval = 2500;
|
||||
unsigned long previousTempHum = 0;
|
||||
float temp = 0;
|
||||
int hum = 0;
|
||||
|
||||
int buttonConfig=0;
|
||||
int lastState = LOW;
|
||||
int currentState;
|
||||
unsigned long pressedTime = 0;
|
||||
unsigned long releasedTime = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Hello");
|
||||
u8g2.begin();
|
||||
sht.init();
|
||||
sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM);
|
||||
//u8g2.setDisplayRotation(U8G2_R0);
|
||||
|
||||
EEPROM.begin(512);
|
||||
delay(500);
|
||||
|
||||
buttonConfig = String(EEPROM.read(addr)).toInt();
|
||||
if (buttonConfig>3) buttonConfig=0;
|
||||
delay(400);
|
||||
setConfig();
|
||||
Serial.println("buttonConfig: "+String(buttonConfig));
|
||||
updateOLED2("Press Button", "Now for", "Config Menu");
|
||||
delay(2000);
|
||||
pinMode(D7, INPUT_PULLUP);
|
||||
currentState = digitalRead(D7);
|
||||
if (currentState == LOW)
|
||||
{
|
||||
updateOLED2("Entering", "Config Menu", "");
|
||||
delay(3000);
|
||||
lastState = HIGH;
|
||||
setConfig();
|
||||
inConf();
|
||||
}
|
||||
|
||||
if (connectWIFI)
|
||||
{
|
||||
connectToWifi();
|
||||
}
|
||||
|
||||
updateOLED2("Warming Up", "Serial Number:", String(ESP.getChipId(), HEX));
|
||||
sgp41.begin(Wire);
|
||||
ag.CO2_Init();
|
||||
ag.PMS_Init();
|
||||
ag.TMP_RH_Init(0x44);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
currentMillis = millis();
|
||||
updateTVOC();
|
||||
updateOLED();
|
||||
updateCo2();
|
||||
updatePm();
|
||||
updateTempHum();
|
||||
sendToServer();
|
||||
}
|
||||
|
||||
void inConf(){
|
||||
setConfig();
|
||||
currentState = digitalRead(D7);
|
||||
|
||||
if (currentState){
|
||||
Serial.println("currentState: high");
|
||||
} else {
|
||||
Serial.println("currentState: low");
|
||||
}
|
||||
|
||||
if(lastState == HIGH && currentState == LOW) {
|
||||
pressedTime = millis();
|
||||
}
|
||||
|
||||
else if(lastState == LOW && currentState == HIGH) {
|
||||
releasedTime = millis();
|
||||
long pressDuration = releasedTime - pressedTime;
|
||||
if( pressDuration < 1000 ) {
|
||||
buttonConfig=buttonConfig+1;
|
||||
if (buttonConfig>3) buttonConfig=0;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastState == LOW && currentState == LOW){
|
||||
long passedDuration = millis() - pressedTime;
|
||||
if( passedDuration > 4000 ) {
|
||||
// to do
|
||||
// if (buttonConfig==4) {
|
||||
// updateOLED2("Saved", "Release", "Button Now");
|
||||
// delay(1000);
|
||||
// updateOLED2("Starting", "CO2", "Calibration");
|
||||
// delay(1000);
|
||||
// Co2Calibration();
|
||||
// } else {
|
||||
updateOLED2("Saved", "Release", "Button Now");
|
||||
delay(1000);
|
||||
updateOLED2("Rebooting", "in", "5 seconds");
|
||||
delay(5000);
|
||||
EEPROM.write(addr, char(buttonConfig));
|
||||
EEPROM.commit();
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
lastState = currentState;
|
||||
delay(100);
|
||||
inConf();
|
||||
}
|
||||
|
||||
|
||||
void setConfig() {
|
||||
if (buttonConfig == 0) {
|
||||
updateOLED2("Temp. in C", "PM in ug/m3", "Long Press Saves");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = false;
|
||||
inUSAQI = false;
|
||||
}
|
||||
if (buttonConfig == 1) {
|
||||
updateOLED2("Temp. in C", "PM in US AQI", "Long Press Saves");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = false;
|
||||
inUSAQI = true;
|
||||
} else if (buttonConfig == 2) {
|
||||
updateOLED2("Temp. in F", "PM in ug/m3", "Long Press Saves");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = true;
|
||||
inUSAQI = false;
|
||||
} else if (buttonConfig == 3) {
|
||||
updateOLED2("Temp. in F", "PM in US AQI", "Long Press Saves");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = true;
|
||||
inUSAQI = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// to do
|
||||
// if (buttonConfig == 8) {
|
||||
// updateOLED2("CO2", "Manual", "Calibration");
|
||||
// }
|
||||
}
|
||||
|
||||
void updateTVOC()
|
||||
{
|
||||
uint16_t error;
|
||||
char errorMessage[256];
|
||||
uint16_t defaultRh = 0x8000;
|
||||
uint16_t defaultT = 0x6666;
|
||||
uint16_t srawVoc = 0;
|
||||
uint16_t srawNox = 0;
|
||||
uint16_t defaultCompenstaionRh = 0x8000; // in ticks as defined by SGP41
|
||||
uint16_t defaultCompenstaionT = 0x6666; // in ticks as defined by SGP41
|
||||
uint16_t compensationRh = 0; // in ticks as defined by SGP41
|
||||
uint16_t compensationT = 0; // in ticks as defined by SGP41
|
||||
|
||||
delay(1000);
|
||||
|
||||
compensationT = static_cast<uint16_t>((temp + 45) * 65535 / 175);
|
||||
compensationRh = static_cast<uint16_t>(hum * 65535 / 100);
|
||||
|
||||
if (conditioning_s > 0) {
|
||||
error = sgp41.executeConditioning(compensationRh, compensationT, srawVoc);
|
||||
conditioning_s--;
|
||||
} else {
|
||||
error = sgp41.measureRawSignals(compensationRh, compensationT, srawVoc,
|
||||
srawNox);
|
||||
}
|
||||
|
||||
if (currentMillis - previousTVOC >= tvocInterval) {
|
||||
previousTVOC += tvocInterval;
|
||||
TVOC = voc_algorithm.process(srawVoc);
|
||||
NOX = nox_algorithm.process(srawNox);
|
||||
Serial.println(String(TVOC));
|
||||
}
|
||||
}
|
||||
|
||||
void updateCo2()
|
||||
{
|
||||
if (currentMillis - previousCo2 >= co2Interval) {
|
||||
previousCo2 += co2Interval;
|
||||
Co2 = ag.getCO2_Raw();
|
||||
Serial.println(String(Co2));
|
||||
}
|
||||
}
|
||||
|
||||
void updatePm()
|
||||
{
|
||||
if (currentMillis - previousPm >= pmInterval) {
|
||||
previousPm += pmInterval;
|
||||
pm01 = ag.getPM1_Raw();
|
||||
pm25 = ag.getPM2_Raw();
|
||||
pm10 = ag.getPM10_Raw();
|
||||
pm03PCount = ag.getPM0_3Count();
|
||||
Serial.println(String(pm25));
|
||||
}
|
||||
}
|
||||
|
||||
void updateTempHum()
|
||||
{
|
||||
if (currentMillis - previousTempHum >= tempHumInterval) {
|
||||
previousTempHum += tempHumInterval;
|
||||
|
||||
if (sht.readSample()) {
|
||||
Serial.print("SHT:\n");
|
||||
Serial.print(" RH: ");
|
||||
Serial.print(sht.getHumidity(), 2);
|
||||
Serial.print("\n");
|
||||
Serial.print(" T: ");
|
||||
Serial.print(sht.getTemperature(), 2);
|
||||
Serial.print("\n");
|
||||
temp = sht.getTemperature();
|
||||
hum = sht.getHumidity();
|
||||
} else {
|
||||
Serial.print("Error in readSample()\n");
|
||||
}
|
||||
Serial.println(String(temp));
|
||||
}
|
||||
}
|
||||
|
||||
void updateOLED() {
|
||||
if (currentMillis - previousOled >= oledInterval) {
|
||||
previousOled += oledInterval;
|
||||
|
||||
String ln3;
|
||||
String ln1;
|
||||
|
||||
if (inUSAQI) {
|
||||
ln1 = "AQI:" + String(PM_TO_AQI_US(pm25)) + " CO2:" + String(Co2);
|
||||
} else {
|
||||
ln1 = "PM:" + String(pm25) + " CO2:" + String(Co2);
|
||||
}
|
||||
|
||||
String ln2 = "TVOC:" + String(TVOC) + " NOX:" + String(NOX);
|
||||
|
||||
if (inF) {
|
||||
ln3 = "F:" + String((temp* 9 / 5) + 32) + " H:" + String(hum)+"%";
|
||||
} else {
|
||||
ln3 = "C:" + String(temp) + " H:" + String(hum)+"%";
|
||||
}
|
||||
updateOLED2(ln1, ln2, ln3);
|
||||
}
|
||||
}
|
||||
|
||||
void updateOLED2(String ln1, String ln2, String ln3) {
|
||||
char buf[9];
|
||||
u8g2.firstPage();
|
||||
u8g2.firstPage();
|
||||
do {
|
||||
u8g2.setFont(u8g2_font_t0_16_tf);
|
||||
u8g2.drawStr(1, 10, String(ln1).c_str());
|
||||
u8g2.drawStr(1, 30, String(ln2).c_str());
|
||||
u8g2.drawStr(1, 50, String(ln3).c_str());
|
||||
} while ( u8g2.nextPage() );
|
||||
}
|
||||
|
||||
void sendToServer() {
|
||||
if (currentMillis - previoussendToServer >= sendToServerInterval) {
|
||||
previoussendToServer += sendToServerInterval;
|
||||
String payload = "{\"wifi\":" + String(WiFi.RSSI())
|
||||
+ (Co2 < 0 ? "" : ", \"rco2\":" + String(Co2))
|
||||
+ (pm01 < 0 ? "" : ", \"pm01\":" + String(pm01))
|
||||
+ (pm25 < 0 ? "" : ", \"pm02\":" + String(pm25))
|
||||
+ (pm10 < 0 ? "" : ", \"pm10\":" + String(pm10))
|
||||
+ (pm03PCount < 0 ? "" : ", \"pm003_count\":" + String(pm03PCount))
|
||||
+ (TVOC < 0 ? "" : ", \"tvoc_index\":" + String(TVOC))
|
||||
+ (NOX < 0 ? "" : ", \"nox_index\":" + String(NOX))
|
||||
+ ", \"atmp\":" + String(temp)
|
||||
+ (hum < 0 ? "" : ", \"rhum\":" + String(hum))
|
||||
+ "}";
|
||||
|
||||
if(WiFi.status()== WL_CONNECTED){
|
||||
Serial.println(payload);
|
||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId(), HEX) + "/measures";
|
||||
Serial.println(POSTURL);
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
http.begin(client, POSTURL);
|
||||
http.addHeader("content-type", "application/json");
|
||||
int httpCode = http.POST(payload);
|
||||
String response = http.getString();
|
||||
Serial.println(httpCode);
|
||||
Serial.println(response);
|
||||
http.end();
|
||||
}
|
||||
else {
|
||||
Serial.println("WiFi Disconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wifi Manager
|
||||
void connectToWifi() {
|
||||
WiFiManager wifiManager;
|
||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||
String HOTSPOT = "AG-" + String(ESP.getChipId(), HEX);
|
||||
updateOLED2("90s to connect", "to Wifi Hotspot", HOTSPOT);
|
||||
wifiManager.setTimeout(90);
|
||||
|
||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||
updateOLED2("booting into", "offline mode", "");
|
||||
Serial.println("failed to connect and hit timeout");
|
||||
delay(6000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Calculate PM2.5 US AQI
|
||||
int PM_TO_AQI_US(int pm02) {
|
||||
if (pm02 <= 12.0) return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0);
|
||||
else if (pm02 <= 35.4) return ((100 - 50) / (35.4 - 12.0) * (pm02 - 12.0) + 50);
|
||||
else if (pm02 <= 55.4) return ((150 - 100) / (55.4 - 35.4) * (pm02 - 35.4) + 100);
|
||||
else if (pm02 <= 150.4) return ((200 - 150) / (150.4 - 55.4) * (pm02 - 55.4) + 150);
|
||||
else if (pm02 <= 250.4) return ((300 - 200) / (250.4 - 150.4) * (pm02 - 150.4) + 200);
|
||||
else if (pm02 <= 350.4) return ((400 - 300) / (350.4 - 250.4) * (pm02 - 250.4) + 300);
|
||||
else if (pm02 <= 500.4) return ((500 - 400) / (500.4 - 350.4) * (pm02 - 350.4) + 400);
|
||||
else return 500;
|
||||
};
|
@ -1,16 +0,0 @@
|
||||
#include <AirGradient.h>
|
||||
AirGradient ag = AirGradient();
|
||||
|
||||
void setup(){
|
||||
Serial.begin(9600);
|
||||
ag.MHZ19_Init(MHZ19B);
|
||||
}
|
||||
|
||||
void loop(){
|
||||
|
||||
int MHZ19_C02 = ag.readMHZ19();
|
||||
Serial.print("C02: ");
|
||||
Serial.println(MHZ19_C02);
|
||||
|
||||
delay(5000);
|
||||
}
|
380
examples/MINI_DISPLAY/MINI_DISPLAY.ino
Normal file
380
examples/MINI_DISPLAY/MINI_DISPLAY.ino
Normal file
@ -0,0 +1,380 @@
|
||||
/*
|
||||
This is the code for the AirGradient DIY Mini Display with an ESP8266 Microcontroller.
|
||||
It can be configures to show the outside air quality as well as one indoor location from the AirGradient platform.
|
||||
|
||||
For build instructions please visit
|
||||
|
||||
https://www.airgradient.com/open-airgradient/blog/airgradient-diy-display/
|
||||
|
||||
The codes needs the following libraries installed:
|
||||
"WifiManager by tzapu, tablatronix" tested with Version 2.0.5-alpha
|
||||
"Adafruit_ILI9341" tested with Version 1.5.10
|
||||
"Adafruit GFX library" tested with Version 1.10.12 (often automatically installed with above ILI9341 library)
|
||||
"ArduinoJSON" by Benoit Blanchon tested with Version 5.13.5
|
||||
|
||||
Configuration:
|
||||
Please set in the code below (line 90-) if you want to display the PM2.5 values in US AQI and temperature in F.
|
||||
|
||||
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||
|
||||
|
||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||
https://www.airgradient.com/
|
||||
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
*/
|
||||
|
||||
#include <WiFiManager.h>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <ESP8266HTTPClient.h>
|
||||
|
||||
#include <SPI.h>
|
||||
|
||||
#include <Adafruit_GFX.h>
|
||||
|
||||
#include <Adafruit_ILI9341.h>
|
||||
|
||||
#include <Fonts/FreeSans9pt7b.h>
|
||||
|
||||
#include <Fonts/FreeSans12pt7b.h>
|
||||
|
||||
#include <Fonts/FreeSans18pt7b.h>
|
||||
|
||||
#define TFT_CS D0
|
||||
#define TFT_DC D8
|
||||
#define TFT_RST - 1
|
||||
#define TS_CS D3
|
||||
|
||||
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
|
||||
|
||||
const char * locNameInside;
|
||||
const char * locNameOutside;
|
||||
|
||||
const char * place_timezone;
|
||||
const char * location;
|
||||
bool outdoor_offline;
|
||||
bool indoor_offline;
|
||||
const char * outdoor_policy;
|
||||
const char * outdoor_date;
|
||||
const char * indoor_date;
|
||||
boolean prodMode = true;
|
||||
|
||||
String deviceID;
|
||||
const char * timex;
|
||||
int pm02;
|
||||
int pi02;
|
||||
int pi02_outside;
|
||||
int rco2;
|
||||
float atmp;
|
||||
float atmp_outside;
|
||||
int rhum_outside;
|
||||
int rhum;
|
||||
int heat;
|
||||
|
||||
const char * pi02_color;
|
||||
const char * pi02_color_outside;
|
||||
const char * pi02_category;
|
||||
const char * pm02_color;
|
||||
const char * pm02_category;
|
||||
const char * rco2_color;
|
||||
const char * rco2_category;
|
||||
const char * heat_color;
|
||||
const char * heat_color_outside;
|
||||
const char * heat_category;
|
||||
|
||||
// Configuration
|
||||
#define API_ROOT "http://hw.airgradient.com/displays/"
|
||||
boolean inUSaqi = false;
|
||||
boolean inF = false;
|
||||
|
||||
String getDeviceId() {
|
||||
return String(ESP.getChipId(), HEX);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Chip ID");
|
||||
Serial.println(String(ESP.getChipId(), HEX));
|
||||
|
||||
tft.begin();
|
||||
tft.setRotation(2);
|
||||
while (!Serial && (millis() <= 1000));
|
||||
welcomeMessage();
|
||||
connectToWifi();
|
||||
|
||||
Serial.print("Connecting");
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
tft.fillScreen(ILI9341_BLACK);
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
http.begin(client, API_ROOT + getDeviceId());
|
||||
|
||||
int httpCode = http.GET();
|
||||
if (httpCode == 200) {
|
||||
String airData = http.getString();
|
||||
payloadToDataInside(airData);
|
||||
Serial.print("airData1 : ");
|
||||
Serial.println(airData);
|
||||
} else {
|
||||
Serial.println("error");
|
||||
Serial.println(httpCode);
|
||||
}
|
||||
http.end();
|
||||
|
||||
delay(1000);
|
||||
updateDisplay();
|
||||
|
||||
delay(120000);
|
||||
tft.fillScreen(ILI9341_BLACK);
|
||||
tft.setTextColor(ILI9341_WHITE);
|
||||
tft.setFont( & FreeSans12pt7b);
|
||||
tft.setCursor(5, 20);
|
||||
tft.println("requesting data...");
|
||||
}
|
||||
|
||||
void payloadToDataInside(String payload) {
|
||||
const size_t capacity = JSON_ARRAY_SIZE(1) + 2 * JSON_OBJECT_SIZE(2) + 2 * JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(10) + JSON_OBJECT_SIZE(13) + 530;
|
||||
DynamicJsonBuffer jsonBuffer(capacity);
|
||||
JsonObject & root = jsonBuffer.parseObject(payload);
|
||||
location = root["place"]["name"];
|
||||
place_timezone = root["place"]["timezone"];
|
||||
JsonObject & outdoor = root["outdoor"];
|
||||
locNameOutside = outdoor["name"];
|
||||
outdoor_offline = outdoor["offline"];
|
||||
outdoor_policy = outdoor["guidelines"][0]["title"];
|
||||
JsonObject & outdoor_current = outdoor["current"];
|
||||
|
||||
atmp_outside = outdoor_current["atmp"];
|
||||
rhum_outside = outdoor_current["rhum"];
|
||||
outdoor_date = outdoor_current["date"];
|
||||
JsonObject & indoor = root["indoor"];
|
||||
locNameInside = indoor["name"];
|
||||
indoor_offline = indoor["offline"];
|
||||
JsonObject & indoor_current = indoor["current"];
|
||||
|
||||
atmp = indoor_current["atmp"];
|
||||
rhum = indoor_current["rhum"];
|
||||
rco2 = indoor_current["rco2"];
|
||||
indoor_date = indoor_current["date"];
|
||||
rco2_color = indoor_current["rco2_clr"];
|
||||
rco2_category = indoor_current["rco2_lbl"];
|
||||
|
||||
if (inUSaqi) {
|
||||
pi02_outside = outdoor_current["pi02"];
|
||||
pi02_color_outside = outdoor_current["pi02_clr"];
|
||||
pi02_category = outdoor_current["pi02_lbl"];
|
||||
pi02 = indoor_current["pi02"];
|
||||
pi02_color = indoor_current["pi02_clr"];
|
||||
pi02_category = indoor_current["pi02_lbl"];
|
||||
} else {
|
||||
pi02_outside = outdoor_current["pm02"];
|
||||
pi02_color_outside = outdoor_current["pm02_clr"];
|
||||
pi02_category = outdoor_current["pm02_lbl"];
|
||||
pi02 = indoor_current["pm02"];
|
||||
pi02_color = indoor_current["pm02_clr"];
|
||||
pi02_category = indoor_current["pm02_lbl"];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void updateDisplay() {
|
||||
int y = 25;
|
||||
int boxHeight = 75;
|
||||
int boxWidth = 110;
|
||||
int radius = 8;
|
||||
tft.fillScreen(ILI9341_BLACK);
|
||||
|
||||
tft.setFont( & FreeSans9pt7b);
|
||||
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
|
||||
tft.setCursor(5, y);
|
||||
tft.println(location);
|
||||
|
||||
tft.drawLine(0, 35, 250, 35, ILI9341_WHITE);
|
||||
|
||||
y = y + 50;
|
||||
|
||||
tft.setFont( & FreeSans9pt7b);
|
||||
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
|
||||
tft.setCursor(5, y);
|
||||
tft.println(locNameOutside);
|
||||
tft.setFont( & FreeSans12pt7b);
|
||||
|
||||
y = y + 12;
|
||||
|
||||
if (String(pi02_color_outside) == "green") {
|
||||
tft.fillRoundRect(5, y, boxWidth, boxHeight, radius, ILI9341_GREEN);
|
||||
} else if (String(pi02_color_outside) == "yellow") {
|
||||
tft.fillRoundRect(5, y, boxWidth, boxHeight, radius, ILI9341_YELLOW);
|
||||
} else if (String(pi02_color_outside) == "orange") {
|
||||
tft.fillRoundRect(5, y, boxWidth, boxHeight, radius, ILI9341_ORANGE);
|
||||
} else if (String(pi02_color_outside) == "red") {
|
||||
tft.fillRoundRect(5, y, boxWidth, boxHeight, radius, ILI9341_RED);
|
||||
} else if (String(pi02_color_outside) == "purple") {
|
||||
tft.fillRoundRect(5, y, boxWidth, boxHeight, radius, ILI9341_PURPLE);
|
||||
} else if (String(pi02_color_outside) == "brown") {
|
||||
tft.fillRoundRect(5, y, boxWidth, boxHeight, radius, ILI9341_MAROON);
|
||||
}
|
||||
|
||||
if (String(heat_color_outside) == "green") {
|
||||
tft.fillRoundRect(5 + boxWidth + 10, y, boxWidth, boxHeight, radius, ILI9341_GREEN);
|
||||
} else if (String(heat_color_outside) == "yellow") {
|
||||
tft.fillRoundRect(5 + boxWidth + 10, y, boxWidth, boxHeight, radius, ILI9341_YELLOW);
|
||||
} else if (String(heat_color_outside) == "orange") {
|
||||
tft.fillRoundRect(5 + boxWidth + 10, y, boxWidth, boxHeight, radius, ILI9341_ORANGE);
|
||||
} else if (String(heat_color_outside) == "red") {
|
||||
tft.fillRoundRect(5 + boxWidth + 10, y, boxWidth, boxHeight, radius, ILI9341_RED);
|
||||
} else if (String(heat_color_outside) == "purple") {
|
||||
tft.fillRoundRect(5 + boxWidth + 10, y, boxWidth, boxHeight, radius, ILI9341_PURPLE);
|
||||
} else if (String(heat_color_outside) == "brown") {
|
||||
tft.fillRoundRect(5 + boxWidth + 10, y, boxWidth, boxHeight, radius, ILI9341_MAROON);
|
||||
}
|
||||
|
||||
tft.setFont( & FreeSans9pt7b);
|
||||
tft.setTextColor(ILI9341_BLACK, ILI9341_BLACK);
|
||||
tft.setCursor(20, y + boxHeight - 10);
|
||||
|
||||
if (inUSaqi) {
|
||||
tft.println("US AQI");
|
||||
} else {
|
||||
tft.println("ug/m3");
|
||||
}
|
||||
|
||||
tft.setFont( & FreeSans18pt7b);
|
||||
tft.setTextColor(ILI9341_BLACK, ILI9341_BLACK);
|
||||
tft.setCursor(20, y + 40);
|
||||
tft.println(String(pi02_outside));
|
||||
|
||||
tft.setFont( & FreeSans9pt7b);
|
||||
|
||||
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
|
||||
|
||||
tft.setCursor(20 + boxWidth + 10, y + 20);
|
||||
|
||||
if (inF) {
|
||||
tft.println(String((atmp_outside * 9 / 5) + 32) + "F");
|
||||
} else {
|
||||
tft.println(String(atmp_outside) + "C");
|
||||
}
|
||||
|
||||
tft.setCursor(20 + boxWidth + 10, y + 40);
|
||||
tft.println(String(rhum_outside) + "%");
|
||||
|
||||
tft.setTextColor(ILI9341_DARKGREY, ILI9341_BLACK);
|
||||
tft.setCursor(20 + boxWidth + 10, y + 60);
|
||||
tft.println(String(outdoor_date));
|
||||
|
||||
//inside
|
||||
|
||||
y = y + 110;
|
||||
|
||||
tft.setFont( & FreeSans9pt7b);
|
||||
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
|
||||
tft.setCursor(5, y);
|
||||
tft.println(locNameInside);
|
||||
tft.setFont( & FreeSans12pt7b);
|
||||
|
||||
y = y + 12;
|
||||
|
||||
if (String(pi02_color) == "green") {
|
||||
tft.fillRoundRect(5, y, boxWidth, boxHeight, radius, ILI9341_GREEN);
|
||||
} else if (String(pi02_color) == "yellow") {
|
||||
tft.fillRoundRect(5, y, boxWidth, boxHeight, radius, ILI9341_YELLOW);
|
||||
} else if (String(pi02_color) == "orange") {
|
||||
tft.fillRoundRect(5, y, boxWidth, boxHeight, radius, ILI9341_ORANGE);
|
||||
} else if (String(pi02_color) == "red") {
|
||||
tft.fillRoundRect(5, y, boxWidth, boxHeight, radius, ILI9341_RED);
|
||||
} else if (String(pi02_color) == "purple") {
|
||||
tft.fillRoundRect(5, y, boxWidth, boxHeight, radius, ILI9341_PURPLE);
|
||||
} else if (String(pi02_color) == "brown") {
|
||||
tft.fillRoundRect(5, y, boxWidth, boxHeight, radius, ILI9341_MAROON);
|
||||
}
|
||||
|
||||
if (String(rco2_color) == "green") {
|
||||
tft.fillRoundRect(5 + boxWidth + 10, y, boxWidth, boxHeight, radius, ILI9341_GREEN);
|
||||
} else if (String(rco2_color) == "yellow") {
|
||||
tft.fillRoundRect(5 + boxWidth + 10, y, boxWidth, boxHeight, radius, ILI9341_YELLOW);
|
||||
} else if (String(rco2_color) == "orange") {
|
||||
tft.fillRoundRect(5 + boxWidth + 10, y, boxWidth, boxHeight, radius, ILI9341_ORANGE);
|
||||
} else if (String(rco2_color) == "red") {
|
||||
tft.fillRoundRect(5 + boxWidth + 10, y, boxWidth, boxHeight, radius, ILI9341_RED);
|
||||
} else if (String(rco2_color) == "purple") {
|
||||
tft.fillRoundRect(5 + boxWidth + 10, y, boxWidth, boxHeight, radius, ILI9341_PURPLE);
|
||||
} else if (String(rco2_color) == "brown") {
|
||||
tft.fillRoundRect(5 + boxWidth + 10, y, boxWidth, boxHeight, radius, ILI9341_MAROON);
|
||||
}
|
||||
|
||||
tft.setFont( & FreeSans9pt7b);
|
||||
tft.setTextColor(ILI9341_BLACK, ILI9341_BLACK);
|
||||
tft.setCursor(20, y + boxHeight - 10);
|
||||
|
||||
if (inUSaqi) {
|
||||
tft.println("US AQI");
|
||||
} else {
|
||||
tft.println("ug/m3");
|
||||
}
|
||||
|
||||
tft.setCursor(20 + boxWidth + 10, y + boxHeight - 10);
|
||||
tft.println("CO2 ppm");
|
||||
|
||||
tft.setFont( & FreeSans18pt7b);
|
||||
tft.setTextColor(ILI9341_BLACK, ILI9341_BLACK);
|
||||
tft.setCursor(20, y + 40);
|
||||
tft.println(String(pi02));
|
||||
tft.setCursor(20 + boxWidth + 10, y + 40);
|
||||
tft.println(String(rco2));
|
||||
|
||||
y = y + 100;
|
||||
|
||||
tft.setFont( & FreeSans9pt7b);
|
||||
tft.setTextColor(ILI9341_DARKGREY, ILI9341_BLACK);
|
||||
tft.setCursor(boxWidth - 30, y);
|
||||
tft.println(String(indoor_date));
|
||||
}
|
||||
|
||||
void welcomeMessage() {
|
||||
Serial.println("Welcome Message 2");
|
||||
tft.setFont( & FreeSans9pt7b);
|
||||
tft.fillScreen(ILI9341_BLACK);
|
||||
tft.setTextColor(ILI9341_WHITE);
|
||||
|
||||
tft.setCursor(40, 24);
|
||||
tft.setFont( & FreeSans12pt7b);
|
||||
tft.setCursor(5, 20);
|
||||
tft.println("AirGradient");
|
||||
|
||||
tft.setFont( & FreeSans9pt7b);
|
||||
tft.setCursor(5, 100);
|
||||
tft.println("id: " + String(ESP.getChipId(), HEX));
|
||||
|
||||
tft.setCursor(5, 140);
|
||||
tft.println("connecting ...");
|
||||
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
void connectToWifi() {
|
||||
delay(2000);
|
||||
|
||||
WiFiManager wifiManager;
|
||||
//chWiFi.disconnect(); //to delete previous saved hotspot
|
||||
String HOTSPOT = "AIRGRADIENT-DISPLAY-" + String(ESP.getChipId(), HEX);
|
||||
wifiManager.setTimeout(120);
|
||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||
Serial.println("failed to connect and hit timeout");
|
||||
delay(3000);
|
||||
ESP.restart();
|
||||
delay(5000);
|
||||
}
|
||||
}
|
722
examples/ONE_V9/ONE_V9.ino
Normal file
722
examples/ONE_V9/ONE_V9.ino
Normal file
@ -0,0 +1,722 @@
|
||||
/*
|
||||
Important: This code is only for the DIY PRO / AirGradient ONE PCB Version 9 with the ESP-C3 MCU.
|
||||
|
||||
It is a high quality sensor showing PM2.5, CO2, TVOC, NOx, Temperature and Humidity on a small display and LEDbar and can send data over Wifi.
|
||||
|
||||
Build Instructions: https://www.airgradient.com/open-airgradient/instructions/
|
||||
|
||||
Kits (including a pre-soldered version) are available: https://www.airgradient.com/indoor/
|
||||
|
||||
The codes needs the following libraries installed:
|
||||
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
|
||||
“U8g2” by oliver tested with version 2.32.15
|
||||
"Sensirion I2C SGP41" by Sensation Version 0.1.0
|
||||
"Sensirion Gas Index Algorithm" by Sensation Version 3.2.1
|
||||
"Arduino-SHT" by Johannes Winkelmann Version 1.2.2
|
||||
"Adafruit NeoPixel" by Adafruit Version 1.11.0
|
||||
|
||||
Configuration:
|
||||
Please set in the code below the configuration parameters.
|
||||
|
||||
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||
|
||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||
https://www.airgradient.com/
|
||||
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
|
||||
*/
|
||||
|
||||
#include "PMS.h"
|
||||
|
||||
#include <HardwareSerial.h>
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
#include "s8_uart.h"
|
||||
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#include <WiFiManager.h>
|
||||
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
|
||||
#include <EEPROM.h>
|
||||
|
||||
#include "SHTSensor.h"
|
||||
|
||||
#include <SensirionI2CSgp41.h>
|
||||
|
||||
#include <NOxGasIndexAlgorithm.h>
|
||||
|
||||
#include <VOCGasIndexAlgorithm.h>
|
||||
|
||||
#include <U8g2lib.h>
|
||||
|
||||
#define DEBUG true
|
||||
|
||||
#define I2C_SDA 7
|
||||
#define I2C_SCL 6
|
||||
|
||||
HTTPClient client;
|
||||
|
||||
Adafruit_NeoPixel pixels(11, 10, NEO_GRB + NEO_KHZ800);
|
||||
SensirionI2CSgp41 sgp41;
|
||||
VOCGasIndexAlgorithm voc_algorithm;
|
||||
NOxGasIndexAlgorithm nox_algorithm;
|
||||
SHTSensor sht;
|
||||
|
||||
PMS pms1(Serial0);
|
||||
|
||||
PMS::DATA data1;
|
||||
|
||||
S8_UART * sensor_S8;
|
||||
S8_sensor sensor;
|
||||
|
||||
// time in seconds needed for NOx conditioning
|
||||
uint16_t conditioning_s = 10;
|
||||
|
||||
// for peristent saving and loading
|
||||
int addr = 4;
|
||||
byte value;
|
||||
|
||||
// Display bottom right
|
||||
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
|
||||
|
||||
String APIROOT = "http://hw.airgradient.com/";
|
||||
|
||||
// set to true to switch from Celcius to Fahrenheit
|
||||
boolean inF = false;
|
||||
|
||||
// PM2.5 in US AQI (default ug/m3)
|
||||
boolean inUSAQI = false;
|
||||
|
||||
// Display Position
|
||||
boolean displayTop = true;
|
||||
|
||||
// use RGB LED Bar
|
||||
boolean useRGBledBar = true;
|
||||
|
||||
// set to true if you want to connect to wifi. You have 60 seconds to connect. Then it will go into an offline mode.
|
||||
boolean connectWIFI = true;
|
||||
|
||||
int loopCount = 0;
|
||||
|
||||
unsigned long currentMillis = 0;
|
||||
|
||||
const int oledInterval = 5000;
|
||||
unsigned long previousOled = 0;
|
||||
|
||||
const int sendToServerInterval = 10000;
|
||||
unsigned long previoussendToServer = 0;
|
||||
|
||||
const int tvocInterval = 1000;
|
||||
unsigned long previousTVOC = 0;
|
||||
int TVOC = -1;
|
||||
int NOX = -1;
|
||||
|
||||
const int co2Interval = 5000;
|
||||
unsigned long previousCo2 = 0;
|
||||
int Co2 = 0;
|
||||
|
||||
const int pmInterval = 5000;
|
||||
unsigned long previousPm = 0;
|
||||
int pm25 = -1;
|
||||
int pm01 = -1;
|
||||
int pm10 = -1;
|
||||
int pm03PCount = -1;
|
||||
|
||||
const int tempHumInterval = 5000;
|
||||
unsigned long previousTempHum = 0;
|
||||
float temp;
|
||||
int hum;
|
||||
|
||||
int buttonConfig = 0;
|
||||
int lastState = LOW;
|
||||
int currentState;
|
||||
unsigned long pressedTime = 0;
|
||||
unsigned long releasedTime = 0;
|
||||
|
||||
void setup() {
|
||||
if (DEBUG) {
|
||||
Serial.begin(115200);
|
||||
// see https://github.com/espressif/arduino-esp32/issues/6983
|
||||
Serial.setTxTimeoutMs(0); // <<<====== solves the delay issue
|
||||
}
|
||||
|
||||
Wire.begin(I2C_SDA, I2C_SCL);
|
||||
pixels.begin();
|
||||
pixels.clear();
|
||||
|
||||
Serial1.begin(9600, SERIAL_8N1, 0, 1);
|
||||
Serial0.begin(9600);
|
||||
u8g2.begin();
|
||||
|
||||
updateOLED2("Warming Up", "Serial Number:", String(getNormalizedMac()));
|
||||
sgp41.begin(Wire);
|
||||
delay(300);
|
||||
|
||||
sht.init(Wire);
|
||||
//sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM);
|
||||
delay(300);
|
||||
|
||||
//init Watchdog
|
||||
pinMode(2, OUTPUT);
|
||||
digitalWrite(2, LOW);
|
||||
|
||||
sensor_S8 = new S8_UART(Serial1);
|
||||
|
||||
EEPROM.begin(512);
|
||||
delay(500);
|
||||
|
||||
// push button
|
||||
pinMode(9, INPUT_PULLUP);
|
||||
|
||||
buttonConfig = String(EEPROM.read(addr)).toInt();
|
||||
if (buttonConfig > 3) buttonConfig = 0;
|
||||
delay(400);
|
||||
setConfig();
|
||||
Serial.println("buttonConfig: " + String(buttonConfig));
|
||||
|
||||
updateOLED2("Press Button", "Now for", "LED Test");
|
||||
delay(2000);
|
||||
currentState = digitalRead(9);
|
||||
if (currentState == LOW) {
|
||||
ledTest();
|
||||
}
|
||||
|
||||
updateOLED2("Press Button", "Now for", "Config Menu");
|
||||
delay(2000);
|
||||
currentState = digitalRead(9);
|
||||
if (currentState == LOW) {
|
||||
updateOLED2("Entering", "Config Menu", "");
|
||||
delay(3000);
|
||||
lastState = HIGH;
|
||||
setConfig();
|
||||
inConf();
|
||||
}
|
||||
|
||||
if (connectWIFI) {
|
||||
WiFi.begin("airgradient", "cleanair");
|
||||
int retries = 0;
|
||||
while ((WiFi.status() != WL_CONNECTED) && (retries < 15)) {
|
||||
retries++;
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
if (retries > 14) {
|
||||
Serial.println(F("WiFi connection to SSID airgradient failed"));
|
||||
if (connectWIFI) connectToWifi();
|
||||
}
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
if (WiFi.SSID() == "airgradient") {
|
||||
ledTest();
|
||||
}
|
||||
sendPing();
|
||||
Serial.println(F("WiFi connected!"));
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
}
|
||||
}
|
||||
updateOLED2("Warming Up", "Serial Number:", String(getNormalizedMac()));
|
||||
}
|
||||
|
||||
void loop() {
|
||||
currentMillis = millis();
|
||||
updateTVOC();
|
||||
updateOLED();
|
||||
updateCo2();
|
||||
updatePm();
|
||||
updateTempHum();
|
||||
sendToServer();
|
||||
}
|
||||
|
||||
void ledTest() {
|
||||
updateOLED2("LED Test", "running", ".....");
|
||||
setRGBledColor('r');
|
||||
delay(1000);
|
||||
setRGBledColor('g');
|
||||
delay(1000);
|
||||
setRGBledColor('b');
|
||||
delay(1000);
|
||||
setRGBledColor('w');
|
||||
delay(1000);
|
||||
setRGBledColor('n');
|
||||
delay(1000);
|
||||
//LED Test
|
||||
}
|
||||
|
||||
void updateTVOC() {
|
||||
uint16_t error;
|
||||
char errorMessage[256];
|
||||
uint16_t defaultRh = 0x8000;
|
||||
uint16_t defaultT = 0x6666;
|
||||
uint16_t srawVoc = 0;
|
||||
uint16_t srawNox = 0;
|
||||
uint16_t defaultCompenstaionRh = 0x8000; // in ticks as defined by SGP41
|
||||
uint16_t defaultCompenstaionT = 0x6666; // in ticks as defined by SGP41
|
||||
uint16_t compensationRh = 0; // in ticks as defined by SGP41
|
||||
uint16_t compensationT = 0; // in ticks as defined by SGP41
|
||||
|
||||
delay(1000);
|
||||
|
||||
compensationT = static_cast < uint16_t > ((temp + 45) * 65535 / 175);
|
||||
compensationRh = static_cast < uint16_t > (hum * 65535 / 100);
|
||||
|
||||
if (conditioning_s > 0) {
|
||||
error = sgp41.executeConditioning(compensationRh, compensationT, srawVoc);
|
||||
conditioning_s--;
|
||||
} else {
|
||||
error = sgp41.measureRawSignals(compensationRh, compensationT, srawVoc,
|
||||
srawNox);
|
||||
}
|
||||
|
||||
if (currentMillis - previousTVOC >= tvocInterval) {
|
||||
previousTVOC += tvocInterval;
|
||||
if (error) {
|
||||
TVOC = -1;
|
||||
NOX = -1;
|
||||
Serial.println(String(TVOC));
|
||||
} else {
|
||||
TVOC = voc_algorithm.process(srawVoc);
|
||||
NOX = nox_algorithm.process(srawNox);
|
||||
Serial.println(String(TVOC));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void updateCo2() {
|
||||
if (currentMillis - previousCo2 >= co2Interval) {
|
||||
previousCo2 += co2Interval;
|
||||
Co2 = sensor_S8 -> get_co2();
|
||||
Serial.println(String(Co2));
|
||||
}
|
||||
}
|
||||
|
||||
void updatePm() {
|
||||
if (currentMillis - previousPm >= pmInterval) {
|
||||
previousPm += pmInterval;
|
||||
if (pms1.readUntil(data1, 2000)) {
|
||||
pm01 = data1.PM_AE_UG_1_0;
|
||||
pm25 = data1.PM_AE_UG_2_5;
|
||||
pm10 = data1.PM_AE_UG_10_0;
|
||||
pm03PCount = data1.PM_RAW_0_3;
|
||||
} else {
|
||||
pm01 = -1;
|
||||
pm25 = -1;
|
||||
pm10 = -1;
|
||||
pm03PCount = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateTempHum() {
|
||||
if (currentMillis - previousTempHum >= tempHumInterval) {
|
||||
previousTempHum += tempHumInterval;
|
||||
|
||||
if (sht.readSample()) {
|
||||
temp = sht.getTemperature();
|
||||
hum = sht.getHumidity();
|
||||
} else {
|
||||
Serial.print("Error in readSample()\n");
|
||||
temp = -10001;
|
||||
hum = -10001;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateOLED() {
|
||||
if (currentMillis - previousOled >= oledInterval) {
|
||||
previousOled += oledInterval;
|
||||
|
||||
String ln3;
|
||||
String ln1;
|
||||
|
||||
if (inUSAQI) {
|
||||
ln1 = "AQI:" + String(PM_TO_AQI_US(pm25)) + " CO2:" + String(Co2);
|
||||
} else {
|
||||
ln1 = "PM:" + String(pm25) + " CO2:" + String(Co2);
|
||||
}
|
||||
|
||||
String ln2 = "TVOC:" + String(TVOC) + " NOX:" + String(NOX);
|
||||
|
||||
if (inF) {
|
||||
ln3 = "F:" + String((temp * 9 / 5) + 32) + " H:" + String(hum) + "%";
|
||||
} else {
|
||||
ln3 = "C:" + String(temp) + " H:" + String(hum) + "%";
|
||||
}
|
||||
//updateOLED2(ln1, ln2, ln3);
|
||||
updateOLED3();
|
||||
setRGBledCO2color(Co2);
|
||||
}
|
||||
}
|
||||
|
||||
void inConf() {
|
||||
setConfig();
|
||||
currentState = digitalRead(9);
|
||||
|
||||
if (currentState) {
|
||||
Serial.println("currentState: high");
|
||||
} else {
|
||||
Serial.println("currentState: low");
|
||||
}
|
||||
|
||||
if (lastState == HIGH && currentState == LOW) {
|
||||
pressedTime = millis();
|
||||
} else if (lastState == LOW && currentState == HIGH) {
|
||||
releasedTime = millis();
|
||||
long pressDuration = releasedTime - pressedTime;
|
||||
if (pressDuration < 1000) {
|
||||
buttonConfig = buttonConfig + 1;
|
||||
if (buttonConfig > 3) buttonConfig = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastState == LOW && currentState == LOW) {
|
||||
long passedDuration = millis() - pressedTime;
|
||||
if (passedDuration > 4000) {
|
||||
updateOLED2("Saved", "Release", "Button Now");
|
||||
delay(1000);
|
||||
updateOLED2("Rebooting", "in", "5 seconds");
|
||||
delay(5000);
|
||||
EEPROM.write(addr, char(buttonConfig));
|
||||
EEPROM.commit();
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
}
|
||||
lastState = currentState;
|
||||
delay(100);
|
||||
inConf();
|
||||
}
|
||||
|
||||
void setConfig() {
|
||||
Serial.println("in setConfig");
|
||||
if (buttonConfig == 0) {
|
||||
updateOLED2("Temp. in C", "PM in ug/m3", "Long Press Saves");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = false;
|
||||
inUSAQI = false;
|
||||
}
|
||||
if (buttonConfig == 1) {
|
||||
updateOLED2("Temp. in C", "PM in US AQI", "Long Press Saves");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = false;
|
||||
inUSAQI = true;
|
||||
} else if (buttonConfig == 2) {
|
||||
updateOLED2("Temp. in F", "PM in ug/m3", "Long Press Saves");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = true;
|
||||
inUSAQI = false;
|
||||
} else if (buttonConfig == 3) {
|
||||
updateOLED2("Temp. in F", "PM in US AQI", "Long Press Saves");
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
inF = true;
|
||||
inUSAQI = true;
|
||||
}
|
||||
}
|
||||
|
||||
void sendPing() {
|
||||
String payload = "{\"wifi\":" + String(WiFi.RSSI()) +
|
||||
", \"boot\":" + loopCount +
|
||||
"}";
|
||||
}
|
||||
|
||||
void updateOLED2(String ln1, String ln2, String ln3) {
|
||||
char buf[9];
|
||||
u8g2.firstPage();
|
||||
u8g2.firstPage();
|
||||
do {
|
||||
u8g2.setFont(u8g2_font_t0_16_tf);
|
||||
u8g2.drawStr(1, 10, String(ln1).c_str());
|
||||
u8g2.drawStr(1, 30, String(ln2).c_str());
|
||||
u8g2.drawStr(1, 50, String(ln3).c_str());
|
||||
} while (u8g2.nextPage());
|
||||
}
|
||||
|
||||
void updateOLED3() {
|
||||
char buf[9];
|
||||
u8g2.firstPage();
|
||||
u8g2.firstPage();
|
||||
do {
|
||||
|
||||
u8g2.setFont(u8g2_font_t0_16_tf);
|
||||
|
||||
if (inF) {
|
||||
if (temp > -10001) {
|
||||
float tempF = (temp * 9 / 5) + 32;
|
||||
sprintf(buf, "%.1f°F", tempF);
|
||||
} else {
|
||||
sprintf(buf, "-°F");
|
||||
}
|
||||
u8g2.drawUTF8(1, 10, buf);
|
||||
} else {
|
||||
if (temp > -10001) {
|
||||
sprintf(buf, "%.1f°C", temp);
|
||||
} else {
|
||||
sprintf(buf, "-°C");
|
||||
}
|
||||
u8g2.drawUTF8(1, 10, buf);
|
||||
}
|
||||
|
||||
if (hum >= 0) {
|
||||
sprintf(buf, "%d%%", hum);
|
||||
} else {
|
||||
sprintf(buf, " -%%");
|
||||
}
|
||||
if (hum > 99) {
|
||||
u8g2.drawStr(97, 10, buf);
|
||||
} else {
|
||||
u8g2.drawStr(105, 10, buf);
|
||||
// there might also be single digits, not considered, sprintf might actually support a leading space
|
||||
}
|
||||
|
||||
u8g2.drawLine(1, 13, 128, 13);
|
||||
u8g2.setFont(u8g2_font_t0_12_tf);
|
||||
u8g2.drawUTF8(1, 27, "CO2");
|
||||
u8g2.setFont(u8g2_font_t0_22b_tf);
|
||||
if (Co2 > 0) {
|
||||
sprintf(buf, "%d", Co2);
|
||||
} else {
|
||||
sprintf(buf, "%s", "-");
|
||||
}
|
||||
u8g2.drawStr(1, 48, buf);
|
||||
u8g2.setFont(u8g2_font_t0_12_tf);
|
||||
u8g2.drawStr(1, 61, "ppm");
|
||||
u8g2.drawLine(45, 15, 45, 64);
|
||||
u8g2.setFont(u8g2_font_t0_12_tf);
|
||||
u8g2.drawStr(48, 27, "PM2.5");
|
||||
u8g2.setFont(u8g2_font_t0_22b_tf);
|
||||
|
||||
if (inUSAQI) {
|
||||
if (pm25 >= 0) {
|
||||
sprintf(buf, "%d", PM_TO_AQI_US(pm25));
|
||||
} else {
|
||||
sprintf(buf, "%s", "-");
|
||||
}
|
||||
u8g2.drawStr(48, 48, buf);
|
||||
u8g2.setFont(u8g2_font_t0_12_tf);
|
||||
u8g2.drawUTF8(48, 61, "AQI");
|
||||
} else {
|
||||
if (pm25 >= 0) {
|
||||
sprintf(buf, "%d", pm25);
|
||||
} else {
|
||||
sprintf(buf, "%s", "-");
|
||||
}
|
||||
u8g2.drawStr(48, 48, buf);
|
||||
u8g2.setFont(u8g2_font_t0_12_tf);
|
||||
u8g2.drawUTF8(48, 61, "ug/m³");
|
||||
}
|
||||
|
||||
u8g2.drawLine(82, 15, 82, 64);
|
||||
u8g2.setFont(u8g2_font_t0_12_tf);
|
||||
u8g2.drawStr(85, 27, "TVOC:");
|
||||
if (TVOC >= 0) {
|
||||
sprintf(buf, "%d", TVOC);
|
||||
} else {
|
||||
sprintf(buf, "%s", "-");
|
||||
}
|
||||
u8g2.drawStr(85, 39, buf);
|
||||
u8g2.drawStr(85, 53, "NOx:");
|
||||
if (NOX >= 0) {
|
||||
sprintf(buf, "%d", NOX);
|
||||
} else {
|
||||
sprintf(buf, "%s", "-");
|
||||
}
|
||||
u8g2.drawStr(85, 63, buf);
|
||||
|
||||
} while (u8g2.nextPage());
|
||||
}
|
||||
|
||||
void sendToServer() {
|
||||
if (currentMillis - previoussendToServer >= sendToServerInterval) {
|
||||
previoussendToServer += sendToServerInterval;
|
||||
String payload = "{\"wifi\":" + String(WiFi.RSSI()) +
|
||||
(Co2 < 0 ? "" : ", \"rco2\":" + String(Co2)) +
|
||||
(pm01 < 0 ? "" : ", \"pm01\":" + String(pm01)) +
|
||||
(pm25 < 0 ? "" : ", \"pm02\":" + String(pm25)) +
|
||||
(pm10 < 0 ? "" : ", \"pm10\":" + String(pm10)) +
|
||||
(pm03PCount < 0 ? "" : ", \"pm003_count\":" + String(pm03PCount)) +
|
||||
(TVOC < 0 ? "" : ", \"tvoc_index\":" + String(TVOC)) +
|
||||
(NOX < 0 ? "" : ", \"nox_index\":" + String(NOX)) +
|
||||
", \"atmp\":" + String(temp) +
|
||||
(hum < 0 ? "" : ", \"rhum\":" + String(hum)) +
|
||||
", \"boot\":" + loopCount +
|
||||
"}";
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
Serial.println(payload);
|
||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(getNormalizedMac()) + "/measures";
|
||||
Serial.println(POSTURL);
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
http.begin(client, POSTURL);
|
||||
http.addHeader("content-type", "application/json");
|
||||
int httpCode = http.POST(payload);
|
||||
String response = http.getString();
|
||||
Serial.println(httpCode);
|
||||
Serial.println(response);
|
||||
http.end();
|
||||
resetWatchdog();
|
||||
loopCount++;
|
||||
} else {
|
||||
Serial.println("WiFi Disconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void countdown(int from) {
|
||||
debug("\n");
|
||||
while (from > 0) {
|
||||
debug(String(from--));
|
||||
debug(" ");
|
||||
delay(1000);
|
||||
}
|
||||
debug("\n");
|
||||
}
|
||||
|
||||
void resetWatchdog() {
|
||||
Serial.println("Watchdog reset");
|
||||
digitalWrite(2, HIGH);
|
||||
delay(20);
|
||||
digitalWrite(2, LOW);
|
||||
}
|
||||
|
||||
// Wifi Manager
|
||||
void connectToWifi() {
|
||||
WiFiManager wifiManager;
|
||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||
String HOTSPOT = "AG-" + String(getNormalizedMac());
|
||||
updateOLED2("180s to connect", "to Wifi Hotspot", HOTSPOT);
|
||||
wifiManager.setTimeout(180);
|
||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||
Serial.println("failed to connect and hit timeout");
|
||||
delay(6000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void debug(String msg) {
|
||||
if (DEBUG)
|
||||
Serial.print(msg);
|
||||
}
|
||||
|
||||
void debug(int msg) {
|
||||
if (DEBUG)
|
||||
Serial.print(msg);
|
||||
}
|
||||
|
||||
void debugln(String msg) {
|
||||
if (DEBUG)
|
||||
Serial.println(msg);
|
||||
}
|
||||
|
||||
void debugln(int msg) {
|
||||
if (DEBUG)
|
||||
Serial.println(msg);
|
||||
}
|
||||
|
||||
String getNormalizedMac() {
|
||||
String mac = WiFi.macAddress();
|
||||
mac.replace(":", "");
|
||||
mac.toLowerCase();
|
||||
return mac;
|
||||
}
|
||||
|
||||
void setRGBledCO2color(int co2Value) {
|
||||
if (co2Value >= 300 && co2Value < 800) setRGBledColor('g');
|
||||
if (co2Value >= 800 && co2Value < 1000) setRGBledColor('y');
|
||||
if (co2Value >= 1000 && co2Value < 1500) setRGBledColor('o');
|
||||
if (co2Value >= 1500 && co2Value < 2000) setRGBledColor('r');
|
||||
if (co2Value >= 2000 && co2Value < 3000) setRGBledColor('p');
|
||||
if (co2Value >= 3000 && co2Value < 10000) setRGBledColor('z');
|
||||
}
|
||||
|
||||
void setRGBledColor(char color) {
|
||||
if (useRGBledBar) {
|
||||
//pixels.clear();
|
||||
switch (color) {
|
||||
case 'g':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(0, 255, 0));
|
||||
delay(100);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(255, 255, 0));
|
||||
delay(100);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(255, 128, 0));
|
||||
delay(100);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(255, 0, 0));
|
||||
delay(100);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(0, 0, 255));
|
||||
delay(100);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(255, 255, 255));
|
||||
delay(100);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(153, 0, 153));
|
||||
delay(100);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'z':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(102, 0, 0));
|
||||
delay(100);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(0, 0, 0));
|
||||
delay(100);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// if nothing else matches, do the default
|
||||
// default is optional
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate PM2.5 US AQI
|
||||
int PM_TO_AQI_US(int pm02) {
|
||||
if (pm02 <= 12.0) return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0);
|
||||
else if (pm02 <= 35.4) return ((100 - 50) / (35.4 - 12.0) * (pm02 - 12.0) + 50);
|
||||
else if (pm02 <= 55.4) return ((150 - 100) / (55.4 - 35.4) * (pm02 - 35.4) + 100);
|
||||
else if (pm02 <= 150.4) return ((200 - 150) / (150.4 - 55.4) * (pm02 - 55.4) + 150);
|
||||
else if (pm02 <= 250.4) return ((300 - 200) / (250.4 - 150.4) * (pm02 - 150.4) + 200);
|
||||
else if (pm02 <= 350.4) return ((400 - 300) / (350.4 - 250.4) * (pm02 - 250.4) + 300);
|
||||
else if (pm02 <= 500.4) return ((500 - 400) / (500.4 - 350.4) * (pm02 - 350.4) + 400);
|
||||
else return 500;
|
||||
};
|
54
examples/PM2_SIMPLE/PM2_SIMPLE.ino
Normal file
54
examples/PM2_SIMPLE/PM2_SIMPLE.ino
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
This is the code for the AirGradient DIY Air Quality Sensor with an ESP8266 Microcontroller.
|
||||
|
||||
It is a high quality sensor showing PM2.5, CO2, Temperature and Humidity on a small display and can send data over Wifi.
|
||||
|
||||
For build instructions please visit https://www.airgradient.com/open-airgradient/instructions/
|
||||
|
||||
Compatible with the following sensors:
|
||||
Plantower PMS5003 (Fine Particle Sensor)
|
||||
|
||||
Please install ESP8266 board manager (tested with version 3.0.0)
|
||||
|
||||
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||
|
||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||
https://www.airgradient.com/
|
||||
|
||||
Kits with all required components are available at https://www.airgradient.com/open-airgradient/shop/
|
||||
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
*/
|
||||
|
||||
#include <AirGradient.h>
|
||||
|
||||
AirGradient ag = AirGradient();
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
ag.PMS_Init();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
int PM2 = ag.getPM2_Raw();
|
||||
|
||||
Serial.print("PM2.5 in ug/m3: ");
|
||||
Serial.println(String(PM2));
|
||||
|
||||
Serial.print("PM2.5 in US AQI: ");
|
||||
Serial.println(String(PM_TO_AQI_US(PM2)));
|
||||
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
int PM_TO_AQI_US(int pm02) {
|
||||
if (pm02 <= 12.0) return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0);
|
||||
else if (pm02 <= 35.4) return ((100 - 50) / (35.4 - 12.0) * (pm02 - 12.0) + 50);
|
||||
else if (pm02 <= 55.4) return ((150 - 100) / (55.4 - 35.4) * (pm02 - 35.4) + 100);
|
||||
else if (pm02 <= 150.4) return ((200 - 150) / (150.4 - 55.4) * (pm02 - 55.4) + 150);
|
||||
else if (pm02 <= 250.4) return ((300 - 200) / (250.4 - 150.4) * (pm02 - 150.4) + 200);
|
||||
else if (pm02 <= 350.4) return ((400 - 300) / (350.4 - 250.4) * (pm02 - 250.4) + 300);
|
||||
else if (pm02 <= 500.4) return ((500 - 400) / (500.4 - 350.4) * (pm02 - 350.4) + 400);
|
||||
else return 500;
|
||||
};
|
@ -1,16 +0,0 @@
|
||||
#include <AirGradient.h>
|
||||
AirGradient ag = AirGradient();
|
||||
|
||||
void setup(){
|
||||
Serial.begin(9600);
|
||||
ag.PMS_Init();
|
||||
}
|
||||
|
||||
void loop(){
|
||||
|
||||
int PM2 = ag.getPM2_Raw();
|
||||
Serial.print("PM2: ");
|
||||
Serial.println(ag.getPM2());
|
||||
|
||||
delay(5000);
|
||||
}
|
45
examples/SHT_SIMPLE/SHT_SIMPLE.ino
Normal file
45
examples/SHT_SIMPLE/SHT_SIMPLE.ino
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
This is the code for the AirGradient DIY Air Quality Sensor with an ESP8266 Microcontroller.
|
||||
|
||||
It is a high quality sensor showing PM2.5, CO2, Temperature and Humidity on a small display and can send data over Wifi.
|
||||
|
||||
For build instructions please visit https://www.airgradient.com/open-airgradient/instructions/
|
||||
|
||||
Compatible with the following sensors:
|
||||
SHT30/31 (Temperature/Humidity Sensor)
|
||||
|
||||
Please install ESP8266 board manager (tested with version 3.0.0)
|
||||
|
||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||
https://www.airgradient.com/
|
||||
|
||||
Kits with all required components are available at https://www.airgradient.com/open-airgradient/shop/
|
||||
|
||||
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
*/
|
||||
|
||||
#include <AirGradient.h>
|
||||
AirGradient ag = AirGradient();
|
||||
|
||||
void setup(){
|
||||
Serial.begin(115200);
|
||||
ag.TMP_RH_Init(0x44); //check for SHT sensor with address 0x44
|
||||
}
|
||||
|
||||
void loop(){
|
||||
|
||||
TMP_RH result = ag.periodicFetchData();
|
||||
|
||||
Serial.print("Relative Humidity in %: ");
|
||||
Serial.println(result.rh);
|
||||
|
||||
Serial.print(" Temperature in Celcius: ");
|
||||
Serial.println(result.t);
|
||||
|
||||
Serial.print(" Temperature in Fahrenheit: ");
|
||||
Serial.println((result.t * 9 / 5) + 32);
|
||||
|
||||
delay(5000);
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#include <AirGradient.h>
|
||||
AirGradient ag = AirGradient();
|
||||
|
||||
void setup(){
|
||||
Serial.begin(9600);
|
||||
ag.TMP_RH_Init(0x44); //check for SHT sensor with address 0x44
|
||||
}
|
||||
|
||||
void loop(){
|
||||
TMP_RH result = ag.periodicFetchData();
|
||||
Serial.print("Humidity: ");
|
||||
Serial.print(result.rh_char);
|
||||
Serial.print(" Temperature: ");
|
||||
Serial.println(result.t_char);
|
||||
delay(5000);
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
name=AirGradient Air Quality Sensor
|
||||
version=1.0.0
|
||||
version=2.4.11
|
||||
author=AirGradient <support@airgradient.com>
|
||||
maintainer=AirGradient <support@airgradient.com>
|
||||
sentence=ESP8266 library for an air quality sensor featuring PM2.5, CO2, Temperature and Humidity with OLED display.
|
||||
paragraph=The library is very robust and works with the Plantower PMS5003 particle sensor, the Senseair S8 CO2 sensor and the SHT30/31 sensor for humidity and temperature. You can also connect an OLED display or send the air quality data to the AirGradient platform or any other backend.
|
||||
sentence=ESP8266 library for an air quality sensor featuring PM2.5, CO2, Temperature, TVOC and Humidity with OLED display.
|
||||
paragraph=Air quality monitoring library supporting the Plantower PMS5003 particle sensor, the Senseair S8 CO2 sensor and the SHT3x/SHT4x sensor for humidity and temperature. Kits with all components including a nice enclosure are available in our online shop. You can also connect an OLED display or send the air quality data to the AirGradient platform or any other backend. Optionally you can connect the Sensirion SGP4x TVOC module from AirGradient.
|
||||
category=Sensors
|
||||
url=https://github.com/airgradienthq/arduino
|
||||
architectures=*
|
||||
url=https://www.airgradient.com/open-airgradient/instructions/
|
||||
architectures=*
|
||||
|
41
readme.txt
41
readme.txt
@ -1,39 +1,8 @@
|
||||
This is an example C++ library for Arduino 0004+, based on one created by
|
||||
Nicholas Zambetti for Wiring 0006+
|
||||
AirGradient Arduino Library for ESP8266 (Wemos D1 MINI)
|
||||
=====================================================================================================
|
||||
|
||||
Installation
|
||||
--------------------------------------------------------------------------------
|
||||
Build your own low cost air quality sensor with optional display measuring PM2.5, CO2, Temperature and Humidity.
|
||||
|
||||
To install this library, just place this entire folder as a subfolder in your
|
||||
Arduino/lib/targets/libraries folder.
|
||||
|
||||
When installed, this library should look like:
|
||||
|
||||
Arduino/lib/targets/libraries/Test (this library's folder)
|
||||
Arduino/lib/targets/libraries/Test/Test.cpp (the library implementation file)
|
||||
Arduino/lib/targets/libraries/Test/Test.h (the library description file)
|
||||
Arduino/lib/targets/libraries/Test/keywords.txt (the syntax coloring file)
|
||||
Arduino/lib/targets/libraries/Test/examples (the examples in the "open" menu)
|
||||
Arduino/lib/targets/libraries/Test/readme.txt (this file)
|
||||
|
||||
Building
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
After this library is installed, you just have to start the Arduino application.
|
||||
You may see a few warning messages as it's built.
|
||||
|
||||
To use this library in a sketch, go to the Sketch | Import Library menu and
|
||||
select Test. This will add a corresponding line to the top of your sketch:
|
||||
#include <Test.h>
|
||||
|
||||
To stop using this library, delete that line from your sketch.
|
||||
|
||||
Geeky information:
|
||||
After a successful build of this library, a new file named "Test.o" will appear
|
||||
in "Arduino/lib/targets/libraries/Test". This file is the built/compiled library
|
||||
code.
|
||||
|
||||
If you choose to modify the code for this library (i.e. "Test.cpp" or "Test.h"),
|
||||
then you must first 'unbuild' this library by deleting the "Test.o" file. The
|
||||
new "Test.o" with your code will appear after the next press of "verify"
|
||||
This library makes it easy to read the sensor data from the Plantower PMS5003 PM2.5 sensor, the Senseair S8 and the SHT30/31 Temperature and Humidity sensor. Visit our DIY section for detailed build instructions and PCB layout.
|
||||
|
||||
https://www.airgradient.com/open-airgradient/instructions/
|
||||
|
Reference in New Issue
Block a user