Merge commit '0370a8aa15ffaf776f9055f84b5d7c221046b9be' into fix/pms-read-data

This commit is contained in:
Phat Nguyen
2024-09-24 10:39:04 +07:00
21 changed files with 309 additions and 245 deletions

View File

@ -41,33 +41,35 @@ You get the following response:
"bootCount": 6, "bootCount": 6,
"ledMode": "pm", "ledMode": "pm",
"firmware": "3.1.3", "firmware": "3.1.3",
"model": "I-9PSL" "model": "I-9PSL",
"monitorDisplayCompensatedValues": true
} }
``` ```
| Properties | Type | Explanation | | Properties | Type | Explanation |
|------------------|--------|--------------------------------------------------------------------| |-----------------------------------|---------|----------------------------------------------------------------------------------------|
| `serialno` | String | Serial Number of the monitor | | `serialno` | String | Serial Number of the monitor |
| `wifi` | Number | WiFi signal strength | | `wifi` | Number | WiFi signal strength |
| `pm01` | Number | PM1 in ug/m3 | | `pm01` | Number | PM1 in ug/m3 |
| `pm02` | Number | PM2.5 in ug/m3 | | `pm02` | Number | PM2.5 in ug/m3 |
| `pm10` | Number | PM10 in ug/m3 | | `pm10` | Number | PM10 in ug/m3 |
| `pm02Compensated` | Number | PM2.5 in ug/m3 with correction applied (from fw version 3.1.4 onwards) | | `pm02Compensated` | Number | PM2.5 in ug/m3 with correction applied (from fw version 3.1.4 onwards) |
| `rco2` | Number | CO2 in ppm | | `rco2` | Number | CO2 in ppm |
| `pm003Count` | Number | Particle count per dL | | `pm003Count` | Number | Particle count per dL |
| `atmp` | Number | Temperature in Degrees Celsius | | `atmp` | Number | Temperature in Degrees Celsius |
| `atmpCompensated` | Number | Temperature in Degrees Celsius with correction applied | | `atmpCompensated` | Number | Temperature in Degrees Celsius with correction applied |
| `rhum` | Number | Relative Humidity | | `rhum` | Number | Relative Humidity |
| `rhumCompensated` | Number | Relative Humidity with correction applied | | `rhumCompensated` | Number | Relative Humidity with correction applied |
| `tvocIndex` | Number | Senisiron VOC Index | | `tvocIndex` | Number | Senisiron VOC Index |
| `tvocRaw` | Number | VOC raw value | | `tvocRaw` | Number | VOC raw value |
| `noxIndex` | Number | Senisirion NOx Index | | `noxIndex` | Number | Senisirion NOx Index |
| `noxRaw` | Number | NOx raw value | | `noxRaw` | Number | NOx raw value |
| `boot` | Number | Counts every measurement cycle. Low boot counts indicate restarts. | | `boot` | Number | Counts every measurement cycle. Low boot counts indicate restarts. |
| `bootCount` | Number | Same as boot property. Required for Home Assistant compatability. Will be depreciated. | | `bootCount` | Number | Same as boot property. Required for Home Assistant compatability. Will be depreciated. |
| `ledMode` | String | Current configuration of the LED mode | | `ledMode` | String | Current configuration of the LED mode |
| `firmware` | String | Current firmware version | | `firmware` | String | Current firmware version |
| `model` | String | Current model name | | `model` | String | Current model name |
| `monitorDisplayCompensatedValues` | Boolean | Switching Display of AirGradient ONE to Compensated / Non Compensated Values |
Compensated values apply correction algorithms to make the sensor values more accurate. Temperature and relative humidity correction is only applied on the outdoor monitor Open Air but the properties _compensated will still be send also for the indoor monitor AirGradient ONE. Compensated values apply correction algorithms to make the sensor values more accurate. Temperature and relative humidity correction is only applied on the outdoor monitor Open Air but the properties _compensated will still be send also for the indoor monitor AirGradient ONE.
@ -75,17 +77,21 @@ Compensated values apply correction algorithms to make the sensor values more ac
With the path "/config" you can get the current configuration. With the path "/config" you can get the current configuration.
```json ```json
{ {
"country": "US", "country": "TH",
"pmStandard": "ugm3", "pmStandard": "ugm3",
"ledBarMode": "pm", "ledBarMode": "pm",
"displayMode": "on", "abcDays": 7,
"abcDays": 30,
"tvocLearningOffset": 12, "tvocLearningOffset": 12,
"noxLearningOffset": 12, "noxLearningOffset": 12,
"mqttBrokerUrl": "", "mqttBrokerUrl": "",
"temperatureUnit": "f", "temperatureUnit": "c",
"configurationControl": "both", "configurationControl": "local",
"postDataToAirGradient": true "postDataToAirGradient": true,
"ledBarBrightness": 100,
"displayBrightness": 100,
"offlineMode": false,
"model": "I-9PSL",
"monitorDisplayCompensatedValues": true
} }
``` ```
@ -110,21 +116,22 @@ If the monitor is set up on the AirGradient dashboard, it will also receive conf
#### Configuration Parameters (GET/PUT) #### Configuration Parameters (GET/PUT)
| Properties | Description | Type | Accepted Values | Example | | Properties | Description | Type | Accepted Values | Example |
|-------------------------|:-------------------------------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------| |-----------------------------------|:-----------------------------------------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------|
| `country` | Country where the device is. | String | Country code as [ALPHA-2 notation](https://www.iban.com/country-codes) | {"country": "TH"} | | `country` | Country where the device is. | String | Country code as [ALPHA-2 notation](https://www.iban.com/country-codes) | `{"country": "TH"}` |
| `model` | Hardware identifier (only GET). | String | I-9PSL-DE | {"model": "I-9PSL-DE"} | | `model` | Hardware identifier (only GET). | String | I-9PSL-DE | `{"model": "I-9PSL-DE"}` |
| `pmStandard` | Particle matter standard used on the display. | String | `ugm3`: ug/m3 <br> `us-aqi`: USAQI | {"pmStandard": "ugm3"} | | `pmStandard` | Particle matter standard used on the display. | String | `ugm3`: ug/m3 <br> `us-aqi`: USAQI | `{"pmStandard": "ugm3"}` |
| `ledBarMode` | Mode in which the led bar can be set. | String | `co2`: LED bar displays CO2 <br>`pm`: LED bar displays PM <br>`off`: Turn off LED bar | {"ledBarMode": "off"} | | `ledBarMode` | Mode in which the led bar can be set. | String | `co2`: LED bar displays CO2 <br>`pm`: LED bar displays PM <br>`off`: Turn off LED bar | `{"ledBarMode": "off"}` |
| `displayBrightness` | Brightness of the Display. | Number | 0-100 | {"displayBrightness": 50} | | `displayBrightness` | Brightness of the Display. | Number | 0-100 | `{"displayBrightness": 50}` |
| `ledBarBrightness` | Brightness of the LEDBar. | Number | 0-100 | {"ledBarBrightness": 40} | | `ledBarBrightness` | Brightness of the LEDBar. | Number | 0-100 | `{"ledBarBrightness": 40}` |
| `abcDays` | Number of days for CO2 automatic baseline calibration. | Number | Maximum 200 days. Default 8 days. | {"abcDays": 8} | | `abcDays` | Number of days for CO2 automatic baseline calibration. | Number | Maximum 200 days. Default 8 days. | `{"abcDays": 8}` |
| `mqttBrokerUrl` | MQTT broker URL. | String | | {"mqttBrokerUrl": "mqtt://192.168.0.18:1883"} | | `mqttBrokerUrl` | MQTT broker URL. | String | | `{"mqttBrokerUrl": "mqtt://192.168.0.18:1883"}` |
| `temperatureUnit` | Temperature unit shown on the display. | String | `c` or `C`: Degree Celsius °C <br>`f` or `F`: Degree Fahrenheit °F | {"temperatureUnit": "c"} | | `temperatureUnit` | Temperature unit shown on the display. | String | `c` or `C`: Degree Celsius °C <br>`f` or `F`: Degree Fahrenheit °F | `{"temperatureUnit": "c"}` |
| `configurationControl` | The configuration source of the device. | String | `both`: Accept local and cloud configuration <br>`local`: Accept only local configuration <br>`cloud`: Accept only cloud configuration | {"configurationControl": "both"} | | `configurationControl` | The configuration source of the device. | String | `both`: Accept local and cloud configuration <br>`local`: Accept only local configuration <br>`cloud`: Accept only cloud configuration | `{"configurationControl": "both"}` |
| `postDataToAirGradient` | Send data to AirGradient cloud. | Boolean | `true`: Enabled <br>`false`: Disabled | {"postDataToAirGradient": true} | | `postDataToAirGradient` | Send data to AirGradient cloud. | Boolean | `true`: Enabled <br>`false`: Disabled | `{"postDataToAirGradient": true}` |
| `co2CalibrationRequested` | Can be set to trigger a calibration. | Boolean | `true`: CO2 calibration (400ppm) will be triggered | {"co2CalibrationRequested": true} | | `co2CalibrationRequested` | Can be set to trigger a calibration. | Boolean | `true`: CO2 calibration (400ppm) will be triggered | `{"co2CalibrationRequested": true}` |
| `ledBarTestRequested` | Can be set to trigger a test. | Boolean | `true` : LEDs will run test sequence | {"ledBarTestRequested": true} | | `ledBarTestRequested` | Can be set to trigger a test. | Boolean | `true` : LEDs will run test sequence | `{"ledBarTestRequested": true}` |
| `noxLearningOffset` | Set NOx learning gain offset. | Number | 0-720 (default 12) | {"noxLearningOffset": 12} | | `noxLearningOffset` | Set NOx learning gain offset. | Number | 0-720 (default 12) | `{"noxLearningOffset": 12}` |
| `tvocLearningOffset` | Set VOC learning gain offset. | Number | 0-720 (default 12) | {"tvocLearningOffset": 12} | | `tvocLearningOffset` | Set VOC learning gain offset. | Number | 0-720 (default 12) | `{"tvocLearningOffset": 12}` |
| `offlineMode` | Set monitor to run without WiFi. | Boolean | `false`: Disabled (default) <br> `true`: Enabled | {"offlineMode": true} | | `offlineMode` | Set monitor to run without WiFi. | Boolean | `false`: Disabled (default) <br> `true`: Enabled | `{"offlineMode": true}` |
| `monitorDisplayCompensatedValues` | Set the display show the PM value with/without compensate value (From [3.1.9]()) | Boolean | `false`: Without compensate (default) <br> `true`: with compensate | `{"monitorDisplayCompensatedValues": false }` |

View File

@ -206,11 +206,7 @@ void loop() {
tvocSchedule.run(); tvocSchedule.run();
} }
/** Auto reset watchdog timer if offline mode or postDataToAirGradient */ watchdogFeedSchedule.run();
if (configuration.isOfflineMode() ||
(configuration.isPostDataToAirGradient() == false)) {
watchdogFeedSchedule.run();
}
/** Check for handle WiFi reconnect */ /** Check for handle WiFi reconnect */
wifiConnector.handle(); wifiConnector.handle();
@ -266,18 +262,23 @@ static void mdnsInit(void) {
} }
static void initMqtt(void) { static void initMqtt(void) {
if (mqttClient.begin(configuration.getMqttBrokerUri())) { String mqttUri = configuration.getMqttBrokerUri();
Serial.println("Setup connect to MQTT broker successful"); if (mqttUri.isEmpty()) {
Serial.println(
"MQTT is not configured, skipping initialization of MQTT client");
return;
}
if (mqttClient.begin(mqttUri)) {
Serial.println("Successfully connected to MQTT broker");
} else { } else {
Serial.println("setup Connect to MQTT broker failed"); Serial.println("Connection to MQTT broker failed");
} }
} }
static void wdgFeedUpdate(void) { static void wdgFeedUpdate(void) {
ag.watchdog.reset(); ag.watchdog.reset();
Serial.println(); Serial.println("External watchdog feed!");
Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset");
Serial.println();
} }
static bool sgp41Init(void) { static bool sgp41Init(void) {
@ -513,6 +514,7 @@ static void updatePm(void) {
Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_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("PM10 ug/m3: %d\r\n", measurements.pm10_1);
Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1); Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1);
Serial.printf("PM firmware version: %d\r\n", ag.pms5003.getFirmwareVersion());
ag.pms5003.resetFailCount(); ag.pms5003.resetFailCount();
} else { } else {
ag.pms5003.updateFailCount(); ag.pms5003.updateFailCount();
@ -541,13 +543,11 @@ static void sendDataToServer(void) {
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(),
&ag, &configuration); &ag, &configuration);
if (apiClient.postToServer(syncData)) { if (apiClient.postToServer(syncData)) {
ag.watchdog.reset();
Serial.println(); Serial.println();
Serial.println( Serial.println(
"Online mode and isPostToAirGradient = true: watchdog reset"); "Online mode and isPostToAirGradient = true: watchdog reset");
Serial.println(); Serial.println();
} }
measurements.bootCount++; measurements.bootCount++;
} }

