Update BASIC.ino example

This commit is contained in:
Phat Nguyen
2024-06-24 18:34:24 +07:00
parent 57c33e4900
commit dbc63194e6
12 changed files with 1079 additions and 471 deletions

View File

@ -31,190 +31,376 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
#include "AgConfigure.h"
#include "AgSchedule.h"
#include "AgWiFiConnector.h"
#include "LocalServer.h"
#include "OpenMetrics.h"
#include "MqttClient.h"
#include <AirGradient.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiClient.h>
#define WIFI_CONNECT_COUNTDOWN_MAX 180 /** sec */
#define WIFI_CONNECT_RETRY_MS 10000 /** ms */
#define LED_BAR_COUNT_INIT_VALUE (-1) /** */
#define LED_BAR_ANIMATION_PERIOD 100 /** ms */
#define DISP_UPDATE_INTERVAL 5000 /** 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 5000 /** ms */
#define SENSOR_PM_UPDATE_INTERVAL 5000 /** 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 WIFI_HOTSPOT_PASSWORD_DEFAULT \
"cleanair" /** default WiFi AP password \
*/
#define FIRMWARE_CHECK_FOR_UPDATE_MS (60 * 60 * 1000) /** ms */
/** Create airgradient instance for 'DIY_BASIC' board */
static AirGradient ag = AirGradient(DIY_BASIC);
static AirGradient ag(DIY_BASIC);
static Configuration configuration(Serial);
static AgApiClient apiClient(Serial, configuration);
static Measurements measurements;
static OledDisplay oledDisp(configuration, measurements, Serial);
static StateMachine sm(oledDisp, Serial, measurements, configuration);
static WifiConnector wifiConnector(oledDisp, Serial, sm, configuration);
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 co2Ppm = -1;
static int pm25 = -1;
static float temp = -1001;
static int hum = -1;
static int pmFailCount = 0;
static int getCO2FailCount = 0;
static AgFirmwareMode fwMode = FW_MODE_I_BASIC_40PS;
static String fwNewVersion;
static void boardInit(void);
static void failedHandler(String msg);
static void executeCo2Calibration(void);
static void updateServerConfiguration(void);
static void co2Update(void);
static void pmUpdate(void);
static void tempHumUpdate(void);
static void configurationUpdateSchedule(void);
static void appDispHandler(void);
static void oledDisplaySchedule(void);
static void updateTvoc(void);
static void updatePm(void);
static void sendDataToServer(void);
static void dispHandler(void);
static String getDevId(void);
static void showNr(void);
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);
bool hasSensorS8 = true;
bool hasSensorPMS = true;
bool hasSensorSHT = true;
int pmFailCount = 0;
int getCO2FailCount = 0;
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplaySchedule);
AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL,
updateServerConfiguration);
AgSchedule serverSchedule(SERVER_SYNC_INTERVAL, sendDataToServer);
AgSchedule dispSchedule(DISP_UPDATE_INTERVAL, dispHandler);
configurationUpdateSchedule);
AgSchedule agApiPostSchedule(SERVER_SYNC_INTERVAL, sendDataToServer);
AgSchedule co2Schedule(SENSOR_CO2_UPDATE_INTERVAL, co2Update);
AgSchedule pmsSchedule(SENSOR_PM_UPDATE_INTERVAL, pmUpdate);
AgSchedule pmsSchedule(SENSOR_PM_UPDATE_INTERVAL, updatePm);
AgSchedule tempHumSchedule(SENSOR_TEMP_HUM_UPDATE_INTERVAL, tempHumUpdate);
AgSchedule tvocSchedule(SENSOR_TVOC_UPDATE_INTERVAL, updateTvoc);
AgSchedule watchdogFeedSchedule(60000, wdgFeedUpdate);
AgSchedule mqttSchedule(MQTT_SYNC_INTERVAL, mqttHandle);
void setup() {
/** Serial for print debug message */
Serial.begin(115200);
showNr();
delay(100); /** For bester show log */
/** Print device ID into log */
Serial.println("Serial nr: " + ag.deviceId());
/** Initialize local configure */
configuration.begin();
/** Init I2C */
Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin());
Wire.endTransmission(1);
delay(1000);
/** Board init */
configuration.setAirGradient(&ag);
oledDisplay.setAirGradient(&ag);
stateMachine.setAirGradient(&ag);
wifiConnector.setAirGradient(&ag);
apiClient.setAirGradient(&ag);
openMetrics.setAirGradient(&ag);
localServer.setAirGraident(&ag);
/** Init sensor */
boardInit();
/** Init AirGradient server */
/** Connecting wifi */
bool connectToWifi = false;
connectToWifi = !configuration.isOfflineMode();
if (connectToWifi) {
apiClient.begin();
apiClient.setAirGradient(&ag);
configuration.setAirGradient(&ag);
wifiConnector.setAirGradient(&ag);
/** Show boot display */
displayShowText("DIY basic", "Lib:" + ag.getVersion(), "");
delay(2000);
/** WiFi connect */
// connectToWifi();
if (wifiConnector.connect()) {
if (WiFi.status() == WL_CONNECTED) {
if (wifiConnector.isConnected()) {
mdnsInit();
localServer.begin();
initMqtt();
sendDataToAg();
apiClient.fetchServerConfiguration();
if (configuration.isCo2CalibrationRequested()) {
executeCo2Calibration();
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();
}
}
}
/** Show serial number display */
ag.display.clear();
ag.display.setCursor(1, 1);
ag.display.setText("Warm Up");
ag.display.setCursor(1, 15);
ag.display.setText("Serial#");
ag.display.setCursor(1, 29);
String id = getNormalizedMac();
Serial.println("Device id: " + id);
String id1 = id.substring(0, 9);
String id2 = id.substring(9, 12);
ag.display.setText("\'" + id1);
ag.display.setCursor(1, 40);
ag.display.setText(id2 + "\'");
ag.display.show();
}
/** 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);
}
delay(5000);
/** 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();
}
void loop() {
/** Handle schedule */
dispLedSchedule.run();
configSchedule.run();
serverSchedule.run();
dispSchedule.run();
if (hasSensorS8) {
agApiPostSchedule.run();
if (configuration.hasSensorS8) {
co2Schedule.run();
}
if (hasSensorPMS) {
if (configuration.hasSensorPMS1) {
pmsSchedule.run();
ag.pms5003.handle();
}
if (hasSensorSHT) {
if (configuration.hasSensorSHT) {
tempHumSchedule.run();
}
if (configuration.hasSensorSGP) {
tvocSchedule.run();
}
/** Auto reset watchdog timer if offline mode or postDataToAirGradient */
if (configuration.isOfflineMode() ||
(configuration.isPostDataToAirGradient() == false)) {
watchdogFeedSchedule.run();
}
/** Check for handle WiFi reconnect */
wifiConnector.handle();
/** Read PMS on loop */
ag.pms5003.handle();
/** 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();
}
static void co2Update(void) {
int value = ag.s8.getCo2();
if (value >= 0) {
measurements.CO2 = value;
getCO2FailCount = 0;
Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2);
} else {
getCO2FailCount++;
Serial.printf("Get CO2 failed: %d\r\n", getCO2FailCount);
if (getCO2FailCount >= 3) {
measurements.CO2 = -1;
}
}
}
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");
MDNS.announce();
}
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() {
// delay(1500);
if (apiClient.sendPing(wifiConnector.RSSI(), 0)) {
// Ping Server succses
/** Change oledDisplay and led state */
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnecting);
delay(1500);
if (apiClient.sendPing(wifiConnector.RSSI(), measurements.bootCount)) {
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnected);
} else {
// Ping server failed
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnectFailed);
}
// delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
}
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();
delay(100);
void dispSensorNotFound(String ss) {
oledDisplay.setText("Sensor", ss.c_str(), "not found");
delay(2000);
}
static void boardInit(void) {
/** Init SHT sensor */
/** 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 */
if (ag.sht.begin(Wire) == false) {
hasSensorSHT = false;
Serial.println("SHT sensor not found");
Serial.println("SHTx sensor not found");
configuration.hasSensorSHT = false;
dispSensorNotFound("SHT");
}
/** CO2 init */
/** Init S8 CO2 sensor */
if (ag.s8.begin(&Serial) == false) {
Serial.println("CO2 S8 snsor not found");
hasSensorS8 = false;
Serial.println("CO2 S8 sensor not found");
configuration.hasSensorS8 = false;
dispSensorNotFound("S8");
}
/** PMS init */
/** Init PMS5003 */
configuration.hasSensorPMS1 = true;
configuration.hasSensorPMS2 = false;
if (ag.pms5003.begin(&Serial) == false) {
Serial.println("PMS sensor not found");
hasSensorPMS = false;
configuration.hasSensorPMS1 = false;
dispSensorNotFound("PMS");
}
/** Display init */
ag.display.begin(Wire);
ag.display.setTextColor(1);
ag.display.clear();
ag.display.show();
delay(100);
/** 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);
}
static void failedHandler(String msg) {
@ -224,181 +410,160 @@ static void failedHandler(String msg) {
}
}
static void executeCo2Calibration(void) {
/** Count down for co2CalibCountdown secs */
for (int i = 0; i < SENSOR_CO2_CALIB_COUNTDOWN_MAX; i++) {
displayShowText("CO2 calib.", "after",
String(SENSOR_CO2_CALIB_COUNTDOWN_MAX - i) + " sec");
delay(1000);
}
if (ag.s8.setBaselineCalibration()) {
displayShowText("Calib", "success", "");
delay(1000);
displayShowText("Wait to", "complete", "...");
int count = 0;
while (ag.s8.isBaseLineCalibrationDone() == false) {
delay(1000);
count++;
}
displayShowText("Finished", "after", String(count) + " sec");
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
} else {
displayShowText("Calibration", "failure", "");
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
}
}
static void updateServerConfiguration(void) {
static void configurationUpdateSchedule(void) {
if (apiClient.fetchServerConfiguration()) {
if (configuration.isCo2CalibrationRequested()) {
if (hasSensorS8) {
executeCo2Calibration();
} else {
Serial.println("CO2 S8 not available, calib ignored");
configUpdateHandle();
}
}
if (configuration.getCO2CalibrationAbcDays() > 0) {
if (hasSensorS8) {
int newHour = configuration.getCO2CalibrationAbcDays() * 24;
Serial.printf("abcDays config: %d days(%d hours)\r\n",
configuration.getCO2CalibrationAbcDays(), newHour);
int curHour = ag.s8.getAbcPeriod();
Serial.printf("Current config: %d (hours)\r\n", curHour);
if (curHour == newHour) {
Serial.println("set 'abcDays' ignored");
} else {
if (ag.s8.setAbcPeriod(configuration.getCO2CalibrationAbcDays() *
24) == false) {
Serial.println("Set S8 abcDays period calib failed");
} else {
Serial.println("Set S8 abcDays period calib success");
static void configUpdateHandle() {
if (configuration.isUpdated() == false) {
return;
}
stateMachine.executeCo2Calibration();
String mqttUri = configuration.getMqttBrokerUri();
if (mqttClient.isCurrentUri(mqttUri) == false) {
mqttClient.end();
initMqtt();
}
} else {
Serial.println("CO2 S8 not available, set 'abcDays' ignored");
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";
}
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);
}
}
}
static void co2Update() {
int value = ag.s8.getCo2();
if (value >= 0) {
co2Ppm = value;
getCO2FailCount = 0;
Serial.printf("CO2 index: %d\r\n", co2Ppm);
} else {
getCO2FailCount++;
Serial.printf("Get CO2 failed: %d\r\n", getCO2FailCount);
if (getCO2FailCount >= 3) {
co2Ppm = -1;
}
}
if (configuration.isDisplayBrightnessChanged()) {
oledDisplay.setBrightness(configuration.getDisplayBrightness());
}
void pmUpdate() {
appDispHandler();
}
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;
}
}
stateMachine.displayHandle(state);
}
static void oledDisplaySchedule(void) {
appDispHandler();
}
static void updateTvoc(void) {
measurements.TVOC = ag.sgp41.getTvocIndex();
measurements.TVOCRaw = ag.sgp41.getTvocRaw();
measurements.NOx = ag.sgp41.getNoxIndex();
measurements.NOxRaw = ag.sgp41.getNoxRaw();
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);
}
static void updatePm(void) {
if (ag.pms5003.isFailed() == false) {
pm25 = ag.pms5003.getPm25Ae();
Serial.printf("PMS2.5: %d\r\n", pm25);
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);
pmFailCount = 0;
} else {
Serial.printf("PM read failed, %d", pmFailCount);
pmFailCount++;
Serial.printf("PMS read failed: %d\r\n", pmFailCount);
if (pmFailCount >= 3) {
pm25 = -1;
measurements.pm01_1 = -1;
measurements.pm25_1 = -1;
measurements.pm10_1 = -1;
measurements.pm03PCount_1 = -1;
}
}
}
static void tempHumUpdate() {
if (ag.sht.measure()) {
temp = ag.sht.getTemperature();
hum = ag.sht.getRelativeHumidity();
Serial.printf("Temperature: %0.2f\r\n", temp);
Serial.printf(" Humidity: %d\r\n", hum);
} else {
Serial.println("Meaure SHT failed");
}
static void sendDataToServer(void) {
/** Ignore send data to server if postToAirGradient disabled */
if (configuration.isPostDataToAirGradient() == false ||
configuration.isOfflineMode()) {
return;
}
static void sendDataToServer() {
String wifi = "\"wifi\":" + String(WiFi.RSSI());
String rco2 = "";
if(co2Ppm >= 0){
rco2 = ",\"rco2\":" + String(co2Ppm);
}
String pm02 = "";
if(pm25) {
pm02 = ",\"pm02\":" + String(pm25);
}
String rhum = "";
if(hum >= 0){
rhum = ",\"rhum\":" + String(rhum);
}
String payload = "{" + wifi + rco2 + pm02 + rhum + "}";
if (apiClient.postToServer(payload) == false) {
Serial.println("Post to server failed");
}
}
static void dispHandler() {
String ln1 = "";
String ln2 = "";
String ln3 = "";
if (configuration.isPmStandardInUSAQI()) {
if (pm25 < 0) {
ln1 = "AQI: -";
} else {
ln1 = "AQI:" + String(ag.pms5003.convertPm25ToUsAqi(pm25));
}
} else {
if (pm25 < 0) {
ln1 = "PM :- ug";
} else {
ln1 = "PM :" + String(pm25) + " ug";
}
}
if (co2Ppm > -1001) {
ln2 = "CO2:" + String(co2Ppm);
} else {
ln2 = "CO2: -";
}
String _hum = "-";
if (hum > 0) {
_hum = String(hum);
}
String _temp = "-";
if (configuration.isTemperatureUnitInF()) {
if (temp > -1001) {
_temp = String((temp * 9 / 5) + 32).substring(0, 4);
}
ln3 = _temp + " " + _hum + "%";
} else {
if (temp > -1001) {
_temp = String(temp).substring(0, 4);
}
ln3 = _temp + " " + _hum + "%";
}
displayShowText(ln1, ln2, ln3);
}
static String getDevId(void) { return getNormalizedMac(); }
static void showNr(void) {
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();
Serial.println("Serial nr: " + getDevId());
}
String getNormalizedMac() {
String mac = WiFi.macAddress();
mac.replace(":", "");
mac.toLowerCase();
return mac;
measurements.bootCount++;
}
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);
}
} else {
Serial.println("SHT read failed");
}
}

