From 59d189e1d3a32943b9117478cc4c19784ab77583 Mon Sep 17 00:00:00 2001 From: Joostlek Date: Tue, 4 Jun 2024 09:14:30 +0200 Subject: [PATCH 01/25] Update configuration parameters --- docs/local-server.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/local-server.md b/docs/local-server.md index 1bd0f65..96ca11c 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -91,15 +91,15 @@ If the monitor is setup on the AirGradient dashboard, it will also receive confi #### Configuration Parameters (GET/PUT) -|Properties|Type|Accepted Values|Example| -|-|-|-|-| -|country|String| Country code as [ALPHA-2 notation](https://www.iban.com/country-codes) | {"country": "TH"}| -|pmStandard|String|ugm3 : ug/m3
usaqi: USAQI | {"pmStandard": "ugm3"}| -|ledBarMode|String|co2: LED bar displays CO2
pm: LED bar displays PM
off: Turn off LED bar | {"ledBarMode": "off"}| -|abcDays|Number|Number of days for CO2 automatic baseline balibration. Maximum 200 days. Default 8 days. | {"abcDays": 8}| -|mqttBrokerUrl|String|MQTT broker URL. | {"mqttBrokerUrl":"mqtt://192.168.0.18:1883"} | -|temperatureUnit|String|c or C: Degree Celsius °C
f or F: Degree Fahrenheit °F | {"temperatureUnit": "c"}| -|configurationControl|String|both : Accept local and cloud configuration
local : Accept only local configuration
cloud : Accept only cloud configuration | {"configurationControl": "both"}| -|postDataToAirGradient|Boolean|Send data to AirGradient cloud:
true : Enabled
false: Disabled | {"postDataToAirGradient": true}| -|co2CalibrationRequested|Boolean|Trigger CO2 calibration (400ppm) on monitor:
true : Calibration will be triggered | {"co2CalibrationRequested": true}| -|ledBarTestRequested|Boolean|Test LED bar:
true : LEDs will run test sequence | {"ledBarTestRequested": true}| +| 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"} | +| pmStandard | Particle matter standard used on the display. | String | `ugm3`: ug/m3
`us-aqi`: USAQI | {"pmStandard": "ugm3"} | +| ledBarMode | Mode in which the led bar can be set. | String | `co2`: LED bar displays CO2
`pm`: LED bar displays PM
`off`: Turn off LED bar | {"ledBarMode": "off"} | +| 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"} | +| temperatureUnit | Temperature unit shown on the display. | String | `c` or `C`: Degree Celsius °C
`f` or `F`: Degree Fahrenheit °F | {"temperatureUnit": "c"} | +| configurationControl | The configuration source of the device. | String | `both`: Accept local and cloud configuration
`local`: Accept only local configuration
`cloud`: Accept only cloud configuration | {"configurationControl": "both"} | +| postDataToAirGradient | Send data to AirGradient cloud. | Boolean | `true`: Enabled
`false`: Disabled | {"postDataToAirGradient": 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} | From 5f93329e968e7776bb527817a3a2a7667f4febcf Mon Sep 17 00:00:00 2001 From: Joostlek Date: Tue, 4 Jun 2024 09:17:33 +0200 Subject: [PATCH 02/25] Fix typos in docs --- docs/local-server.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/local-server.md b/docs/local-server.md index 1bd0f65..47bc1c3 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -17,7 +17,7 @@ With the path "/measures/current" you can get the current air quality data. http://airgradient_ecda3b1eaaaf.local/measures/current -“ecda3b1eaaaf” being the serial number of your monitor +“ecda3b1eaaaf” being the serial number of your monitor. You get the following response: ~~~ @@ -82,12 +82,12 @@ Example to force CO2 calibration ```curl -X PUT -H "Content-Type: application/json" -d '{"co2CalibrationRequested":true}' http://airgradient_84fce612eff4.local/config ``` -Example to set monitor to Celcius +Example to set monitor to Celsius ```curl -X PUT -H "Content-Type: application/json" -d '{"temperatureUnit":"c"}' http://airgradient_84fce612eff4.local/config ``` #### Avoiding Conflicts with Configuration on AirGradient Server -If the monitor is setup on the AirGradient dashboard, it will also receive configurations from there. In case you do not want this, please set "configurationControl" to local. In case you set it to cloud and want to change it to local, you need to make a factory reset. +If the monitor is set up on the AirGradient dashboard, it will also receive configurations from there. In case you do not want this, please set `configurationControl` to `local`. In case you set it to `cloud` and want to change it to `local`, you need to make a factory reset. #### Configuration Parameters (GET/PUT) From 4a58b0b1c7ba326077c9c08c88a32bb585e14c6c Mon Sep 17 00:00:00 2001 From: Joostlek Date: Tue, 4 Jun 2024 09:20:18 +0200 Subject: [PATCH 03/25] Reformat JSON in docs --- docs/local-server.md | 68 +++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/docs/local-server.md b/docs/local-server.md index 1bd0f65..09379f4 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -20,25 +20,27 @@ http://airgradient_ecda3b1eaaaf.local/measures/current “ecda3b1eaaaf” being the serial number of your monitor You get the following response: -~~~ -{"wifi":-46, -"serialno":"ecda3b1eaaaf", -"rco2":447, -"pm01":3, -"pm02":7, -"pm10":8, -"pm003Count":442, -"atmp":25.87, -"rhum":43, -"tvocIndex":100, -"tvoc_raw":33051, -"noxIndex":1, -"nox_raw":16307, -"boot":6, -"ledMode":"pm", -"firmwareVersion":"3.0.10beta", -"fwMode":"I-9PSL"} -~~~ +```json +{ + "wifi": -46, + "serialno": "ecda3b1eaaaf", + "rco2": 447, + "pm01": 3, + "pm02": 7, + "pm10": 8, + "pm003Count": 442, + "atmp": 25.87, + "rhum": 43, + "tvocIndex": 100, + "tvoc_raw": 33051, + "noxIndex": 1, + "nox_raw": 16307, + "boot": 6, + "ledMode": "pm", + "firmwareVersion": "3.0.10beta", + "fwMode": "I-9PSL" +} +``` |Properties|Type|Explanation| |-|-|-| @@ -60,19 +62,21 @@ You get the following response: #### Get Configuration Parameters (GET) With the path "/config" you can get the current configuration. -~~~ -{"country":"US", -"pmStandard":"ugm3", -"ledBarMode":"pm", -"displayMode":"on", -"abcDays":30, -"tvocLearningOffset":12, -"noxLearningOffset":12, -"mqttBrokerUrl":"", -"temperatureUnit":"f", -"configurationControl":"both", -"postDataToAirGradient":true} -~~~ +```json +{ + "country": "US", + "pmStandard": "ugm3", + "ledBarMode": "pm", + "displayMode": "on", + "abcDays": 30, + "tvocLearningOffset": 12, + "noxLearningOffset": 12, + "mqttBrokerUrl": "", + "temperatureUnit": "f", + "configurationControl": "both", + "postDataToAirGradient": true +} +``` #### Set Configuration Parameters (PUT) From 4249a86fd55a211060c2ce56676b777985af3a43 Mon Sep 17 00:00:00 2001 From: Joostlek Date: Tue, 4 Jun 2024 09:21:29 +0200 Subject: [PATCH 04/25] Reformat table --- docs/local-server.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/local-server.md b/docs/local-server.md index 1bd0f65..9440a0d 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -40,23 +40,23 @@ You get the following response: "fwMode":"I-9PSL"} ~~~ -|Properties|Type|Explanation| -|-|-|-| -|serialno|String| Serial Number of the monitor| -|wifi|Number| WiFi signal strength| -|pm01, pm02, pm10|Number| PM1, PM2.5 and PM10 in ug/m3| -|rco2|Number| CO2 in ppm| -|pm003Count|Number| Particle count per dL| -|atmp|Number| Temperature in Degrees Celcius| -|rhum|Number| Relative Humidity| -|tvocIndex|Number| Senisiron VOC Index| -|tvoc_raw|Number| VOC raw value| -|noxIndex|Number| Senisirion NOx Index| -|nox_raw|Number| NOx raw value| -|boot|Number| Counts every measurement cycle. Low boot counts indicate restarts.| -|ledMode|String| Current configuration of the LED mode| -|firmwareVersion|String| Current firmware version| -|fwMode|String| Current model name| +| Properties | Type | Explanation | +|------------------|--------|--------------------------------------------------------------------| +| serialno | String | Serial Number of the monitor | +| wifi | Number | WiFi signal strength | +| pm01, pm02, pm10 | Number | PM1, PM2.5 and PM10 in ug/m3 | +| rco2 | Number | CO2 in ppm | +| pm003Count | Number | Particle count per dL | +| atmp | Number | Temperature in Degrees Celcius | +| rhum | Number | Relative Humidity | +| tvocIndex | Number | Senisiron VOC Index | +| tvoc_raw | Number | VOC raw value | +| noxIndex | Number | Senisirion NOx Index | +| nox_raw | Number | NOx raw value | +| boot | Number | Counts every measurement cycle. Low boot counts indicate restarts. | +| ledMode | String | Current configuration of the LED mode | +| firmwareVersion | String | Current firmware version | +| fwMode | String | Current model name | #### Get Configuration Parameters (GET) With the path "/config" you can get the current configuration. From adabb9baa483ccd109c5a35506f620b17362b493 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Wed, 12 Jun 2024 06:04:13 +0700 Subject: [PATCH 05/25] OpenAir set WiFi connect to `airgradient` on factory reset --- examples/OneOpenAir/OneOpenAir.ino | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index d3ec231..ecda5c9 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -418,8 +418,12 @@ static void factoryConfigReset(void) { } /** Reset WIFI */ - WiFi.enableSTA(true); // Incase offline mode + WiFi.enableSTA(true); // Incase offline mode WiFi.disconnect(true, true); + if (ag->isOne() == false) { + WiFi.begin("airgradient", "cleanair"); + Serial.println("Set WiFi connect to \"airgradient\""); + } /** Reset local config */ configuration.reset(); From 3b6859f483f494d0556d36acdb95bfc3d7957f08 Mon Sep 17 00:00:00 2001 From: Achim Date: Mon, 17 Jun 2024 08:46:56 +0700 Subject: [PATCH 06/25] Updated documentation for local server --- docs/local-server.md | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/docs/local-server.md b/docs/local-server.md index f719632..925117f 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -30,15 +30,18 @@ You get the following response: "pm10": 8, "pm003Count": 442, "atmp": 25.87, + "atmpCompensated": 24.47, "rhum": 43, + "rhumCompensated": 49, "tvocIndex": 100, - "tvoc_raw": 33051, + "tvocRaw": 33051, "noxIndex": 1, - "nox_raw": 16307, + "noxRaw": 16307, "boot": 6, + "bootCount": 6, "ledMode": "pm", - "firmwareVersion": "3.0.10beta", - "fwMode": "I-9PSL" + "firmware": "3.1.3", + "model": "I-9PSL" } ``` @@ -46,19 +49,27 @@ You get the following response: |------------------|--------|--------------------------------------------------------------------| | serialno | String | Serial Number of the monitor | | wifi | Number | WiFi signal strength | -| pm01, pm02, pm10 | Number | PM1, PM2.5 and PM10 in ug/m3 | +| pm01 | Number | PM1 in ug/m3 | +| pm02 | Number | PM2.5 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) | | rco2 | Number | CO2 in ppm | | pm003Count | Number | Particle count per dL | -| atmp | Number | Temperature in Degrees Celcius | +| atmp | Number | Temperature in Degrees Celsius | +| atmpCompensated | Number | Temperature in Degrees Celsius with correction applied | | rhum | Number | Relative Humidity | +| rhumCompensated | Number | Relative Humidity with correction applied | | tvocIndex | Number | Senisiron VOC Index | -| tvoc_raw | Number | VOC raw value | +| tvocRaw | Number | VOC raw value | | noxIndex | Number | Senisirion NOx Index | -| nox_raw | Number | NOx raw value | +| noxRaw | Number | NOx raw value | | 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. | | ledMode | String | Current configuration of the LED mode | -| firmwareVersion | String | Current firmware version | -| fwMode | String | Current model name | +| firmware | String | Current firmware version | +| model | String | Current model name | + +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. #### Get Configuration Parameters (GET) With the path "/config" you can get the current configuration. @@ -98,8 +109,11 @@ If the monitor is set up on the AirGradient dashboard, it will also receive conf | 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"} | +| 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
`us-aqi`: USAQI | {"pmStandard": "ugm3"} | | ledBarMode | Mode in which the led bar can be set. | String | `co2`: LED bar displays CO2
`pm`: LED bar displays PM
`off`: Turn off LED bar | {"ledBarMode": "off"} | +| displayBrightness | Brightness of the Display. | Number | 0-100 | {"displayBrightness": 50} | +| 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} | | 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
`f` or `F`: Degree Fahrenheit °F | {"temperatureUnit": "c"} | @@ -107,3 +121,6 @@ If the monitor is set up on the AirGradient dashboard, it will also receive conf | postDataToAirGradient | Send data to AirGradient cloud. | Boolean | `true`: Enabled
`false`: Disabled | {"postDataToAirGradient": 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} | +| 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} | +| offlineMode | Set monitor to run without WiFi. | Boolean | `false`: Disabled (default)
`true`: Enabled | {"offlineMode": true} | From 4af77d532ed38e852c4baaec07272c9e5b61afea Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Sat, 20 Jul 2024 09:39:29 +0700 Subject: [PATCH 07/25] Update local-server.md Quote properties name --- docs/local-server.md | 74 ++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/docs/local-server.md b/docs/local-server.md index 925117f..7092b35 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -47,27 +47,27 @@ You get the following response: | Properties | Type | Explanation | |------------------|--------|--------------------------------------------------------------------| -| serialno | String | Serial Number of the monitor | -| wifi | Number | WiFi signal strength | -| pm01 | Number | PM1 in ug/m3 | -| pm02 | Number | PM2.5 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) | -| rco2 | Number | CO2 in ppm | -| pm003Count | Number | Particle count per dL | -| atmp | Number | Temperature in Degrees Celsius | -| atmpCompensated | Number | Temperature in Degrees Celsius with correction applied | -| rhum | Number | Relative Humidity | -| rhumCompensated | Number | Relative Humidity with correction applied | -| tvocIndex | Number | Senisiron VOC Index | -| tvocRaw | Number | VOC raw value | -| noxIndex | Number | Senisirion NOx Index | -| noxRaw | Number | NOx raw value | -| 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. | -| ledMode | String | Current configuration of the LED mode | -| firmware | String | Current firmware version | -| model | String | Current model name | +| `serialno` | String | Serial Number of the monitor | +| `wifi` | Number | WiFi signal strength | +| `pm01` | Number | PM1 in ug/m3 | +| `pm02` | Number | PM2.5 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) | +| `rco2` | Number | CO2 in ppm | +| `pm003Count` | Number | Particle count per dL | +| `atmp` | Number | Temperature in Degrees Celsius | +| `atmpCompensated` | Number | Temperature in Degrees Celsius with correction applied | +| `rhum` | Number | Relative Humidity | +| `rhumCompensated` | Number | Relative Humidity with correction applied | +| `tvocIndex` | Number | Senisiron VOC Index | +| `tvocRaw` | Number | VOC raw value | +| `noxIndex` | Number | Senisirion NOx Index | +| `noxRaw` | Number | NOx raw value | +| `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. | +| `ledMode` | String | Current configuration of the LED mode | +| `firmware` | String | Current firmware version | +| `model` | String | Current model name | 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. @@ -108,19 +108,19 @@ If the monitor is set up on the AirGradient dashboard, it will also receive conf | 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"} | -| 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
`us-aqi`: USAQI | {"pmStandard": "ugm3"} | -| ledBarMode | Mode in which the led bar can be set. | String | `co2`: LED bar displays CO2
`pm`: LED bar displays PM
`off`: Turn off LED bar | {"ledBarMode": "off"} | -| displayBrightness | Brightness of the Display. | Number | 0-100 | {"displayBrightness": 50} | -| 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} | -| 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
`f` or `F`: Degree Fahrenheit °F | {"temperatureUnit": "c"} | -| configurationControl | The configuration source of the device. | String | `both`: Accept local and cloud configuration
`local`: Accept only local configuration
`cloud`: Accept only cloud configuration | {"configurationControl": "both"} | -| postDataToAirGradient | Send data to AirGradient cloud. | Boolean | `true`: Enabled
`false`: Disabled | {"postDataToAirGradient": 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} | -| 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} | -| offlineMode | Set monitor to run without WiFi. | Boolean | `false`: Disabled (default)
`true`: Enabled | {"offlineMode": true} | +| `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"} | +| `pmStandard` | Particle matter standard used on the display. | String | `ugm3`: ug/m3
`us-aqi`: USAQI | {"pmStandard": "ugm3"} | +| `ledBarMode` | Mode in which the led bar can be set. | String | `co2`: LED bar displays CO2
`pm`: LED bar displays PM
`off`: Turn off LED bar | {"ledBarMode": "off"} | +| `displayBrightness` | Brightness of the Display. | Number | 0-100 | {"displayBrightness": 50} | +| `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} | +| `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
`f` or `F`: Degree Fahrenheit °F | {"temperatureUnit": "c"} | +| `configurationControl` | The configuration source of the device. | String | `both`: Accept local and cloud configuration
`local`: Accept only local configuration
`cloud`: Accept only cloud configuration | {"configurationControl": "both"} | +| `postDataToAirGradient` | Send data to AirGradient cloud. | Boolean | `true`: Enabled
`false`: Disabled | {"postDataToAirGradient": 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} | +| `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} | +| `offlineMode` | Set monitor to run without WiFi. | Boolean | `false`: Disabled (default)
`true`: Enabled | {"offlineMode": true} | From 4b2a5f55400d67aa0d33a289f1adc625c43b4c08 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Sun, 21 Jul 2024 07:13:34 +0700 Subject: [PATCH 08/25] Add PM2.5 correction formula, #182 --- src/AgOledDisplay.cpp | 16 ++++++++++++---- src/AgValue.cpp | 18 ++++++++++++++++++ src/PMS/PMS.cpp | 35 +++++++++++++++++++++++++++++++++++ src/PMS/PMS.h | 1 + src/PMS/PMS5003.cpp | 11 +++++++++++ src/PMS/PMS5003.h | 1 + src/PMS/PMS5003T.cpp | 11 +++++++++++ src/PMS/PMS5003T.h | 1 + 8 files changed, 90 insertions(+), 4 deletions(-) diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 22d25b2..abd0a04 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -10,7 +10,7 @@ * @param hasStatus */ void OledDisplay::showTempHum(bool hasStatus) { - char buf[10]; + char buf[16]; if (value.Temperature > -1001) { if (config.isTemperatureUnitInF()) { float tempF = (value.Temperature * 9) / 5 + 32; @@ -307,10 +307,14 @@ void OledDisplay::showDashboard(const char *status) { DISP()->drawStr(48, 27, "PM2.5"); /** Draw PM2.5 value */ + int pm25 = value.pm25_1; + if (config.hasSensorSHT) { + pm25 = ag->pms5003.pm25Compensated(pm25, value.Humidity); + } DISP()->setFont(u8g2_font_t0_22b_tf); if (config.isPmStandardInUSAQI()) { if (value.pm25_1 >= 0) { - sprintf(strBuf, "%d", ag->pms5003.convertPm25ToUsAqi(value.pm25_1)); + sprintf(strBuf, "%d", ag->pms5003.convertPm25ToUsAqi(pm25)); } else { sprintf(strBuf, "%s", "-"); } @@ -319,7 +323,7 @@ void OledDisplay::showDashboard(const char *status) { DISP()->drawUTF8(48, 61, "AQI"); } else { if (value.pm25_1 >= 0) { - sprintf(strBuf, "%d", value.pm25_1); + sprintf(strBuf, "%d", pm25); } else { sprintf(strBuf, "%s", "-"); } @@ -358,8 +362,12 @@ void OledDisplay::showDashboard(const char *status) { ag->display.setText(strBuf); /** Set PM */ + int pm25 = value.pm25_1; + if(config.hasSensorSHT) { + pm25 = (int)ag->pms5003.pm25Compensated(pm25, value.Humidity); + } ag->display.setCursor(0, 12); - snprintf(strBuf, sizeof(strBuf), "PM2.5:%d", value.pm25_1); + snprintf(strBuf, sizeof(strBuf), "PM2.5:%d", pm25); ag->display.setText(strBuf); /** Set temperature and humidity */ diff --git a/src/AgValue.cpp b/src/AgValue.cpp index bd729ea..1cc029b 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -50,6 +50,13 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } + if (config->hasSensorSHT && config->hasSensorPMS1) { + int pm25 = ag->pms5003.pm25Compensated(this->pm25_1, this->Humidity); + if (pm25 >= 0) { + root["pm02Compensated"] = pm25; + } + } + } else { if (config->hasSensorPMS1 && config->hasSensorPMS2) { root["pm01"] = ag->round2((this->pm01_1 + this->pm01_2) / 2.0); @@ -66,6 +73,11 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, root["rhumCompensated"] = (int)ag->pms5003t_2.humidityCompensated( (this->hum_1 + this->hum_2) / 2.0f); } + + int pm25 = (ag->pms5003t_1.pm25Compensated(this->pm25_1, this->temp_1) + + ag->pms5003t_2.pm25Compensated(this->pm25_2, this->temp_2)) / + 2; + root["pm02Compensated"] = pm25; } if (fwMode == FW_MODE_O_1PS || fwMode == FW_MODE_O_1PST) { @@ -82,6 +94,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, root["rhumCompensated"] = (int)ag->pms5003t_1.humidityCompensated(this->hum_1); } + root["pm02Compensated"] = ag->pms5003t_1.pm25Compensated(this->pm25_1, this->temp_1); } if (config->hasSensorPMS2) { root["pm01"] = this->pm01_2; @@ -96,6 +109,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, root["rhumCompensated"] = (int)ag->pms5003t_2.humidityCompensated(this->hum_2); } + root["pm02Compensated"] = ag->pms5003t_2.pm25Compensated(this->pm25_2, this->temp_2); } } else { if (fwMode == FW_MODE_O_1P) { @@ -112,6 +126,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, root["rhumCompensated"] = (int)ag->pms5003t_1.humidityCompensated(this->hum_1); } + root["pm02Compensated"] = ag->pms5003t_1.pm25Compensated(this->pm25_1, this->temp_1); } else if (config->hasSensorPMS2) { root["pm01"] = this->pm01_2; root["pm02"] = this->pm25_2; @@ -125,6 +140,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, root["rhumCompensated"] = (int)ag->pms5003t_1.humidityCompensated(this->hum_2); } + root["pm02Compensated"] = ag->pms5003t_1.pm25Compensated(this->pm25_1, this->temp_1); } } else { if (config->hasSensorPMS1) { @@ -140,6 +156,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, root["channels"]["1"]["rhumCompensated"] = (int)ag->pms5003t_1.humidityCompensated(this->hum_1); } + root["channels"]["1"]["pm02Compensated"] = ag->pms5003t_1.pm25Compensated(this->pm25_1, this->temp_1); } if (config->hasSensorPMS2) { root["channels"]["2"]["pm01"] = this->pm01_2; @@ -154,6 +171,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, root["channels"]["2"]["rhumCompensated"] = (int)ag->pms5003t_1.humidityCompensated(this->hum_2); } + root["channels"]["2"]["pm02Compensated"] = ag->pms5003t_2.pm25Compensated(this->pm25_2, this->temp_2); } } } diff --git a/src/PMS/PMS.cpp b/src/PMS/PMS.cpp index e9a1527..0a091fd 100644 --- a/src/PMS/PMS.cpp +++ b/src/PMS/PMS.cpp @@ -270,6 +270,41 @@ int PMSBase::pm25ToAQI(int pm02) { return 500; } +/** + * @brief Correction PM2.5 + * + * @param pm25 Raw PM2.5 value + * @param humidity Humidity value (%) + * @return float + */ +int PMSBase::pm25Compensated(int pm25, float humidity) { + float value; + if (humidity < 0) { + humidity = 0; + } + if (humidity > 100) { + humidity = 100; + } + + if(pm25 < 30) { + value = (pm25 * 0.524f) - (humidity * 0.0862f) + 5.75f; + } else if(pm25 < 50) { + value = (0.786f * (pm25 / 20 - 3 / 2) + 0.524f * (1 - (pm25 / 20 - 3 / 2))) * pm25 - (0.0862f * humidity) + 5.75f; + } else if(pm25 < 210) { + value = (0.786f * pm25) - (0.0862f * humidity) + 5.75f; + } else if(pm25 < 260) { + value = (0.69f * (pm25/50 - 21/5) + 0.786f * (1 - (pm25/50 - 21/5))) * pm25 - (0.0862f * humidity * (1 - (pm25/50 - 21/5))) + (2.966f * (pm25/50 -21/5)) + (5.75f * (1 - (pm25/50 - 21/5))) + (8.84f * (1.e-4) * pm25* (pm25/50 - 21/5)); + } else { + value = 2.966f + (0.69f * pm25) + (8.84f * (1.e-4) * pm25); + } + + if(value < 0) { + value = 0; + } + + return (int)value; +} + /** * @brief Convert two byte value to uint16_t value * diff --git a/src/PMS/PMS.h b/src/PMS/PMS.h index 4fa086a..c748d6b 100644 --- a/src/PMS/PMS.h +++ b/src/PMS/PMS.h @@ -28,6 +28,7 @@ public: uint16_t getHum(void); int pm25ToAQI(int pm02); + int pm25Compensated(int pm25, float humidity); private: Stream *stream; diff --git a/src/PMS/PMS5003.cpp b/src/PMS/PMS5003.cpp index 0e7c68b..4134855 100644 --- a/src/PMS/PMS5003.cpp +++ b/src/PMS/PMS5003.cpp @@ -118,6 +118,17 @@ int PMS5003::getPm03ParticleCount(void) { return pms.getCount0_3(); } */ int PMS5003::convertPm25ToUsAqi(int pm25) { return pms.pm25ToAQI(pm25); } +/** + * @brief Correct PM2.5 + * + * @param pm25 PM2.5 raw value + * @param humidity Humidity value + * @return float + */ +int PMS5003::pm25Compensated(int pm25, float humidity) { + return pms.pm25Compensated(pm25, humidity); +} + /** * @brief Check device initialized or not * diff --git a/src/PMS/PMS5003.h b/src/PMS/PMS5003.h index cd66b07..b5be03c 100644 --- a/src/PMS/PMS5003.h +++ b/src/PMS/PMS5003.h @@ -24,6 +24,7 @@ public: int getPm10Ae(void); int getPm03ParticleCount(void); int convertPm25ToUsAqi(int pm25); + int pm25Compensated(int pm25, float humidity); private: bool _isBegin = false; diff --git a/src/PMS/PMS5003T.cpp b/src/PMS/PMS5003T.cpp index f0f9a1d..7cf6e3b 100644 --- a/src/PMS/PMS5003T.cpp +++ b/src/PMS/PMS5003T.cpp @@ -161,6 +161,17 @@ float PMS5003T::getRelativeHumidity(void) { return pms.getHum()/10.0f; } +/** + * @brief Correct PM2.5 + * + * @param pm25 PM2.5 raw value + * @param humidity Humidity value + * @return float + */ +float PMS5003T::pm25Compensated(int pm25, float humidity) { + return pms.pm25Compensated(pm25, humidity); +} + /** * @brief Check device initialized or not * diff --git a/src/PMS/PMS5003T.h b/src/PMS/PMS5003T.h index 2c99b7e..48b8333 100644 --- a/src/PMS/PMS5003T.h +++ b/src/PMS/PMS5003T.h @@ -29,6 +29,7 @@ public: int convertPm25ToUsAqi(int pm25); float getTemperature(void); float getRelativeHumidity(void); + float pm25Compensated(int pm25, float humidity); private: bool _isBegin = false; From 7b9dac756bd8fdf362926fd87fe3cffff9b12a54 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 21 Jul 2024 19:38:50 +1200 Subject: [PATCH 09/25] Reintroduce 'ROOTAPI' so domain and protocol can be configured eg. setter for api root added as comment in examples/BASIC/BASIC.ino --- examples/BASIC/BASIC.ino | 3 +++ src/AgApiClient.cpp | 15 +++++++++------ src/AgApiClient.h | 3 +++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/examples/BASIC/BASIC.ino b/examples/BASIC/BASIC.ino index 8928c68..7a6e200 100644 --- a/examples/BASIC/BASIC.ino +++ b/examples/BASIC/BASIC.ino @@ -126,6 +126,9 @@ void setup() { openMetrics.setAirGradient(&ag); localServer.setAirGraident(&ag); + /** Example set custom API root URL */ + // apiClient.setApiRoot("https://example.custom.api"); + /** Init sensor */ boardInit(); diff --git a/src/AgApiClient.cpp b/src/AgApiClient.cpp index 1f7c83b..bff6789 100644 --- a/src/AgApiClient.cpp +++ b/src/AgApiClient.cpp @@ -22,6 +22,7 @@ AgApiClient::~AgApiClient() {} void AgApiClient::begin(void) { getConfigFailed = false; postToServerFailed = false; + logInfo("Init apiRoot: " + apiRoot); logInfo("begin"); } @@ -44,9 +45,8 @@ bool AgApiClient::fetchServerConfiguration(void) { return false; } - String uri = - "http://hw.airgradient.com/sensors/airgradient:" + ag->deviceId() + - "/one/config"; + String uri = apiRoot + "/sensors/airgradient:" + + ag->deviceId() + "/one/config"; /** Init http client */ #ifdef ESP8266 @@ -109,9 +109,8 @@ bool AgApiClient::postToServer(String data) { return false; } - String uri = - "http://hw.airgradient.com/sensors/airgradient:" + ag->deviceId() + - "/measures"; + String uri = apiRoot + "/sensors/airgradient:" + + ag->deviceId() + "/measures"; logInfo("Post uri: " + uri); logInfo("Post data: " + data); @@ -177,3 +176,7 @@ bool AgApiClient::sendPing(int rssi, int bootCount) { root["boot"] = bootCount; return postToServer(JSON.stringify(root)); } + +String AgApiClient::getApiRoot() const { return apiRoot; } + +void AgApiClient::setApiRoot(const String &apiRoot) { this->apiRoot = apiRoot; } diff --git a/src/AgApiClient.h b/src/AgApiClient.h index 4b37bf3..7e6037d 100644 --- a/src/AgApiClient.h +++ b/src/AgApiClient.h @@ -20,6 +20,7 @@ class AgApiClient : public PrintLog { private: Configuration &config; AirGradient *ag; + String apiRoot = "http://hw.airgradient.com"; bool getConfigFailed; bool postToServerFailed; @@ -37,6 +38,8 @@ public: bool isNotAvailableOnDashboard(void); void setAirGradient(AirGradient *ag); bool sendPing(int rssi, int bootCount); + String getApiRoot() const; + void setApiRoot(const String &apiRoot); }; #endif /** _AG_API_CLIENT_H_ */ From ed7b8df6fe2d5e683ae598f3a967455ced58332b Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Mon, 29 Jul 2024 06:00:54 +0700 Subject: [PATCH 10/25] log URLs of all HTTP requests to AG backend / log status codes of responses --- src/AgApiClient.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/AgApiClient.cpp b/src/AgApiClient.cpp index 1f7c83b..8a523ad 100644 --- a/src/AgApiClient.cpp +++ b/src/AgApiClient.cpp @@ -66,6 +66,10 @@ bool AgApiClient::fetchServerConfiguration(void) { /** Get data */ int retCode = client.GET(); + + logInfo(String("GET: ") + uri); + logInfo(String("Return code: ") + String(retCode)); + if (retCode != 200) { client.end(); getConfigFailed = true; @@ -112,18 +116,22 @@ bool AgApiClient::postToServer(String data) { String uri = "http://hw.airgradient.com/sensors/airgradient:" + ag->deviceId() + "/measures"; - logInfo("Post uri: " + uri); - logInfo("Post data: " + data); + // logInfo("Post uri: " + uri); + // logInfo("Post data: " + data); WiFiClient wifiClient; HTTPClient client; if (client.begin(wifiClient, uri.c_str()) == false) { + logError("Init client failed"); return false; } client.addHeader("content-type", "application/json"); int retCode = client.POST(data); client.end(); + logInfo(String("POST: ") + uri); + logInfo(String("Return code: ") + String(retCode)); + if ((retCode == 200) || (retCode == 429)) { postToServerFailed = false; return true; From b0ae851427fb202f840a43ec87334286abb86625 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Tue, 30 Jul 2024 20:26:19 +0700 Subject: [PATCH 11/25] Fix nox position --- src/AgOledDisplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 044fb8e..05f54f6 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -338,7 +338,7 @@ void OledDisplay::showDashboard(const char *status) { DISP()->drawStr(100, 39, strBuf); /** Draw NOx label */ - DISP()->drawStr(85, 53, "NOx:"); + DISP()->drawStr(100, 53, "NOx:"); if (utils::isValidNOx(value.NOx)) { sprintf(strBuf, "%d", value.NOx); } else { From b5cced40d2643e4b04c58b15825e7d885819bfea Mon Sep 17 00:00:00 2001 From: AirGradient <68736665+airgradienthq@users.noreply.github.com> Date: Wed, 7 Aug 2024 11:05:39 +0700 Subject: [PATCH 12/25] Update local-server.md --- docs/local-server.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/local-server.md b/docs/local-server.md index 7092b35..9bb9bcc 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -101,6 +101,9 @@ Example to set monitor to Celsius ```curl -X PUT -H "Content-Type: application/json" -d '{"temperatureUnit":"c"}' http://airgradient_84fce612eff4.local/config ``` + If you use command prompt on Windows, you need to escape the quotes: + -d "{\"param\":\"value\"}" + #### Avoiding Conflicts with Configuration on AirGradient Server If the monitor is set up on the AirGradient dashboard, it will also receive configurations from there. In case you do not want this, please set `configurationControl` to `local`. In case you set it to `cloud` and want to change it to `local`, you need to make a factory reset. From dc261f668dae55ec64829cccddaccb825bd24402 Mon Sep 17 00:00:00 2001 From: AirGradient <68736665+airgradienthq@users.noreply.github.com> Date: Wed, 7 Aug 2024 11:08:48 +0700 Subject: [PATCH 13/25] Update local-server.md II --- docs/local-server.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/local-server.md b/docs/local-server.md index 9bb9bcc..1daede0 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -102,7 +102,8 @@ Example to set monitor to Celsius ```curl -X PUT -H "Content-Type: application/json" -d '{"temperatureUnit":"c"}' http://airgradient_84fce612eff4.local/config ``` If you use command prompt on Windows, you need to escape the quotes: - -d "{\"param\":\"value\"}" + + ``` -d "{\"param\":\"value\"}" ``` #### Avoiding Conflicts with Configuration on AirGradient Server If the monitor is set up on the AirGradient dashboard, it will also receive configurations from there. In case you do not want this, please set `configurationControl` to `local`. In case you set it to `cloud` and want to change it to `local`, you need to make a factory reset. From e46e11c03066a4aba3706b90f9184582c1c94178 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Thu, 8 Aug 2024 05:53:24 +0700 Subject: [PATCH 14/25] `round2` support negative value --- src/AirGradient.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/AirGradient.cpp b/src/AirGradient.cpp index e036982..46ed874 100644 --- a/src/AirGradient.cpp +++ b/src/AirGradient.cpp @@ -41,7 +41,14 @@ String AirGradient::getVersion(void) { return GIT_VERSION; } BoardType AirGradient::getBoardType(void) { return boardType; } double AirGradient::round2(double value) { - return (int)(value * 100 + 0.5) / 100.0; + double ret; + if (value >= 0) { + ret = (int)(value * 100 + 0.5f); + } else { + ret = (int)(value * 100 - 0.5f); + } + + return ret / 100; } String AirGradient::getBoardName(void) { From 97f0696002853035a1f4592a23671c78f2573d51 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Fri, 9 Aug 2024 13:19:28 +0700 Subject: [PATCH 15/25] set default wifi on factory reset. --- examples/OneOpenAir/OneOpenAir.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index 5f026ca..80deb36 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -421,8 +421,8 @@ static void factoryConfigReset(void) { } /** Reset WIFI */ - WiFi.enableSTA(true); // Incase offline mode - WiFi.disconnect(true, true); + Serial.println("Set wifi connect to 'airgradient' as default"); + WiFi.begin("airgradient", "cleanair"); /** Reset local config */ configuration.reset(); From 5edb21cfe9b6474327cdf89a87dac644fa9b3d84 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Thu, 15 Aug 2024 09:10:48 +0700 Subject: [PATCH 16/25] Fix: PMS5003T only return positive temperature value --- src/PMS/PMS.cpp | 48 +++++++++++++++++++++++++++++------------------- src/PMS/PMS.h | 5 +++-- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/PMS/PMS.cpp b/src/PMS/PMS.cpp index e9a1527..d4101fe 100644 --- a/src/PMS/PMS.cpp +++ b/src/PMS/PMS.cpp @@ -3,7 +3,7 @@ /** * @brief Init and check that sensor has connected - * + * * @param stream UART stream * @return true Sucecss * @return false Failure @@ -86,7 +86,7 @@ void PMSBase::handle() { case 2: { buf[bufIndex++] = value; if (bufIndex >= 4) { - len = toValue(&buf[2]); + len = toI16(&buf[2]); if (len != 28) { // Serial.printf("Got good bad len %d\r\n", len); len += 4; @@ -152,98 +152,98 @@ bool PMSBase::isFailed(void) { return failed; } * * @return uint16_t */ -uint16_t PMSBase::getRaw0_1(void) { return toValue(&package[4]); } +uint16_t PMSBase::getRaw0_1(void) { return toU16(&package[4]); } /** * @brief Read PMS 2.5 ug/m3 with CF = 1 PM estimates * * @return uint16_t */ -uint16_t PMSBase::getRaw2_5(void) { return toValue(&package[6]); } +uint16_t PMSBase::getRaw2_5(void) { return toU16(&package[6]); } /** * @brief Read PMS 10 ug/m3 with CF = 1 PM estimates * * @return uint16_t */ -uint16_t PMSBase::getRaw10(void) { return toValue(&package[8]); } +uint16_t PMSBase::getRaw10(void) { return toU16(&package[8]); } /** * @brief Read PMS 0.1 ug/m3 * * @return uint16_t */ -uint16_t PMSBase::getPM0_1(void) { return toValue(&package[10]); } +uint16_t PMSBase::getPM0_1(void) { return toU16(&package[10]); } /** * @brief Read PMS 2.5 ug/m3 * * @return uint16_t */ -uint16_t PMSBase::getPM2_5(void) { return toValue(&package[12]); } +uint16_t PMSBase::getPM2_5(void) { return toU16(&package[12]); } /** * @brief Read PMS 10 ug/m3 * * @return uint16_t */ -uint16_t PMSBase::getPM10(void) { return toValue(&package[14]); } +uint16_t PMSBase::getPM10(void) { return toU16(&package[14]); } /** * @brief Get numnber concentrations over 0.3 um/0.1L * * @return uint16_t */ -uint16_t PMSBase::getCount0_3(void) { return toValue(&package[16]); } +uint16_t PMSBase::getCount0_3(void) { return toU16(&package[16]); } /** * @brief Get numnber concentrations over 0.5 um/0.1L * * @return uint16_t */ -uint16_t PMSBase::getCount0_5(void) { return toValue(&package[18]); } +uint16_t PMSBase::getCount0_5(void) { return toU16(&package[18]); } /** * @brief Get numnber concentrations over 1.0 um/0.1L * * @return uint16_t */ -uint16_t PMSBase::getCount1_0(void) { return toValue(&package[20]); } +uint16_t PMSBase::getCount1_0(void) { return toU16(&package[20]); } /** * @brief Get numnber concentrations over 2.5 um/0.1L * * @return uint16_t */ -uint16_t PMSBase::getCount2_5(void) { return toValue(&package[22]); } +uint16_t PMSBase::getCount2_5(void) { return toU16(&package[22]); } /** * @brief Get numnber concentrations over 5.0 um/0.1L (only PMS5003) * * @return uint16_t */ -uint16_t PMSBase::getCount5_0(void) { return toValue(&package[24]); } +uint16_t PMSBase::getCount5_0(void) { return toU16(&package[24]); } /** * @brief Get numnber concentrations over 10.0 um/0.1L (only PMS5003) * * @return uint16_t */ -uint16_t PMSBase::getCount10(void) { return toValue(&package[26]); } +uint16_t PMSBase::getCount10(void) { return toU16(&package[26]); } /** * @brief Get temperature (only PMS5003T) * * @return uint16_t */ -uint16_t PMSBase::getTemp(void) { return toValue(&package[24]); } +int16_t PMSBase::getTemp(void) { return toI16(&package[24]); } /** * @brief Get humidity (only PMS5003T) * * @return uint16_t */ -uint16_t PMSBase::getHum(void) { return toValue(&package[26]); } +uint16_t PMSBase::getHum(void) { return toU16(&package[26]); } /** * @brief Convert PMS2.5 to US AQI unit @@ -274,9 +274,19 @@ int PMSBase::pm25ToAQI(int pm02) { * @brief Convert two byte value to uint16_t value * * @param buf bytes array (must be >= 2) - * @return uint16_t + * @return int16_t */ -uint16_t PMSBase::toValue(char *buf) { return (buf[0] << 8) | buf[1]; } +int16_t PMSBase::toI16(char *buf) { + int16_t value = buf[0]; + value = (value << 8) | buf[1]; + return value; +} + +uint16_t PMSBase::toU16(char *buf) { + uint16_t value = buf[0]; + value = (value << 8) | buf[1]; + return value; +} /** * @brief Validate package data @@ -290,7 +300,7 @@ bool PMSBase::validate(char *buf) { for (int i = 0; i < 30; i++) { sum += buf[i]; } - if (sum == toValue(&buf[30])) { + if (sum == toU16(&buf[30])) { for (int i = 0; i < 32; i++) { package[i] = buf[i]; } diff --git a/src/PMS/PMS.h b/src/PMS/PMS.h index 4fa086a..2e65930 100644 --- a/src/PMS/PMS.h +++ b/src/PMS/PMS.h @@ -24,7 +24,7 @@ public: uint16_t getCount10(void); /** For PMS5003T*/ - uint16_t getTemp(void); + int16_t getTemp(void); uint16_t getHum(void); int pm25ToAQI(int pm02); @@ -36,7 +36,8 @@ private: bool failed = false; uint32_t lastRead; - uint16_t toValue(char *buf); + int16_t toI16(char *buf); + uint16_t toU16(char* buf); bool validate(char *buf); }; From 3be321811592cb6d4faee660cd7fc67d55bda985 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Thu, 15 Aug 2024 09:11:38 +0700 Subject: [PATCH 17/25] Update invalid value and optimize code operator. --- src/Main/utils.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Main/utils.cpp b/src/Main/utils.cpp index 386a520..1c8175d 100644 --- a/src/Main/utils.cpp +++ b/src/Main/utils.cpp @@ -12,10 +12,14 @@ #define VALID_PMS_MIN (0) #define INVALID_PMS (-1) +#define VALID_PMS03COUNT_MIN (0) + #define VALID_CO2_MAX (10000) #define VALID_CO2_MIN (0) #define INVALID_CO2 (-1) +#define VALID_NOX_MIN (0) +#define VALID_VOC_MIN (0) #define INVALID_NOX (-1) #define INVALID_VOC (-1) @@ -24,49 +28,49 @@ utils::utils(/* args */) {} utils::~utils() {} bool utils::isValidTemperature(float value) { - if (value >= VALID_TEMPERATURE_MIN && value <= VALID_TEMPERATURE_MAX) { + if ((value >= VALID_TEMPERATURE_MIN) && (value <= VALID_TEMPERATURE_MAX)) { return true; } return false; } bool utils::isValidHumidity(float value) { - if (value >= VALID_HUMIDITY_MIN && value <= VALID_HUMIDITY_MAX) { + if ((value >= VALID_HUMIDITY_MIN) && (value <= VALID_HUMIDITY_MAX)) { return true; } return false; } bool utils::isValidCO2(int16_t value) { - if (value >= VALID_CO2_MIN && value <= VALID_CO2_MAX) { + if ((value >= VALID_CO2_MIN) && (value <= VALID_CO2_MAX)) { return true; } return false; } bool utils::isValidPMS(int value) { - if (value >= VALID_PMS_MIN && value <= VALID_PMS_MAX) { + if ((value >= VALID_PMS_MIN) && (value <= VALID_PMS_MAX)) { return true; } return false; } bool utils::isValidPMS03Count(int value) { - if (value >= 0) { + if (value >= VALID_PMS03COUNT_MIN) { return true; } return false; } bool utils::isValidNOx(int value) { - if (value > INVALID_NOX) { + if (value >= VALID_NOX_MIN) { return true; } return false; } bool utils::isValidVOC(int value) { - if (value > INVALID_VOC) { + if (value >= VALID_VOC_MIN) { return true; } return false; From f3a9c722b254dfcd9a910034c736765bb14cd84b Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Fri, 16 Aug 2024 06:39:52 +0700 Subject: [PATCH 18/25] Change `pm25Compensated` to `compensated` --- src/AgOledDisplay.cpp | 4 ++-- src/AgValue.cpp | 18 +++++++++--------- src/PMS/PMS.cpp | 4 ++-- src/PMS/PMS.h | 2 +- src/PMS/PMS5003.cpp | 4 ++-- src/PMS/PMS5003.h | 2 +- src/PMS/PMS5003T.cpp | 4 ++-- src/PMS/PMS5003T.h | 2 +- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index adf2ab5..8b07ada 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -306,7 +306,7 @@ void OledDisplay::showDashboard(const char *status) { /** Draw PM2.5 value */ int pm25 = value.pm25_1; if (config.hasSensorSHT) { - pm25 = ag->pms5003.pm25Compensated(pm25, value.Humidity); + pm25 = ag->pms5003.compensated(pm25, value.Humidity); } DISP()->setFont(u8g2_font_t0_22b_tf); if (config.isPmStandardInUSAQI()) { @@ -366,7 +366,7 @@ void OledDisplay::showDashboard(const char *status) { /** Set PM */ int pm25 = value.pm25_1; if(config.hasSensorSHT) { - pm25 = (int)ag->pms5003.pm25Compensated(pm25, value.Humidity); + pm25 = (int)ag->pms5003.compensated(pm25, value.Humidity); } ag->display.setCursor(0, 12); if (utils::isValidPMS(value.pm25_1)) { diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 0f39922..015a788 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -51,7 +51,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } if (config->hasSensorSHT && config->hasSensorPMS1) { - int pm25 = ag->pms5003.pm25Compensated(this->pm25_1, this->Humidity); + int pm25 = ag->pms5003.compensated(this->pm25_1, this->Humidity); if (pm25 >= 0) { root["pm02Compensated"] = pm25; } @@ -92,8 +92,8 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } - int pm25 = (ag->pms5003t_1.pm25Compensated(this->pm25_1, this->temp_1) + - ag->pms5003t_2.pm25Compensated(this->pm25_2, this->temp_2)) / + int pm25 = (ag->pms5003t_1.compensated(this->pm25_1, this->temp_1) + + ag->pms5003t_2.compensated(this->pm25_2, this->temp_2)) / 2; root["pm02Compensated"] = pm25; } @@ -133,7 +133,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } } - root["pm02Compensated"] = ag->pms5003t_1.pm25Compensated(this->pm25_1, this->temp_1); + root["pm02Compensated"] = ag->pms5003t_1.compensated(this->pm25_1, this->temp_1); } if (config->hasSensorPMS2) { if(utils::isValidPMS(this->pm01_2)) { @@ -170,7 +170,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } } - root["pm02Compensated"] = ag->pms5003t_2.pm25Compensated(this->pm25_2, this->temp_2); + root["pm02Compensated"] = ag->pms5003t_2.compensated(this->pm25_2, this->temp_2); } } else { if (fwMode == FW_MODE_O_1P) { @@ -207,7 +207,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } } - root["pm02Compensated"] = ag->pms5003t_1.pm25Compensated(this->pm25_1, this->temp_1); + root["pm02Compensated"] = ag->pms5003t_1.compensated(this->pm25_1, this->temp_1); } else if (config->hasSensorPMS2) { if(utils::isValidPMS(this->pm01_2)) { root["pm01"] = this->pm01_2; @@ -241,7 +241,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } } - root["pm02Compensated"] = ag->pms5003t_1.pm25Compensated(this->pm25_1, this->temp_1); + root["pm02Compensated"] = ag->pms5003t_1.compensated(this->pm25_1, this->temp_1); } } else { float val; @@ -278,7 +278,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } } - root["channels"]["1"]["pm02Compensated"] = ag->pms5003t_1.pm25Compensated(this->pm25_1, this->temp_1); + root["channels"]["1"]["pm02Compensated"] = ag->pms5003t_1.compensated(this->pm25_1, this->temp_1); } if (config->hasSensorPMS2) { float val; @@ -314,7 +314,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } } - root["channels"]["2"]["pm02Compensated"] = ag->pms5003t_2.pm25Compensated(this->pm25_2, this->temp_2); + root["channels"]["2"]["pm02Compensated"] = ag->pms5003t_2.compensated(this->pm25_2, this->temp_2); } } } diff --git a/src/PMS/PMS.cpp b/src/PMS/PMS.cpp index 0a091fd..fb32767 100644 --- a/src/PMS/PMS.cpp +++ b/src/PMS/PMS.cpp @@ -275,9 +275,9 @@ int PMSBase::pm25ToAQI(int pm02) { * * @param pm25 Raw PM2.5 value * @param humidity Humidity value (%) - * @return float + * @return int */ -int PMSBase::pm25Compensated(int pm25, float humidity) { +int PMSBase::compensated(int pm25, float humidity) { float value; if (humidity < 0) { humidity = 0; diff --git a/src/PMS/PMS.h b/src/PMS/PMS.h index c748d6b..43e61b0 100644 --- a/src/PMS/PMS.h +++ b/src/PMS/PMS.h @@ -28,7 +28,7 @@ public: uint16_t getHum(void); int pm25ToAQI(int pm02); - int pm25Compensated(int pm25, float humidity); + int compensated(int pm25, float humidity); private: Stream *stream; diff --git a/src/PMS/PMS5003.cpp b/src/PMS/PMS5003.cpp index 09a9e8d..8a49550 100644 --- a/src/PMS/PMS5003.cpp +++ b/src/PMS/PMS5003.cpp @@ -128,8 +128,8 @@ int PMS5003::convertPm25ToUsAqi(int pm25) { return pms.pm25ToAQI(pm25); } * @param humidity Humidity value * @return float */ -int PMS5003::pm25Compensated(int pm25, float humidity) { - return pms.pm25Compensated(pm25, humidity); +int PMS5003::compensated(int pm25, float humidity) { + return pms.compensated(pm25, humidity); } /** diff --git a/src/PMS/PMS5003.h b/src/PMS/PMS5003.h index b5be03c..aa6fcd9 100644 --- a/src/PMS/PMS5003.h +++ b/src/PMS/PMS5003.h @@ -24,7 +24,7 @@ public: int getPm10Ae(void); int getPm03ParticleCount(void); int convertPm25ToUsAqi(int pm25); - int pm25Compensated(int pm25, float humidity); + int compensated(int pm25, float humidity); private: bool _isBegin = false; diff --git a/src/PMS/PMS5003T.cpp b/src/PMS/PMS5003T.cpp index e754c6e..97cc9c7 100644 --- a/src/PMS/PMS5003T.cpp +++ b/src/PMS/PMS5003T.cpp @@ -171,8 +171,8 @@ float PMS5003T::getRelativeHumidity(void) { * @param humidity Humidity value * @return float */ -float PMS5003T::pm25Compensated(int pm25, float humidity) { - return pms.pm25Compensated(pm25, humidity); +float PMS5003T::compensated(int pm25, float humidity) { + return pms.compensated(pm25, humidity); } /** diff --git a/src/PMS/PMS5003T.h b/src/PMS/PMS5003T.h index 48b8333..3d2d567 100644 --- a/src/PMS/PMS5003T.h +++ b/src/PMS/PMS5003T.h @@ -29,7 +29,7 @@ public: int convertPm25ToUsAqi(int pm25); float getTemperature(void); float getRelativeHumidity(void); - float pm25Compensated(int pm25, float humidity); + float compensated(int pm25, float humidity); private: bool _isBegin = false; From 6746d25dc2fd5565b157401837a90e565f95f961 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 18 Aug 2024 16:46:56 +1200 Subject: [PATCH 19/25] Add comment to all example scripts showing how to change APIROOT --- examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino | 3 +++ examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino | 3 +++ examples/OneOpenAir/OneOpenAir.ino | 3 +++ 3 files changed, 9 insertions(+) diff --git a/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino b/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino index d1eaa76..295a2ca 100644 --- a/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino +++ b/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino @@ -126,6 +126,9 @@ void setup() { openMetrics.setAirGradient(&ag); localServer.setAirGraident(&ag); + /** Example set custom API root URL */ + // apiClient.setApiRoot("https://example.custom.api"); + /** Init sensor */ boardInit(); diff --git a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino index 3b16795..c158856 100644 --- a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino +++ b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino @@ -127,6 +127,9 @@ void setup() { openMetrics.setAirGradient(&ag); localServer.setAirGraident(&ag); + /** Example set custom API root URL */ + // apiClient.setApiRoot("https://example.custom.api"); + /** Init sensor */ boardInit(); diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index 96633a6..81afb84 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -163,6 +163,9 @@ void setup() { openMetrics.setAirGradient(ag); localServer.setAirGraident(ag); + /** Example set custom API root URL */ + // apiClient.setApiRoot("https://example.custom.api"); + /** Init sensor */ boardInit(); From 900a2da2ac865f963730113c3082a5681ea4b56c Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Wed, 21 Aug 2024 12:27:56 +0700 Subject: [PATCH 20/25] Log POST data --- src/AgApiClient.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/AgApiClient.cpp b/src/AgApiClient.cpp index d64c8e3..fe582a4 100644 --- a/src/AgApiClient.cpp +++ b/src/AgApiClient.cpp @@ -130,6 +130,7 @@ bool AgApiClient::postToServer(String data) { client.end(); logInfo(String("POST: ") + uri); + logInfo(String("DATA: ") + data); logInfo(String("Return code: ") + String(retCode)); if ((retCode == 200) || (retCode == 429)) { From 88e3d0bd3fdf846a93ea8fb1f39d50531c77046c Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Wed, 21 Aug 2024 12:54:06 +0700 Subject: [PATCH 21/25] Ignore and return failed if `configurationControl` is `cloud` --- src/AgConfigure.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index 4518f24..99e00c1 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -264,12 +264,14 @@ bool Configuration::parse(String data, bool isLocal) { jconfig[jprop_configurationControl]); } - /** Check to return result if configurationControl is 'cloud' */ - if (ctrl == - String(CONFIGURATION_CONTROL_NAME - [ConfigurationControl::ConfigurationControlCloud])) { - failedMessage = String(msg); - return true; + /** Return failed if new "configurationControl" new and old is "cloud" */ + if (ctrl == String(CONFIGURATION_CONTROL_NAME [ConfigurationControl::ConfigurationControlCloud])) { + if(ctrl != lastCtrl) { + return true; + } else { + failedMessage = String(msg); + return false; + } } } else { failedMessage = From 73089b51f5d6e5fef282919571763eb510f38dde Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Thu, 22 Aug 2024 11:23:53 +0700 Subject: [PATCH 22/25] add firmware version for arduino build --- src/AirGradient.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AirGradient.h b/src/AirGradient.h index 54abd91..6285f0d 100644 --- a/src/AirGradient.h +++ b/src/AirGradient.h @@ -15,7 +15,7 @@ #include "Main/utils.h" #ifndef GIT_VERSION -#define GIT_VERSION "snapshot" +#define GIT_VERSION "3.1.4" #endif /** From 1cc8941a5c1b1a15362a29735e1849b3d183439a Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Thu, 22 Aug 2024 11:27:25 +0700 Subject: [PATCH 23/25] csv alignment format --- partitions.csv | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/partitions.csv b/partitions.csv index 080f491..2854b60 100644 --- a/partitions.csv +++ b/partitions.csv @@ -1,7 +1,7 @@ -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x5000, -otadata, data, ota, 0xe000, 0x2000, -app0, app, ota_0, 0x10000, 0x1E0000, -app1, app, ota_1, 0x1F0000,0x1E0000, -spiffs, data, spiffs, 0x3D0000,0x20000, -coredump, data, coredump,0x3F0000,0x10000, \ No newline at end of file +# Name ,Type ,SubType ,Offset ,Size ,Flags +nvs ,data ,nvs ,0x9000 ,0x5000 , +otadata ,data ,ota ,0xe000 ,0x2000 , +app0 ,app ,ota_0 ,0x10000 ,0x1E0000 , +app1 ,app ,ota_1 ,0x1F0000 ,0x1E0000 , +spiffs ,data ,spiffs ,0x3D0000 ,0x20000 , +coredump ,data ,coredump ,0x3F0000 ,0x10000 , From 186f0d27abd28985ce3f18d9003d5e237ba1d15c Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Fri, 23 Aug 2024 08:59:28 +0700 Subject: [PATCH 24/25] Update version --- library.properties | 2 +- src/AirGradient.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.properties b/library.properties index a1065af..40d9338 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=AirGradient Air Quality Sensor -version=3.1.4 +version=3.1.5 author=AirGradient maintainer=AirGradient sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display. diff --git a/src/AirGradient.h b/src/AirGradient.h index 6285f0d..9e1d220 100644 --- a/src/AirGradient.h +++ b/src/AirGradient.h @@ -15,7 +15,7 @@ #include "Main/utils.h" #ifndef GIT_VERSION -#define GIT_VERSION "3.1.4" +#define GIT_VERSION "3.1.5-snapshot" #endif /** From b1aaa04421e9b3c8f5a700284936833fc629317f Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Fri, 23 Aug 2024 09:12:43 +0700 Subject: [PATCH 25/25] update GIT_BUILD version --- src/AirGradient.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AirGradient.h b/src/AirGradient.h index 9e1d220..e94b25e 100644 --- a/src/AirGradient.h +++ b/src/AirGradient.h @@ -15,7 +15,7 @@ #include "Main/utils.h" #ifndef GIT_VERSION -#define GIT_VERSION "3.1.5-snapshot" +#define GIT_VERSION "3.1.5-snap" #endif /**