View File

@ -204,11 +204,7 @@ void loop() {
tvocSchedule.run(); tvocSchedule.run();
} }
/** Auto reset watchdog timer if offline mode or postDataToAirGradient */ watchdogFeedSchedule.run();
if (configuration.isOfflineMode() ||
(configuration.isPostDataToAirGradient() == false)) {
watchdogFeedSchedule.run();
}
/** Check for handle WiFi reconnect */ /** Check for handle WiFi reconnect */
wifiConnector.handle(); wifiConnector.handle();
@ -264,10 +260,17 @@ static void mdnsInit(void) {
} }
static void initMqtt(void) { static void initMqtt(void) {
if (mqttClient.begin(configuration.getMqttBrokerUri())) { String mqttUri = configuration.getMqttBrokerUri();
Serial.println("Setup connect to MQTT broker successful"); if (mqttUri.isEmpty()) {
Serial.println(
"MQTT is not configured, skipping initialization of MQTT client");
return;
}
if (mqttClient.begin(mqttUri)) {
Serial.println("Successfully connected to MQTT broker");
} else { } else {
Serial.println("setup Connect to MQTT broker failed"); Serial.println("Connection to MQTT broker failed");
} }
} }
@ -332,9 +335,7 @@ static void factoryConfigReset(void) {
static void wdgFeedUpdate(void) { static void wdgFeedUpdate(void) {
ag.watchdog.reset(); ag.watchdog.reset();
Serial.println(); Serial.println("External watchdog feed!");
Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset");
Serial.println();
} }
static bool sgp41Init(void) { static bool sgp41Init(void) {
@ -565,6 +566,7 @@ static void updatePm(void) {
Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_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("PM10 ug/m3: %d\r\n", measurements.pm10_1);
Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1); Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1);
Serial.printf("PM firmware version: %d\r\n", ag.pms5003.getFirmwareVersion());
ag.pms5003.resetFailCount(); ag.pms5003.resetFailCount();
} else { } else {
ag.pms5003.updateFailCount(); ag.pms5003.updateFailCount();
@ -593,13 +595,11 @@ static void sendDataToServer(void) {
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(),
&ag, &configuration); &ag, &configuration);
if (apiClient.postToServer(syncData)) { if (apiClient.postToServer(syncData)) {
ag.watchdog.reset();
Serial.println(); Serial.println();
Serial.println( Serial.println(
"Online mode and isPostToAirGradient = true: watchdog reset"); "Online mode and isPostToAirGradient = true: watchdog reset");
Serial.println(); Serial.println();
} }
measurements.bootCount++; measurements.bootCount++;
} }

