2024-02-03 10:46:26 +07:00
|
|
|
/*
|
2024-02-06 10:41:10 +07:00
|
|
|
This is the code for the AirGradient DIY BASIC Air Quality Monitor with an D1
|
|
|
|
ESP8266 Microcontroller.
|
2024-02-03 10:46:26 +07:00
|
|
|
|
2024-02-06 10:41:10 +07:00
|
|
|
It is an air quality monitor for PM2.5, CO2, Temperature and Humidity with a
|
|
|
|
small display and can send data over Wifi.
|
2024-02-03 10:46:26 +07:00
|
|
|
|
|
|
|
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/
|
|
|
|
|
2024-02-06 10:41:10 +07:00
|
|
|
Please make sure you have esp8266 board manager installed. Tested with
|
|
|
|
version 3.1.2.
|
2024-02-03 10:46:26 +07:00
|
|
|
|
|
|
|
Set board to "LOLIN(WEMOS) D1 R2 & mini"
|
|
|
|
|
2024-02-06 10:41:10 +07:00
|
|
|
Configuration parameters, e.g. Celsius / Fahrenheit or PM unit (US AQI vs ug/m3)
|
|
|
|
can be set through the AirGradient dashboard.
|
2024-02-03 10:46:26 +07:00
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2024-04-04 18:35:15 +07:00
|
|
|
#include "AgApiClient.h"
|
2024-04-07 16:39:01 +07:00
|
|
|
#include "AgConfigure.h"
|
|
|
|
#include "AgSchedule.h"
|
|
|
|
#include "AgWiFiConnector.h"
|
2024-06-24 18:34:24 +07:00
|
|
|
#include "LocalServer.h"
|
|
|
|
#include "OpenMetrics.h"
|
|
|
|
#include "MqttClient.h"
|
2024-04-07 16:39:01 +07:00
|
|
|
#include <AirGradient.h>
|
2024-02-03 10:46:26 +07:00
|
|
|
#include <ESP8266HTTPClient.h>
|
|
|
|
#include <ESP8266WiFi.h>
|
2024-06-24 18:34:24 +07:00
|
|
|
#include <ESP8266mDNS.h>
|
2024-02-03 10:46:26 +07:00
|
|
|
#include <WiFiClient.h>
|
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
#define LED_BAR_ANIMATION_PERIOD 100 /** ms */
|
|
|
|
#define DISP_UPDATE_INTERVAL 2500 /** ms */
|
|
|
|
#define SERVER_CONFIG_SYNC_INTERVAL 60000 /** ms */
|
|
|
|
#define SERVER_SYNC_INTERVAL 60000 /** ms */
|
|
|
|
#define MQTT_SYNC_INTERVAL 60000 /** ms */
|
|
|
|
#define SENSOR_CO2_CALIB_COUNTDOWN_MAX 5 /** sec */
|
|
|
|
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */
|
|
|
|
#define SENSOR_CO2_UPDATE_INTERVAL 4000 /** ms */
|
|
|
|
#define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */
|
|
|
|
#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 2000 /** ms */
|
|
|
|
#define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */
|
|
|
|
#define FIRMWARE_CHECK_FOR_UPDATE_MS (60 * 60 * 1000) /** ms */
|
|
|
|
|
|
|
|
static AirGradient ag(DIY_BASIC);
|
2024-04-07 16:39:01 +07:00
|
|
|
static Configuration configuration(Serial);
|
|
|
|
static AgApiClient apiClient(Serial, configuration);
|
2024-06-18 19:59:29 +07:00
|
|
|
static Measurements measurements;
|
2024-06-24 18:34:24 +07:00
|
|
|
static OledDisplay oledDisplay(configuration, measurements, Serial);
|
|
|
|
static StateMachine stateMachine(oledDisplay, Serial, measurements,
|
|
|
|
configuration);
|
|
|
|
static WifiConnector wifiConnector(oledDisplay, Serial, stateMachine,
|
|
|
|
configuration);
|
|
|
|
static OpenMetrics openMetrics(measurements, configuration, wifiConnector,
|
|
|
|
apiClient);
|
|
|
|
static LocalServer localServer(Serial, openMetrics, measurements, configuration,
|
|
|
|
wifiConnector);
|
|
|
|
static MqttClient mqttClient(Serial);
|
|
|
|
|
|
|
|
static int getCO2FailCount = 0;
|
|
|
|
static AgFirmwareMode fwMode = FW_MODE_I_BASIC_40PS;
|
|
|
|
|
|
|
|
static String fwNewVersion;
|
2024-02-04 15:04:38 +07:00
|
|
|
|
|
|
|
static void boardInit(void);
|
|
|
|
static void failedHandler(String msg);
|
2024-06-24 18:34:24 +07:00
|
|
|
static void configurationUpdateSchedule(void);
|
|
|
|
static void appDispHandler(void);
|
|
|
|
static void oledDisplaySchedule(void);
|
|
|
|
static void updateTvoc(void);
|
|
|
|
static void updatePm(void);
|
2024-02-04 15:04:38 +07:00
|
|
|
static void sendDataToServer(void);
|
2024-06-24 18:34:24 +07:00
|
|
|
static void tempHumUpdate(void);
|
|
|
|
static void co2Update(void);
|
|
|
|
static void mdnsInit(void);
|
|
|
|
static void initMqtt(void);
|
|
|
|
static void factoryConfigReset(void);
|
|
|
|
static void wdgFeedUpdate(void);
|
|
|
|
static bool sgp41Init(void);
|
|
|
|
static void wifiFactoryConfigure(void);
|
|
|
|
static void mqttHandle(void);
|
|
|
|
|
|
|
|
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplaySchedule);
|
2024-06-04 18:29:35 +07:00
|
|
|
AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL,
|
2024-06-24 18:34:24 +07:00
|
|
|
configurationUpdateSchedule);
|
|
|
|
AgSchedule agApiPostSchedule(SERVER_SYNC_INTERVAL, sendDataToServer);
|
2024-02-29 15:07:23 +07:00
|
|
|
AgSchedule co2Schedule(SENSOR_CO2_UPDATE_INTERVAL, co2Update);
|
2024-06-24 18:34:24 +07:00
|
|
|
AgSchedule pmsSchedule(SENSOR_PM_UPDATE_INTERVAL, updatePm);
|
2024-02-29 15:07:23 +07:00
|
|
|
AgSchedule tempHumSchedule(SENSOR_TEMP_HUM_UPDATE_INTERVAL, tempHumUpdate);
|
2024-06-24 18:34:24 +07:00
|
|
|
AgSchedule tvocSchedule(SENSOR_TVOC_UPDATE_INTERVAL, updateTvoc);
|
|
|
|
AgSchedule watchdogFeedSchedule(60000, wdgFeedUpdate);
|
|
|
|
AgSchedule mqttSchedule(MQTT_SYNC_INTERVAL, mqttHandle);
|
2024-02-03 10:46:26 +07:00
|
|
|
|
|
|
|
void setup() {
|
2024-06-24 18:34:24 +07:00
|
|
|
/** Serial for print debug message */
|
2024-02-03 10:46:26 +07:00
|
|
|
Serial.begin(115200);
|
2024-06-24 18:34:24 +07:00
|
|
|
delay(100); /** For bester show log */
|
|
|
|
|
|
|
|
/** Print device ID into log */
|
|
|
|
Serial.println("Serial nr: " + ag.deviceId());
|
|
|
|
|
|
|
|
/** Initialize local configure */
|
|
|
|
configuration.begin();
|
2024-02-03 10:46:26 +07:00
|
|
|
|
|
|
|
/** Init I2C */
|
|
|
|
Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin());
|
2024-02-16 13:39:33 +07:00
|
|
|
delay(1000);
|
2024-02-03 10:46:26 +07:00
|
|
|
|
2024-04-25 06:49:14 +07:00
|
|
|
configuration.setAirGradient(&ag);
|
2024-06-24 18:34:24 +07:00
|
|
|
oledDisplay.setAirGradient(&ag);
|
|
|
|
stateMachine.setAirGradient(&ag);
|
2024-04-07 16:39:01 +07:00
|
|
|
wifiConnector.setAirGradient(&ag);
|
2024-06-24 18:34:24 +07:00
|
|
|
apiClient.setAirGradient(&ag);
|
|
|
|
openMetrics.setAirGradient(&ag);
|
|
|
|
localServer.setAirGraident(&ag);
|
2024-02-04 15:04:38 +07:00
|
|
|
|
2024-07-21 19:38:50 +12:00
|
|
|
/** Example set custom API root URL */
|
|
|
|
// apiClient.setApiRoot("https://example.custom.api");
|
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
/** Init sensor */
|
|
|
|
boardInit();
|
2024-04-07 16:39:01 +07:00
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
/** Connecting wifi */
|
|
|
|
bool connectToWifi = false;
|
|
|
|
|
|
|
|
connectToWifi = !configuration.isOfflineMode();
|
|
|
|
if (connectToWifi) {
|
|
|
|
apiClient.begin();
|
|
|
|
|
|
|
|
if (wifiConnector.connect()) {
|
|
|
|
if (wifiConnector.isConnected()) {
|
|
|
|
mdnsInit();
|
|
|
|
localServer.begin();
|
|
|
|
initMqtt();
|
|
|
|
sendDataToAg();
|
|
|
|
|
|
|
|
apiClient.fetchServerConfiguration();
|
|
|
|
configSchedule.update();
|
|
|
|
if (apiClient.isFetchConfigureFailed()) {
|
|
|
|
if (apiClient.isNotAvailableOnDashboard()) {
|
|
|
|
stateMachine.displaySetAddToDashBoard();
|
|
|
|
stateMachine.displayHandle(
|
|
|
|
AgStateMachineWiFiOkServerOkSensorConfigFailed);
|
|
|
|
} else {
|
|
|
|
stateMachine.displayClearAddToDashBoard();
|
|
|
|
}
|
|
|
|
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (wifiConnector.isConfigurePorttalTimeout()) {
|
|
|
|
oledDisplay.showRebooting();
|
|
|
|
delay(2500);
|
|
|
|
oledDisplay.setText("", "", "");
|
|
|
|
ESP.restart();
|
|
|
|
}
|
2024-04-07 16:39:01 +07:00
|
|
|
}
|
2024-02-04 15:04:38 +07:00
|
|
|
}
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
2024-06-24 18:34:24 +07:00
|
|
|
/** Set offline mode without saving, cause wifi is not configured */
|
|
|
|
if (wifiConnector.hasConfigurated() == false) {
|
|
|
|
Serial.println("Set offline mode cause wifi is not configurated");
|
|
|
|
configuration.setOfflineModeWithoutSave(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Show display Warning up */
|
|
|
|
String sn = "SN:" + ag.deviceId();
|
|
|
|
oledDisplay.setText("Warming Up", sn.c_str(), "");
|
|
|
|
|
|
|
|
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
|
|
|
|
|
|
|
Serial.println("Display brightness: " +
|
|
|
|
String(configuration.getDisplayBrightness()));
|
|
|
|
oledDisplay.setBrightness(configuration.getDisplayBrightness());
|
|
|
|
|
|
|
|
appDispHandler();
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void loop() {
|
2024-06-24 18:34:24 +07:00
|
|
|
/** Handle schedule */
|
|
|
|
dispLedSchedule.run();
|
2024-02-04 15:04:38 +07:00
|
|
|
configSchedule.run();
|
2024-06-24 18:34:24 +07:00
|
|
|
agApiPostSchedule.run();
|
|
|
|
|
|
|
|
if (configuration.hasSensorS8) {
|
2024-02-18 12:43:37 +07:00
|
|
|
co2Schedule.run();
|
|
|
|
}
|
2024-06-24 18:34:24 +07:00
|
|
|
if (configuration.hasSensorPMS1) {
|
2024-02-18 12:43:37 +07:00
|
|
|
pmsSchedule.run();
|
2024-06-24 18:34:24 +07:00
|
|
|
ag.pms5003.handle();
|
2024-02-18 12:43:37 +07:00
|
|
|
}
|
2024-06-24 18:34:24 +07:00
|
|
|
if (configuration.hasSensorSHT) {
|
2024-02-18 12:43:37 +07:00
|
|
|
tempHumSchedule.run();
|
|
|
|
}
|
2024-06-24 18:34:24 +07:00
|
|
|
if (configuration.hasSensorSGP) {
|
|
|
|
tvocSchedule.run();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Auto reset watchdog timer if offline mode or postDataToAirGradient */
|
|
|
|
if (configuration.isOfflineMode() ||
|
|
|
|
(configuration.isPostDataToAirGradient() == false)) {
|
|
|
|
watchdogFeedSchedule.run();
|
|
|
|
}
|
2024-02-04 15:04:38 +07:00
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
/** Check for handle WiFi reconnect */
|
2024-04-07 16:39:01 +07:00
|
|
|
wifiConnector.handle();
|
2024-03-14 21:17:43 +07:00
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
/** factory reset handle */
|
|
|
|
// factoryConfigReset();
|
|
|
|
|
|
|
|
/** check that local configura changed then do some action */
|
|
|
|
configUpdateHandle();
|
|
|
|
|
|
|
|
localServer._handle();
|
|
|
|
|
|
|
|
if (configuration.hasSensorSGP) {
|
|
|
|
ag.sgp41.handle();
|
|
|
|
}
|
|
|
|
|
|
|
|
MDNS.update();
|
|
|
|
|
|
|
|
mqttSchedule.run();
|
|
|
|
mqttClient.handle();
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
static void co2Update(void) {
|
|
|
|
int value = ag.s8.getCo2();
|
2024-07-24 09:05:57 +07:00
|
|
|
if (utils::isValidCO2(value)) {
|
2024-06-24 18:34:24 +07:00
|
|
|
measurements.CO2 = value;
|
|
|
|
getCO2FailCount = 0;
|
|
|
|
Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2);
|
2024-02-04 15:04:38 +07:00
|
|
|
} else {
|
2024-06-24 18:34:24 +07:00
|
|
|
getCO2FailCount++;
|
|
|
|
Serial.printf("Get CO2 failed: %d\r\n", getCO2FailCount);
|
|
|
|
if (getCO2FailCount >= 3) {
|
2024-07-24 09:05:57 +07:00
|
|
|
measurements.CO2 = utils::getInvalidCO2();
|
2024-06-24 18:34:24 +07:00
|
|
|
}
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
static void mdnsInit(void) {
|
|
|
|
Serial.println("mDNS init");
|
|
|
|
if (!MDNS.begin(localServer.getHostname().c_str())) {
|
|
|
|
Serial.println("Init mDNS failed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
MDNS.addService("_airgradient", "_tcp", 80);
|
|
|
|
MDNS.addServiceTxt("_airgradient", "_tcp", "model",
|
|
|
|
AgFirmwareModeName(fwMode));
|
|
|
|
MDNS.addServiceTxt("_airgradient", "_tcp", "serialno", ag.deviceId());
|
|
|
|
MDNS.addServiceTxt("_airgradient", "_tcp", "fw_ver", ag.getVersion());
|
|
|
|
MDNS.addServiceTxt("_airgradient", "_tcp", "vendor", "AirGradient");
|
2024-02-03 10:46:26 +07:00
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
MDNS.announce();
|
|
|
|
}
|
2024-02-03 10:46:26 +07:00
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
static void initMqtt(void) {
|
|
|
|
if (mqttClient.begin(configuration.getMqttBrokerUri())) {
|
|
|
|
Serial.println("Setup connect to MQTT broker successful");
|
|
|
|
} else {
|
|
|
|
Serial.println("setup Connect to MQTT broker failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wdgFeedUpdate(void) {
|
|
|
|
ag.watchdog.reset();
|
|
|
|
Serial.println();
|
|
|
|
Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset");
|
|
|
|
Serial.println();
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool sgp41Init(void) {
|
|
|
|
ag.sgp41.setNoxLearningOffset(configuration.getNoxLearningOffset());
|
|
|
|
ag.sgp41.setTvocLearningOffset(configuration.getTvocLearningOffset());
|
|
|
|
if (ag.sgp41.begin(Wire)) {
|
|
|
|
Serial.println("Init SGP41 success");
|
|
|
|
configuration.hasSensorSGP = true;
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
Serial.println("Init SGP41 failuire");
|
|
|
|
configuration.hasSensorSGP = false;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wifiFactoryConfigure(void) {
|
|
|
|
WiFi.persistent(true);
|
|
|
|
WiFi.begin("airgradient", "cleanair");
|
|
|
|
WiFi.persistent(false);
|
|
|
|
oledDisplay.setText("Configure WiFi", "connect to", "\'airgradient\'");
|
|
|
|
delay(2500);
|
|
|
|
oledDisplay.setText("Rebooting...", "", "");
|
|
|
|
delay(2500);
|
|
|
|
oledDisplay.setText("", "", "");
|
|
|
|
ESP.restart();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mqttHandle(void) {
|
|
|
|
if(mqttClient.isConnected() == false) {
|
|
|
|
mqttClient.connect(String("airgradient-") + ag.deviceId());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mqttClient.isConnected()) {
|
|
|
|
String payload = measurements.toString(true, fwMode, wifiConnector.RSSI(),
|
|
|
|
&ag, &configuration);
|
|
|
|
String topic = "airgradient/readings/" + ag.deviceId();
|
|
|
|
if (mqttClient.publish(topic.c_str(), payload.c_str(), payload.length())) {
|
|
|
|
Serial.println("MQTT sync success");
|
|
|
|
} else {
|
|
|
|
Serial.println("MQTT sync failure");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sendDataToAg() {
|
|
|
|
/** Change oledDisplay and led state */
|
|
|
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnecting);
|
|
|
|
|
|
|
|
delay(1500);
|
|
|
|
if (apiClient.sendPing(wifiConnector.RSSI(), measurements.bootCount)) {
|
|
|
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnected);
|
|
|
|
} else {
|
|
|
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnectFailed);
|
|
|
|
}
|
|
|
|
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dispSensorNotFound(String ss) {
|
|
|
|
oledDisplay.setText("Sensor", ss.c_str(), "not found");
|
|
|
|
delay(2000);
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
|
|
|
|
2024-02-04 15:04:38 +07:00
|
|
|
static void boardInit(void) {
|
2024-06-24 18:34:24 +07:00
|
|
|
/** Display init */
|
|
|
|
oledDisplay.begin();
|
|
|
|
|
|
|
|
/** Show boot display */
|
|
|
|
Serial.println("Firmware Version: " + ag.getVersion());
|
|
|
|
|
|
|
|
if (ag.isBasic()) {
|
|
|
|
oledDisplay.setText("DIY Basic", ag.getVersion().c_str(), "");
|
|
|
|
} else {
|
|
|
|
oledDisplay.setText("AirGradient ONE",
|
|
|
|
"FW Version: ", ag.getVersion().c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
|
|
|
|
|
|
|
ag.watchdog.begin();
|
|
|
|
|
|
|
|
/** Show message init sensor */
|
|
|
|
oledDisplay.setText("Sensor", "init...", "");
|
|
|
|
|
|
|
|
/** Init sensor SGP41 */
|
|
|
|
configuration.hasSensorSGP = false;
|
|
|
|
// if (sgp41Init() == false) {
|
|
|
|
// dispSensorNotFound("SGP41");
|
|
|
|
// }
|
|
|
|
|
|
|
|
/** Init SHT */
|
2024-02-16 13:39:33 +07:00
|
|
|
if (ag.sht.begin(Wire) == false) {
|
2024-06-24 18:34:24 +07:00
|
|
|
Serial.println("SHTx sensor not found");
|
|
|
|
configuration.hasSensorSHT = false;
|
|
|
|
dispSensorNotFound("SHT");
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
/** Init S8 CO2 sensor */
|
2024-02-03 10:46:26 +07:00
|
|
|
if (ag.s8.begin(&Serial) == false) {
|
2024-06-24 18:34:24 +07:00
|
|
|
Serial.println("CO2 S8 sensor not found");
|
|
|
|
configuration.hasSensorS8 = false;
|
|
|
|
dispSensorNotFound("S8");
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
/** Init PMS5003 */
|
|
|
|
configuration.hasSensorPMS1 = true;
|
|
|
|
configuration.hasSensorPMS2 = false;
|
2024-02-03 10:46:26 +07:00
|
|
|
if (ag.pms5003.begin(&Serial) == false) {
|
2024-02-18 12:43:37 +07:00
|
|
|
Serial.println("PMS sensor not found");
|
2024-06-24 18:34:24 +07:00
|
|
|
configuration.hasSensorPMS1 = false;
|
|
|
|
|
|
|
|
dispSensorNotFound("PMS");
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
/** Set S8 CO2 abc days period */
|
|
|
|
if (configuration.hasSensorS8) {
|
|
|
|
if (ag.s8.setAbcPeriod(configuration.getCO2CalibrationAbcDays() * 24)) {
|
|
|
|
Serial.println("Set S8 AbcDays successful");
|
|
|
|
} else {
|
|
|
|
Serial.println("Set S8 AbcDays failure");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
localServer.setFwMode(fwMode);
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
|
|
|
|
2024-02-04 15:04:38 +07:00
|
|
|
static void failedHandler(String msg) {
|
|
|
|
while (true) {
|
|
|
|
Serial.println(msg);
|
|
|
|
delay(1000);
|
|
|
|
}
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
static void configurationUpdateSchedule(void) {
|
|
|
|
if (apiClient.fetchServerConfiguration()) {
|
|
|
|
configUpdateHandle();
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
2024-06-24 18:34:24 +07:00
|
|
|
}
|
2024-02-03 10:46:26 +07:00
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
static void configUpdateHandle() {
|
|
|
|
if (configuration.isUpdated() == false) {
|
|
|
|
return;
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
stateMachine.executeCo2Calibration();
|
|
|
|
|
|
|
|
String mqttUri = configuration.getMqttBrokerUri();
|
|
|
|
if (mqttClient.isCurrentUri(mqttUri) == false) {
|
|
|
|
mqttClient.end();
|
|
|
|
initMqtt();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (configuration.hasSensorSGP) {
|
|
|
|
if (configuration.noxLearnOffsetChanged() ||
|
|
|
|
configuration.tvocLearnOffsetChanged()) {
|
|
|
|
ag.sgp41.end();
|
|
|
|
|
|
|
|
int oldTvocOffset = ag.sgp41.getTvocLearningOffset();
|
|
|
|
int oldNoxOffset = ag.sgp41.getNoxLearningOffset();
|
|
|
|
bool result = sgp41Init();
|
|
|
|
const char *resultStr = "successful";
|
|
|
|
if (!result) {
|
|
|
|
resultStr = "failure";
|
2024-02-18 12:43:37 +07:00
|
|
|
}
|
2024-06-24 18:34:24 +07:00
|
|
|
if (oldTvocOffset != configuration.getTvocLearningOffset()) {
|
|
|
|
Serial.printf("Setting tvocLearningOffset from %d to %d hours %s\r\n",
|
|
|
|
oldTvocOffset, configuration.getTvocLearningOffset(),
|
|
|
|
resultStr);
|
|
|
|
}
|
|
|
|
if (oldNoxOffset != configuration.getNoxLearningOffset()) {
|
|
|
|
Serial.printf("Setting noxLearningOffset from %d to %d hours %s\r\n",
|
|
|
|
oldNoxOffset, configuration.getNoxLearningOffset(),
|
|
|
|
resultStr);
|
2024-02-06 10:41:10 +07:00
|
|
|
}
|
|
|
|
}
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
if (configuration.isDisplayBrightnessChanged()) {
|
|
|
|
oledDisplay.setBrightness(configuration.getDisplayBrightness());
|
2024-03-06 17:20:55 +07:00
|
|
|
}
|
2024-06-24 18:34:24 +07:00
|
|
|
|
|
|
|
appDispHandler();
|
2024-02-04 15:04:38 +07:00
|
|
|
}
|
2024-02-03 10:46:26 +07:00
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
static void appDispHandler(void) {
|
|
|
|
AgStateMachineState state = AgStateMachineNormal;
|
|
|
|
|
|
|
|
/** Only show display status on online mode. */
|
|
|
|
if (configuration.isOfflineMode() == false) {
|
|
|
|
if (wifiConnector.isConnected() == false) {
|
|
|
|
state = AgStateMachineWiFiLost;
|
|
|
|
} else if (apiClient.isFetchConfigureFailed()) {
|
|
|
|
state = AgStateMachineSensorConfigFailed;
|
|
|
|
if (apiClient.isNotAvailableOnDashboard()) {
|
|
|
|
stateMachine.displaySetAddToDashBoard();
|
|
|
|
} else {
|
|
|
|
stateMachine.displayClearAddToDashBoard();
|
|
|
|
}
|
|
|
|
} else if (apiClient.isPostToServerFailed()) {
|
|
|
|
state = AgStateMachineServerLost;
|
2024-02-20 20:36:06 +07:00
|
|
|
}
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
2024-06-24 18:34:24 +07:00
|
|
|
stateMachine.displayHandle(state);
|
2024-02-04 15:04:38 +07:00
|
|
|
}
|
2024-02-03 10:46:26 +07:00
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
static void oledDisplaySchedule(void) {
|
|
|
|
|
|
|
|
appDispHandler();
|
2024-02-04 15:04:38 +07:00
|
|
|
}
|
2024-02-03 10:46:26 +07:00
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
static void updateTvoc(void) {
|
|
|
|
measurements.TVOC = ag.sgp41.getTvocIndex();
|
|
|
|
measurements.TVOCRaw = ag.sgp41.getTvocRaw();
|
|
|
|
measurements.NOx = ag.sgp41.getNoxIndex();
|
|
|
|
measurements.NOxRaw = ag.sgp41.getNoxRaw();
|
2024-02-03 10:46:26 +07:00
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
Serial.println();
|
|
|
|
Serial.printf("TVOC index: %d\r\n", measurements.TVOC);
|
|
|
|
Serial.printf("TVOC raw: %d\r\n", measurements.TVOCRaw);
|
|
|
|
Serial.printf("NOx index: %d\r\n", measurements.NOx);
|
|
|
|
Serial.printf("NOx raw: %d\r\n", measurements.NOxRaw);
|
2024-02-04 15:04:38 +07:00
|
|
|
}
|
2024-02-03 10:46:26 +07:00
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
static void updatePm(void) {
|
|
|
|
if (ag.pms5003.isFailed() == false) {
|
|
|
|
measurements.pm01_1 = ag.pms5003.getPm01Ae();
|
|
|
|
measurements.pm25_1 = ag.pms5003.getPm25Ae();
|
|
|
|
measurements.pm10_1 = ag.pms5003.getPm10Ae();
|
|
|
|
measurements.pm03PCount_1 = ag.pms5003.getPm03ParticleCount();
|
|
|
|
|
|
|
|
Serial.println();
|
|
|
|
Serial.printf("PM1 ug/m3: %d\r\n", measurements.pm01_1);
|
|
|
|
Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_1);
|
|
|
|
Serial.printf("PM10 ug/m3: %d\r\n", measurements.pm10_1);
|
|
|
|
Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1);
|
2024-09-16 10:36:45 +07:00
|
|
|
Serial.printf("PM firmware version: %d\r\n", ag.pms5003.getFirmwareVersion());
|
2024-08-25 20:21:26 +07:00
|
|
|
ag.pms5003.resetFailCount();
|
2024-02-20 21:05:04 +07:00
|
|
|
} else {
|
2024-08-25 20:21:26 +07:00
|
|
|
ag.pms5003.updateFailCount();
|
2024-08-26 14:14:42 +07:00
|
|
|
Serial.printf("PMS read failed %d times\r\n", ag.pms5003.getFailCount());
|
2024-08-25 20:21:26 +07:00
|
|
|
if (ag.pms5003.getFailCount() >= PMS_FAIL_COUNT_SET_INVALID) {
|
2024-08-26 14:14:42 +07:00
|
|
|
measurements.pm01_1 = utils::getInvalidPmValue();
|
|
|
|
measurements.pm25_1 = utils::getInvalidPmValue();
|
|
|
|
measurements.pm10_1 = utils::getInvalidPmValue();
|
|
|
|
measurements.pm03PCount_1 = utils::getInvalidPmValue();
|
2024-02-20 21:05:04 +07:00
|
|
|
}
|
2024-08-25 20:21:26 +07:00
|
|
|
|
|
|
|
if(ag.pms5003.getFailCount() >= ag.pms5003.getFailCountMax()) {
|
2024-08-26 15:54:41 +07:00
|
|
|
Serial.printf("PMS failure count reach to max set %d, restarting...", ag.pms5003.getFailCountMax());
|
2024-08-25 20:21:26 +07:00
|
|
|
ESP.restart();
|
|
|
|
}
|
2024-02-20 21:05:04 +07:00
|
|
|
}
|
2024-06-24 18:34:24 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void sendDataToServer(void) {
|
|
|
|
/** Ignore send data to server if postToAirGradient disabled */
|
|
|
|
if (configuration.isPostDataToAirGradient() == false ||
|
|
|
|
configuration.isOfflineMode()) {
|
|
|
|
return;
|
2024-02-20 21:05:04 +07:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(),
|
|
|
|
&ag, &configuration);
|
|
|
|
if (apiClient.postToServer(syncData)) {
|
|
|
|
ag.watchdog.reset();
|
|
|
|
Serial.println();
|
|
|
|
Serial.println(
|
|
|
|
"Online mode and isPostToAirGradient = true: watchdog reset");
|
|
|
|
Serial.println();
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
2024-02-20 21:05:04 +07:00
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
measurements.bootCount++;
|
|
|
|
}
|
2024-02-03 10:46:26 +07:00
|
|
|
|
2024-06-24 18:34:24 +07:00
|
|
|
static void tempHumUpdate(void) {
|
|
|
|
delay(100);
|
|
|
|
if (ag.sht.measure()) {
|
|
|
|
measurements.Temperature = ag.sht.getTemperature();
|
|
|
|
measurements.Humidity = ag.sht.getRelativeHumidity();
|
|
|
|
|
|
|
|
Serial.printf("Temperature in C: %0.2f\r\n", measurements.Temperature);
|
|
|
|
Serial.printf("Relative Humidity: %d\r\n", measurements.Humidity);
|
|
|
|
Serial.printf("Temperature compensated in C: %0.2f\r\n",
|
|
|
|
measurements.Temperature);
|
|
|
|
Serial.printf("Relative Humidity compensated: %d\r\n",
|
|
|
|
measurements.Humidity);
|
|
|
|
|
|
|
|
// Update compensation temperature and humidity for SGP41
|
|
|
|
if (configuration.hasSensorSGP) {
|
|
|
|
ag.sgp41.setCompensationTemperatureHumidity(measurements.Temperature,
|
|
|
|
measurements.Humidity);
|
2024-02-20 21:05:04 +07:00
|
|
|
}
|
2024-02-04 15:04:38 +07:00
|
|
|
} else {
|
2024-06-24 18:34:24 +07:00
|
|
|
Serial.println("SHT read failed");
|
2024-07-24 09:05:57 +07:00
|
|
|
measurements.Temperature = utils::getInvalidTemperature();
|
|
|
|
measurements.Humidity = utils::getInvalidHumidity();
|
2024-02-03 10:46:26 +07:00
|
|
|
}
|
2024-02-04 15:04:38 +07:00
|
|
|
}
|