View File

@ -0,0 +1,61 @@
#include "LocalServer.h"
LocalServer::LocalServer(Stream &log, OpenMetrics &openMetrics,
Measurements &measure, Configuration &config,
WifiConnector &wifiConnector)
: PrintLog(log, "LocalServer"), openMetrics(openMetrics), measure(measure),
config(config), wifiConnector(wifiConnector), server(80) {}
LocalServer::~LocalServer() {}
bool LocalServer::begin(void) {
server.on("/measures/current", HTTP_GET, [this]() { _GET_measure(); });
server.on(openMetrics.getApi(), HTTP_GET, [this]() { _GET_metrics(); });
server.on("/config", HTTP_GET, [this]() { _GET_config(); });
server.on("/config", HTTP_PUT, [this]() { _PUT_config(); });
server.begin();
logInfo("Init: " + getHostname() + ".local");
return true;
}
void LocalServer::setAirGraident(AirGradient *ag) { this->ag = ag; }
String LocalServer::getHostname(void) {
return "airgradient_" + ag->deviceId();
}
void LocalServer::_handle(void) { server.handleClient(); }
void LocalServer::_GET_config(void) {
if(ag->isOne()) {
server.send(200, "application/json", config.toString());
} else {
server.send(200, "application/json", config.toString(fwMode));
}
}
void LocalServer::_PUT_config(void) {
String data = server.arg(0);
String response = "";
int statusCode = 400; // Status code for data invalid
if (config.parse(data, true)) {
statusCode = 200;
response = "Success";
} else {
response = config.getFailedMesage();
}
server.send(statusCode, "text/plain", response);
}
void LocalServer::_GET_metrics(void) {
server.send(200, openMetrics.getApiContentType(), openMetrics.getPayload());
}
void LocalServer::_GET_measure(void) {
server.send(
200, "application/json",
measure.toString(true, fwMode, wifiConnector.RSSI(), ag, &config));
}
void LocalServer::setFwMode(AgFirmwareMode fwMode) { this->fwMode = fwMode; }