View File

@ -231,11 +231,7 @@ void loop() {
tvocSchedule.run(); tvocSchedule.run();
} }
/** Auto reset watchdog timer if offline mode or postDataToAirGradient */ watchdogFeedSchedule.run();
if (configuration.isOfflineMode() ||
(configuration.isPostDataToAirGradient() == false)) {
watchdogFeedSchedule.run();
}
/** Check for handle WiFi reconnect */ /** Check for handle WiFi reconnect */
wifiConnector.handle(); wifiConnector.handle();
@ -291,10 +287,17 @@ static void mdnsInit(void) {
} }
static void initMqtt(void) { static void initMqtt(void) {
if (mqttClient.begin(configuration.getMqttBrokerUri())) { String mqttUri = configuration.getMqttBrokerUri();
Serial.println("Setup connect to MQTT broker successful"); if (mqttUri.isEmpty()) {
Serial.println(
"MQTT is not configured, skipping initialization of MQTT client");
return;
}
if (mqttClient.begin(mqttUri)) {
Serial.println("Successfully connected to MQTT broker");
} else { } else {
Serial.println("setup Connect to MQTT broker failed"); Serial.println("Connection to MQTT broker failed");
} }
} }
@ -327,7 +330,7 @@ static void factoryConfigReset(void) {
// } // }
/** Reset WIFI */ /** Reset WIFI */
WiFi.disconnect(true, true); wifiConnector.reset();
/** Reset local config */ /** Reset local config */
configuration.reset(); configuration.reset();
@ -355,9 +358,7 @@ static void factoryConfigReset(void) {
static void wdgFeedUpdate(void) { static void wdgFeedUpdate(void) {
ag.watchdog.reset(); ag.watchdog.reset();
Serial.println(); Serial.println("External watchdog feed!");
Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset");
Serial.println();
} }
static bool sgp41Init(void) { static bool sgp41Init(void) {
@ -606,6 +607,7 @@ static void updatePm(void) {
Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_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("PM10 ug/m3: %d\r\n", measurements.pm10_1);
Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1); Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1);
Serial.printf("PM firmware version: %d\r\n", ag.pms5003.getFirmwareVersion());
ag.pms5003.resetFailCount(); ag.pms5003.resetFailCount();
} else { } else {
ag.pms5003.updateFailCount(); ag.pms5003.updateFailCount();
@ -634,13 +636,11 @@ static void sendDataToServer(void) {
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(),
&ag, &configuration); &ag, &configuration);
if (apiClient.postToServer(syncData)) { if (apiClient.postToServer(syncData)) {
ag.watchdog.reset();
Serial.println(); Serial.println();
Serial.println( Serial.println(
"Online mode and isPostToAirGradient = true: watchdog reset"); "Online mode and isPostToAirGradient = true: watchdog reset");
Serial.println(); Serial.println();
} }
measurements.bootCount++; measurements.bootCount++;
} }

View File

