mirror of
https://github.com/airgradienthq/arduino.git
synced 2025-06-26 16:21:33 +02:00
Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 |
197
AirGradient.cpp
197
AirGradient.cpp
@ -109,6 +109,127 @@ int AirGradient::getPM2_Raw(){
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Private Methods /////////////////////////////////////////////////////////////
|
||||
// Functions only available to other functions in this library
|
||||
@ -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;
|
||||
@ -589,48 +725,6 @@ 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;
|
||||
// }
|
||||
// ctr++;
|
||||
// }
|
||||
// sprintf(Char_CO2,"%d", result_CO2);
|
||||
// return Char_CO2;
|
||||
//}
|
||||
|
||||
|
||||
//int AirGradient::getCO2_Raw(){
|
||||
// const byte CO2Command[] = {0xFE, 0X44, 0X00, 0X08, 0X02, 0X9F, 0X25};
|
||||
// byte CO2Response[] = {0,0,0,0,0,0,0};
|
||||
//
|
||||
// _SoftSerial_CO2->write(CO2Command, 7);
|
||||
// delay(100); //give the sensor a bit of time to respond
|
||||
//
|
||||
// if (_SoftSerial_CO2->available()){
|
||||
// for (int i=0; i < 7; i++) {
|
||||
// int byte = _SoftSerial_CO2->read();
|
||||
// CO2Response[i] = byte;
|
||||
// if (CO2Response[0] != 254) {
|
||||
// return -1; //error code for debugging
|
||||
// }
|
||||
// }
|
||||
// unsigned long val = CO2Response[3]*256 + CO2Response[4];
|
||||
// return val;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return -2; //error code for debugging
|
||||
// }
|
||||
//}
|
||||
|
||||
int AirGradient::getCO2(int numberOfSamplesToTake) {
|
||||
int successfulSamplesCounter = 0;
|
||||
@ -664,14 +758,12 @@ int AirGradient::getCO2_Raw() {
|
||||
while(_SoftSerial_CO2->available()) // flush whatever we might have
|
||||
_SoftSerial_CO2->read();
|
||||
|
||||
const byte CO2Command[] = {0xFE, 0X44, 0X00, 0X08, 0X02, 0X9F, 0X25};
|
||||
const byte CO2Command[] = {0XFE, 0X04, 0X00, 0X03, 0X00, 0X01, 0XD5, 0XC5};
|
||||
byte CO2Response[] = {0,0,0,0,0,0,0};
|
||||
|
||||
// tt
|
||||
int datapos = -1;
|
||||
//
|
||||
|
||||
const int commandSize = 7;
|
||||
const int commandSize = 8;
|
||||
const int responseSize = 7;
|
||||
|
||||
int numberOfBytesWritten = _SoftSerial_CO2->write(CO2Command, commandSize);
|
||||
|
||||
@ -682,7 +774,7 @@ int AirGradient::getCO2_Raw() {
|
||||
|
||||
// attempt to read response
|
||||
int timeoutCounter = 0;
|
||||
while (_SoftSerial_CO2->available() < commandSize) {
|
||||
while (_SoftSerial_CO2->available() < responseSize) {
|
||||
timeoutCounter++;
|
||||
if (timeoutCounter > 10) {
|
||||
// timeout when reading response
|
||||
@ -692,22 +784,15 @@ int AirGradient::getCO2_Raw() {
|
||||
}
|
||||
|
||||
// we have 7 bytes ready to be read
|
||||
for (int i=0; i < commandSize; i++) {
|
||||
for (int i=0; i < responseSize; i++) {
|
||||
CO2Response[i] = _SoftSerial_CO2->read();
|
||||
|
||||
// tt
|
||||
if ((CO2Response[i] == 0xFE) && (datapos == -1)){
|
||||
datapos = i;
|
||||
}
|
||||
Serial.print (CO2Response[i],HEX);
|
||||
Serial.print (":");
|
||||
//
|
||||
}
|
||||
// return CO2Response[3]*256 + CO2Response[4];
|
||||
// tt
|
||||
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
|
||||
|
||||
@ -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
|
||||
|
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() );
|
||||
}
|
@ -17,7 +17,8 @@ https://www.airgradient.com/
|
||||
|
||||
Kits with all required components are available at https://www.airgradient.com/open-airgradient/shop/
|
||||
|
||||
MIT License
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
|
||||
*/
|
||||
|
||||
#include <AirGradient.h>
|
||||
|
@ -21,7 +21,8 @@ If you have any questions please visit our forum at https://forum.airgradient.co
|
||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||
https://www.airgradient.com/
|
||||
|
||||
MIT License
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
|
||||
*/
|
||||
|
||||
#include <AirGradient.h>
|
||||
|
@ -9,7 +9,8 @@ Kits (including a pre-soldered version) are available: https://www.airgradient.c
|
||||
|
||||
The codes needs the following libraries installed:
|
||||
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
|
||||
"ESP8266 and ESP32 OLED driver for SSD1306 displays by ThingPulse, Fabrice Weinberg" tested with Version 4.1.0
|
||||
“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.
|
||||
@ -29,26 +30,32 @@ MIT License
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <Wire.h>
|
||||
#include "SSD1306Wire.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
|
||||
|
||||
SSD1306Wire display(0x3c, SDA, SCL);
|
||||
|
||||
// CONFIGURATION START
|
||||
|
||||
// set to true to switch PM2.5 from ug/m3 to US AQI
|
||||
boolean inUSaqi = true;
|
||||
//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. The display will show values only when the sensor has wifi connection
|
||||
// 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;
|
||||
@ -69,28 +76,24 @@ const int tempHumInterval = 2500;
|
||||
unsigned long previousTempHum = 0;
|
||||
float temp = 0;
|
||||
int hum = 0;
|
||||
int displaypage = 0;
|
||||
|
||||
String APIROOT = "http://hw.airgradient.com/";
|
||||
|
||||
|
||||
long val;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
display.init();
|
||||
display.flipScreenVertically();
|
||||
sht.init();
|
||||
sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM);
|
||||
u8g2.setBusClock(100000);
|
||||
u8g2.begin();
|
||||
updateOLED();
|
||||
|
||||
if (connectWIFI) {
|
||||
connectToWifi();
|
||||
}
|
||||
|
||||
showTextRectangle("Init", String(ESP.getChipId(), HEX), true);
|
||||
|
||||
updateOLED2("Warm Up", "Serial#", String(ESP.getChipId(), HEX));
|
||||
ag.CO2_Init();
|
||||
ag.PMS_Init();
|
||||
ag.TMP_RH_Init(0x44);
|
||||
//ag.TMP_RH_Init(0x44);
|
||||
}
|
||||
|
||||
|
||||
@ -126,9 +129,19 @@ void updateTempHum()
|
||||
{
|
||||
if (currentMillis - previousTempHum >= tempHumInterval) {
|
||||
previousTempHum += tempHumInterval;
|
||||
TMP_RH result = ag.periodicFetchData();
|
||||
temp = result.t;
|
||||
hum = result.rh;
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -137,46 +150,38 @@ void updateOLED() {
|
||||
if (currentMillis - previousOled >= oledInterval) {
|
||||
previousOled += oledInterval;
|
||||
|
||||
switch (displaypage) {
|
||||
case 0:
|
||||
if (inUSaqi) {
|
||||
showTextRectangle("AQI", String(PM_TO_AQI_US(pm25)), false);
|
||||
} else {
|
||||
showTextRectangle("PM2", String(pm25), false);
|
||||
}
|
||||
displaypage = 1;
|
||||
break;
|
||||
case 1:
|
||||
showTextRectangle("CO2", String(Co2), false);
|
||||
displaypage = 2;
|
||||
break;
|
||||
case 2:
|
||||
if (inF) {
|
||||
showTextRectangle("F", String((temp * 9 / 5) + 32), false);
|
||||
} else {
|
||||
showTextRectangle("C", String(temp), false);
|
||||
}
|
||||
displaypage = 3;
|
||||
break;
|
||||
case 3:
|
||||
showTextRectangle("Hum", String(hum)+"%", false);
|
||||
displaypage = 0;
|
||||
break;
|
||||
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 showTextRectangle(String ln1, String ln2, boolean small) {
|
||||
display.clear();
|
||||
display.setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
if (small) {
|
||||
display.setFont(ArialMT_Plain_16);
|
||||
} else {
|
||||
display.setFont(ArialMT_Plain_24);
|
||||
}
|
||||
display.drawString(32, 16, ln1);
|
||||
display.drawString(32, 38, ln2);
|
||||
display.display();
|
||||
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() {
|
||||
@ -190,7 +195,6 @@ void sendToServer() {
|
||||
+ (hum < 0 ? "" : ", \"rhum\":" + String(hum))
|
||||
+ "}";
|
||||
|
||||
|
||||
if(WiFi.status()== WL_CONNECTED){
|
||||
Serial.println(payload);
|
||||
String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId(), HEX) + "/measures";
|
||||
@ -215,10 +219,12 @@ void sendToServer() {
|
||||
void connectToWifi() {
|
||||
WiFiManager wifiManager;
|
||||
//WiFi.disconnect(); //to delete previous saved hotspot
|
||||
String HOTSPOT = "AIRGRADIENT-" + String(ESP.getChipId(), HEX);
|
||||
wifiManager.setTimeout(60);
|
||||
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())) {
|
||||
showTextRectangle("offline", "mode", true);
|
||||
updateOLED2("Booting", "offline", "mode");
|
||||
Serial.println("failed to connect and hit timeout");
|
||||
delay(6000);
|
||||
}
|
||||
|
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/DIY_OUTDOOR_C3.ino
Normal file
277
examples/DIY_OUTDOOR_C3/DIY_OUTDOOR_C3.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;
|
||||
}
|
@ -19,7 +19,7 @@ If you have any questions please visit our forum at https://forum.airgradient.co
|
||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||
https://www.airgradient.com/
|
||||
|
||||
MIT License
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
|
||||
*/
|
||||
|
||||
@ -78,7 +78,7 @@ int hum = 0;
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
u8g2.setBusClock(100000);
|
||||
u8g2.begin();
|
||||
updateOLED();
|
||||
|
||||
|
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;
|
||||
};
|
424
examples/DIY_PRO_V4_2/DIY_PRO_V4_2.ino
Normal file
424
examples/DIY_PRO_V4_2/DIY_PRO_V4_2.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
|
||||
"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 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=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();
|
||||
updatePm25();
|
||||
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 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 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;
|
||||
};
|
@ -1,248 +0,0 @@
|
||||
/*
|
||||
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/
|
||||
|
||||
IMPORTANT: If you use SGP30 modules, you need to remove the Pull up resistors to make it work.
|
||||
See https://www.airgradient.com/open-airgradient/instructions/tvoc-on-airgradient-diy-sensor/
|
||||
|
||||
If you want to use TVOC we strongly recommend you use the AirGradient SGP4x module available in our online shop.
|
||||
|
||||
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
|
||||
“SGP30” by Rob Tilaart tested with Version 0.1.5
|
||||
|
||||
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 "SGP30.h"
|
||||
#include <U8g2lib.h>
|
||||
|
||||
AirGradient ag = AirGradient();
|
||||
SGP30 SGP;
|
||||
|
||||
// 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 tvocInterval = 1000;
|
||||
unsigned long previousTVOC = 0;
|
||||
int TVOC = 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.begin();
|
||||
updateOLED();
|
||||
|
||||
if (connectWIFI) {
|
||||
connectToWifi();
|
||||
}
|
||||
|
||||
updateOLED2("Warming up the", "sensors.", "");
|
||||
|
||||
Serial.println(SGP.begin());
|
||||
SGP.GenericReset();
|
||||
|
||||
ag.CO2_Init();
|
||||
ag.PMS_Init();
|
||||
ag.TMP_RH_Init(0x44);
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
currentMillis = millis();
|
||||
updateTVOC();
|
||||
updateOLED();
|
||||
updateCo2();
|
||||
updatePm25();
|
||||
updateTempHum();
|
||||
sendToServer();
|
||||
}
|
||||
|
||||
void updateTVOC()
|
||||
{
|
||||
if (currentMillis - previousTVOC >= tvocInterval) {
|
||||
previousTVOC += tvocInterval;
|
||||
SGP.measure(true);
|
||||
TVOC = SGP.getTVOC();
|
||||
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 = "PM:" + String(pm25) + " CO2:" + String(Co2);
|
||||
String ln2 = "AQI:" + String(PM_TO_AQI_US(pm25)) + " TVOC:" + String(TVOC);
|
||||
|
||||
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\":" + String(TVOC))
|
||||
+ ", \"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;
|
||||
};
|
@ -1,255 +0,0 @@
|
||||
/*
|
||||
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/
|
||||
|
||||
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
|
||||
“DFRobot_SGP40” by DFRobot tested with Version 1.0.3
|
||||
|
||||
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 "SGP30.h"
|
||||
#include <DFRobot_SGP40.h>
|
||||
#include <U8g2lib.h>
|
||||
|
||||
AirGradient ag = AirGradient();
|
||||
DFRobot_SGP40 sgp40;
|
||||
|
||||
// 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 tvocInterval = 1000;
|
||||
unsigned long previousTVOC = 0;
|
||||
int TVOC = 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.begin();
|
||||
updateOLED();
|
||||
|
||||
if (connectWIFI) {
|
||||
connectToWifi();
|
||||
}
|
||||
|
||||
updateOLED2("Warming up the", "sensors.", "");
|
||||
sgp40.begin();
|
||||
ag.CO2_Init();
|
||||
ag.PMS_Init();
|
||||
ag.TMP_RH_Init(0x44);
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
currentMillis = millis();
|
||||
updateTVOC();
|
||||
updateOLED();
|
||||
updateCo2();
|
||||
updatePm25();
|
||||
updateTempHum();
|
||||
sendToServer();
|
||||
}
|
||||
|
||||
void updateTVOC()
|
||||
{
|
||||
if (currentMillis - previousTVOC >= tvocInterval) {
|
||||
previousTVOC += tvocInterval;
|
||||
TVOC = sgp40.getVoclndex();
|
||||
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 = "PM:" + String(pm25) + " CO2:" + String(Co2);
|
||||
String ln2 = "AQI:" + String(PM_TO_AQI_US(pm25)) + " TVOC:" + String(TVOC);
|
||||
|
||||
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))
|
||||
+ ", \"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);
|
||||
|
||||
|
||||
WiFiManagerParameter custom_text("<p>This is just a text paragraph</p>");
|
||||
wifiManager.addParameter(&custom_text);
|
||||
|
||||
WiFiManagerParameter parameter("parameterId", "Parameter Label", "default value", 40);
|
||||
wifiManager.addParameter(¶meter);
|
||||
|
||||
|
||||
Serial.println("Parameter 1:");
|
||||
Serial.println(parameter.getValue());
|
||||
|
||||
if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
|
||||
updateOLED2("booting into", "offline mode", "");
|
||||
Serial.println("failed to connect and hit timeout");
|
||||
delay(6000);
|
||||
}
|
||||
|
||||
Serial.println("Parameter 2:");
|
||||
Serial.println(parameter.getValue());
|
||||
|
||||
}
|
||||
|
||||
// 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;
|
||||
};
|
@ -10,7 +10,7 @@ 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 6.18.5
|
||||
"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.
|
||||
@ -21,7 +21,7 @@ If you have any questions please visit our forum at https://forum.airgradient.co
|
||||
If you are a school or university contact us for a free trial on the AirGradient platform.
|
||||
https://www.airgradient.com/
|
||||
|
||||
MIT License
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
*/
|
||||
|
||||
#include <WiFiManager.h>
|
||||
|
@ -17,7 +17,7 @@ https://www.airgradient.com/
|
||||
|
||||
Kits with all required components are available at https://www.airgradient.com/open-airgradient/shop/
|
||||
|
||||
MIT License
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
*/
|
||||
|
||||
#include <AirGradient.h>
|
||||
|
@ -17,7 +17,7 @@ Kits with all required components are available at https://www.airgradient.com/o
|
||||
|
||||
If you have any questions please visit our forum at https://forum.airgradient.com/
|
||||
|
||||
MIT License
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
*/
|
||||
|
||||
#include <AirGradient.h>
|
||||
|
@ -1,5 +1,5 @@
|
||||
name=AirGradient Air Quality Sensor
|
||||
version=2.2.0
|
||||
version=2.4.3
|
||||
author=AirGradient <support@airgradient.com>
|
||||
maintainer=AirGradient <support@airgradient.com>
|
||||
sentence=ESP8266 library for an air quality sensor featuring PM2.5, CO2, Temperature, TVOC and Humidity with OLED display.
|
||||
|
Reference in New Issue
Block a user