View File

@ -0,0 +1,38 @@
#ifndef _LOCAL_SERVER_H_
#define _LOCAL_SERVER_H_
#include "AgConfigure.h"
#include "AgValue.h"
#include "AirGradient.h"
#include "OpenMetrics.h"
#include "AgWiFiConnector.h"
#include <Arduino.h>
#include <ESP8266WebServer.h>
class LocalServer : public PrintLog {
private:
AirGradient *ag;
OpenMetrics &openMetrics;
Measurements &measure;
Configuration &config;
WifiConnector &wifiConnector;
ESP8266WebServer server;
AgFirmwareMode fwMode;
public:
LocalServer(Stream &log, OpenMetrics &openMetrics, Measurements &measure,
Configuration &config, WifiConnector& wifiConnector);
~LocalServer();
bool begin(void);
void setAirGraident(AirGradient *ag);
String getHostname(void);
void setFwMode(AgFirmwareMode fwMode);
void _handle(void);
void _GET_config(void);
void _PUT_config(void);
void _GET_metrics(void);
void _GET_measure(void);
};
#endif /** _LOCAL_SERVER_H_ */

View File

@ -0,0 +1,186 @@
#include "OpenMetrics.h"
OpenMetrics::OpenMetrics(Measurements &measure, Configuration &config,
WifiConnector &wifiConnector, AgApiClient &apiClient)
: measure(measure), config(config), wifiConnector(wifiConnector),
apiClient(apiClient) {}
OpenMetrics::~OpenMetrics() {}
void OpenMetrics::setAirGradient(AirGradient *ag) { this->ag = ag; }
const char *OpenMetrics::getApiContentType(void) {
return "application/openmetrics-text; version=1.0.0; charset=utf-8";
}
const char *OpenMetrics::getApi(void) { return "/metrics"; }
String OpenMetrics::getPayload(void) {
String response;
String current_metric_name;
const auto add_metric = [&](const String &name, const String &help,
const String &type, const String &unit = "") {
current_metric_name = "airgradient_" + name;
if (!unit.isEmpty())
current_metric_name += "_" + unit;
response += "# HELP " + current_metric_name + " " + help + "\n";
response += "# TYPE " + current_metric_name + " " + type + "\n";
if (!unit.isEmpty())
response += "# UNIT " + current_metric_name + " " + unit + "\n";
};
const auto add_metric_point = [&](const String &labels, const String &value) {
response += current_metric_name + "{" + labels + "} " + value + "\n";
};
add_metric("info", "AirGradient device information", "info");
add_metric_point("airgradient_serial_number=\"" + ag->deviceId() +
"\",airgradient_device_type=\"" + ag->getBoardName() +
"\",airgradient_library_version=\"" + ag->getVersion() +
"\"",
"1");
add_metric("config_ok",
"1 if the AirGradient device was able to successfully fetch its "
"configuration from the server",
"gauge");
add_metric_point("", apiClient.isFetchConfigureFailed() ? "0" : "1");
add_metric(
"post_ok",
"1 if the AirGradient device was able to successfully send to the server",
"gauge");
add_metric_point("", apiClient.isPostToServerFailed() ? "0" : "1");
add_metric(
"wifi_rssi",
"WiFi signal strength from the AirGradient device perspective, in dBm",
"gauge", "dbm");
add_metric_point("", String(wifiConnector.RSSI()));
if (config.hasSensorS8 && measure.CO2 >= 0) {
add_metric("co2",
"Carbon dioxide concentration as measured by the AirGradient S8 "
"sensor, in parts per million",
"gauge", "ppm");
add_metric_point("", String(measure.CO2));
}
float _temp = -1001;
float _hum = -1;
int pm01 = -1;
int pm25 = -1;
int pm10 = -1;
int pm03PCount = -1;
int atmpCompensated = -1;
int ahumCompensated = -1;
if (config.hasSensorSHT) {
_temp = measure.Temperature;
_hum = measure.Humidity;
atmpCompensated = _temp;
ahumCompensated = _hum;
}
if (config.hasSensorPMS1) {
pm01 = measure.pm01_1;
pm25 = measure.pm25_1;
pm10 = measure.pm10_1;
pm03PCount = measure.pm03PCount_1;
}
if (config.hasSensorPMS1) {
if (pm01 >= 0) {
add_metric("pm1",
"PM1.0 concentration as measured by the AirGradient PMS "
"sensor, in micrograms per cubic meter",
"gauge", "ugm3");
add_metric_point("", String(pm01));
}
if (pm25 >= 0) {
add_metric("pm2d5",
"PM2.5 concentration as measured by the AirGradient PMS "
"sensor, in micrograms per cubic meter",
"gauge", "ugm3");
add_metric_point("", String(pm25));
}
if (pm10 >= 0) {
add_metric("pm10",
"PM10 concentration as measured by the AirGradient PMS "
"sensor, in micrograms per cubic meter",
"gauge", "ugm3");
add_metric_point("", String(pm10));
}
if (pm03PCount >= 0) {
add_metric("pm0d3",
"PM0.3 concentration as measured by the AirGradient PMS "
"sensor, in number of particules per 100 milliliters",
"gauge", "p100ml");
add_metric_point("", String(pm03PCount));
}
}
if (config.hasSensorSGP) {
if (measure.TVOC >= 0) {
add_metric("tvoc_index",
"The processed Total Volatile Organic Compounds (TVOC) index "
"as measured by the AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.TVOC));
}
if (measure.TVOCRaw >= 0) {
add_metric("tvoc_raw",
"The raw input value to the Total Volatile Organic Compounds "
"(TVOC) index as measured by the AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.TVOCRaw));
}
if (measure.NOx >= 0) {
add_metric("nox_index",
"The processed Nitrous Oxide (NOx) index as measured by the "
"AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.NOx));
}
if (measure.NOxRaw >= 0) {
add_metric("nox_raw",
"The raw input value to the Nitrous Oxide (NOx) index as "
"measured by the AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.NOxRaw));
}
}
if (_temp > -1001) {
add_metric(
"temperature",
"The ambient temperature as measured by the AirGradient SHT / PMS "
"sensor, in degrees Celsius",
"gauge", "celsius");
add_metric_point("", String(_temp));
}
if (atmpCompensated > -1001) {
add_metric("temperature_compensated",
"The compensated ambient temperature as measured by the "
"AirGradient SHT / PMS "
"sensor, in degrees Celsius",
"gauge", "celsius");
add_metric_point("", String(atmpCompensated));
}
if (_hum >= 0) {
add_metric(
"humidity",
"The relative humidity as measured by the AirGradient SHT sensor",
"gauge", "percent");
add_metric_point("", String(_hum));
}
if (ahumCompensated >= 0) {
add_metric("humidity_compensated",
"The compensated relative humidity as measured by the "
"AirGradient SHT / PMS sensor",
"gauge", "percent");
add_metric_point("", String(ahumCompensated));
}
response += "# EOF\n";
return response;
}

View File

@ -0,0 +1,28 @@
#ifndef _OPEN_METRICS_H_
#define _OPEN_METRICS_H_
#include "AgConfigure.h"
#include "AgValue.h"
#include "AgWiFiConnector.h"
#include "AirGradient.h"
#include "AgApiClient.h"
class OpenMetrics {
private:
AirGradient *ag;
Measurements &measure;
Configuration &config;
WifiConnector &wifiConnector;
AgApiClient &apiClient;
public:
OpenMetrics(Measurements &measure, Configuration &conig,
WifiConnector &wifiConnector, AgApiClient& apiClient);
~OpenMetrics();
void setAirGradient(AirGradient *ag);
const char *getApiContentType(void);
const char* getApi(void);
String getPayload(void);
};
#endif /** _OPEN_METRICS_H_ */

View File

@ -58,7 +58,6 @@ void OledDisplay::setCentralText(int y, const char *text) {
DISP()->drawStr(x, y, text);
}
/**
* @brief Construct a new Ag Oled Display:: Ag Oled Display object
*
@ -66,7 +65,8 @@ void OledDisplay::setCentralText(int y, const char *text) {
* @param value Measurements
* @param log Serial Stream
*/
OledDisplay::OledDisplay(Configuration &config, Measurements &value, Stream &log)
OledDisplay::OledDisplay(Configuration &config, Measurements &value,
Stream &log)
: PrintLog(log, "OledDisplay"), config(config), value(value) {}
/**
@ -90,6 +90,7 @@ bool OledDisplay::begin(void) {
return true;
}
if (ag->isOne() || ag->isPro3_7() || ag->isPro4_2()) {
/** Create u8g2 instance */
u8g2 = new U8G2_SH1106_128X64_NONAME_F_HW_I2C(U8G2_R0, U8X8_PIN_NONE);
if (u8g2 == NULL) {
@ -102,6 +103,13 @@ bool OledDisplay::begin(void) {
logError("U8G2 'begin' failed");
return false;
}
} else if (ag->isBasic()) {
logInfo("DIY_BASIC init");
ag->display.begin(Wire);
ag->display.setTextColor(1);
ag->display.clear();
ag->display.show();
}
/** Show low brightness on startup. then it's completely turn off on main
* application */
@ -125,9 +133,13 @@ void OledDisplay::end(void) {
return;
}
if (ag->isOne() || ag->isPro3_7() || ag->isPro4_2()) {
/** Free u8g2 */
delete DISP();
u8g2 = NULL;
} else if (ag->isBasic()) {
ag->display.end();
}
isBegin = false;
logInfo("end");
@ -157,6 +169,7 @@ void OledDisplay::setText(const char *line1, const char *line2,
return;
}
if (ag->isOne() || ag->isPro3_7() || ag->isPro4_2()) {
DISP()->firstPage();
do {
DISP()->setFont(u8g2_font_t0_16_tf);
@ -164,6 +177,18 @@ void OledDisplay::setText(const char *line1, const char *line2,
DISP()->drawStr(1, 30, line2);
DISP()->drawStr(1, 50, line3);
} while (DISP()->nextPage());
} else if (ag->isBasic()) {
ag->display.clear();
ag->display.setCursor(1, 1);
ag->display.setText(line1);
ag->display.setCursor(1, 17);
ag->display.setText(line2);
ag->display.setCursor(1, 33);
ag->display.setText(line3);
ag->display.show();
}
}
/**
@ -193,6 +218,7 @@ void OledDisplay::setText(const char *line1, const char *line2,
return;
}
if (ag->isOne() || ag->isPro3_7() || ag->isPro4_2()) {
DISP()->firstPage();
do {
DISP()->setFont(u8g2_font_t0_16_tf);
@ -201,6 +227,16 @@ void OledDisplay::setText(const char *line1, const char *line2,
DISP()->drawStr(1, 40, line3);
DISP()->drawStr(1, 55, line4);
} while (DISP()->nextPage());
} else if (ag->isBasic()) {
ag->display.clear();
ag->display.setCursor(0, 0);
ag->display.setText(line1);
ag->display.setCursor(0, 10);
ag->display.setText(line2);
ag->display.setCursor(0, 20);
ag->display.setText(line3);
ag->display.show();
}
}
/**
@ -218,8 +254,8 @@ void OledDisplay::showDashboard(const char *status) {
return;
}
char strBuf[10];
char strBuf[16];
if (ag->isOne() || ag->isPro3_7() || ag->isPro4_2()) {
DISP()->firstPage();
do {
DISP()->setFont(u8g2_font_t0_16_tf);
@ -313,9 +349,47 @@ void OledDisplay::showDashboard(const char *status) {
}
DISP()->drawStr(85, 63, strBuf);
} while (DISP()->nextPage());
} else if (ag->isBasic()) {
ag->display.clear();
/** Set CO2 */
snprintf(strBuf, sizeof(strBuf), "CO2:%d", value.CO2);
ag->display.setCursor(0, 0);
ag->display.setText(strBuf);
/** Set PM */
ag->display.setCursor(0, 12);
snprintf(strBuf, sizeof(strBuf), "PM2.5:%d", value.pm25_1);
ag->display.setText(strBuf);
/** Set temperature and humidity */
if (value.Temperature <= -1001.0f) {
if (config.isTemperatureUnitInF()) {
snprintf(strBuf, sizeof(strBuf), "T:-F");
} else {
snprintf(strBuf, sizeof(strBuf), "T:-C");
}
} else {
if (config.isTemperatureUnitInF()) {
float tempF = (value.Temperature * 9) / 5 + 32;
snprintf(strBuf, sizeof(strBuf), "T:%d F", (int)tempF);
} else {
snprintf(strBuf, sizeof(strBuf), "T:%d C", (int)value.Temperature);
}
}
ag->display.setCursor(0, 24);
ag->display.setText(strBuf);
snprintf(strBuf, sizeof(strBuf), "H:%d %%", (int)value.Humidity);
ag->display.setCursor(0, 36);
ag->display.setText(strBuf);
ag->display.show();
}
}
void OledDisplay::setBrightness(int percent) {
if (ag->isOne() || ag->isPro3_7() || ag->isPro4_2()) {
if (percent == 0) {
isDisplayOff = true;
@ -328,8 +402,12 @@ void OledDisplay::setBrightness(int percent) {
isDisplayOff = false;
DISP()->setContrast((127 * percent) / 100);
}
} else if (ag->isBasic()) {
ag->display.setContrast((255 * percent) / 100);
}
}
#ifdef ESP32
void OledDisplay::showFirmwareUpdateVersion(String version) {
if (isDisplayOff) {
return;
@ -410,13 +488,25 @@ void OledDisplay::showFirmwareUpdateUpToDate(void) {
setCentralText(40, "up to date");
} while (DISP()->nextPage());
}
#else
#endif
void OledDisplay::showRebooting(void) {
if (ag->isOne() || ag->isPro3_7() || ag->isPro4_2()) {
DISP()->firstPage();
do {
DISP()->setFont(u8g2_font_t0_16_tf);
// setCentralText(20, "Firmware Update");
setCentralText(40, "Rebooting...");
setCentralText(40, "Reboot...");
// setCentralText(60, String("Retry after 24h"));
} while (DISP()->nextPage());
} else if (ag->isBasic()) {
ag->display.clear();
ag->display.setCursor(0, 20);
ag->display.setText("Rebooting...");
ag->display.show();
}
}

View File

@ -36,12 +36,16 @@ public:
void showDashboard(void);
void showDashboard(const char *status);
void setBrightness(int percent);
#ifdef ESP32
void showFirmwareUpdateVersion(String version);
void showFirmwareUpdateProgress(int percent);
void showFirmwareUpdateSuccess(int count);
void showFirmwareUpdateFailed(void);
void showFirmwareUpdateSkipped(void);
void showFirmwareUpdateUpToDate(void);
#else
#endif
void showRebooting(void);
};

View File

@ -228,6 +228,9 @@ void StateMachine::co2Calibration(void) {
String str =
"after " + String(SENSOR_CO2_CALIB_COUNTDOWN_MAX - i) + " sec";
disp.setText("Start CO2 calib", str.c_str(), "");
} else if (ag->isBasic()) {
String str = String(SENSOR_CO2_CALIB_COUNTDOWN_MAX - i) + " sec";
disp.setText("CO2 Calib", "after", str.c_str());
} else {
logInfo("Start CO2 calib after " +
String(SENSOR_CO2_CALIB_COUNTDOWN_MAX - i) + " sec");
@ -238,6 +241,8 @@ void StateMachine::co2Calibration(void) {
if (ag->s8.setBaselineCalibration()) {
if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_7()) {
disp.setText("Calibration", "success", "");
} else if (ag->isBasic()) {
disp.setText("CO2 Calib", "success", "");
} else {
logInfo("CO2 Calibration: success");
}
@ -264,7 +269,10 @@ void StateMachine::co2Calibration(void) {
} else {
if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_7()) {
disp.setText("Calibration", "failure!!!", "");
} else {
} else if(ag->isBasic()) {
disp.setText("CO2 calib", "failure!!!", "");
}
else {
logInfo("CO2 Calibration: failure!!!");
}
delay(2000);
@ -287,7 +295,8 @@ void StateMachine::co2Calibration(void) {
if (config.getCO2CalibrationAbcDays() == 0) {
toStr = "off";
}
String msg = "Setting S8 from " + fromStr + " to " + toStr + " " + resultStr;
String msg =
"Setting S8 from " + fromStr + " to " + toStr + " " + resultStr;
logInfo(msg);
}
} else {
@ -314,9 +323,7 @@ void StateMachine::ledBarTest(void) {
}
}
void StateMachine::ledBarPowerUpTest(void) {
ledBarRunTest();
}
void StateMachine::ledBarPowerUpTest(void) { ledBarRunTest(); }
void StateMachine::ledBarRunTest(void) {
disp.setText("LED Test", "running", ".....");
@ -398,8 +405,8 @@ StateMachine::~StateMachine() {}
* @param state
*/
void StateMachine::displayHandle(AgStateMachineState state) {
// Ignore handle if not ONE_INDOOR board
if (!(ag->isOne() || (ag->isPro4_2()) || ag->isPro3_7())) {
// Ignore handle if not support display
if (!(ag->isOne() || (ag->isPro4_2()) || ag->isPro3_7() || ag->isBasic())) {
if (state == AgStateMachineCo2Calibration) {
co2Calibration();
}
@ -417,11 +424,17 @@ void StateMachine::displayHandle(AgStateMachineState state) {
case AgStateMachineWiFiManagerMode:
case AgStateMachineWiFiManagerPortalActive: {
if (wifiConnectCountDown >= 0) {
if (ag->isBasic()) {
String ssid = "\"airgradient-" + ag->deviceId() + "\" " +
String(wifiConnectCountDown) + String("s");
disp.setText("Connect tohotspot:", ssid.c_str(), "");
} else {
String line1 = String(wifiConnectCountDown) + "s to connect";
String line2 = "to WiFi hotspot:";
String line3 = "\"airgradient-";
String line4 = ag->deviceId() + "\"";
disp.setText(line1, line2, line3, line4);
}
wifiConnectCountDown--;
}
break;
@ -435,7 +448,12 @@ void StateMachine::displayHandle(AgStateMachineState state) {
break;
}
case AgStateMachineWiFiOkServerConnecting: {
if (ag->isBasic()) {
disp.setText("Connecting", "to", "Server...");
} else {
disp.setText("Connecting to", "Server", "...");
}
break;
}
case AgStateMachineWiFiOkServerConnected: {
@ -451,7 +469,11 @@ void StateMachine::displayHandle(AgStateMachineState state) {
break;
}
case AgStateMachineWiFiOkServerOkSensorConfigFailed: {
if (ag->isBasic()) {
disp.setText("Monitor", "not on", "dashboard");
} else {
disp.setText("Monitor not", "setup on", "dashboard");
}
break;
}
case AgStateMachineWiFiLost: {
@ -527,7 +549,8 @@ void StateMachine::displayWiFiConnectCountDown(int count) {
void StateMachine::ledAnimationInit(void) { ledBarAnimationCount = -1; }
/**
* @brief Handle LED from state, only handle LED if board type is: One Indoor or Open Air
* @brief Handle LED from state, only handle LED if board type is: One Indoor or
* Open Air
*
* @param state
*/

View File

@ -19,7 +19,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
}
}
if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_7()) {
if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_7() || ag->isBasic()) {
if (config->hasSensorPMS1) {
if (this->pm01_1 >= 0) {
root["pm01"] = this->pm01_1;
@ -177,7 +177,9 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
root["bootCount"] = bootCount;
if (localServer) {
if (ag->isOne()) {
root["ledMode"] = config->getLedBarModeName();
}
root["firmware"] = ag->getVersion();
root["model"] = AgFirmwareModeName(fwMode);
}

View File

@ -49,8 +49,8 @@ bool WifiConnector::connect(void) {
WIFI()->setSaveConfigCallback([this]() { _wifiSaveConfig(); });
WIFI()->setSaveParamsCallback([this]() { _wifiSaveParamCallback(); });
WIFI()->setConfigPortalTimeoutCallback([this]() {_wifiTimeoutCallback();});
if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_7()) {
disp.setText("Connecting to", "WiFi", "...");
if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_7() || ag->isBasic()) {
disp.setText("Connect to", "WiFi", "...");
} else {
logInfo("Connecting to WiFi...");
}
@ -81,6 +81,7 @@ bool WifiConnector::connect(void) {
WifiConnector *connector = (WifiConnector *)obj;
while (connector->_wifiConfigPortalActive()) {
connector->_wifiProcess();
vTaskDelay(1);
}
vTaskDelete(NULL);
},
@ -142,7 +143,7 @@ bool WifiConnector::connect(void) {
/** Show display wifi connect result failed */
if (WiFi.isConnected() == false) {
sm.handleLeds(AgStateMachineWiFiManagerConnectFailed);
if (ag->isOne() || ag->isPro4_2() || ag->isPro3_7()) {
if (ag->isOne() || ag->isPro4_2() || ag->isPro3_7() || ag->isBasic()) {
sm.displayHandle(AgStateMachineWiFiManagerConnectFailed);
}
delay(6000);
@ -247,7 +248,7 @@ void WifiConnector::_wifiProcess() {
if (WiFi.isConnected() == false) {
/** Display countdown */
uint32_t ms;
if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_7()) {
if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_7() || ag->isBasic()) {
ms = (uint32_t)(millis() - dispPeriod);
if (ms >= 1000) {
dispPeriod = millis();

View File

@ -66,6 +66,8 @@ bool AirGradient::isPro3_7(void) {
return boardType == BoardType::DIY_PRO_INDOOR_V3_7;
}
bool AirGradient::isBasic(void) { return boardType == BoardType::DIY_BASIC; }
String AirGradient::deviceId(void) {
String mac = WiFi.macAddress();
mac.replace(":", "");

View File

@ -149,6 +149,14 @@ public:
*/
bool isPro3_7(void);
/**
* @brief Check that Airgradient object is DIY_BASIC
*
* @return true Yes
* @return false No
*/
bool isBasic(void);
/**
* @brief Get device Id
*