@ -98,9 +98,7 @@ static String fwNewVersion;
static void boardInit(void); static void boardInit(void);
static void failedHandler(String msg); static void failedHandler(String msg);
static void configurationUpdateSchedule(void); static void configurationUpdateSchedule(void);
static void appLedHandler(void); static void updateDisplayAndLedBar(void);
static void appDispHandler(void);
static void oledDisplayLedBarSchedule(void);
static void updateTvoc(void); static void updateTvoc(void);
static void updatePm(void); static void updatePm(void);
static void sendDataToServer(void); static void sendDataToServer(void);
@ -118,7 +116,7 @@ static void otaHandlerCallback(OtaState state, String mesasge);
static void displayExecuteOta(OtaState state, String msg, static void displayExecuteOta(OtaState state, String msg,
int processing); int processing);
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplayLedBarSchedule); AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, updateDisplayAndLedBar);
AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL, AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL,
configurationUpdateSchedule); configurationUpdateSchedule);
AgSchedule agApiPostSchedule(SERVER_SYNC_INTERVAL, sendDataToServer); AgSchedule agApiPostSchedule(SERVER_SYNC_INTERVAL, sendDataToServer);
@ -261,8 +259,8 @@ void setup() {
oledDisplay.setBrightness(configuration.getDisplayBrightness()); oledDisplay.setBrightness(configuration.getDisplayBrightness());
} }
appLedHandler(); // Update display and led bar after finishing setup to show dashboard
appDispHandler(); updateDisplayAndLedBar();
} }
void loop() { void loop() {
@ -303,11 +301,7 @@ void loop() {
} }
} }
/** Auto reset watchdog timer if offline mode or postDataToAirGradient */ watchdogFeedSchedule.run();
if (configuration.isOfflineMode() ||
(configuration.isPostDataToAirGradient() == false)) {
watchdogFeedSchedule.run();
}
/** Check for handle WiFi reconnect */ /** Check for handle WiFi reconnect */
wifiConnector.handle(); wifiConnector.handle();
@ -387,11 +381,18 @@ static void createMqttTask(void) {
} }
static void initMqtt(void) { static void initMqtt(void) {
if (mqttClient.begin(configuration.getMqttBrokerUri())) { String mqttUri = configuration.getMqttBrokerUri();
Serial.println("Connect to MQTT broker successful"); if (mqttUri.isEmpty()) {
Serial.println(
"MQTT is not configured, skipping initialization of MQTT client");
return;
}
if (mqttClient.begin(mqttUri)) {
Serial.println("Successfully connected to MQTT broker");
createMqttTask(); createMqttTask();
} else { } else {
Serial.println("Connect to MQTT broker failed"); Serial.println("Connection to MQTT broker failed");
} }
} }
@ -447,7 +448,7 @@ static void factoryConfigReset(void) {
/** Show current content cause reset ignore */ /** Show current content cause reset ignore */
factoryBtnPressTime = 0; factoryBtnPressTime = 0;
if (ag->isOne()) { if (ag->isOne()) {
appDispHandler(); updateDisplayAndLedBar();
} }
} }
} }
@ -455,7 +456,7 @@ static void factoryConfigReset(void) {
if (factoryBtnPressTime != 0) { if (factoryBtnPressTime != 0) {
if (ag->isOne()) { if (ag->isOne()) {
/** Restore last display content */ /** Restore last display content */
appDispHandler(); updateDisplayAndLedBar();
} }
} }
factoryBtnPressTime = 0; factoryBtnPressTime = 0;
@ -464,9 +465,7 @@ static void factoryConfigReset(void) {
static void wdgFeedUpdate(void) { static void wdgFeedUpdate(void) {
ag->watchdog.reset(); ag->watchdog.reset();
Serial.println(); Serial.println("External watchdog feed!");
Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset");
Serial.println();
} }
static void ledBarEnabledUpdate(void) { static void ledBarEnabledUpdate(void) {
@ -702,7 +701,7 @@ static void oneIndoorInit(void) {
ledBarEnabledUpdate(); ledBarEnabledUpdate();
/** Show message init sensor */ /** Show message init sensor */
oledDisplay.setText("Sensor", "initializing...", ""); oledDisplay.setText("Monitor", "initializing...", "");
/** Init sensor SGP41 */ /** Init sensor SGP41 */
if (sgp41Init() == false) { if (sgp41Init() == false) {
@ -783,27 +782,27 @@ static void openAirInit(void) {
} }
} }
/** Try to find the PMS on other difference port with S8 */ /** Attempt to detect PM sensors */
if (fwMode == FW_MODE_O_1PST) { if (fwMode == FW_MODE_O_1PST) {
bool pmInitSuccess = false; bool pmInitSuccess = false;
if (serial0Available) { if (serial0Available) {
if (ag->pms5003t_1.begin(Serial0) == false) { if (ag->pms5003t_1.begin(Serial0) == false) {
configuration.hasSensorPMS1 = false; configuration.hasSensorPMS1 = false;
Serial.println("PMS1 sensor not found"); Serial.println("No PM sensor detected on Serial0");
} else { } else {
serial0Available = false; serial0Available = false;
pmInitSuccess = true; pmInitSuccess = true;
Serial.println("Found PMS 1 on Serial0"); Serial.println("Detected PM 1 on Serial0");
} }
} }
if (pmInitSuccess == false) { if (pmInitSuccess == false) {
if (serial1Available) { if (serial1Available) {
if (ag->pms5003t_1.begin(Serial1) == false) { if (ag->pms5003t_1.begin(Serial1) == false) {
configuration.hasSensorPMS1 = false; configuration.hasSensorPMS1 = false;
Serial.println("PMS1 sensor not found"); Serial.println("No PM sensor detected on Serial1");
} else { } else {
serial1Available = false; serial1Available = false;
Serial.println("Found PMS 1 on Serial1"); Serial.println("Detected PM 1 on Serial1");
} }
} }
} }
@ -811,15 +810,15 @@ static void openAirInit(void) {
} else { } else {
if (ag->pms5003t_1.begin(Serial0) == false) { if (ag->pms5003t_1.begin(Serial0) == false) {
configuration.hasSensorPMS1 = false; configuration.hasSensorPMS1 = false;
Serial.println("PMS1 sensor not found"); Serial.println("No PM sensor detected on Serial0");
} else { } else {
Serial.println("Found PMS 1 on Serial0"); Serial.println("Detected PM 1 on Serial0");
} }
if (ag->pms5003t_2.begin(Serial1) == false) { if (ag->pms5003t_2.begin(Serial1) == false) {
configuration.hasSensorPMS2 = false; configuration.hasSensorPMS2 = false;
Serial.println("PMS2 sensor not found"); Serial.println("No PM sensor detected on Serial1");
} else { } else {
Serial.println("Found PMS 2 on Serial1"); Serial.println("Detected PM 2 on Serial1");
} }
if (fwMode == FW_MODE_O_1PP) { if (fwMode == FW_MODE_O_1PP) {
@ -947,57 +946,41 @@ static void configUpdateHandle() {
stateMachine.executeLedBarTest(); stateMachine.executeLedBarTest();
} }
appDispHandler(); // Update display and led bar notification based on updated configuration
appLedHandler(); updateDisplayAndLedBar();
} }
static void appLedHandler(void) { static void updateDisplayAndLedBar(void) {
if (factoryBtnPressTime != 0) {
// Do not distrub factory reset sequence countdown
return;
}
if (configuration.isOfflineMode()) {
// Ignore network related status when in offline mode
stateMachine.displayHandle(AgStateMachineNormal);
stateMachine.handleLeds(AgStateMachineNormal);
return;
}
AgStateMachineState state = AgStateMachineNormal; AgStateMachineState state = AgStateMachineNormal;
if (configuration.isOfflineMode() == false) { if (wifiConnector.isConnected() == false) {
if (wifiConnector.isConnected() == false) { state = AgStateMachineWiFiLost;
state = AgStateMachineWiFiLost; } else if (apiClient.isFetchConfigureFailed()) {
} else if (apiClient.isFetchConfigureFailed()) { state = AgStateMachineSensorConfigFailed;
state = AgStateMachineSensorConfigFailed; if (apiClient.isNotAvailableOnDashboard()) {
} else if (apiClient.isPostToServerFailed()) { stateMachine.displaySetAddToDashBoard();
state = AgStateMachineServerLost; } else {
stateMachine.displayClearAddToDashBoard();
} }
} else if (apiClient.isPostToServerFailed() && configuration.isPostDataToAirGradient()) {
state = AgStateMachineServerLost;
} }
stateMachine.displayHandle(state);
stateMachine.handleLeds(state); stateMachine.handleLeds(state);
} }
static void appDispHandler(void) {
if (ag->isOne()) {
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 oledDisplayLedBarSchedule(void) {
if (ag->isOne()) {
if (factoryBtnPressTime == 0) {
appDispHandler();
}
}
appLedHandler();
}
static void updateTvoc(void) { static void updateTvoc(void) {
measurements.TVOC = ag->sgp41.getTvocIndex(); measurements.TVOC = ag->sgp41.getTvocIndex();
measurements.TVOCRaw = ag->sgp41.getTvocRaw(); measurements.TVOCRaw = ag->sgp41.getTvocRaw();
@ -1025,10 +1008,11 @@ static void updatePm(void) {
Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_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("PM10 ug/m3: %d\r\n", measurements.pm10_1);
Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1); Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1);
Serial.printf("PM firmware version: %d\r\n", ag->pms5003.getFirmwareVersion());
ag->pms5003.resetFailCount(); ag->pms5003.resetFailCount();
} else { } else {
ag->pms5003.updateFailCount(); ag->pms5003.updateFailCount();
Serial.printf("PMS read faile %d times\r\n", ag->pms5003.getFailCount()); Serial.printf("PMS read failed %d times\r\n", ag->pms5003.getFailCount());
if (ag->pms5003.getFailCount() >= PMS_FAIL_COUNT_SET_INVALID) { if (ag->pms5003.getFailCount() >= PMS_FAIL_COUNT_SET_INVALID) {
measurements.pm01_1 = utils::getInvalidPmValue(); measurements.pm01_1 = utils::getInvalidPmValue();
measurements.pm25_1 = utils::getInvalidPmValue(); measurements.pm25_1 = utils::getInvalidPmValue();
@ -1064,6 +1048,7 @@ static void updatePm(void) {
ag->pms5003t_1.compensateTemp(measurements.temp_1)); ag->pms5003t_1.compensateTemp(measurements.temp_1));
Serial.printf("[1] Relative Humidity compensated: %0.2f\r\n", Serial.printf("[1] Relative Humidity compensated: %0.2f\r\n",
ag->pms5003t_1.compensateHum(measurements.hum_1)); ag->pms5003t_1.compensateHum(measurements.hum_1));
Serial.printf("[1] PM firmware version: %d\r\n", ag->pms5003t_1.getFirmwareVersion());
ag->pms5003t_1.resetFailCount(); ag->pms5003t_1.resetFailCount();
} else { } else {
@ -1107,6 +1092,7 @@ static void updatePm(void) {
ag->pms5003t_1.compensateTemp(measurements.temp_2)); ag->pms5003t_1.compensateTemp(measurements.temp_2));
Serial.printf("[2] Relative Humidity compensated: %0.2f\r\n", Serial.printf("[2] Relative Humidity compensated: %0.2f\r\n",
ag->pms5003t_1.compensateHum(measurements.hum_2)); ag->pms5003t_1.compensateHum(measurements.hum_2));
Serial.printf("[2] PM firmware version: %d\r\n", ag->pms5003t_2.getFirmwareVersion());
ag->pms5003t_2.resetFailCount(); ag->pms5003t_2.resetFailCount();
} else { } else {
@ -1242,13 +1228,11 @@ static void sendDataToServer(void) {
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(),
ag, &configuration); ag, &configuration);
if (apiClient.postToServer(syncData)) { if (apiClient.postToServer(syncData)) {
ag->watchdog.reset();
Serial.println(); Serial.println();
Serial.println( Serial.println(
"Online mode and isPostToAirGradient = true: watchdog reset"); "Online mode and isPostToAirGradient = true: watchdog reset");
Serial.println(); Serial.println();
} }
measurements.bootCount++; measurements.bootCount++;
} }
@ -1275,4 +1259,4 @@ static void tempHumUpdate(void) {
measurements.Humidity = utils::getInvalidHumidity(); measurements.Humidity = utils::getInvalidHumidity();
Serial.println("SHT read failed"); Serial.println("SHT read failed");
} }
} }

View File

@ -1,5 +1,5 @@
name=AirGradient Air Quality Sensor name=AirGradient Air Quality Sensor
version=3.1.7 version=3.1.9
author=AirGradient <support@airgradient.com> author=AirGradient <support@airgradient.com>
maintainer=AirGradient <support@airgradient.com> maintainer=AirGradient <support@airgradient.com>
sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display. sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display.

View File

@ -58,6 +58,7 @@ bool AgApiClient::fetchServerConfiguration(void) {
} }
#else #else
HTTPClient client; HTTPClient client;
client.setTimeout(timeoutMs);
if (client.begin(uri) == false) { if (client.begin(uri) == false) {
getConfigFailed = true; getConfigFailed = true;
return false; return false;
@ -113,14 +114,13 @@ bool AgApiClient::postToServer(String data) {
return false; return false;
} }
String uri = String uri = apiRoot + "/sensors/airgradient:" + ag->deviceId() + "/measures";
"http://hw.airgradient.com/sensors/airgradient:" + ag->deviceId() +
"/measures";
// logInfo("Post uri: " + uri); // logInfo("Post uri: " + uri);
// logInfo("Post data: " + data); // logInfo("Post data: " + data);
WiFiClient wifiClient; WiFiClient wifiClient;
HTTPClient client; HTTPClient client;
client.setTimeout(timeoutMs);
if (client.begin(wifiClient, uri.c_str()) == false) { if (client.begin(wifiClient, uri.c_str()) == false) {
logError("Init client failed"); logError("Init client failed");
return false; return false;
@ -190,3 +190,12 @@ bool AgApiClient::sendPing(int rssi, int bootCount) {
String AgApiClient::getApiRoot() const { return apiRoot; } String AgApiClient::getApiRoot() const { return apiRoot; }
void AgApiClient::setApiRoot(const String &apiRoot) { this->apiRoot = apiRoot; } void AgApiClient::setApiRoot(const String &apiRoot) { this->apiRoot = apiRoot; }
/**
* @brief Set http request timeout. (Default: 10s)
*
* @param timeoutMs
*/
void AgApiClient::setTimeout(uint16_t timeoutMs) {
this->timeoutMs = timeoutMs;
}

View File

@ -25,6 +25,7 @@ private:
bool getConfigFailed; bool getConfigFailed;
bool postToServerFailed; bool postToServerFailed;
bool notAvailableOnDashboard = false; // Device not setup on Airgradient cloud dashboard. bool notAvailableOnDashboard = false; // Device not setup on Airgradient cloud dashboard.
uint16_t timeoutMs = 10000; // Default set to 10s
public: public:
AgApiClient(Stream &stream, Configuration &config); AgApiClient(Stream &stream, Configuration &config);
@ -40,6 +41,7 @@ public:
bool sendPing(int rssi, int bootCount); bool sendPing(int rssi, int bootCount);
String getApiRoot() const; String getApiRoot() const;
void setApiRoot(const String &apiRoot); void setApiRoot(const String &apiRoot);
void setTimeout(uint16_t timeoutMs);
}; };
#endif /** _AG_API_CLIENT_H_ */ #endif /** _AG_API_CLIENT_H_ */

View File

@ -41,21 +41,23 @@ JSON_PROP_DEF(displayBrightness);
JSON_PROP_DEF(co2CalibrationRequested); JSON_PROP_DEF(co2CalibrationRequested);
JSON_PROP_DEF(ledBarTestRequested); JSON_PROP_DEF(ledBarTestRequested);
JSON_PROP_DEF(offlineMode); JSON_PROP_DEF(offlineMode);
JSON_PROP_DEF(monitorDisplayCompensatedValues);
#define jprop_model_default "" #define jprop_model_default ""
#define jprop_country_default "TH" #define jprop_country_default "TH"
#define jprop_pmStandard_default getPMStandardString(false) #define jprop_pmStandard_default getPMStandardString(false)
#define jprop_ledBarMode_default getLedBarModeName(LedBarMode::LedBarModeCO2) #define jprop_ledBarMode_default getLedBarModeName(LedBarMode::LedBarModeCO2)
#define jprop_abcDays_default 8 #define jprop_abcDays_default 8
#define jprop_tvocLearningOffset_default 12 #define jprop_tvocLearningOffset_default 12
#define jprop_noxLearningOffset_default 12 #define jprop_noxLearningOffset_default 12
#define jprop_mqttBrokerUrl_default "" #define jprop_mqttBrokerUrl_default ""
#define jprop_temperatureUnit_default "c" #define jprop_temperatureUnit_default "c"
#define jprop_configurationControl_default String(CONFIGURATION_CONTROL_NAME[ConfigurationControl::ConfigurationControlBoth]) #define jprop_configurationControl_default String(CONFIGURATION_CONTROL_NAME[ConfigurationControl::ConfigurationControlBoth])
#define jprop_postDataToAirGradient_default true #define jprop_postDataToAirGradient_default true
#define jprop_ledBarBrightness_default 100 #define jprop_ledBarBrightness_default 100
#define jprop_displayBrightness_default 100 #define jprop_displayBrightness_default 100
#define jprop_offlineMode_default false #define jprop_offlineMode_default false
#define jprop_monitorDisplayCompensatedValues_default false
JSONVar jconfig; JSONVar jconfig;
@ -167,6 +169,7 @@ void Configuration::defaultConfig(void) {
jconfig[jprop_abcDays] = jprop_abcDays_default; jconfig[jprop_abcDays] = jprop_abcDays_default;
jconfig[jprop_model] = jprop_model_default; jconfig[jprop_model] = jprop_model_default;
jconfig[jprop_offlineMode] = jprop_offlineMode_default; jconfig[jprop_offlineMode] = jprop_offlineMode_default;
jconfig[jprop_monitorDisplayCompensatedValues] = jprop_monitorDisplayCompensatedValues_default;
saveConfig(); saveConfig();
} }
@ -628,6 +631,27 @@ bool Configuration::parse(String data, bool isLocal) {
} }
} }
if (JSON.typeof_(root[jprop_monitorDisplayCompensatedValues]) == "boolean") {
bool value = root[jprop_monitorDisplayCompensatedValues];
bool oldValue = jconfig[jprop_monitorDisplayCompensatedValues];
if (value != oldValue) {
changed = true;
jconfig[jprop_monitorDisplayCompensatedValues] = value;
configLogInfo(String(jprop_monitorDisplayCompensatedValues),
String(oldValue ? "true" : "false"),
String(value ? "true" : "false"));
}
} else {
if (jsonTypeInvalid(root[jprop_monitorDisplayCompensatedValues],
"boolean")) {
failedMessage = jsonTypeInvalidMessage(
String(jprop_monitorDisplayCompensatedValues), "boolean");
jsonInvalid();
return false;
}
}
if (ag->getBoardType() == ONE_INDOOR || if (ag->getBoardType() == ONE_INDOOR ||
ag->getBoardType() == OPEN_AIR_OUTDOOR) { ag->getBoardType() == OPEN_AIR_OUTDOOR) {
if (JSON.typeof_(root["targetFirmware"]) == "string") { if (JSON.typeof_(root["targetFirmware"]) == "string") {
@ -1082,12 +1106,16 @@ void Configuration::toConfig(const char *buf) {
} }
if (JSON.typeof_(jconfig[jprop_offlineMode]) != "boolean") { if (JSON.typeof_(jconfig[jprop_offlineMode]) != "boolean") {
isInvalid = true; changed = true;
} else { jconfig[jprop_offlineMode] = jprop_offlineMode_default;
isInvalid = false;
} }
if (isInvalid) {
jconfig[jprop_offlineMode] = false; /** Validate monitorDisplayCompensatedValues */
if (JSON.typeof_(jconfig[jprop_monitorDisplayCompensatedValues]) !=
"boolean") {
changed = true;
jconfig[jprop_monitorDisplayCompensatedValues] =
jprop_monitorDisplayCompensatedValues_default;
} }
if (changed) { if (changed) {
@ -1173,6 +1201,10 @@ bool Configuration::isLedBarModeChanged(void) {
return changed; return changed;
} }
bool Configuration::isMonitorDisplayCompensatedValues(void) {
return jconfig[jprop_monitorDisplayCompensatedValues];
}
bool Configuration::isDisplayBrightnessChanged(void) { bool Configuration::isDisplayBrightnessChanged(void) {
bool changed = displayBrightnessChanged; bool changed = displayBrightnessChanged;
displayBrightnessChanged = false; displayBrightnessChanged = false;

View File

@ -82,6 +82,7 @@ public:
void setOfflineMode(bool offline); void setOfflineMode(bool offline);
void setOfflineModeWithoutSave(bool offline); void setOfflineModeWithoutSave(bool offline);
bool isLedBarModeChanged(void); bool isLedBarModeChanged(void);
bool isMonitorDisplayCompensatedValues(void);
}; };
#endif /** _AG_CONFIG_H_ */ #endif /** _AG_CONFIG_H_ */

View File

@ -10,37 +10,43 @@
* *
* @param hasStatus * @param hasStatus
*/ */
void OledDisplay::showTempHum(bool hasStatus) { void OledDisplay::showTempHum(bool hasStatus, char *buf, int buf_size) {
char buf[16]; /** Temperature */
if (utils::isValidTemperature(value.Temperature)) { if (utils::isValidTemperature(value.Temperature)) {
float t = 0.0f;
if (config.isTemperatureUnitInF()) {
t = utils::degreeC_To_F(value.Temperature);
} else {
t = value.Temperature;
}
if (config.isTemperatureUnitInF()) { if (config.isTemperatureUnitInF()) {
float tempF = (value.Temperature * 9) / 5 + 32;
if (hasStatus) { if (hasStatus) {
snprintf(buf, sizeof(buf), "%0.1f", tempF); snprintf(buf, buf_size, "%0.1f", t);
} else { } else {
snprintf(buf, sizeof(buf), "%0.1f°F", tempF); snprintf(buf, buf_size, "%0.1f°F", t);
} }
} else { } else {
if (hasStatus) { if (hasStatus) {
snprintf(buf, sizeof(buf), "%.1f", value.Temperature); snprintf(buf, buf_size, "%.1f", t);
} else { } else {
snprintf(buf, sizeof(buf), "%.1f°C", value.Temperature); snprintf(buf, buf_size, "%.1f°C", t);
} }
} }
} else { } else { /** Show invalid value */
if (config.isTemperatureUnitInF()) { if (config.isTemperatureUnitInF()) {
snprintf(buf, sizeof(buf), "-°F"); snprintf(buf, buf_size, "-°F");
} else { } else {
snprintf(buf, sizeof(buf), "-°C"); snprintf(buf, buf_size, "-°C");
} }
} }
DISP()->drawUTF8(1, 10, buf); DISP()->drawUTF8(1, 10, buf);
/** Show humidty */ /** Show humidity */
if (utils::isValidHumidity(value.Humidity)) { if (utils::isValidHumidity(value.Humidity)) {
snprintf(buf, sizeof(buf), "%d%%", value.Humidity); snprintf(buf, buf_size, "%d%%", value.Humidity);
} else { } else {
snprintf(buf, sizeof(buf), "-%%"); snprintf(buf, buf_size, "-%%");
} }
if (value.Humidity > 99) { if (value.Humidity > 99) {
@ -261,7 +267,7 @@ void OledDisplay::showDashboard(const char *status) {
do { do {
DISP()->setFont(u8g2_font_t0_16_tf); DISP()->setFont(u8g2_font_t0_16_tf);
if ((status == NULL) || (strlen(status) == 0)) { if ((status == NULL) || (strlen(status) == 0)) {
showTempHum(false); showTempHum(false, strBuf, sizeof(strBuf));
} else { } else {
String strStatus = "Show status: " + String(status); String strStatus = "Show status: " + String(status);
logInfo(strStatus); logInfo(strStatus);
@ -272,7 +278,7 @@ void OledDisplay::showDashboard(const char *status) {
/** Show WiFi NA*/ /** Show WiFi NA*/
if (strcmp(status, "WiFi N/A") == 0) { if (strcmp(status, "WiFi N/A") == 0) {
DISP()->setFont(u8g2_font_t0_12_tf); DISP()->setFont(u8g2_font_t0_12_tf);
showTempHum(true); showTempHum(true, strBuf, sizeof(strBuf));
} }
} }
@ -304,29 +310,31 @@ void OledDisplay::showDashboard(const char *status) {
DISP()->drawStr(55, 27, "PM2.5"); DISP()->drawStr(55, 27, "PM2.5");
/** Draw PM2.5 value */ /** Draw PM2.5 value */
int pm25 = value.pm25_1; if (utils::isValidPm(value.pm25_1)) {
if (config.hasSensorSHT) { int pm25 = value.pm25_1;
pm25 = ag->pms5003.compensate(pm25, value.Humidity);
logInfo("PM2.5:" + String(value.pm25_1) + String("Compensated:") + String(pm25)); /** Compensate PM2.5 value. */
} if (config.hasSensorSHT && config.isMonitorDisplayCompensatedValues()) {
DISP()->setFont(u8g2_font_t0_22b_tf); pm25 = ag->pms5003.compensate(pm25, value.Humidity);
if (config.isPmStandardInUSAQI()) { logInfo("PM2.5 compensate: " + String(pm25));
if (utils::isValidPm(pm25)) { }
if (config.isPmStandardInUSAQI()) {
sprintf(strBuf, "%d", ag->pms5003.convertPm25ToUsAqi(pm25)); sprintf(strBuf, "%d", ag->pms5003.convertPm25ToUsAqi(pm25));
} else { } else {
sprintf(strBuf, "%s", "-"); sprintf(strBuf, "%d", pm25);
} }
DISP()->drawStr(55, 48, strBuf); } else { /** Show invalid value. */
DISP()->setFont(u8g2_font_t0_12_tf); sprintf(strBuf, "%s", "-");
}
DISP()->setFont(u8g2_font_t0_22b_tf);
DISP()->drawStr(55, 48, strBuf);
/** Draw PM2.5 unit */
DISP()->setFont(u8g2_font_t0_12_tf);
if (config.isPmStandardInUSAQI()) {
DISP()->drawUTF8(55, 61, "AQI"); DISP()->drawUTF8(55, 61, "AQI");
} else { } else {
if (utils::isValidPm(pm25)) {
sprintf(strBuf, "%d", pm25);
} else {
sprintf(strBuf, "%s", "-");
}
DISP()->drawStr(55, 48, strBuf);
DISP()->setFont(u8g2_font_t0_12_tf);
DISP()->drawUTF8(55, 61, "ug/m³"); DISP()->drawUTF8(55, 61, "ug/m³");
} }
@ -355,20 +363,21 @@ void OledDisplay::showDashboard(const char *status) {
ag->display.clear(); ag->display.clear();
/** Set CO2 */ /** Set CO2 */
if(utils::isValidCO2(value.CO2)) { if (utils::isValidCO2(value.CO2)) {
snprintf(strBuf, sizeof(strBuf), "CO2:%d", value.CO2); snprintf(strBuf, sizeof(strBuf), "CO2:%d", value.CO2);
} else { } else {
snprintf(strBuf, sizeof(strBuf), "CO2:-"); snprintf(strBuf, sizeof(strBuf), "CO2:-");
} }
ag->display.setCursor(0, 0); ag->display.setCursor(0, 0);
ag->display.setText(strBuf); ag->display.setText(strBuf);
/** Set PM */ /** Set PM */
int pm25 = value.pm25_1; int pm25 = value.pm25_1;
if(config.hasSensorSHT) { if (config.hasSensorSHT && config.isMonitorDisplayCompensatedValues()) {
pm25 = (int)ag->pms5003.compensate(pm25, value.Humidity); pm25 = (int)ag->pms5003.compensate(pm25, value.Humidity);
} }
ag->display.setCursor(0, 12); ag->display.setCursor(0, 12);
if (utils::isValidPm(pm25)) { if (utils::isValidPm(pm25)) {
snprintf(strBuf, sizeof(strBuf), "PM2.5:%d", pm25); snprintf(strBuf, sizeof(strBuf), "PM2.5:%d", pm25);
@ -380,8 +389,8 @@ void OledDisplay::showDashboard(const char *status) {
/** Set temperature and humidity */ /** Set temperature and humidity */
if (utils::isValidTemperature(value.Temperature)) { if (utils::isValidTemperature(value.Temperature)) {
if (config.isTemperatureUnitInF()) { if (config.isTemperatureUnitInF()) {
float tempF = (value.Temperature * 9) / 5 + 32; snprintf(strBuf, sizeof(strBuf), "T:%0.1f F",
snprintf(strBuf, sizeof(strBuf), "T:%0.1f F", tempF); utils::degreeC_To_F(value.Temperature));
} else { } else {
snprintf(strBuf, sizeof(strBuf), "T:%0.f1 C", value.Temperature); snprintf(strBuf, sizeof(strBuf), "T:%0.f1 C", value.Temperature);
} }
@ -424,7 +433,17 @@ void OledDisplay::setBrightness(int percent) {
DISP()->setContrast((127 * percent) / 100); DISP()->setContrast((127 * percent) / 100);
} }
} else if (ag->isBasic()) { } else if (ag->isBasic()) {
ag->display.setContrast((255 * percent) / 100); if (percent == 0) {
isDisplayOff = true;
// Clear display.
ag->display.clear();
ag->display.show();
}
else {
isDisplayOff = false;
ag->display.setContrast((255 * percent) / 100);
}
} }
} }
@ -519,7 +538,7 @@ void OledDisplay::showRebooting(void) {
do { do {
DISP()->setFont(u8g2_font_t0_16_tf); DISP()->setFont(u8g2_font_t0_16_tf);
// setCentralText(20, "Firmware Update"); // setCentralText(20, "Firmware Update");
setCentralText(40, "Reboot..."); setCentralText(40, "Rebooting...");
// setCentralText(60, String("Retry after 24h")); // setCentralText(60, String("Retry after 24h"));
} while (DISP()->nextPage()); } while (DISP()->nextPage());
} else if (ag->isBasic()) { } else if (ag->isBasic()) {

View File

@ -16,7 +16,7 @@ private:
Measurements &value; Measurements &value;
bool isDisplayOff = false; bool isDisplayOff = false;
void showTempHum(bool hasStatus); void showTempHum(bool hasStatus, char* buf, int buf_size);
void setCentralText(int y, String text); void setCentralText(int y, String text);
void setCentralText(int y, const char *text); void setCentralText(int y, const char *text);

View File

@ -142,6 +142,10 @@ void StateMachine::co2handleLeds(void) {
*/ */
void StateMachine::pm25handleLeds(void) { void StateMachine::pm25handleLeds(void) {
int pm25Value = value.pm25_1; int pm25Value = value.pm25_1;
if (config.isMonitorDisplayCompensatedValues() && config.hasSensorSHT) {
pm25Value = ag->pms5003.compensate(value.pm25_1, value.Humidity);
}
if (pm25Value < 5) { if (pm25Value < 5) {
/** G; 1 */ /** G; 1 */
ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1);
@ -495,7 +499,7 @@ void StateMachine::displayHandle(AgStateMachineState state) {
break; break;
} }
case AgStateMachineServerLost: { case AgStateMachineServerLost: {
disp.showDashboard("Server N/A"); disp.showDashboard("AG Server N/A");
break; break;
} }
case AgStateMachineSensorConfigFailed: { case AgStateMachineSensorConfigFailed: {
@ -504,7 +508,7 @@ void StateMachine::displayHandle(AgStateMachineState state) {
if (ms >= 5000) { if (ms >= 5000) {
addToDashboardTime = millis(); addToDashboardTime = millis();
if (addToDashBoardToggle) { if (addToDashBoardToggle) {
disp.showDashboard("Add to Dashboard"); disp.showDashboard("Add to AG Dashb.");
} else { } else {
disp.showDashboard(ag->deviceId().c_str()); disp.showDashboard(ag->deviceId().c_str());
} }

View File

@ -130,8 +130,8 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
} }
} }
int pm25 = (ag->pms5003t_1.compensate(this->pm25_1, this->temp_1) + int pm25 = (ag->pms5003t_1.compensate(this->pm25_1, this->hum_1) +
ag->pms5003t_2.compensate(this->pm25_2, this->temp_2)) / ag->pms5003t_2.compensate(this->pm25_2, this->hum_2)) /
2; 2;
root["pm02Compensated"] = pm25; root["pm02Compensated"] = pm25;
} }
@ -171,7 +171,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
} }
} }
} }
root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->temp_1); root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1);
if (!localServer) { if (!localServer) {
root[json_prop_pmFirmware] = root[json_prop_pmFirmware] =
pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion()); pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion());
@ -212,7 +212,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
} }
} }
} }
root["pm02Compensated"] = ag->pms5003t_2.compensate(this->pm25_2, this->temp_2); root["pm02Compensated"] = ag->pms5003t_2.compensate(this->pm25_2, this->hum_2);
if(!localServer) { if(!localServer) {
root[json_prop_pmFirmware] = root[json_prop_pmFirmware] =
pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion()); pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion());
@ -253,7 +253,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
} }
} }
} }
root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->temp_1); root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1);
if(!localServer) { if(!localServer) {
root[json_prop_pmFirmware] = root[json_prop_pmFirmware] =
pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion()); pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion());
@ -291,7 +291,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
} }
} }
} }
root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->temp_1); root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1);
if(!localServer) { if(!localServer) {
root[json_prop_pmFirmware] = root[json_prop_pmFirmware] =
pms5003TFirmwareVersion(ag->pms5003t_2.getFirmwareVersion()); pms5003TFirmwareVersion(ag->pms5003t_2.getFirmwareVersion());
@ -332,7 +332,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
} }
} }
} }
root["channels"]["1"]["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->temp_1); root["channels"]["1"]["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1);
// PMS5003T version // PMS5003T version
if(!localServer) { if(!localServer) {
@ -374,7 +374,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
} }
} }
} }
root["channels"]["2"]["pm02Compensated"] = ag->pms5003t_2.compensate(this->pm25_2, this->temp_2); root["channels"]["2"]["pm02Compensated"] = ag->pms5003t_2.compensate(this->pm25_2, this->hum_2);
// PMS5003T version // PMS5003T version
if(!localServer) { if(!localServer) {
root["channels"]["2"][json_prop_pmFirmware] = root["channels"]["2"][json_prop_pmFirmware] =

View File

@ -72,7 +72,7 @@ bool WifiConnector::connect(void) {
WIFI()->setSaveParamsCallback([this]() { _wifiSaveParamCallback(); }); WIFI()->setSaveParamsCallback([this]() { _wifiSaveParamCallback(); });
WIFI()->setConfigPortalTimeoutCallback([this]() {_wifiTimeoutCallback();}); WIFI()->setConfigPortalTimeoutCallback([this]() {_wifiTimeoutCallback();});
if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_3() || ag->isBasic()) { if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_3() || ag->isBasic()) {
disp.setText("Connect to", "WiFi", "..."); disp.setText("Connecting to", "WiFi", "...");
} else { } else {
logInfo("Connecting to WiFi..."); logInfo("Connecting to WiFi...");
} }

View File

@ -15,7 +15,7 @@
#include "Main/utils.h" #include "Main/utils.h"
#ifndef GIT_VERSION #ifndef GIT_VERSION
#define GIT_VERSION "3.1.7-snap" #define GIT_VERSION "3.1.9-snap"
#endif #endif
/** /**

View File

@ -87,3 +87,8 @@ int utils::getInvalidPmValue(void) { return INVALID_PMS; }
int utils::getInvalidNOx(void) { return INVALID_NOX; } int utils::getInvalidNOx(void) { return INVALID_NOX; }
int utils::getInvalidVOC(void) { return INVALID_VOC; } int utils::getInvalidVOC(void) { return INVALID_VOC; }
float utils::degreeC_To_F(float t) {
/** (t * 9)/5 + 32 */
return t * 1.8f + 32.0f;
}

