mirror of
https://github.com/airgradienthq/arduino.git
synced 2025-07-29 00:17:16 +02:00
Complete rewrite of the library with modular structure.
This commit is contained in:
460
examples/BASIC_v4/BASIC_v4.ino
Normal file
460
examples/BASIC_v4/BASIC_v4.ino
Normal file
@ -0,0 +1,460 @@
|
||||
/*
|
||||
This is the code for the AirGradient DIY BASIC Air Quality Monitor with an D1 ESP8266 Microcontroller.
|
||||
|
||||
It is an air quality monitor for PM2.5, CO2, Temperature and Humidity with a small display and can send data over Wifi.
|
||||
|
||||
Open source air quality monitors and kits are available:
|
||||
Indoor Monitor: https://www.airgradient.com/indoor/
|
||||
Outdoor Monitor: https://www.airgradient.com/outdoor/
|
||||
|
||||
Build Instructions:
|
||||
https://www.airgradient.com/documentation/diy-v4/
|
||||
|
||||
Following libraries need to be installed:
|
||||
“WifiManager by tzapu, tablatronix” tested with version 2.0.16-rc.2
|
||||
"Arduino_JSON" by Arduino version 0.2.0
|
||||
"U8g2" by oliver version 2.34.22
|
||||
|
||||
Please make sure you have esp8266 board manager installed. Tested with version 3.1.2.
|
||||
|
||||
Set board to "LOLIN(WEMOS) D1 R2 & mini"
|
||||
|
||||
Configuration parameters, e.g. Celsius / Fahrenheit or PM unit (US AQI vs ug/m3) can be set through the AirGradient dashboard.
|
||||
|
||||
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>
|
||||
#include <Arduino_JSON.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <U8g2lib.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiManager.h>
|
||||
|
||||
typedef struct {
|
||||
bool inF; /** Temperature unit */
|
||||
bool inUSAQI; /** PMS standard */
|
||||
uint8_t ledBarMode; /** @ref UseLedBar*/
|
||||
char model[16]; /** Model string value, Just define, don't know how much
|
||||
memory usage */
|
||||
char mqttBroker[128]; /** Mqtt broker link */
|
||||
uint32_t _check; /** Checksum configuration data */
|
||||
} ServerConfig_t;
|
||||
static ServerConfig_t serverConfig;
|
||||
|
||||
AirGradient ag = AirGradient(BOARD_DIY_BASIC_KIT);
|
||||
|
||||
// CONFIGURATION START
|
||||
|
||||
// set to the endpoint you would like to use
|
||||
String APIROOT = "http://hw.airgradient.com/";
|
||||
|
||||
String wifiApPass = "cleanair";
|
||||
|
||||
// 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;
|
||||
bool co2CalibrationRequest = false;
|
||||
uint32_t serverConfigLoadTime = 0;
|
||||
String HOSTPOT = "";
|
||||
|
||||
const int sendToServerInterval = 60000;
|
||||
const int pollServerConfigInterval = 30000;
|
||||
const int co2CalibCountdown = 5; /** Seconds */
|
||||
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 failedHandler(String msg);
|
||||
void boardInit(void);
|
||||
void getServerConfig(void);
|
||||
void co2Calibration(void);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
/** Init I2C */
|
||||
Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin());
|
||||
|
||||
/** Board init */
|
||||
boardInit();
|
||||
|
||||
/** Show boot display */
|
||||
displayShowText("Basic v4", "Lib:" + ag.getVersion(), "");
|
||||
delay(2000);
|
||||
|
||||
if (connectWIFI) {
|
||||
connectToWifi();
|
||||
}
|
||||
|
||||
/** Show display */
|
||||
displayShowText("Warm Up", "Serial#", String(ESP.getChipId(), HEX));
|
||||
delay(10000);
|
||||
|
||||
getServerConfig();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
currentMillis = millis();
|
||||
updateOLED();
|
||||
updateCo2();
|
||||
updatePm25();
|
||||
updateTempHum();
|
||||
sendToServer();
|
||||
getServerConfig();
|
||||
}
|
||||
|
||||
void updateCo2() {
|
||||
if (currentMillis - previousCo2 >= co2Interval) {
|
||||
previousCo2 += co2Interval;
|
||||
Co2 = ag.s8.getCo2();
|
||||
Serial.println(String(Co2));
|
||||
}
|
||||
}
|
||||
|
||||
void updatePm25() {
|
||||
if (currentMillis - previousPm25 >= pm25Interval) {
|
||||
previousPm25 += pm25Interval;
|
||||
if (ag.pms5003.readData()) {
|
||||
pm25 = ag.pms5003.getPm25Ae();
|
||||
Serial.printf("PM25: %d\r\n", pm25);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateTempHum() {
|
||||
if (currentMillis - previousTempHum >= tempHumInterval) {
|
||||
previousTempHum += tempHumInterval;
|
||||
|
||||
/** Get temperature and humidity */
|
||||
temp = ag.sht.getTemperature();
|
||||
hum = ag.sht.getRelativeHumidity();
|
||||
|
||||
/** Print debug message */
|
||||
Serial.printf("SHT Humidity: %d%, Temperature: %0.2f\r\n", hum, temp);
|
||||
}
|
||||
}
|
||||
|
||||
void updateOLED() {
|
||||
if (currentMillis - previousOled >= oledInterval) {
|
||||
previousOled += oledInterval;
|
||||
|
||||
String ln1;
|
||||
String ln2;
|
||||
String ln3;
|
||||
|
||||
if (serverConfig.inUSAQI) {
|
||||
ln1 = "AQI:" + String(ag.pms5003.convertPm25ToUsAqi(pm25));
|
||||
} else {
|
||||
ln1 = "PM :" + String(pm25) + " ug";
|
||||
}
|
||||
ln2 = "CO2:" + String(Co2);
|
||||
|
||||
if (serverConfig.inF) {
|
||||
ln3 =
|
||||
String((temp * 9 / 5) + 32).substring(0, 4) + " " + String(hum) + "%";
|
||||
} else {
|
||||
ln3 = String(temp).substring(0, 4) + " " + String(hum) + "%";
|
||||
}
|
||||
displayShowText(ln1, ln2, ln3);
|
||||
}
|
||||
}
|
||||
|
||||
void displayShowText(String ln1, String ln2, String ln3) {
|
||||
char buf[9];
|
||||
ag.display.clear();
|
||||
|
||||
ag.display.setCursor(1, 1);
|
||||
ag.display.setText(ln1);
|
||||
ag.display.setCursor(1, 19);
|
||||
ag.display.setText(ln2);
|
||||
ag.display.setCursor(1, 37);
|
||||
ag.display.setText(ln3);
|
||||
|
||||
ag.display.show();
|
||||
}
|
||||
|
||||
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);
|
||||
// displayShowText("Connect", "AG-", String(ESP.getChipId(), HEX));
|
||||
delay(2000);
|
||||
// wifiManager.setTimeout(90);
|
||||
wifiManager.setConfigPortalBlocking(false);
|
||||
wifiManager.setConfigPortalTimeout(180);
|
||||
wifiManager.autoConnect(HOTSPOT.c_str(), wifiApPass.c_str());
|
||||
|
||||
uint32_t lastTime = millis();
|
||||
int count = 179;
|
||||
displayShowText("180 sec", "SSID:",HOTSPOT);
|
||||
while (wifiManager.getConfigPortalActive()) {
|
||||
wifiManager.process();
|
||||
uint32_t ms = (uint32_t)(millis() - lastTime);
|
||||
if (ms >= 1000) {
|
||||
lastTime = millis();
|
||||
displayShowText(String(count) + " sec", "SSID:",HOTSPOT);
|
||||
count--;
|
||||
|
||||
// Timeout
|
||||
if (count == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!WiFi.isConnected()) {
|
||||
displayShowText("Booting", "offline", "mode");
|
||||
Serial.println("failed to connect and hit timeout");
|
||||
delay(6000);
|
||||
}
|
||||
}
|
||||
|
||||
void failedHandler(String msg) {
|
||||
while (true) {
|
||||
Serial.println(msg);
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
void boardInit(void) {
|
||||
/** Init SHT sensor */
|
||||
if (ag.sht.begin(Wire) == false) {
|
||||
failedHandler("SHT init failed");
|
||||
}
|
||||
|
||||
/** CO2 init */
|
||||
if (ag.s8.begin(&Serial) == false) {
|
||||
failedHandler("SenseAirS8 init failed");
|
||||
}
|
||||
|
||||
/** PMS init */
|
||||
if (ag.pms5003.begin(&Serial) == false) {
|
||||
failedHandler("PMS5003 init failed");
|
||||
}
|
||||
|
||||
/** Display init */
|
||||
ag.display.begin(Wire);
|
||||
ag.display.setTextColor(1);
|
||||
}
|
||||
|
||||
void showConfig(void) {
|
||||
Serial.println("Server configuration: ");
|
||||
Serial.printf(" inF: %s\r\n", serverConfig.inF ? "true" : "false");
|
||||
Serial.printf(" inUSAQI: %s\r\n",
|
||||
serverConfig.inUSAQI ? "true" : "false");
|
||||
Serial.printf("useRGBLedBar: %d\r\n", (int)serverConfig.ledBarMode);
|
||||
Serial.printf(" Model: %.*s\r\n", sizeof(serverConfig.model),
|
||||
serverConfig.model);
|
||||
Serial.printf(" Mqtt Broker: %.*s\r\n", sizeof(serverConfig.mqttBroker),
|
||||
serverConfig.mqttBroker);
|
||||
}
|
||||
|
||||
void updateServerConfigLoadTime(void) {
|
||||
serverConfigLoadTime = millis();
|
||||
if (serverConfigLoadTime == 0) {
|
||||
serverConfigLoadTime = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void getServerConfig(void) {
|
||||
/** Only trigger load configuration again after pollServerConfigInterval sec
|
||||
*/
|
||||
if (serverConfigLoadTime) {
|
||||
uint32_t ms = (uint32_t)(millis() - serverConfigLoadTime);
|
||||
if (ms < pollServerConfigInterval) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
updateServerConfigLoadTime();
|
||||
|
||||
Serial.println("Trigger load server configuration");
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.println(
|
||||
"Ignore get server configuration because WIFI not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
// WiFiClient wifiClient;
|
||||
HTTPClient httpClient;
|
||||
|
||||
String getUrl = "http://hw.airgradient.com/sensors/airgradient:" +
|
||||
String(ESP.getChipId(), HEX) + "/one/config";
|
||||
Serial.println("HttpClient get: " + getUrl);
|
||||
WiFiClient client;
|
||||
if (httpClient.begin(client, getUrl) == false) {
|
||||
Serial.println("HttpClient init failed");
|
||||
updateServerConfigLoadTime();
|
||||
return;
|
||||
}
|
||||
|
||||
int respCode = httpClient.GET();
|
||||
|
||||
/** get failure */
|
||||
if (respCode != 200) {
|
||||
Serial.printf("HttpClient get failed: %d\r\n", respCode);
|
||||
updateServerConfigLoadTime();
|
||||
return;
|
||||
}
|
||||
|
||||
String respContent = httpClient.getString();
|
||||
Serial.println("Server config: " + respContent);
|
||||
|
||||
/** Parse JSON */
|
||||
JSONVar root = JSON.parse(respContent);
|
||||
if (JSON.typeof_(root) == "undefined") {
|
||||
Serial.println("Server configura JSON invalid");
|
||||
updateServerConfigLoadTime();
|
||||
return;
|
||||
}
|
||||
|
||||
/** Get "country" */
|
||||
bool inF = serverConfig.inF;
|
||||
if (JSON.typeof_(root["country"]) == "string") {
|
||||
String country = root["country"];
|
||||
if (country == "US") {
|
||||
inF = true;
|
||||
} else {
|
||||
inF = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get "pmStandard" */
|
||||
bool inUSAQI = serverConfig.inUSAQI;
|
||||
if (JSON.typeof_(root["pmStandard"]) == "string") {
|
||||
String standard = root["pmStandard"];
|
||||
if (standard == "ugm3") {
|
||||
inUSAQI = false;
|
||||
} else {
|
||||
inUSAQI = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get CO2 "co2CalibrationRequested" */
|
||||
co2CalibrationRequest = false;
|
||||
if (JSON.typeof_(root["co2CalibrationRequested"]) == "boolean") {
|
||||
co2CalibrationRequest = root["co2CalibrationRequested"];
|
||||
}
|
||||
|
||||
/** get "model" */
|
||||
String model = "";
|
||||
if (JSON.typeof_(root["model"]) == "string") {
|
||||
String _model = root["model"];
|
||||
model = _model;
|
||||
}
|
||||
|
||||
/** get "mqttBrokerUrl" */
|
||||
String mqtt = "";
|
||||
if (JSON.typeof_(root["mqttBrokerUrl"]) == "string") {
|
||||
String _mqtt = root["mqttBrokerUrl"];
|
||||
mqtt = _mqtt;
|
||||
}
|
||||
|
||||
if (inF != serverConfig.inF) {
|
||||
serverConfig.inF = inF;
|
||||
}
|
||||
if (inUSAQI != serverConfig.inUSAQI) {
|
||||
serverConfig.inUSAQI = inUSAQI;
|
||||
}
|
||||
if (model.length()) {
|
||||
if (model != String(serverConfig.model)) {
|
||||
memset(serverConfig.model, 0, sizeof(serverConfig.model));
|
||||
memcpy(serverConfig.model, model.c_str(), model.length());
|
||||
}
|
||||
}
|
||||
if (mqtt.length()) {
|
||||
if (mqtt != String(serverConfig.mqttBroker)) {
|
||||
memset(serverConfig.mqttBroker, 0, sizeof(serverConfig.mqttBroker));
|
||||
memcpy(serverConfig.mqttBroker, mqtt.c_str(), mqtt.length());
|
||||
}
|
||||
}
|
||||
|
||||
/** Show server configuration */
|
||||
showConfig();
|
||||
|
||||
/** Calibration */
|
||||
if (co2CalibrationRequest) {
|
||||
co2Calibration();
|
||||
}
|
||||
}
|
||||
|
||||
void co2Calibration(void) {
|
||||
/** Count down for co2CalibCountdown secs */
|
||||
for (int i = 0; i < co2CalibCountdown; i++) {
|
||||
displayShowText("CO2 calib", "after",
|
||||
String(co2CalibCountdown - i) + " sec");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
if (ag.s8.setBaselineCalibration()) {
|
||||
displayShowText("Calib", "success", "");
|
||||
delay(1000);
|
||||
displayShowText("Wait for", "finish", "...");
|
||||
int count = 0;
|
||||
while (ag.s8.isBaseLineCalibrationDone() == false) {
|
||||
delay(1000);
|
||||
count++;
|
||||
}
|
||||
displayShowText("Finish", "after", String(count) + " sec");
|
||||
delay(2000);
|
||||
} else {
|
||||
displayShowText("Calib", "failure!!!", "");
|
||||
delay(2000);
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
/*
|
||||
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() );
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
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,132 +0,0 @@
|
||||
/*
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,243 +0,0 @@
|
||||
/*
|
||||
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;
|
||||
};
|
@ -1,212 +0,0 @@
|
||||
/*
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,277 +0,0 @@
|
||||
/*
|
||||
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;
|
||||
}
|
@ -1,338 +0,0 @@
|
||||
/*
|
||||
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
|
||||
"S8_UART" by Josep Comas Version 1.0.1
|
||||
“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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -1,220 +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/
|
||||
|
||||
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;
|
||||
};
|
@ -1,424 +0,0 @@
|
||||
/*
|
||||
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;
|
||||
};
|
@ -1,433 +0,0 @@
|
||||
/*
|
||||
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,380 +0,0 @@
|
||||
/*
|
||||
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);
|
||||
}
|
||||
}
|
1212
examples/ONE_I-9PSL/ONE_I-9PSL.ino
Normal file
1212
examples/ONE_I-9PSL/ONE_I-9PSL.ino
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,752 +0,0 @@
|
||||
/*
|
||||
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
|
||||
"pms" by Markusz Kakl version 1.1.0
|
||||
"S8_UART" by Josep Comas Version 1.0.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 persistent 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;
|
||||
|
||||
// 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 > 7) buttonConfig = 0;
|
||||
delay(400);
|
||||
setConfig();
|
||||
Serial.println("buttonConfig: " + String(buttonConfig));
|
||||
|
||||
updateOLED2("Press Button", "for LED test &", "offline mode");
|
||||
delay(2000);
|
||||
currentState = digitalRead(9);
|
||||
if (currentState == LOW) {
|
||||
ledTest();
|
||||
return;
|
||||
}
|
||||
|
||||
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) connectToWifi();
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
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 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 > 7) 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) {
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
updateOLED2("T:C, PM:ug/m3", "LED Bar: on", "Long Press Saves");
|
||||
inF = false;
|
||||
inUSAQI = false;
|
||||
useRGBledBar = true;
|
||||
} else if (buttonConfig == 1) {
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
updateOLED2("T:C, PM:US AQI", "LED Bar: on", "Long Press Saves");
|
||||
inF = false;
|
||||
inUSAQI = true;
|
||||
useRGBledBar = true;
|
||||
} else if (buttonConfig == 2) {
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
updateOLED2("T:F PM:ug/m3", "LED Bar: on", "Long Press Saves");
|
||||
inF = true;
|
||||
inUSAQI = false;
|
||||
useRGBledBar = true;
|
||||
} else if (buttonConfig == 3) {
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
updateOLED2("T:F PM:US AQI", "LED Bar: on", "Long Press Saves");
|
||||
inF = true;
|
||||
inUSAQI = true;
|
||||
useRGBledBar = true;
|
||||
} else if (buttonConfig == 4) {
|
||||
updateOLED2("T:C, PM:ug/m3", "LED Bar: off", "Long Press Saves");
|
||||
inF = false;
|
||||
inUSAQI = false;
|
||||
useRGBledBar = false;
|
||||
} else if (buttonConfig == 5) {
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
updateOLED2("T:C, PM:US AQI", "LED Bar: off", "Long Press Saves");
|
||||
inF = false;
|
||||
inUSAQI = true;
|
||||
useRGBledBar = false;
|
||||
} else if (buttonConfig == 6) {
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
updateOLED2("T:F PM:ug/m3", "LED Bar: off", "Long Press Saves");
|
||||
inF = true;
|
||||
inUSAQI = false;
|
||||
useRGBledBar = false;
|
||||
} else if (buttonConfig == 7) {
|
||||
u8g2.setDisplayRotation(U8G2_R0);
|
||||
updateOLED2("T:F PM:US AQI", "LED Bar: off", "Long Press Saves");
|
||||
inF = true;
|
||||
inUSAQI = true;
|
||||
useRGBledBar = false;
|
||||
}
|
||||
}
|
||||
|
||||
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(30);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(255, 255, 0));
|
||||
delay(30);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(255, 128, 0));
|
||||
delay(30);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(255, 0, 0));
|
||||
delay(30);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(0, 0, 255));
|
||||
delay(30);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(255, 255, 255));
|
||||
delay(30);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(153, 0, 153));
|
||||
delay(30);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'z':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(102, 0, 0));
|
||||
delay(30);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(0, 0, 0));
|
||||
delay(30);
|
||||
pixels.show();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// if nothing else matches, do the default
|
||||
// default is optional
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ledTest() {
|
||||
updateOLED2("LED Test", "running", ".....");
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(255, 0, 0));
|
||||
delay(30);
|
||||
pixels.show();
|
||||
}
|
||||
delay(500);
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(0, 255, 0));
|
||||
delay(30);
|
||||
pixels.show();
|
||||
}
|
||||
delay(500);
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(0, 0, 255));
|
||||
delay(30);
|
||||
pixels.show();
|
||||
}
|
||||
delay(500);
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(255, 255, 255));
|
||||
delay(30);
|
||||
pixels.show();
|
||||
}
|
||||
delay(500);
|
||||
for (int i = 0; i < 11; i++) {
|
||||
pixels.setPixelColor(i, pixels.Color(0, 0, 0));
|
||||
delay(30);
|
||||
pixels.show();
|
||||
}
|
||||
delay(500);
|
||||
}
|
||||
|
||||
// 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;
|
||||
};
|
687
examples/Open_Air_O-1PST/Open_Air_O-1PST.ino
Normal file
687
examples/Open_Air_O-1PST/Open_Air_O-1PST.ino
Normal file
@ -0,0 +1,687 @@
|
||||
/*
|
||||
This is the code for the AirGradient Open Air open-source hardware outdoor Air Quality Monitor with an ESP32-C3 Microcontroller.
|
||||
|
||||
It is an air quality monitor for PM2.5, CO2, TVOCs, NOx, Temperature and Humidity and can send data over Wifi.
|
||||
|
||||
Open source air quality monitors and kits are available:
|
||||
Indoor Monitor: https://www.airgradient.com/indoor/
|
||||
Outdoor Monitor: https://www.airgradient.com/outdoor/
|
||||
|
||||
Build Instructions: https://www.airgradient.com/documentation/open-air-pst-kit-1-3/
|
||||
|
||||
The codes needs the following libraries installed:
|
||||
“WifiManager by tzapu, tablatronix” tested with version 2.0.16-rc.2
|
||||
"Arduino_JSON" by Arduino Version 0.2.0
|
||||
|
||||
Please make sure you have esp32 board manager installed. Tested with version 2.0.11.
|
||||
|
||||
Important flashing settings:
|
||||
- Set board to "ESP32C3 Dev Module"
|
||||
- Enable "USB CDC On Boot"
|
||||
- Flash frequency "80Mhz"
|
||||
- Flash mode "QIO"
|
||||
- Flash size "4MB"
|
||||
- Partition scheme "Default 4MB with spiffs (1.2MB APP/1,5MB SPIFFS)"
|
||||
- JTAG adapter "Disabled"
|
||||
|
||||
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>
|
||||
#include <Arduino_JSON.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <HardwareSerial.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <Wire.h>
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Application state machine state
|
||||
*
|
||||
*/
|
||||
enum {
|
||||
APP_SM_WIFI_MANAGER_MODE, /** In WiFi Manger Mode */
|
||||
APP_SM_WIFI_MAMAGER_PORTAL_ACTIVE, /** WiFi Manager has connected to mobile
|
||||
phone */
|
||||
APP_SM_WIFI_MANAGER_STA_CONNECTING, /** After SSID and PW entered and OK
|
||||
clicked, connection to WiFI network is
|
||||
attempted*/
|
||||
APP_SM_WIFI_MANAGER_STA_CONNECTED, /** Connecting to WiFi worked */
|
||||
APP_SM_WIFI_OK_SERVER_CONNECTING, /** Once connected to WiFi an attempt to
|
||||
reach the server is performed */
|
||||
APP_SM_WIFI_OK_SERVER_CONNNECTED, /** Server is reachable, all fine */
|
||||
/** Exceptions during WIFi Setup */
|
||||
APP_SM_WIFI_MANAGER_CONNECT_FAILED, /** Cannot connect to WiFi (e.g. wrong
|
||||
password, WPA Enterprise etc.) */
|
||||
APP_SM_WIFI_OK_SERVER_CONNECT_FAILED, /** Connected to WiFi but server not
|
||||
reachable, e.g. firewall block/
|
||||
whitelisting needed etc. */
|
||||
APP_SM_WIFI_OK_SERVER_OK_SENSOR_CONFIG_FAILED, /** Server reachable but sensor
|
||||
not configured correctly*/
|
||||
|
||||
/** During Normal Operation */
|
||||
APP_SM_WIFI_LOST, /** Connection to WiFi network failed credentials incorrect
|
||||
encryption not supported etc. */
|
||||
APP_SM_SERVER_LOST, /** Connected to WiFi network but the server cannot be
|
||||
reached through the internet, e.g. blocked by firewall
|
||||
*/
|
||||
APP_SM_SENSOR_CONFIG_FAILED, /** Server is reachable but there is some
|
||||
configuration issue to be fixed on the server
|
||||
side */
|
||||
APP_SM_NORMAL,
|
||||
};
|
||||
|
||||
#define DEBUG true
|
||||
|
||||
#define WIFI_CONNECT_COUNTDOWN_MAX 180 /** sec */
|
||||
#define WIFI_HOTSPOT_PASSWORD_DEFAULT "cleanair"
|
||||
|
||||
AirGradient ag(BOARD_OUTDOOR_MONITOR_V1_3);
|
||||
|
||||
// time in seconds needed for NOx conditioning
|
||||
uint16_t conditioning_s = 10;
|
||||
|
||||
String APIROOT = "http://hw.airgradient.com/";
|
||||
|
||||
typedef struct {
|
||||
bool inF; /** Temperature unit */
|
||||
bool inUSAQI; /** PMS standard */
|
||||
uint8_t ledBarMode; /** @ref UseLedBar*/
|
||||
char model[16]; /** Model string value, Just define, don't know how much
|
||||
memory usage */
|
||||
char mqttBroker[128]; /** Mqtt broker link */
|
||||
uint32_t _check; /** Checksum configuration data */
|
||||
} ServerConfig_t;
|
||||
static ServerConfig_t serverConfig;
|
||||
|
||||
// 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;
|
||||
|
||||
static int ledSmState = APP_SM_NORMAL;
|
||||
static bool serverFailed = false;
|
||||
static bool configFailed = false;
|
||||
static bool wifiHasConfig = false;
|
||||
|
||||
int loopCount = 0;
|
||||
|
||||
WiFiManager wifiManager; /** wifi manager instance */
|
||||
|
||||
unsigned long currentMillis = 0;
|
||||
|
||||
const int oledInterval = 5000;
|
||||
unsigned long previousOled = 0;
|
||||
|
||||
const int sendToServerInterval = 60000;
|
||||
const int pollServerConfigInterval = 30000;
|
||||
const int co2CalibCountdown = 5; /** Seconds */
|
||||
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;
|
||||
|
||||
bool co2CalibrationRequest = false;
|
||||
uint32_t serverConfigLoadTime = 0;
|
||||
String HOTSPOT = "";
|
||||
|
||||
// const int tempHumInterval = 2500;
|
||||
// unsigned long previousTempHum = 0;
|
||||
|
||||
void boardInit(void);
|
||||
void failedHandler(String msg);
|
||||
void getServerConfig(void);
|
||||
void co2Calibration(void);
|
||||
|
||||
void setup() {
|
||||
if (DEBUG) {
|
||||
Serial.begin(115200);
|
||||
}
|
||||
|
||||
/** Board init */
|
||||
boardInit();
|
||||
|
||||
delay(500);
|
||||
|
||||
countdown(3);
|
||||
|
||||
if (connectWIFI) {
|
||||
connectToWifi();
|
||||
}
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
sendPing();
|
||||
Serial.println(F("WiFi connected!"));
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
}
|
||||
|
||||
getServerConfig();
|
||||
if (configFailed) {
|
||||
ledSmHandler(APP_SM_SENSOR_CONFIG_FAILED);
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
ledSmHandler(APP_SM_NORMAL);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
currentMillis = millis();
|
||||
updateTVOC();
|
||||
updateCo2();
|
||||
updatePm();
|
||||
sendToServer();
|
||||
getServerConfig();
|
||||
}
|
||||
|
||||
void updateTVOC() {
|
||||
delay(1000);
|
||||
|
||||
if (currentMillis - previousTVOC >= tvocInterval) {
|
||||
previousTVOC += tvocInterval;
|
||||
TVOC = ag.sgp41.getTvocIndex();
|
||||
NOX = ag.sgp41.getNoxIndex();
|
||||
}
|
||||
}
|
||||
|
||||
void updateCo2() {
|
||||
if (currentMillis - previousCo2 >= co2Interval) {
|
||||
previousCo2 += co2Interval;
|
||||
Co2 = ag.s8.getCo2();
|
||||
Serial.printf("CO2: %d\r\n", Co2);
|
||||
}
|
||||
}
|
||||
|
||||
void updatePm() {
|
||||
if (currentMillis - previousPm >= pmInterval) {
|
||||
previousPm += pmInterval;
|
||||
if (ag.pms5003t_1.readData()) {
|
||||
pm01 = ag.pms5003t_1.getPm01Ae();
|
||||
pm25 = ag.pms5003t_1.getPm25Ae();
|
||||
pm10 = ag.pms5003t_1.getPm10Ae();
|
||||
pm03PCount = ag.pms5003t_1.getPm03ParticleCount();
|
||||
temp = ag.pms5003t_1.getTemperature();
|
||||
hum = ag.pms5003t_1.getRelativeHumidity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sendPing() {
|
||||
String payload =
|
||||
"{\"wifi\":" + String(WiFi.RSSI()) + ", \"boot\":" + loopCount + "}";
|
||||
if (postToServer(payload)) {
|
||||
ledSmHandler(APP_SM_WIFI_OK_SERVER_CONNNECTED);
|
||||
} else {
|
||||
ledSmHandler(APP_SM_WIFI_OK_SERVER_CONNECT_FAILED);
|
||||
}
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
bool postToServer(String &payload) {
|
||||
String POSTURL = APIROOT +
|
||||
"sensors/airgradient:" + String(getNormalizedMac()) +
|
||||
"/measures";
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
|
||||
ag.statusLed.setOn();
|
||||
|
||||
http.begin(client, POSTURL);
|
||||
http.addHeader("content-type", "application/json");
|
||||
int httpCode = http.POST(payload);
|
||||
Serial.printf("Post to %s, %d\r\n", POSTURL.c_str(), httpCode);
|
||||
http.end();
|
||||
|
||||
ag.statusLed.setOff();
|
||||
|
||||
return (httpCode == 200);
|
||||
}
|
||||
|
||||
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) {
|
||||
postToServer(payload);
|
||||
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");
|
||||
ag.watchdog.reset();
|
||||
}
|
||||
|
||||
bool wifiMangerClientConnected(void) {
|
||||
return WiFi.softAPgetStationNum() ? true : false;
|
||||
}
|
||||
|
||||
// Wifi Manager
|
||||
void connectToWifi() {
|
||||
HOTSPOT = "airgradient-" + String(getNormalizedMac());
|
||||
|
||||
wifiManager.setConfigPortalBlocking(false);
|
||||
wifiManager.setTimeout(WIFI_CONNECT_COUNTDOWN_MAX);
|
||||
|
||||
wifiManager.setAPCallback([](WiFiManager *obj) {
|
||||
/** This callback if wifi connnected failed and try to start configuration
|
||||
* portal */
|
||||
ledSmState = APP_SM_WIFI_MANAGER_MODE;
|
||||
});
|
||||
wifiManager.setSaveConfigCallback([]() {
|
||||
/** Wifi connected save the configuration */
|
||||
ledSmHandler(APP_SM_WIFI_MANAGER_STA_CONNECTED);
|
||||
});
|
||||
wifiManager.setSaveParamsCallback([]() {
|
||||
/** Wifi set connect: ssid, password */
|
||||
ledSmHandler(APP_SM_WIFI_MANAGER_STA_CONNECTING);
|
||||
});
|
||||
wifiManager.autoConnect(HOTSPOT.c_str(), WIFI_HOTSPOT_PASSWORD_DEFAULT);
|
||||
|
||||
xTaskCreate(
|
||||
[](void *obj) {
|
||||
while (wifiManager.getConfigPortalActive()) {
|
||||
wifiManager.process();
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
},
|
||||
"wifi_cfg", 4096, NULL, 10, NULL);
|
||||
|
||||
uint32_t stimer = millis();
|
||||
bool clientConnectChanged = false;
|
||||
while (wifiManager.getConfigPortalActive()) {
|
||||
if (WiFi.isConnected() == false) {
|
||||
if (ledSmState == APP_SM_WIFI_MANAGER_MODE) {
|
||||
uint32_t ms = (uint32_t)(millis() - stimer);
|
||||
if (ms >= 100) {
|
||||
stimer = millis();
|
||||
ledSmHandler(ledSmState);
|
||||
}
|
||||
}
|
||||
|
||||
/** Check for client connect to change led color */
|
||||
bool clientConnected = wifiMangerClientConnected();
|
||||
if (clientConnected != clientConnectChanged) {
|
||||
clientConnectChanged = clientConnected;
|
||||
if (clientConnectChanged) {
|
||||
ledSmHandler(APP_SM_WIFI_MAMAGER_PORTAL_ACTIVE);
|
||||
} else {
|
||||
ledSmHandler(APP_SM_WIFI_MANAGER_MODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Show display wifi connect result failed */
|
||||
if (WiFi.isConnected() == false) {
|
||||
ledSmHandler(APP_SM_WIFI_MANAGER_CONNECT_FAILED);
|
||||
} else {
|
||||
wifiHasConfig = true;
|
||||
}
|
||||
}
|
||||
|
||||
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 boardInit(void) {
|
||||
if (Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin()) == false) {
|
||||
failedHandler("Init I2C failed");
|
||||
}
|
||||
|
||||
ag.watchdog.begin();
|
||||
|
||||
ag.button.begin();
|
||||
|
||||
ag.statusLed.begin();
|
||||
|
||||
if (ag.pms5003t_1.begin(Serial0) == false) {
|
||||
failedHandler("Init PMS5003T failed");
|
||||
}
|
||||
|
||||
if (ag.s8.begin(Serial1) == false) {
|
||||
failedHandler("Init SenseAirS8 failed");
|
||||
}
|
||||
|
||||
if (ag.sgp41.begin(Wire) == false) {
|
||||
failedHandler("Init SGP41 failed");
|
||||
}
|
||||
}
|
||||
|
||||
void failedHandler(String msg) {
|
||||
while (true) {
|
||||
Serial.println(msg);
|
||||
vTaskDelay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
void updateServerConfigLoadTime(void) {
|
||||
serverConfigLoadTime = millis();
|
||||
if (serverConfigLoadTime == 0) {
|
||||
serverConfigLoadTime = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void showConfig(void) {
|
||||
Serial.println("Server configuration: ");
|
||||
Serial.printf(" inF: %s\r\n", serverConfig.inF ? "true" : "false");
|
||||
Serial.printf(" inUSAQI: %s\r\n",
|
||||
serverConfig.inUSAQI ? "true" : "false");
|
||||
Serial.printf("useRGBLedBar: %d\r\n", (int)serverConfig.ledBarMode);
|
||||
Serial.printf(" Model: %.*s\r\n", sizeof(serverConfig.model),
|
||||
serverConfig.model);
|
||||
Serial.printf(" Mqtt Broker: %.*s\r\n", sizeof(serverConfig.mqttBroker),
|
||||
serverConfig.mqttBroker);
|
||||
}
|
||||
|
||||
void getServerConfig(void) {
|
||||
/** Only trigger load configuration again after pollServerConfigInterval sec
|
||||
*/
|
||||
if (serverConfigLoadTime) {
|
||||
uint32_t ms = (uint32_t)(millis() - serverConfigLoadTime);
|
||||
if (ms < pollServerConfigInterval) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
updateServerConfigLoadTime();
|
||||
|
||||
Serial.println("Trigger load server configuration");
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.println(
|
||||
"Ignore get server configuration because WIFI not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
// WiFiClient wifiClient;
|
||||
HTTPClient httpClient;
|
||||
|
||||
String getUrl = "http://hw.airgradient.com/sensors/airgradient:" +
|
||||
String(getNormalizedMac()) + "/one/config";
|
||||
Serial.println("HttpClient get: " + getUrl);
|
||||
if (httpClient.begin(getUrl) == false) {
|
||||
Serial.println("HttpClient init failed");
|
||||
updateServerConfigLoadTime();
|
||||
return;
|
||||
}
|
||||
|
||||
int respCode = httpClient.GET();
|
||||
|
||||
/** get failure */
|
||||
if (respCode != 200) {
|
||||
Serial.printf("HttpClient get failed: %d\r\n", respCode);
|
||||
updateServerConfigLoadTime();
|
||||
httpClient.end();
|
||||
configFailed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
String respContent = httpClient.getString();
|
||||
Serial.println("Server config: " + respContent);
|
||||
httpClient.end();
|
||||
|
||||
/** Parse JSON */
|
||||
JSONVar root = JSON.parse(respContent);
|
||||
if (JSON.typeof_(root) == "undefined") {
|
||||
Serial.println("Server configura JSON invalid");
|
||||
updateServerConfigLoadTime();
|
||||
configFailed = true;
|
||||
return;
|
||||
}
|
||||
configFailed = false;
|
||||
|
||||
/** Get "country" */
|
||||
bool inF = serverConfig.inF;
|
||||
if (JSON.typeof_(root["country"]) == "string") {
|
||||
String country = root["country"];
|
||||
if (country == "US") {
|
||||
inF = true;
|
||||
} else {
|
||||
inF = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get "pmStandard" */
|
||||
bool inUSAQI = serverConfig.inUSAQI;
|
||||
if (JSON.typeof_(root["pmStandard"]) == "string") {
|
||||
String standard = root["pmStandard"];
|
||||
if (standard == "ugm3") {
|
||||
inUSAQI = false;
|
||||
} else {
|
||||
inUSAQI = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get CO2 "co2CalibrationRequested" */
|
||||
co2CalibrationRequest = false;
|
||||
if (JSON.typeof_(root["co2CalibrationRequested"]) == "boolean") {
|
||||
co2CalibrationRequest = root["co2CalibrationRequested"];
|
||||
}
|
||||
|
||||
/** get "model" */
|
||||
String model = "";
|
||||
if (JSON.typeof_(root["model"]) == "string") {
|
||||
String _model = root["model"];
|
||||
model = _model;
|
||||
}
|
||||
|
||||
/** get "mqttBrokerUrl" */
|
||||
String mqtt = "";
|
||||
if (JSON.typeof_(root["mqttBrokerUrl"]) == "string") {
|
||||
String _mqtt = root["mqttBrokerUrl"];
|
||||
mqtt = _mqtt;
|
||||
}
|
||||
|
||||
if (inF != serverConfig.inF) {
|
||||
serverConfig.inF = inF;
|
||||
}
|
||||
if (inUSAQI != serverConfig.inUSAQI) {
|
||||
serverConfig.inUSAQI = inUSAQI;
|
||||
}
|
||||
if (model.length()) {
|
||||
if (model != String(serverConfig.model)) {
|
||||
memset(serverConfig.model, 0, sizeof(serverConfig.model));
|
||||
memcpy(serverConfig.model, model.c_str(), model.length());
|
||||
}
|
||||
}
|
||||
if (mqtt.length()) {
|
||||
if (mqtt != String(serverConfig.mqttBroker)) {
|
||||
memset(serverConfig.mqttBroker, 0, sizeof(serverConfig.mqttBroker));
|
||||
memcpy(serverConfig.mqttBroker, mqtt.c_str(), mqtt.length());
|
||||
}
|
||||
}
|
||||
|
||||
/** Show server configuration */
|
||||
showConfig();
|
||||
|
||||
/** Calibration */
|
||||
if (co2CalibrationRequest) {
|
||||
co2Calibration();
|
||||
}
|
||||
}
|
||||
|
||||
void co2Calibration(void) {
|
||||
/** Count down for co2CalibCountdown secs */
|
||||
for (int i = 0; i < co2CalibCountdown; i++) {
|
||||
Serial.printf("Start CO2 calib after %d sec\r\n", co2CalibCountdown - i);
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
if (ag.s8.setBaselineCalibration()) {
|
||||
Serial.println("Calibration success");
|
||||
delay(1000);
|
||||
Serial.println("Wait for calib finish...");
|
||||
int count = 0;
|
||||
while (ag.s8.isBaseLineCalibrationDone() == false) {
|
||||
delay(1000);
|
||||
count++;
|
||||
}
|
||||
Serial.printf("Calib finish after %d sec\r\n", count);
|
||||
delay(2000);
|
||||
} else {
|
||||
Serial.println("Calibration failure!!!");
|
||||
delay(2000);
|
||||
}
|
||||
}
|
||||
|
||||
void ledSmHandler(int sm) {
|
||||
if (sm > APP_SM_NORMAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ledSmState = sm;
|
||||
switch (sm) {
|
||||
case APP_SM_WIFI_MANAGER_MODE: {
|
||||
ag.statusLed.setToggle();
|
||||
break;
|
||||
}
|
||||
case APP_SM_WIFI_MAMAGER_PORTAL_ACTIVE: {
|
||||
ag.statusLed.setOn();
|
||||
break;
|
||||
}
|
||||
case APP_SM_WIFI_MANAGER_STA_CONNECTING: {
|
||||
ag.statusLed.setOff();
|
||||
break;
|
||||
}
|
||||
case APP_SM_WIFI_MANAGER_STA_CONNECTED: {
|
||||
ag.statusLed.setOff();
|
||||
break;
|
||||
}
|
||||
case APP_SM_WIFI_OK_SERVER_CONNECTING: {
|
||||
ag.statusLed.setOff();
|
||||
break;
|
||||
}
|
||||
case APP_SM_WIFI_OK_SERVER_CONNNECTED: {
|
||||
ag.statusLed.setOff();
|
||||
ag.statusLed.setOn();
|
||||
delay(50);
|
||||
ag.statusLed.setOff();
|
||||
delay(950);
|
||||
ag.statusLed.setOn();
|
||||
delay(50);
|
||||
ag.statusLed.setOff();
|
||||
break;
|
||||
}
|
||||
case APP_SM_WIFI_MANAGER_CONNECT_FAILED: {
|
||||
ag.statusLed.setOff();
|
||||
for (int j = 0; j < 3; j++) {
|
||||
for (int i = 0; i < 3 * 2; i++) {
|
||||
ag.statusLed.setToggle();
|
||||
delay(100);
|
||||
}
|
||||
delay(2000);
|
||||
}
|
||||
ag.statusLed.setOff();
|
||||
break;
|
||||
}
|
||||
case APP_SM_WIFI_OK_SERVER_CONNECT_FAILED: {
|
||||
ag.statusLed.setOff();
|
||||
for (int j = 0; j < 3; j++) {
|
||||
for (int i = 0; i < 4 * 2; i++) {
|
||||
ag.statusLed.setToggle();
|
||||
delay(100);
|
||||
}
|
||||
delay(2000);
|
||||
}
|
||||
ag.statusLed.setOff();
|
||||
break;
|
||||
}
|
||||
case APP_SM_WIFI_OK_SERVER_OK_SENSOR_CONFIG_FAILED: {
|
||||
ag.statusLed.setOff();
|
||||
for (int j = 0; j < 3; j++) {
|
||||
for (int i = 0; i < 5 * 2; i++) {
|
||||
ag.statusLed.setToggle();
|
||||
delay(100);
|
||||
}
|
||||
delay(2000);
|
||||
}
|
||||
ag.statusLed.setOff();
|
||||
break;
|
||||
}
|
||||
case APP_SM_WIFI_LOST: {
|
||||
ag.statusLed.setOff();
|
||||
break;
|
||||
}
|
||||
case APP_SM_SERVER_LOST: {
|
||||
ag.statusLed.setOff();
|
||||
break;
|
||||
}
|
||||
case APP_SM_SENSOR_CONFIG_FAILED: {
|
||||
ag.statusLed.setOff();
|
||||
break;
|
||||
}
|
||||
case APP_SM_NORMAL: {
|
||||
ag.statusLed.setOff();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
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,45 +0,0 @@
|
||||
/*
|
||||
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);
|
||||
}
|
43
examples/TestCO2/TestCO2.ino
Normal file
43
examples/TestCO2/TestCO2.ino
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
This is sample code for the AirGradient library with a minimal implementation to read CO2 values from the SenseAir S8 sensor.
|
||||
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
*/
|
||||
|
||||
#include <AirGradient.h>
|
||||
|
||||
#ifdef ESP8266
|
||||
AirGradient ag = AirGradient(BOARD_DIY_PRO_INDOOR_V4_2);
|
||||
// AirGradient ag = AirGradient(BOARD_DIY_BASIC_KIT);
|
||||
#else
|
||||
// AirGradient ag = AirGradient(BOARD_ONE_INDOOR_MONITOR_V9_0);
|
||||
AirGradient ag = AirGradient(BOARD_OUTDOOR_MONITOR_V1_3);
|
||||
#endif
|
||||
|
||||
void failedHandler(String msg);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
/** Init CO2 sensor */
|
||||
#ifdef ESP8266
|
||||
if (ag.s8.begin(&Serial) == false) {
|
||||
#else
|
||||
if (ag.s8.begin(Serial1) == false) {
|
||||
#endif
|
||||
failedHandler("SenseAir S8 init failed");
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
int CO2 = ag.s8.getCo2();
|
||||
Serial.printf("CO2: %d\r\n", CO2);
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
void failedHandler(String msg) {
|
||||
while (true) {
|
||||
Serial.println(msg);
|
||||
delay(1000);
|
||||
}
|
||||
}
|
369
examples/TestESP32/TestESP32.ino
Normal file
369
examples/TestESP32/TestESP32.ino
Normal file
@ -0,0 +1,369 @@
|
||||
#include <AirGradient.h>
|
||||
#include <HardwareSerial.h>
|
||||
#include <Wire.h>
|
||||
|
||||
/**
|
||||
* AirGradient use ESP32C3 has default Serial0 use for PMS5003, to print log
|
||||
* should use esp-hal-log instead.
|
||||
*/
|
||||
#include <esp32-hal-log.h>
|
||||
|
||||
/**
|
||||
* @brief Define test board
|
||||
*/
|
||||
#define TEST_BOARD_OUTDOOR_MONITOR_V1_3 0
|
||||
#define TEST_BOARD_ONE_INDOOR_MONITOR_V9_0 1
|
||||
/**
|
||||
* @brief Define test sensor
|
||||
*/
|
||||
#define TEST_SENSOR_SenseAirS8 0
|
||||
#define TEST_SENSOR_SHT4x 0
|
||||
#define TEST_SENSOR_SGP4x 0
|
||||
#define TEST_SWITCH 0
|
||||
#define TEST_OLED 0
|
||||
#if TEST_BOARD_OUTDOOR_MONITOR_V1_3
|
||||
#define TEST_STATUS_LED 0
|
||||
#define TEST_PMS5003T 1
|
||||
#endif
|
||||
#define TEST_WATCHDOG 1
|
||||
|
||||
#if TEST_BOARD_ONE_INDOOR_MONITOR_V9_0
|
||||
#define TEST_LED_BAR 1
|
||||
#define TEST_SENSOR_PMS5003 0
|
||||
#endif
|
||||
|
||||
#if TEST_BOARD_OUTDOOR_MONITOR_V1_3
|
||||
AirGradient ag(BOARD_OUTDOOR_MONITOR_V1_3);
|
||||
#elif TEST_BOARD_ONE_INDOOR_MONITOR_V9_0
|
||||
AirGradient ag(BOARD_ONE_INDOOR_MONITOR_V9_0);
|
||||
#else
|
||||
#error "Must enable board test
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
|
||||
/** Print All AirGradient board define */
|
||||
printBoardDef(NULL);
|
||||
|
||||
#if TEST_SENSOR_SenseAirS8
|
||||
/** Cause Serial is use default for PMS, CO2S8 should be use Serial 1
|
||||
* Serial 1 will be init by SenseAirS8 don't need to init any more on user
|
||||
* code
|
||||
*/
|
||||
if (ag.s8.begin(Serial1)) {
|
||||
log_i("CO2S8 sensor init success");
|
||||
} else {
|
||||
log_i("CO2S8 sensor init failure");
|
||||
}
|
||||
|
||||
log_i("Start baseline calib");
|
||||
if (ag.s8.setBaselineCalibration()) {
|
||||
log_i("Calib success");
|
||||
} else {
|
||||
log_e("Calib failure");
|
||||
}
|
||||
delay(5000); // Wait for calib done
|
||||
#endif
|
||||
|
||||
#if TEST_SENSOR_PMS5003
|
||||
if (ag.pms5003.begin(Serial0)) {
|
||||
log_i("PMS5003 sensor init success");
|
||||
} else {
|
||||
log_i("PMS5003 sensor init failure");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_PMS5003T
|
||||
/**
|
||||
* @brief PMS5003T_1 alway connect to Serial (esp32c3 RXD0, RXD0)
|
||||
*/
|
||||
if (ag.pms5003t_1.begin(Serial)) {
|
||||
log_i("PMS5003T_1 sensor init success");
|
||||
} else {
|
||||
log_i("PMS5003T_1 sensor init failure");
|
||||
}
|
||||
|
||||
// TODO Only test without senseair s8 because it's share the UART bus
|
||||
#if TEST_SENSOR_SenseAirS8 == 0
|
||||
if (ag.pms5003t_2.begin(Serial1)) {
|
||||
log_i("PMS5003T_2 sensor init success");
|
||||
} else {
|
||||
log_i("PMS5003T_2 sensor init failure");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if TEST_SENSOR_SHT4x || TEST_SENSOR_SGP4x || TEST_OLED
|
||||
Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin());
|
||||
#endif
|
||||
|
||||
#if TEST_SENSOR_SHT4x
|
||||
|
||||
if (ag.sht.begin(Wire)) {
|
||||
log_i("SHT init success");
|
||||
} else {
|
||||
log_i("SHT init failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_SENSOR_SGP4x
|
||||
if (ag.sgp41.begin(Wire)) {
|
||||
log_i("SGP init success");
|
||||
} else {
|
||||
log_e("SGP init failure");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_LED
|
||||
led.begin();
|
||||
#endif
|
||||
|
||||
#if TEST_SWITCH
|
||||
ag.button.begin();
|
||||
#endif
|
||||
|
||||
#if TEST_OLED
|
||||
ag.display.begin(Wire);
|
||||
ag.display.setTextSize(1);
|
||||
ag.display.setCursor(0, 0);
|
||||
ag.display.setTextColor(1);
|
||||
ag.display.setText("180s to connect to wifi hostpost AC-xxxxx");
|
||||
ag.display.show();
|
||||
#endif
|
||||
|
||||
#if TEST_STATUS_LED
|
||||
ag.statusLed.begin();
|
||||
#endif
|
||||
|
||||
#if TEST_LED_BAR
|
||||
ag.ledBar.begin();
|
||||
#endif
|
||||
|
||||
#if TEST_WATCHDOG
|
||||
ag.watchdog.begin();
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop() {
|
||||
uint32_t ms;
|
||||
#if TEST_SENSOR_SenseAirS8
|
||||
static uint32_t lastTime = 0;
|
||||
|
||||
/** Wait for sensor ready */
|
||||
ms = (uint32_t)(millis() - lastTime);
|
||||
if (ms >= 1000) {
|
||||
lastTime = millis();
|
||||
log_i("CO2: %d (PPM)", ag.s8.getCo2());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_SENSOR_PMS5003
|
||||
static uint32_t pmsTime = 0;
|
||||
ms = (uint32_t)(millis() - pmsTime);
|
||||
|
||||
if (ms >= 1000) {
|
||||
pmsTime = millis();
|
||||
if (ag.pms5003.readData()) {
|
||||
log_i("Passive mode PM 1.0 (ug/m3): %d", ag.pms5003.getPm10Ae());
|
||||
log_i("Passive mode PM 2.5 (ug/m3): %d", ag.pms5003.getPm25Ae());
|
||||
log_i("Passive mode PM 10.0 (ug/m3): %d", ag.pms5003.getPm10Ae());
|
||||
} else {
|
||||
log_i("PMS sensor read failure");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_PMS5003T
|
||||
static uint32_t pmsTime = 0;
|
||||
ms = (uint32_t)(millis() - pmsTime);
|
||||
|
||||
if (ms >= 1000) {
|
||||
pmsTime = millis();
|
||||
if (ag.pms5003t_1.readData()) {
|
||||
log_i("PMS5003_1 PM 1.0 (ug/m3): %d", ag.pms5003t_1.getPm10Ae());
|
||||
log_i("PMS5003_1 PM 2.5 (ug/m3): %d", ag.pms5003t_1.getPm25Ae());
|
||||
log_i("PMS5003_1 PM 10.0 (ug/m3): %d", ag.pms5003t_1.getPm10Ae());
|
||||
log_i("PMS5003_1 PM 3.0 (ug/m3): %d",
|
||||
ag.pms5003t_1.getPm03ParticleCount());
|
||||
log_i("Temperature : %02f °C",
|
||||
ag.pms5003t_1.getTemperature());
|
||||
log_i("Humidity : %02f %%",
|
||||
ag.pms5003t_1.getRelativeHumidity());
|
||||
} else {
|
||||
log_i("PMS5003_1 sensor read failure");
|
||||
}
|
||||
|
||||
if (ag.pms5003t_2.readData()) {
|
||||
log_i("PMS5003_2 PM 1.0 (ug/m3): %d", ag.pms5003t_2.getPm10Ae());
|
||||
log_i("PMS5003_2 PM 2.5 (ug/m3): %d", ag.pms5003t_2.getPm25Ae());
|
||||
log_i("PMS5003_2 PM 10.0 (ug/m3): %d", ag.pms5003t_2.getPm10Ae());
|
||||
log_i("PMS5003_2 PM 3.0 (ug/m3): %d",
|
||||
ag.pms5003t_2.getPm03ParticleCount());
|
||||
// log_i("Temperature : %02f °C",
|
||||
// ag.pms5003t_1.getTemperature());
|
||||
// log_i("Humidity : %02f %%",
|
||||
// ag.pms5003t_1.getRelativeHumidity());
|
||||
} else {
|
||||
log_i("PMS5003_2 sensor read failure");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_SENSOR_SHT4x
|
||||
/**
|
||||
* @brief Get SHT sensor data each 1sec
|
||||
*
|
||||
*/
|
||||
static uint32_t shtTime = 0;
|
||||
ms = (uint32_t)(millis() - shtTime);
|
||||
if (ms >= 1000) {
|
||||
shtTime = millis();
|
||||
log_i("Get sht temperature: %0.2f (degree celsius)",
|
||||
ag.sht.getTemperature());
|
||||
log_i("Get sht temperature: %0.2f (%%)", ag.sht.getRelativeHumidity());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_SENSOR_SGP4x
|
||||
static uint32_t sgpTime;
|
||||
ms = (uint32_t)(millis() - sgpTime);
|
||||
if (ms >= 1000) {
|
||||
sgpTime = millis();
|
||||
uint16_t rawVOC;
|
||||
log_i("Get TVOC: %d", ag.sgp41.getTvocIndex());
|
||||
log_i("Get NOx: %d", ag.sgp41.getNoxIndex());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_LED
|
||||
static uint32_t ledTime;
|
||||
#if TEST_BOARD_OUTDOOR_MONITOR_V1_3
|
||||
// ms = (uint32_t)(millis() - ledTime);
|
||||
// if(ms >= 500)
|
||||
// {
|
||||
// ledTime = millis();
|
||||
// led.ledToggle();
|
||||
// }
|
||||
#elif TEST_BOARD_ONE_INDOOR_MONITOR_V9_0
|
||||
|
||||
static int ledIndex;
|
||||
static int ledIndexOld;
|
||||
ms = (uint32_t)(millis() - ledTime);
|
||||
if (ms >= 50) {
|
||||
ledTime = millis();
|
||||
if (ledIndex == ledIndexOld) {
|
||||
led.ledOff();
|
||||
} else {
|
||||
// Turn last LED off
|
||||
led.ledSetColor(0, 0, 0, ledIndexOld);
|
||||
}
|
||||
|
||||
// Turn new led ON
|
||||
led.ledSetColor(255, 0, 0, ledIndex);
|
||||
ledIndexOld = ledIndex;
|
||||
ledIndex++;
|
||||
if (ledIndex >= led.getNumberOfLed()) {
|
||||
ledIndex = 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if TEST_SWITCH
|
||||
static PushButton::State stateOld = PushButton::State::BUTTON_RELEASED;
|
||||
PushButton::State state = ag.button.getState();
|
||||
if (state != stateOld) {
|
||||
stateOld = state;
|
||||
log_i("Button state changed: %s", ag.button.toString(state).c_str());
|
||||
if (state == PushButton::State::BUTTON_PRESSED) {
|
||||
ag.statusLed.setOn();
|
||||
} else {
|
||||
ag.statusLed.setOff();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_LED_BAR
|
||||
static uint32_t ledTime;
|
||||
static uint8_t ledNum = 0;
|
||||
static uint8_t ledIndex = 0;
|
||||
static uint8_t ledStep = 0;
|
||||
static bool ledOn = false;
|
||||
|
||||
if (ledNum == 0) {
|
||||
ledNum = ag.ledBar.getNumberOfLed();
|
||||
log_i("Get number of led: %d", ledNum);
|
||||
if (ledNum) {
|
||||
ag.ledBar.setBrighness(0xff);
|
||||
for (int i = 0; i < ledNum; i++) {
|
||||
// ag.ledBar.setColor(0xff, 0xff, 0xff, i);
|
||||
// ag.ledBar.setColor(204, 136, 153, i);
|
||||
// ag.ledBar.setColor(204, 0, 0, i);
|
||||
// ag.ledBar.setColor(204, 100, 153, i);
|
||||
ag.ledBar.setColor(0, 136, 255, i);
|
||||
}
|
||||
ag.ledBar.show();
|
||||
}
|
||||
} else {
|
||||
ms = (uint32_t)(millis() - ledTime);
|
||||
if (ms >= 500) {
|
||||
ledTime = millis();
|
||||
switch (ledStep) {
|
||||
case 0: {
|
||||
ag.ledBar.setColor(255, 0, 0, ledIndex);
|
||||
ledIndex++;
|
||||
if (ledIndex >= ledNum) {
|
||||
ag.ledBar.setColor(0, 0, 0);
|
||||
ledIndex = 0;
|
||||
ledStep = 1;
|
||||
}
|
||||
ag.ledBar.show();
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
ledIndex++;
|
||||
if (ledIndex >= ledNum) {
|
||||
ag.ledBar.setColor(255, 0, 0);
|
||||
ag.ledBar.show();
|
||||
ledIndex = ledNum - 1;
|
||||
ledStep = 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (ledOn) {
|
||||
ag.ledBar.setColor(255, 0, 0);
|
||||
} else {
|
||||
ag.ledBar.setColor(0, 0, 0);
|
||||
}
|
||||
ledOn = !ledOn;
|
||||
ag.ledBar.show();
|
||||
|
||||
ledIndex--;
|
||||
if (ledIndex == 0) {
|
||||
ag.ledBar.setColor(0, 0, 0);
|
||||
ag.ledBar.show();
|
||||
ledStep = 0;
|
||||
ledIndex = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_WATCHDOG
|
||||
static uint32_t wdgTime;
|
||||
ms = (uint32_t)(millis() - wdgTime);
|
||||
if (ms >= (1000 * 60)) {
|
||||
wdgTime = millis();
|
||||
/** Reset watchdog reach 1 minutes */
|
||||
ag.watchdog.reset();
|
||||
}
|
||||
#endif
|
||||
}
|
164
examples/TestESP8266/TestESP8266.ino
Normal file
164
examples/TestESP8266/TestESP8266.ino
Normal file
@ -0,0 +1,164 @@
|
||||
#include <AirGradient.h>
|
||||
#include <Wire.h>
|
||||
|
||||
/**
|
||||
* @brief Define test board
|
||||
*/
|
||||
#define TEST_BOARD_DIY_BASIC_KIT 0
|
||||
#define TEST_BOARD_DIY_PRO_INDOOR_V4_2 1
|
||||
|
||||
/**
|
||||
* @brief Define test sensor
|
||||
*/
|
||||
#define TEST_SENSOR_SenseAirS8 0
|
||||
#define TEST_SENSOR_PMS5003 0
|
||||
#define TEST_SENSOR_SHT4x 0
|
||||
#define TEST_SENSOR_SGP4x 1
|
||||
#define TEST_SWITCH 0
|
||||
#define TEST_OLED 0
|
||||
|
||||
#if TEST_BOARD_DIY_BASIC_KIT
|
||||
AirGradient ag(BOARD_DIY_BASIC_KIT);
|
||||
#elif TEST_BOARD_DIY_PRO_INDOOR_V4_2
|
||||
AirGradient ag(BOARD_DIY_PRO_INDOOR_V4_2);
|
||||
#else
|
||||
#error "Board test not defined"
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
/** Print All AirGradient board define */
|
||||
printBoardDef(&Serial);
|
||||
|
||||
#if TEST_SENSOR_SenseAirS8
|
||||
if (ag.s8.begin(&Serial) == true) {
|
||||
Serial.println("CO2S8 sensor init success");
|
||||
} else {
|
||||
Serial.println("CO2S8 sensor init failure");
|
||||
}
|
||||
|
||||
if (ag.s8.setBaselineCalibration()) {
|
||||
Serial.println("Manual calib success");
|
||||
} else {
|
||||
Serial.println("Manual calib failure");
|
||||
}
|
||||
delay(5000);
|
||||
#endif
|
||||
|
||||
#if TEST_SENSOR_PMS5003
|
||||
if (ag.pms5003.begin(&Serial) == true) {
|
||||
Serial.println("PMS5003 sensor init success");
|
||||
} else {
|
||||
Serial.println("PMS5003 sensor init failure");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_SENSOR_SHT4x || TEST_SENSOR_SGP4x || TEST_OLED
|
||||
Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin());
|
||||
#endif
|
||||
|
||||
#if TEST_SENSOR_SHT4x
|
||||
if (ag.sht.begin(Wire, Serial)) {
|
||||
Serial.println("SHT init success");
|
||||
} else {
|
||||
Serial.println("SHT init failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_SENSOR_SGP4x
|
||||
if (ag.sgp41.begin(Wire, Serial)) {
|
||||
Serial.println("SGP init succses");
|
||||
} else {
|
||||
Serial.println("SGP init failure");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_SWITCH
|
||||
ag.button.begin(Serial);
|
||||
#endif
|
||||
|
||||
#if TEST_OLED
|
||||
ag.display.begin(Wire, Serial);
|
||||
ag.display.setTextSize(1);
|
||||
ag.display.setCursor(0, 0);
|
||||
ag.display.setTextColor(1);
|
||||
ag.display.setText("Hello");
|
||||
ag.display.show();
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop() {
|
||||
uint32_t ms;
|
||||
#if TEST_SENSOR_SenseAirS8
|
||||
static uint32_t lastTime = 0;
|
||||
|
||||
/** Wait for sensor ready */
|
||||
// if(co2s8.isReady())
|
||||
// {
|
||||
// Get sensor data each 1sec
|
||||
ms = (uint32_t)(millis() - lastTime);
|
||||
if (ms >= 1000) {
|
||||
lastTime = millis();
|
||||
Serial.printf("CO2: %d (PMM)\r\n", ag.s8.getCo2());
|
||||
}
|
||||
// }
|
||||
#endif
|
||||
|
||||
#if TEST_SENSOR_PMS5003
|
||||
static uint32_t pmsTime = 0;
|
||||
ms = (uint32_t)(millis() - pmsTime);
|
||||
if (ms >= 1000) {
|
||||
pmsTime = millis();
|
||||
if (ag.pms5003.readData()) {
|
||||
Serial.printf("Passive mode PM 1.0 (ug/m3): %d\r\n",
|
||||
ag.pms5003.getPm01Ae());
|
||||
Serial.printf("Passive mode PM 2.5 (ug/m3): %d\r\n",
|
||||
ag.pms5003.getPm25Ae());
|
||||
Serial.printf("Passive mode PM 10.5 (ug/m3): %d\r\n",
|
||||
ag.pms5003.getPm10Ae());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_SENSOR_SHT4x
|
||||
/**
|
||||
* @brief Get SHT sensor data each 1sec
|
||||
*
|
||||
*/
|
||||
static uint32_t shtTime = 0;
|
||||
ms = (uint32_t)(millis() - shtTime);
|
||||
if (ms >= 1000) {
|
||||
shtTime = millis();
|
||||
float temperature, humidity;
|
||||
Serial.printf("SHT Temperature: %f, Humidity: %f\r\n",
|
||||
ag.sht.getTemperature(), ag.sht.getRelativeHumidity());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_SENSOR_SGP4x
|
||||
static uint32_t sgpTime;
|
||||
ms = (uint32_t)(millis() - sgpTime);
|
||||
|
||||
/***
|
||||
* Must call this task on loop and avoid delay on loop over 1000 ms
|
||||
*/
|
||||
ag.sgp41.handle();
|
||||
|
||||
if (ms >= 1000) {
|
||||
sgpTime = millis();
|
||||
Serial.printf("SGP TVOC: %d, NOx: %d\r\n", ag.sgp41.getTvocIndex(),
|
||||
ag.sgp41.getNoxIndex());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEST_SWITCH
|
||||
static PushButton::State stateOld = PushButton::State::BUTTON_RELEASED;
|
||||
PushButton::State state = ag.button.getState();
|
||||
if (state != stateOld) {
|
||||
stateOld = state;
|
||||
Serial.printf("Button state changed: %s\r\n",
|
||||
ag.button.toString(state).c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
69
examples/TestPM/TestPM.ino
Normal file
69
examples/TestPM/TestPM.ino
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
This is sample code for the AirGradient library with a minimal implementation to read PM values from the Plantower sensor.
|
||||
|
||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
*/
|
||||
|
||||
#include <AirGradient.h>
|
||||
|
||||
#ifdef ESP8266
|
||||
AirGradient ag = AirGradient(BOARD_DIY_PRO_INDOOR_V4_2);
|
||||
// AirGradient ag = AirGradient(BOARD_DIY_BASIC_KIT);
|
||||
#else
|
||||
// AirGradient ag = AirGradient(BOARD_ONE_INDOOR_MONITOR_V9_0);
|
||||
AirGradient ag = AirGradient(BOARD_OUTDOOR_MONITOR_V1_3);
|
||||
#endif
|
||||
|
||||
void failedHandler(String msg);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
#ifdef ESP8266
|
||||
if(ag.pms5003.begin(&Serial) == false) {
|
||||
failedHandler("Init PMS5003 failed");
|
||||
}
|
||||
#else
|
||||
if (ag.getBoardType() == BOARD_OUTDOOR_MONITOR_V1_3) {
|
||||
if(ag.pms5003t_1.begin(Serial0) == false) {
|
||||
failedHandler("Init PMS5003T failed");
|
||||
}
|
||||
} else {
|
||||
if(ag.pms5003.begin(Serial0) == false) {
|
||||
failedHandler("Init PMS5003T failed");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop() {
|
||||
int PM2;
|
||||
#ifdef ESP8266
|
||||
PM2 = ag.pms5003.getPm25Ae();
|
||||
Serial.printf("PM2.5 in ug/m3: %d\r\n", PM2);
|
||||
Serial.printf("PM2.5 in US AQI: %d\r\n", ag.pms5003.convertPm25ToUsAqi(PM2));
|
||||
#else
|
||||
if (ag.getBoardType() == BOARD_OUTDOOR_MONITOR_V1_3) {
|
||||
PM2 = ag.pms5003t_1.getPm25Ae();
|
||||
} else {
|
||||
PM2 = ag.pms5003.getPm25Ae();
|
||||
}
|
||||
|
||||
Serial.printf("PM2.5 in ug/m3: %d\r\n", PM2);
|
||||
if (ag.getBoardType() == BOARD_OUTDOOR_MONITOR_V1_3) {
|
||||
Serial.printf("PM2.5 in US AQI: %d\r\n",
|
||||
ag.pms5003t_1.convertPm25ToUsAqi(PM2));
|
||||
} else {
|
||||
Serial.printf("PM2.5 in US AQI: %d\r\n",
|
||||
ag.pms5003.convertPm25ToUsAqi(PM2));
|
||||
}
|
||||
#endif
|
||||
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
void failedHandler(String msg) {
|
||||
while (true) {
|
||||
Serial.println(msg);
|
||||
delay(1000);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user