View File

@ -24,6 +24,7 @@ public:
static int getInvalidPmValue(void); static int getInvalidPmValue(void);
static int getInvalidNOx(void); static int getInvalidNOx(void);
static int getInvalidVOC(void); static int getInvalidVOC(void);
static float degreeC_To_F(float t);
}; };

View File

@ -2,7 +2,7 @@
#include "../Main/BoardDef.h" #include "../Main/BoardDef.h"
/** /**
* @brief Init and check that sensor has connected * @brief Initializes the sensor and attempts to read data.
* *
* @param stream UART stream * @param stream UART stream
* @return true Sucecss * @return true Sucecss

View File

@ -5,6 +5,9 @@
#define PMS_FAIL_COUNT_SET_INVALID 3 #define PMS_FAIL_COUNT_SET_INVALID 3
/**
* Known to work with these sensors: Plantower PMS5003, Plantower PMS5003, Cubic PM2009X
*/
class PMSBase { class PMSBase {
public: public:
bool begin(HardwareSerial& serial); bool begin(HardwareSerial& serial);

View File

@ -38,14 +38,11 @@ bool PMS5003::begin(HardwareSerial &serial) {
PMS5003::PMS5003(BoardType def) : _boardDef(def) {} PMS5003::PMS5003(BoardType def) : _boardDef(def) {}
/** /**
* @brief Init sensor * Initializes the sensor.
*
* @return true Success
* @return false Failure
*/ */
bool PMS5003::begin(void) { bool PMS5003::begin(void) {
if (this->_isBegin) { if (this->_isBegin) {
AgLog("Initialized, call end() then try again"); AgLog("Already initialized, call end() then try again");
return true; return true;
} }