Compare commits

..

135 Commits
3.1.4 ... 3.1.9

Author SHA1 Message Date
c841476ca4 Merge pull request #247 from airgradienthq/fix/pms-read-data
Fix: PMS sensor read failed in case PM value is low
2024-09-24 20:45:04 +07:00
359394af53 fix: compile failed for esp32-c3 2024-09-24 20:13:01 +07:00
b8e10f473e update API change on example 2024-09-24 20:07:31 +07:00
cb511903ef Update the API use Stream instead of Hardware/Software serial 2024-09-24 20:07:14 +07:00
ebb3f01dcd set active mode on init 2024-09-24 10:39:17 +07:00
2e0ba26c97 Merge commit '0370a8aa15ffaf776f9055f84b5d7c221046b9be' into fix/pms-read-data 2024-09-24 10:39:04 +07:00
c1a4758c6c update timeout handle 2024-09-24 10:28:41 +07:00
0370a8aa15 Update AirGradient.h to 3.1.9 2024-09-24 10:05:04 +07:00
863a37132a Update library.properties to 3.1.9 2024-09-24 10:04:28 +07:00
612317d976 Update local-server config example 2024-09-24 09:46:18 +07:00
8873bacf55 Merge pull request #243 from airgradienthq/feature/add-pm-configuratin-for-display
Add configuration: monitorDisplayCompensatedValues
2024-09-24 09:42:50 +07:00
bf2388b121 Merge pull request #241 from DmitryPustovit/support-display-disable-for-diy-board
Added support clearing display at 0 brightness for DIY Boards
2024-09-24 09:41:01 +07:00
b3918bd1fb Merge pull request #242 from airgradienthq/hw-watchdog-feed
Update hardware watchdog reset
2024-09-24 09:40:11 +07:00
2a6fce674e add variable comment 2024-09-23 06:51:01 +07:00
2f0663ced0 Merge pull request #246 from airgradienthq/fix/display-msg
Fix showing "Server N/A" when postDataToAirGradient is false
2024-09-22 14:26:17 +07:00
3adf58537a Changed error message 2024-09-22 14:13:24 +07:00
e10c9ff854 Update status notification in 1 function call 2024-09-22 13:18:15 +07:00
12c6ec9910 format code 2024-09-21 17:48:18 +07:00
d108b63a57 Update read proccess 2024-09-21 17:47:59 +07:00
6e212714fc Fix/mqtt-log (#235)
Ignore init mqtt when it's not configured
2024-09-21 14:57:05 +07:00
866684eb30 fix load configuration value changed 2024-09-21 14:26:06 +07:00
9d01479406 Update show PM compensate value on display and documents 2024-09-21 14:08:42 +07:00
20245f2110 Saving work 2024-09-21 13:06:01 +07:00
3890919f54 Update log message 2024-09-21 08:46:05 +07:00
76e40fea8c let hw watchdog run independently of POST success 2024-09-21 08:09:58 +07:00
c4024f49fb Added support clearing display at 0 brightness for DIY Boards to AgOledDisplay.
Currently, the only affect the brightness setting has with the DIY boards is an attempt to set the contrast. 

Setting the contrast to 0 does not have any effect. 
This appears to be a know limitation for these display boards.
2024-09-20 00:36:34 -07:00
ca5fc8d65b fix WiFi reset 2024-09-18 12:10:23 +07:00
fd2cef153e Merge pull request #239 from airgradienthq/hotfix/led-bar-show-pm-status
Fix: Correct LED bar show PM status
2024-09-17 10:25:42 +07:00
507b958fdf Correct LED bar show PM value use compensate 2024-09-17 10:15:47 +07:00
335c29ebb1 Merge remote-tracking branch 'origin/develop' into hotfix/led-bar-show-pm-status 2024-09-17 10:01:58 +07:00
2907d6f14e Merge pull request #238 from airgradienthq/hotfix/PM-compensation-receiving-temperature-instead-of-RH
Fix pm compensation: receiving temperature instead of humidity
2024-09-17 09:44:50 +07:00
c8d5b546ed correct PM compensate the input argument value humidity instead of temperature, fix #234 2024-09-16 14:52:04 +07:00
b7cfdc4c4d Update AirGradient.h to v 3.1.8 2024-09-16 12:47:50 +07:00
994d281e02 Update Version to 3.1.8 2024-09-16 12:47:08 +07:00
39470384e4 Merge pull request #233 from airgradienthq/cubic-PM2009X
Changed PM initialization to also support the Cubic PM2009X
2024-09-16 12:08:18 +07:00
c25ba764bf Merge pull request #236 from airgradienthq/add-log-pms-version-code
Add log: PMS5003x sensor print log firmware version
2024-09-16 11:02:32 +07:00
826ff00f42 add log message PM sensor firmware version 2024-09-16 10:36:45 +07:00
520550037d Explicitly set active mode for PM sensor upon initialization 2024-09-15 08:26:38 +07:00
90f336dee7 Revert "Explicitly set active mode for PM sensor upon initialization"
This reverts commit 0d39643e76.
2024-09-15 08:23:32 +07:00
0d39643e76 Explicitly set active mode for PM sensor upon initialization 2024-09-15 08:22:50 +07:00
21232ec49d Optimize PMS sensor read data on active mode send each second 2024-09-14 14:05:35 +07:00
b7339de31f Merge pull request #232 from samuelbles07/feat/ag-client-timeout
Feat/ag-client-timeout
2024-09-12 15:06:23 +07:00
013fb94307 Only for tcp timeout
Ignoring connect to server timeout
2024-09-11 16:37:50 +07:00
e16373a64d Add new public member to set http client timeout by caller 2024-09-11 16:02:13 +07:00
f929623443 Fix uri formatting postToServer to use apiRoot 2024-09-11 16:01:16 +07:00
59587ce2b7 Add http request timeout number for ApiClient 2024-09-11 15:48:44 +07:00
9ec74450a5 Merge branch 'master' into develop 2024-09-02 19:56:46 +07:00
28096e9faf Update version to 3.1.7 2024-09-02 19:55:16 +07:00
682378a47c Merge pull request #231 from airgradienthq/develop
Add WiFi feature
2024-09-02 19:53:33 +07:00
a1861be7b7 Merge pull request #230 from airgradienthq/feature/wifi-connect-to-default
Add default WiFi connect
2024-09-02 19:50:47 +07:00
99ddd24432 Merge branch 'develop' into feature/wifi-connect-to-default 2024-09-02 19:44:53 +07:00
29491e4cbe Merge pull request #229 from airgradienthq/develop
Merge to Master to Release 3.1.6
2024-09-02 12:19:46 +07:00
87cc3fc45f Update library.properties to v 3.1.6 2024-09-02 12:18:04 +07:00
7471d8079a Update AirGradient.h to v 3.1.6 2024-09-02 12:17:08 +07:00
8b0fe967f1 Merge pull request #223 from airgradienthq/hotfix/print-log-wrong-format
Fix print log message number format
2024-09-02 12:11:43 +07:00
6f1cef4e67 Merge pull request #224 from airgradienthq/hotfix/pms25-compensated-show-on-display
[Fix] PM2.5 compensated show on display
2024-09-02 12:09:51 +07:00
02b63ff816 Merge pull request #226 from airgradienthq/fix/pm2.5-compensated-formula
Fix pm2.5 compensation formula
2024-09-02 12:06:39 +07:00
228bf83e92 Merge pull request #228 from airgradienthq/feature/support-led-test-on-openair
`OpenAir` handle `ledBarTestRequested`
2024-09-02 12:05:20 +07:00
d3534cda52 handle ledBarTestRequested on OpenAir 2024-09-01 20:19:18 +07:00
aafaa42a68 Update formula link 2024-09-01 19:56:11 +07:00
2e9ff0d7dd add link to formula document 2024-08-30 19:21:54 +07:00
244b7814a6 add link to formula documents 2024-08-30 19:20:08 +07:00
28d27ee8fd Rename temperatureCompensated to compensateTemp and humidityCompensated to compensateHum 2024-08-30 19:17:58 +07:00
753f22923c rename isValidPMS to isValidPm 2024-08-30 19:07:31 +07:00
c45901706f Merge branch 'develop' into hotfix/pms25-compensated-show-on-display 2024-08-30 19:02:50 +07:00
663836e277 Merge pull request #205 from airgradienthq/feature/send-pms-sensor-fw-version-to-ag-cloud
Send PMS5003T firmware version to Ag Cloud
2024-08-30 10:54:36 +07:00
d39e10908d Merge branch 'develop' into hotfix/print-log-wrong-format 2024-08-28 09:57:45 +07:00
c52962d628 Update float constant 2024-08-26 20:47:48 +07:00
6b65efd3d6 fix pm2.5 compensated formula, #225 2024-08-26 20:43:48 +07:00
8bb87a75ef Merge pull request #222 from airgradienthq/hotfix/pms-fail-count-restart
Hotfix/pms fail count restart
2024-08-26 20:17:20 +07:00
1afcca25a1 Fix compile failed. 2024-08-26 15:54:41 +07:00
17238cff86 fix compile failed. 2024-08-26 15:52:31 +07:00
03e2afbf54 WiFi Connect to default airgradient if WiFi connected is empty 2024-08-26 15:47:49 +07:00
104d58a8c0 resolve review #222 2024-08-26 14:14:42 +07:00
7a988ea6c1 rename compensated to compensate 2024-08-26 11:56:01 +07:00
54ed83cb89 Revert las misstake commit changed. 2024-08-25 20:56:30 +07:00
e461b92c9f Fix build failed 2024-08-25 20:51:07 +07:00
db21648e91 Merge branch 'develop' into feature/send-pms-sensor-fw-version-to-ag-cloud 2024-08-25 20:46:28 +07:00
a9654506f5 Update log format, #218 2024-08-25 20:40:45 +07:00
63f653d5cd fix PM2.5 compensated on display, #221 2024-08-25 20:37:38 +07:00
b049a23657 Restart device after PMS sensor read failed 10 times 2024-08-25 20:21:26 +07:00
d6766ef68b Correct print log number format, fix #218 2024-08-25 08:37:25 +07:00
6c3259b94b Merge branch 'master' into develop 2024-08-23 09:16:10 +07:00
b1aaa04421 update GIT_BUILD version 2024-08-23 09:12:43 +07:00
2df78e9066 Merge branch 'master' into develop 2024-08-23 08:59:49 +07:00
186f0d27ab Update version 2024-08-23 08:59:28 +07:00
e25aa87ecc Merge pull request #215 from airgradienthq/develop
Develop
2024-08-23 08:50:04 +07:00
1cc8941a5c csv alignment format 2024-08-22 11:27:25 +07:00
9bf1495be7 Merge branch 'hotfix/firmware-version' into develop 2024-08-22 11:24:26 +07:00
73089b51f5 add firmware version for arduino build 2024-08-22 11:23:53 +07:00
625e60a5bf Merge branch 'hotfix/local-configuration-return-result' into develop 2024-08-21 12:54:47 +07:00
88e3d0bd3f Ignore and return failed if configurationControl is cloud 2024-08-21 12:54:06 +07:00
171821cfcf Merge pull request #214 from airgradienthq/hotfix/api-client-log
API client log post data
2024-08-21 12:28:50 +07:00
900a2da2ac Log POST data 2024-08-21 12:27:56 +07:00
fb57a112c9 Merge pull request #201 from McJoppy/feature/root-api
Reintroduce 'ROOTAPI' so domain and protocol can be configured
2024-08-20 09:25:48 +07:00
ab69b686ec Merge branch 'develop' into feature/root-api 2024-08-20 09:22:36 +07:00
6746d25dc2 Add comment to all example scripts showing how to change APIROOT 2024-08-18 16:46:56 +12:00
be150e105a Merge pull request #204 from airgradienthq/feature/add-http-request-to-ag-log
Add log message HTTP request and response to AG server
2024-08-18 11:37:42 +07:00
ecadeeb156 Merge pull request #209 from airgradienthq/hotfix/json-round-2-decimal-place-support-negative-value
`round2` support negative value
2024-08-18 11:33:39 +07:00
219ff73132 Merge pull request #212 from airgradienthq/feature/correct-pm2.5-formula
correct pm2.5 formula to show value on display
2024-08-18 11:32:50 +07:00
0a9142204d Merge pull request #213 from airgradienthq/hotfix/sensor-value-out-of-range
sensor value out of range
2024-08-18 11:30:27 +07:00
81b13134d2 update PM2.5 firmware prefix to PMS5003x 2024-08-16 06:42:43 +07:00
f3a9c722b2 Change pm25Compensated to compensated 2024-08-16 06:39:52 +07:00
3be3218115 Update invalid value and optimize code operator. 2024-08-15 09:11:38 +07:00
5edb21cfe9 Fix: PMS5003T only return positive temperature value 2024-08-15 09:10:48 +07:00
6cd587b008 Merge remote-tracking branch 'origin/develop' into feature/correct-pm2.5-formula 2024-08-15 08:19:10 +07:00
6d01366887 change pmsFirmare to firmware 2024-08-15 08:04:30 +07:00
1a347e9cfe Merge pull request #206 from airgradienthq/hotfix/display-show-nox-incorrect-position
`NOx` show incorrect position on display
2024-08-12 09:20:21 +07:00
6432e4451e Merge pull request #210 from airgradienthq/hotfix/set-connect-to-default-wifi-on-factory-reset
Set WiFi connect to default WiFi on factory reset
2024-08-10 07:45:06 +07:00
97f0696002 set default wifi on factory reset. 2024-08-09 13:19:28 +07:00
e46e11c030 round2 support negative value 2024-08-08 05:53:24 +07:00
dc261f668d Update local-server.md II 2024-08-07 11:08:48 +07:00
b5cced40d2 Update local-server.md 2024-08-07 11:05:39 +07:00
040bd28038 Add report PMS5003 and PMS5003T firmware version 2024-08-07 08:50:43 +07:00
b0ae851427 Fix nox position 2024-07-30 20:26:19 +07:00
01943f594d Send PMS5003T firmware version to cloud 2024-07-29 13:20:07 +07:00
01a69668cc Merge branch 'develop' into feature/send-pms-sensor-fw-version-to-ag-cloud 2024-07-29 12:56:45 +07:00
ed7b8df6fe log URLs of all HTTP requests to AG backend / log status codes of responses 2024-07-29 06:00:54 +07:00
6c1c914716 Merge pull request #196 from airgradienthq/hotfix/led-bar-show-sensor-data-incorrect
Correct LED bar show sensor value level color
2024-07-26 06:16:33 +07:00
6a0d88ff10 Merge pull request #197 from airgradienthq/hotfix/ignore-parameter-out-of-range
Ignore parameter values out of range
2024-07-26 06:12:04 +07:00
9097eed137 [fix] typo comment 2024-07-24 20:31:43 +07:00
c9b5e5f0d7 Merge branch 'develop' into hotfix/ignore-parameter-out-of-range 2024-07-24 20:24:27 +07:00
c12bac4ce3 Update invalid temperature value 2024-07-24 20:19:06 +07:00
9ae9b2ac9c display float value on display with 1 digit 2024-07-24 20:18:48 +07:00
7fb3e68b6d Merge pull request #195 from airgradienthq/hotfix/change-tvoc-to-VOC
Change `tvoc` to `VOC` on display
2024-07-24 09:19:03 +02:00
cf65a1f901 Merge pull request #200 from airgradienthq/feature/doc-quote-properties-name
Update local-server.md
2024-07-24 09:16:09 +02:00
5fb27b6d1e Check value sensor value 2024-07-24 09:05:57 +07:00
7b9dac756b Reintroduce 'ROOTAPI' so domain and protocol can be configured
eg. setter for api root added as comment in examples/BASIC/BASIC.ino
2024-07-21 19:38:50 +12:00
4b2a5f5540 Add PM2.5 correction formula, #182 2024-07-21 07:13:34 +07:00
4af77d532e Update local-server.md
Quote properties name
2024-07-20 09:39:29 +07:00
812c2ab803 add PMS5003T get module firmware version code 2024-07-20 08:53:19 +07:00
0ece16f434 Update screen layout, #139 2024-07-18 11:28:12 +07:00
df6cca3714 Ignore parameter values out of range #190 2024-07-11 06:25:31 +07:00
c8aa07ae20 Correct LED bar show sensor value level color, #161 2024-07-09 06:44:04 +07:00
a1d216ac77 Change tvoc to VOC, #139 2024-07-09 06:10:07 +07:00
39 changed files with 1645 additions and 724 deletions

View File

@ -41,33 +41,35 @@ You get the following response:
"bootCount": 6,
"ledMode": "pm",
"firmware": "3.1.3",
"model": "I-9PSL"
"model": "I-9PSL",
"monitorDisplayCompensatedValues": true
}
```
| 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 |
| 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 |
| `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.
@ -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.
```json
{
"country": "US",
"country": "TH",
"pmStandard": "ugm3",
"ledBarMode": "pm",
"displayMode": "on",
"abcDays": 30,
"abcDays": 7,
"tvocLearningOffset": 12,
"noxLearningOffset": 12,
"mqttBrokerUrl": "",
"temperatureUnit": "f",
"configurationControl": "both",
"postDataToAirGradient": true
"temperatureUnit": "c",
"configurationControl": "local",
"postDataToAirGradient": true,
"ledBarBrightness": 100,
"displayBrightness": 100,
"offlineMode": false,
"model": "I-9PSL",
"monitorDisplayCompensatedValues": true
}
```
@ -101,26 +107,31 @@ 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.
#### Configuration Parameters (GET/PUT)
| 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 <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"} |
| 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 <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"} |
| 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} |
| 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) <br> `true`: Enabled | {"offlineMode": 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"}` |
| `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"}` |
| `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}` |
| `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 <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"}` |
| `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}` |
| `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) <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

@ -68,7 +68,6 @@ static LocalServer localServer(Serial, openMetrics, measurements, configuration,
wifiConnector);
static MqttClient mqttClient(Serial);
static int pmFailCount = 0;
static int getCO2FailCount = 0;
static AgFirmwareMode fwMode = FW_MODE_I_BASIC_40PS;
@ -126,6 +125,9 @@ void setup() {
openMetrics.setAirGradient(&ag);
localServer.setAirGraident(&ag);
/** Example set custom API root URL */
// apiClient.setApiRoot("https://example.custom.api");
/** Init sensor */
boardInit();
@ -204,11 +206,7 @@ void loop() {
tvocSchedule.run();
}
/** Auto reset watchdog timer if offline mode or postDataToAirGradient */
if (configuration.isOfflineMode() ||
(configuration.isPostDataToAirGradient() == false)) {
watchdogFeedSchedule.run();
}
watchdogFeedSchedule.run();
/** Check for handle WiFi reconnect */
wifiConnector.handle();
@ -233,7 +231,7 @@ void loop() {
static void co2Update(void) {
int value = ag.s8.getCo2();
if (value >= 0) {
if (utils::isValidCO2(value)) {
measurements.CO2 = value;
getCO2FailCount = 0;
Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2);
@ -241,7 +239,7 @@ static void co2Update(void) {
getCO2FailCount++;
Serial.printf("Get CO2 failed: %d\r\n", getCO2FailCount);
if (getCO2FailCount >= 3) {
measurements.CO2 = -1;
measurements.CO2 = utils::getInvalidCO2();
}
}
}
@ -264,18 +262,23 @@ static void mdnsInit(void) {
}
static void initMqtt(void) {
if (mqttClient.begin(configuration.getMqttBrokerUri())) {
Serial.println("Setup connect to MQTT broker successful");
String mqttUri = configuration.getMqttBrokerUri();
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 {
Serial.println("setup Connect to MQTT broker failed");
Serial.println("Connection to MQTT broker failed");
}
}
static void wdgFeedUpdate(void) {
ag.watchdog.reset();
Serial.println();
Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset");
Serial.println();
Serial.println("External watchdog feed!");
}
static bool sgp41Init(void) {
@ -500,7 +503,7 @@ static void updateTvoc(void) {
}
static void updatePm(void) {
if (ag.pms5003.isFailed() == false) {
if (ag.pms5003.connected()) {
measurements.pm01_1 = ag.pms5003.getPm01Ae();
measurements.pm25_1 = ag.pms5003.getPm25Ae();
measurements.pm10_1 = ag.pms5003.getPm10Ae();
@ -511,15 +514,21 @@ static void updatePm(void) {
Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_1);
Serial.printf("PM10 ug/m3: %d\r\n", measurements.pm10_1);
Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1);
pmFailCount = 0;
Serial.printf("PM firmware version: %d\r\n", ag.pms5003.getFirmwareVersion());
ag.pms5003.resetFailCount();
} else {
pmFailCount++;
Serial.printf("PMS read failed: %d\r\n", pmFailCount);
if (pmFailCount >= 3) {
measurements.pm01_1 = -1;
measurements.pm25_1 = -1;
measurements.pm10_1 = -1;
measurements.pm03PCount_1 = -1;
ag.pms5003.updateFailCount();
Serial.printf("PMS read failed %d times\r\n", ag.pms5003.getFailCount());
if (ag.pms5003.getFailCount() >= PMS_FAIL_COUNT_SET_INVALID) {
measurements.pm01_1 = utils::getInvalidPmValue();
measurements.pm25_1 = utils::getInvalidPmValue();
measurements.pm10_1 = utils::getInvalidPmValue();
measurements.pm03PCount_1 = utils::getInvalidPmValue();
}
if(ag.pms5003.getFailCount() >= ag.pms5003.getFailCountMax()) {
Serial.printf("PMS failure count reach to max set %d, restarting...", ag.pms5003.getFailCountMax());
ESP.restart();
}
}
}
@ -534,13 +543,11 @@ static void sendDataToServer(void) {
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(),
&ag, &configuration);
if (apiClient.postToServer(syncData)) {
ag.watchdog.reset();
Serial.println();
Serial.println(
"Online mode and isPostToAirGradient = true: watchdog reset");
Serial.println();
}
measurements.bootCount++;
}
@ -564,5 +571,7 @@ static void tempHumUpdate(void) {
}
} else {
Serial.println("SHT read failed");
measurements.Temperature = utils::getInvalidTemperature();
measurements.Humidity = utils::getInvalidHumidity();
}
}

View File

@ -65,14 +65,14 @@ String OpenMetrics::getPayload(void) {
add_metric_point("", String(measure.CO2));
}
float _temp = -1001;
float _hum = -1;
int pm01 = -1;
int pm25 = -1;
int pm10 = -1;
int pm03PCount = -1;
int atmpCompensated = -1;
int ahumCompensated = -1;
float _temp = utils::getInvalidTemperature();
float _hum = utils::getInvalidHumidity();
int pm01 = utils::getInvalidPmValue();
int pm25 = utils::getInvalidPmValue();
int pm10 = utils::getInvalidPmValue();
int pm03PCount = utils::getInvalidPmValue();
int atmpCompensated = utils::getInvalidTemperature();
int ahumCompensated = utils::getInvalidHumidity();
if (config.hasSensorSHT) {
_temp = measure.Temperature;
@ -89,28 +89,28 @@ String OpenMetrics::getPayload(void) {
}
if (config.hasSensorPMS1) {
if (pm01 >= 0) {
if (utils::isValidPm(pm01)) {
add_metric("pm1",
"PM1.0 concentration as measured by the AirGradient PMS "
"sensor, in micrograms per cubic meter",
"gauge", "ugm3");
add_metric_point("", String(pm01));
}
if (pm25 >= 0) {
if (utils::isValidPm(pm25)) {
add_metric("pm2d5",
"PM2.5 concentration as measured by the AirGradient PMS "
"sensor, in micrograms per cubic meter",
"gauge", "ugm3");
add_metric_point("", String(pm25));
}
if (pm10 >= 0) {
if (utils::isValidPm(pm10)) {
add_metric("pm10",
"PM10 concentration as measured by the AirGradient PMS "
"sensor, in micrograms per cubic meter",
"gauge", "ugm3");
add_metric_point("", String(pm10));
}
if (pm03PCount >= 0) {
if (utils::isValidPm03Count(pm03PCount)) {
add_metric("pm0d3",
"PM0.3 concentration as measured by the AirGradient PMS "
"sensor, in number of particules per 100 milliliters",
@ -120,28 +120,28 @@ String OpenMetrics::getPayload(void) {
}
if (config.hasSensorSGP) {
if (measure.TVOC >= 0) {
if (utils::isValidVOC(measure.TVOC)) {
add_metric("tvoc_index",
"The processed Total Volatile Organic Compounds (TVOC) index "
"as measured by the AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.TVOC));
}
if (measure.TVOCRaw >= 0) {
if (utils::isValidVOC(measure.TVOCRaw)) {
add_metric("tvoc_raw",
"The raw input value to the Total Volatile Organic Compounds "
"(TVOC) index as measured by the AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.TVOCRaw));
}
if (measure.NOx >= 0) {
if (utils::isValidNOx(measure.NOx)) {
add_metric("nox_index",
"The processed Nitrous Oxide (NOx) index as measured by the "
"AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.NOx));
}
if (measure.NOxRaw >= 0) {
if (utils::isValidNOx(measure.NOxRaw)) {
add_metric("nox_raw",
"The raw input value to the Nitrous Oxide (NOx) index as "
"measured by the AirGradient SGP sensor",
@ -150,7 +150,7 @@ String OpenMetrics::getPayload(void) {
}
}
if (_temp > -1001) {
if (utils::isValidTemperature(_temp)) {
add_metric(
"temperature",
"The ambient temperature as measured by the AirGradient SHT / PMS "
@ -158,7 +158,7 @@ String OpenMetrics::getPayload(void) {
"gauge", "celsius");
add_metric_point("", String(_temp));
}
if (atmpCompensated > -1001) {
if (utils::isValidTemperature(atmpCompensated)) {
add_metric("temperature_compensated",
"The compensated ambient temperature as measured by the "
"AirGradient SHT / PMS "
@ -166,14 +166,14 @@ String OpenMetrics::getPayload(void) {
"gauge", "celsius");
add_metric_point("", String(atmpCompensated));
}
if (_hum >= 0) {
if (utils::isValidHumidity(_hum)) {
add_metric(
"humidity",
"The relative humidity as measured by the AirGradient SHT sensor",
"gauge", "percent");
add_metric_point("", String(_hum));
}
if (ahumCompensated >= 0) {
if (utils::isValidHumidity(ahumCompensated)) {
add_metric("humidity_compensated",
"The compensated relative humidity as measured by the "
"AirGradient SHT / PMS sensor",

View File

@ -68,7 +68,6 @@ static LocalServer localServer(Serial, openMetrics, measurements, configuration,
wifiConnector);
static MqttClient mqttClient(Serial);
static int pmFailCount = 0;
static int getCO2FailCount = 0;
static AgFirmwareMode fwMode = FW_MODE_I_33PS;
@ -126,6 +125,9 @@ void setup() {
openMetrics.setAirGradient(&ag);
localServer.setAirGraident(&ag);
/** Example set custom API root URL */
// apiClient.setApiRoot("https://example.custom.api");
/** Init sensor */
boardInit();
@ -202,11 +204,7 @@ void loop() {
tvocSchedule.run();
}
/** Auto reset watchdog timer if offline mode or postDataToAirGradient */
if (configuration.isOfflineMode() ||
(configuration.isPostDataToAirGradient() == false)) {
watchdogFeedSchedule.run();
}
watchdogFeedSchedule.run();
/** Check for handle WiFi reconnect */
wifiConnector.handle();
@ -231,7 +229,7 @@ void loop() {
static void co2Update(void) {
int value = ag.s8.getCo2();
if (value >= 0) {
if (utils::isValidCO2(value)) {
measurements.CO2 = value;
getCO2FailCount = 0;
Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2);
@ -239,7 +237,7 @@ static void co2Update(void) {
getCO2FailCount++;
Serial.printf("Get CO2 failed: %d\r\n", getCO2FailCount);
if (getCO2FailCount >= 3) {
measurements.CO2 = -1;
measurements.CO2 = utils::getInvalidCO2();
}
}
}
@ -262,10 +260,17 @@ static void mdnsInit(void) {
}
static void initMqtt(void) {
if (mqttClient.begin(configuration.getMqttBrokerUri())) {
Serial.println("Setup connect to MQTT broker successful");
String mqttUri = configuration.getMqttBrokerUri();
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 {
Serial.println("setup Connect to MQTT broker failed");
Serial.println("Connection to MQTT broker failed");
}
}
@ -330,9 +335,7 @@ static void factoryConfigReset(void) {
static void wdgFeedUpdate(void) {
ag.watchdog.reset();
Serial.println();
Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset");
Serial.println();
Serial.println("External watchdog feed!");
}
static bool sgp41Init(void) {
@ -552,7 +555,7 @@ static void updateTvoc(void) {
}
static void updatePm(void) {
if (ag.pms5003.isFailed() == false) {
if (ag.pms5003.connected()) {
measurements.pm01_1 = ag.pms5003.getPm01Ae();
measurements.pm25_1 = ag.pms5003.getPm25Ae();
measurements.pm10_1 = ag.pms5003.getPm10Ae();
@ -563,15 +566,21 @@ static void updatePm(void) {
Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_1);
Serial.printf("PM10 ug/m3: %d\r\n", measurements.pm10_1);
Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1);
pmFailCount = 0;
Serial.printf("PM firmware version: %d\r\n", ag.pms5003.getFirmwareVersion());
ag.pms5003.resetFailCount();
} else {
pmFailCount++;
Serial.printf("PMS read failed: %d\r\n", pmFailCount);
if (pmFailCount >= 3) {
measurements.pm01_1 = -1;
measurements.pm25_1 = -1;
measurements.pm10_1 = -1;
measurements.pm03PCount_1 = -1;
ag.pms5003.updateFailCount();
Serial.printf("PMS read failed %d times\r\n", ag.pms5003.getFailCount());
if (ag.pms5003.getFailCount() >= PMS_FAIL_COUNT_SET_INVALID) {
measurements.pm01_1 = utils::getInvalidPmValue();
measurements.pm25_1 = utils::getInvalidPmValue();
measurements.pm10_1 = utils::getInvalidPmValue();
measurements.pm03PCount_1 = utils::getInvalidPmValue();
}
if(ag.pms5003.getFailCount() >= ag.pms5003.getFailCountMax()) {
Serial.printf("PMS failure count reach to max set %d, restarting...", ag.pms5003.getFailCountMax());
ESP.restart();
}
}
}
@ -586,13 +595,11 @@ static void sendDataToServer(void) {
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(),
&ag, &configuration);
if (apiClient.postToServer(syncData)) {
ag.watchdog.reset();
Serial.println();
Serial.println(
"Online mode and isPostToAirGradient = true: watchdog reset");
Serial.println();
}
measurements.bootCount++;
}
@ -616,5 +623,7 @@ static void tempHumUpdate(void) {
}
} else {
Serial.println("SHT read failed");
measurements.Temperature = utils::getInvalidTemperature();
measurements.Humidity = utils::getInvalidHumidity();
}
}

View File

@ -65,14 +65,14 @@ String OpenMetrics::getPayload(void) {
add_metric_point("", String(measure.CO2));
}
float _temp = -1001;
float _hum = -1;
int pm01 = -1;
int pm25 = -1;
int pm10 = -1;
int pm03PCount = -1;
int atmpCompensated = -1;
int ahumCompensated = -1;
float _temp = utils::getInvalidTemperature();
float _hum = utils::getInvalidHumidity();
int pm01 = utils::getInvalidPmValue();
int pm25 = utils::getInvalidPmValue();
int pm10 = utils::getInvalidPmValue();
int pm03PCount = utils::getInvalidPmValue();
int atmpCompensated = utils::getInvalidTemperature();
int ahumCompensated = utils::getInvalidHumidity();
if (config.hasSensorSHT) {
_temp = measure.Temperature;
@ -89,28 +89,28 @@ String OpenMetrics::getPayload(void) {
}
if (config.hasSensorPMS1) {
if (pm01 >= 0) {
if (utils::isValidPm(pm01)) {
add_metric("pm1",
"PM1.0 concentration as measured by the AirGradient PMS "
"sensor, in micrograms per cubic meter",
"gauge", "ugm3");
add_metric_point("", String(pm01));
}
if (pm25 >= 0) {
if (utils::isValidPm(pm25)) {
add_metric("pm2d5",
"PM2.5 concentration as measured by the AirGradient PMS "
"sensor, in micrograms per cubic meter",
"gauge", "ugm3");
add_metric_point("", String(pm25));
}
if (pm10 >= 0) {
if (utils::isValidPm(pm10)) {
add_metric("pm10",
"PM10 concentration as measured by the AirGradient PMS "
"sensor, in micrograms per cubic meter",
"gauge", "ugm3");
add_metric_point("", String(pm10));
}
if (pm03PCount >= 0) {
if (utils::isValidPm03Count(pm03PCount)) {
add_metric("pm0d3",
"PM0.3 concentration as measured by the AirGradient PMS "
"sensor, in number of particules per 100 milliliters",
@ -120,28 +120,28 @@ String OpenMetrics::getPayload(void) {
}
if (config.hasSensorSGP) {
if (measure.TVOC >= 0) {
if (utils::isValidVOC(measure.TVOC)) {
add_metric("tvoc_index",
"The processed Total Volatile Organic Compounds (TVOC) index "
"as measured by the AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.TVOC));
}
if (measure.TVOCRaw >= 0) {
if (utils::isValidVOC(measure.TVOCRaw)) {
add_metric("tvoc_raw",
"The raw input value to the Total Volatile Organic Compounds "
"(TVOC) index as measured by the AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.TVOCRaw));
}
if (measure.NOx >= 0) {
if (utils::isValidNOx(measure.NOx)) {
add_metric("nox_index",
"The processed Nitrous Oxide (NOx) index as measured by the "
"AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.NOx));
}
if (measure.NOxRaw >= 0) {
if (utils::isValidNOx(measure.NOxRaw)) {
add_metric("nox_raw",
"The raw input value to the Nitrous Oxide (NOx) index as "
"measured by the AirGradient SGP sensor",
@ -150,7 +150,7 @@ String OpenMetrics::getPayload(void) {
}
}
if (_temp > -1001) {
if (utils::isValidTemperature(_temp)) {
add_metric(
"temperature",
"The ambient temperature as measured by the AirGradient SHT / PMS "
@ -158,7 +158,7 @@ String OpenMetrics::getPayload(void) {
"gauge", "celsius");
add_metric_point("", String(_temp));
}
if (atmpCompensated > -1001) {
if (utils::isValidTemperature(atmpCompensated)) {
add_metric("temperature_compensated",
"The compensated ambient temperature as measured by the "
"AirGradient SHT / PMS "
@ -166,14 +166,14 @@ String OpenMetrics::getPayload(void) {
"gauge", "celsius");
add_metric_point("", String(atmpCompensated));
}
if (_hum >= 0) {
if (utils::isValidHumidity(_hum)) {
add_metric(
"humidity",
"The relative humidity as measured by the AirGradient SHT sensor",
"gauge", "percent");
add_metric_point("", String(_hum));
}
if (ahumCompensated >= 0) {
if (utils::isValidHumidity(ahumCompensated)) {
add_metric("humidity_compensated",
"The compensated relative humidity as measured by the "
"AirGradient SHT / PMS sensor",

View File

@ -68,7 +68,6 @@ static LocalServer localServer(Serial, openMetrics, measurements, configuration,
wifiConnector);
static MqttClient mqttClient(Serial);
static int pmFailCount = 0;
static uint32_t factoryBtnPressTime = 0;
static int getCO2FailCount = 0;
static AgFirmwareMode fwMode = FW_MODE_I_42PS;
@ -127,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();
@ -229,11 +231,7 @@ void loop() {
tvocSchedule.run();
}
/** Auto reset watchdog timer if offline mode or postDataToAirGradient */
if (configuration.isOfflineMode() ||
(configuration.isPostDataToAirGradient() == false)) {
watchdogFeedSchedule.run();
}
watchdogFeedSchedule.run();
/** Check for handle WiFi reconnect */
wifiConnector.handle();
@ -258,7 +256,7 @@ void loop() {
static void co2Update(void) {
int value = ag.s8.getCo2();
if (value >= 0) {
if (utils::isValidCO2(value)) {
measurements.CO2 = value;
getCO2FailCount = 0;
Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2);
@ -266,7 +264,7 @@ static void co2Update(void) {
getCO2FailCount++;
Serial.printf("Get CO2 failed: %d\r\n", getCO2FailCount);
if (getCO2FailCount >= 3) {
measurements.CO2 = -1;
measurements.CO2 = utils::getInvalidCO2();
}
}
}
@ -289,10 +287,17 @@ static void mdnsInit(void) {
}
static void initMqtt(void) {
if (mqttClient.begin(configuration.getMqttBrokerUri())) {
Serial.println("Setup connect to MQTT broker successful");
String mqttUri = configuration.getMqttBrokerUri();
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 {
Serial.println("setup Connect to MQTT broker failed");
Serial.println("Connection to MQTT broker failed");
}
}
@ -325,8 +330,6 @@ static void factoryConfigReset(void) {
// }
/** Reset WIFI */
// WiFi.enableSTA(true); // Incase offline mode
// WiFi.disconnect(true, true);
wifiConnector.reset();
/** Reset local config */
@ -355,9 +358,7 @@ static void factoryConfigReset(void) {
static void wdgFeedUpdate(void) {
ag.watchdog.reset();
Serial.println();
Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset");
Serial.println();
Serial.println("External watchdog feed!");
}
static bool sgp41Init(void) {
@ -595,7 +596,7 @@ static void updateTvoc(void) {
}
static void updatePm(void) {
if (ag.pms5003.isFailed() == false) {
if (ag.pms5003.connected()) {
measurements.pm01_1 = ag.pms5003.getPm01Ae();
measurements.pm25_1 = ag.pms5003.getPm25Ae();
measurements.pm10_1 = ag.pms5003.getPm10Ae();
@ -606,15 +607,21 @@ static void updatePm(void) {
Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_1);
Serial.printf("PM10 ug/m3: %d\r\n", measurements.pm10_1);
Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1);
pmFailCount = 0;
Serial.printf("PM firmware version: %d\r\n", ag.pms5003.getFirmwareVersion());
ag.pms5003.resetFailCount();
} else {
pmFailCount++;
Serial.printf("PMS read failed: %d\r\n", pmFailCount);
if (pmFailCount >= 3) {
measurements.pm01_1 = -1;
measurements.pm25_1 = -1;
measurements.pm10_1 = -1;
measurements.pm03PCount_1 = -1;
ag.pms5003.updateFailCount();
Serial.printf("PMS read failed %d times\r\n", ag.pms5003.getFailCount());
if (ag.pms5003.getFailCount() >= PMS_FAIL_COUNT_SET_INVALID) {
measurements.pm01_1 = utils::getInvalidPmValue();
measurements.pm25_1 = utils::getInvalidPmValue();
measurements.pm10_1 = utils::getInvalidPmValue();
measurements.pm03PCount_1 = utils::getInvalidPmValue();
}
if(ag.pms5003.getFailCount() >= ag.pms5003.getFailCountMax()) {
Serial.printf("PMS failure count reach to max set %d, restarting...", ag.pms5003.getFailCountMax());
ESP.restart();
}
}
}
@ -629,13 +636,11 @@ static void sendDataToServer(void) {
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(),
&ag, &configuration);
if (apiClient.postToServer(syncData)) {
ag.watchdog.reset();
Serial.println();
Serial.println(
"Online mode and isPostToAirGradient = true: watchdog reset");
Serial.println();
}
measurements.bootCount++;
}
@ -659,5 +664,7 @@ static void tempHumUpdate(void) {
}
} else {
Serial.println("SHT read failed");
measurements.Temperature = utils::getInvalidTemperature();
measurements.Humidity = utils::getInvalidHumidity();
}
}

View File

@ -65,14 +65,14 @@ String OpenMetrics::getPayload(void) {
add_metric_point("", String(measure.CO2));
}
float _temp = -1001;
float _hum = -1;
int pm01 = -1;
int pm25 = -1;
int pm10 = -1;
int pm03PCount = -1;
int atmpCompensated = -1;
int ahumCompensated = -1;
float _temp = utils::getInvalidTemperature();
float _hum = utils::getInvalidHumidity();
int pm01 = utils::getInvalidPmValue();
int pm25 = utils::getInvalidPmValue();
int pm10 = utils::getInvalidPmValue();
int pm03PCount = utils::getInvalidPmValue();
int atmpCompensated = utils::getInvalidTemperature();
int ahumCompensated = utils::getInvalidHumidity();
if (config.hasSensorSHT) {
_temp = measure.Temperature;
@ -89,28 +89,28 @@ String OpenMetrics::getPayload(void) {
}
if (config.hasSensorPMS1) {
if (pm01 >= 0) {
if (utils::isValidPm(pm01)) {
add_metric("pm1",
"PM1.0 concentration as measured by the AirGradient PMS "
"sensor, in micrograms per cubic meter",
"gauge", "ugm3");
add_metric_point("", String(pm01));
}
if (pm25 >= 0) {
if (utils::isValidPm(pm25)) {
add_metric("pm2d5",
"PM2.5 concentration as measured by the AirGradient PMS "
"sensor, in micrograms per cubic meter",
"gauge", "ugm3");
add_metric_point("", String(pm25));
}
if (pm10 >= 0) {
if (utils::isValidPm(pm10)) {
add_metric("pm10",
"PM10 concentration as measured by the AirGradient PMS "
"sensor, in micrograms per cubic meter",
"gauge", "ugm3");
add_metric_point("", String(pm10));
}
if (pm03PCount >= 0) {
if (utils::isValidPm03Count(pm03PCount)) {
add_metric("pm0d3",
"PM0.3 concentration as measured by the AirGradient PMS "
"sensor, in number of particules per 100 milliliters",
@ -120,28 +120,28 @@ String OpenMetrics::getPayload(void) {
}
if (config.hasSensorSGP) {
if (measure.TVOC >= 0) {
if (utils::isValidVOC(measure.TVOC)) {
add_metric("tvoc_index",
"The processed Total Volatile Organic Compounds (TVOC) index "
"as measured by the AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.TVOC));
}
if (measure.TVOCRaw >= 0) {
if (utils::isValidVOC(measure.TVOCRaw)) {
add_metric("tvoc_raw",
"The raw input value to the Total Volatile Organic Compounds "
"(TVOC) index as measured by the AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.TVOCRaw));
}
if (measure.NOx >= 0) {
if (utils::isValidNOx(measure.NOx)) {
add_metric("nox_index",
"The processed Nitrous Oxide (NOx) index as measured by the "
"AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.NOx));
}
if (measure.NOxRaw >= 0) {
if (utils::isValidNOx(measure.NOxRaw)) {
add_metric("nox_raw",
"The raw input value to the Nitrous Oxide (NOx) index as "
"measured by the AirGradient SGP sensor",
@ -150,7 +150,7 @@ String OpenMetrics::getPayload(void) {
}
}
if (_temp > -1001) {
if (utils::isValidTemperature(_temp)) {
add_metric(
"temperature",
"The ambient temperature as measured by the AirGradient SHT / PMS "
@ -158,7 +158,7 @@ String OpenMetrics::getPayload(void) {
"gauge", "celsius");
add_metric_point("", String(_temp));
}
if (atmpCompensated > -1001) {
if (utils::isValidTemperature(atmpCompensated)) {
add_metric("temperature_compensated",
"The compensated ambient temperature as measured by the "
"AirGradient SHT / PMS "
@ -166,14 +166,14 @@ String OpenMetrics::getPayload(void) {
"gauge", "celsius");
add_metric_point("", String(atmpCompensated));
}
if (_hum >= 0) {
if (utils::isValidHumidity(_hum)) {
add_metric(
"humidity",
"The relative humidity as measured by the AirGradient SHT sensor",
"gauge", "percent");
add_metric_point("", String(_hum));
}
if (ahumCompensated >= 0) {
if (utils::isValidHumidity(ahumCompensated)) {
add_metric("humidity_compensated",
"The compensated relative humidity as measured by the "
"AirGradient SHT / PMS sensor",

View File

@ -88,7 +88,6 @@ static OtaHandler otaHandler;
static LocalServer localServer(Serial, openMetrics, measurements, configuration,
wifiConnector);
static int pmFailCount = 0;
static uint32_t factoryBtnPressTime = 0;
static int getCO2FailCount = 0;
static AgFirmwareMode fwMode = FW_MODE_I_9PSL;
@ -99,9 +98,7 @@ static String fwNewVersion;
static void boardInit(void);
static void failedHandler(String msg);
static void configurationUpdateSchedule(void);
static void appLedHandler(void);
static void appDispHandler(void);
static void oledDisplayLedBarSchedule(void);
static void updateDisplayAndLedBar(void);
static void updateTvoc(void);
static void updatePm(void);
static void sendDataToServer(void);
@ -119,7 +116,7 @@ static void otaHandlerCallback(OtaState state, String mesasge);
static void displayExecuteOta(OtaState state, String msg,
int processing);
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplayLedBarSchedule);
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, updateDisplayAndLedBar);
AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL,
configurationUpdateSchedule);
AgSchedule agApiPostSchedule(SERVER_SYNC_INTERVAL, sendDataToServer);
@ -163,6 +160,9 @@ void setup() {
openMetrics.setAirGradient(ag);
localServer.setAirGraident(ag);
/** Example set custom API root URL */
// apiClient.setApiRoot("https://example.custom.api");
/** Init sensor */
boardInit();
@ -259,8 +259,8 @@ void setup() {
oledDisplay.setBrightness(configuration.getDisplayBrightness());
}
appLedHandler();
appDispHandler();
// Update display and led bar after finishing setup to show dashboard
updateDisplayAndLedBar();
}
void loop() {
@ -286,6 +286,11 @@ void loop() {
if (ag->isOne()) {
if (configuration.hasSensorPMS1) {
ag->pms5003.handle();
static bool pmsConnected = false;
if (pmsConnected != ag->pms5003.connected()) {
pmsConnected = ag->pms5003.connected();
Serial.printf("PMS sensor %s ", pmsConnected?"connected":"removed");
}
}
} else {
if (configuration.hasSensorPMS1) {
@ -296,11 +301,7 @@ void loop() {
}
}
/** Auto reset watchdog timer if offline mode or postDataToAirGradient */
if (configuration.isOfflineMode() ||
(configuration.isPostDataToAirGradient() == false)) {
watchdogFeedSchedule.run();
}
watchdogFeedSchedule.run();
/** Check for handle WiFi reconnect */
wifiConnector.handle();
@ -317,7 +318,7 @@ void loop() {
static void co2Update(void) {
int value = ag->s8.getCo2();
if (value >= 0) {
if (utils::isValidCO2(value)) {
measurements.CO2 = value;
getCO2FailCount = 0;
Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2);
@ -325,7 +326,7 @@ static void co2Update(void) {
getCO2FailCount++;
Serial.printf("Get CO2 failed: %d\r\n", getCO2FailCount);
if (getCO2FailCount >= 3) {
measurements.CO2 = -1;
measurements.CO2 = utils::getInvalidCO2();
}
}
}
@ -380,11 +381,18 @@ static void createMqttTask(void) {
}
static void initMqtt(void) {
if (mqttClient.begin(configuration.getMqttBrokerUri())) {
Serial.println("Connect to MQTT broker successful");
String mqttUri = configuration.getMqttBrokerUri();
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();
} else {
Serial.println("Connect to MQTT broker failed");
Serial.println("Connection to MQTT broker failed");
}
}
@ -421,12 +429,7 @@ static void factoryConfigReset(void) {
}
/** Reset WIFI */
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();
@ -445,7 +448,7 @@ static void factoryConfigReset(void) {
/** Show current content cause reset ignore */
factoryBtnPressTime = 0;
if (ag->isOne()) {
appDispHandler();
updateDisplayAndLedBar();
}
}
}
@ -453,7 +456,7 @@ static void factoryConfigReset(void) {
if (factoryBtnPressTime != 0) {
if (ag->isOne()) {
/** Restore last display content */
appDispHandler();
updateDisplayAndLedBar();
}
}
factoryBtnPressTime = 0;
@ -462,9 +465,7 @@ static void factoryConfigReset(void) {
static void wdgFeedUpdate(void) {
ag->watchdog.reset();
Serial.println();
Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset");
Serial.println();
Serial.println("External watchdog feed!");
}
static void ledBarEnabledUpdate(void) {
@ -700,7 +701,7 @@ static void oneIndoorInit(void) {
ledBarEnabledUpdate();
/** Show message init sensor */
oledDisplay.setText("Sensor", "initializing...", "");
oledDisplay.setText("Monitor", "initializing...", "");
/** Init sensor SGP41 */
if (sgp41Init() == false) {
@ -781,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) {
bool pmInitSuccess = false;
if (serial0Available) {
if (ag->pms5003t_1.begin(Serial0) == false) {
configuration.hasSensorPMS1 = false;
Serial.println("PMS1 sensor not found");
Serial.println("No PM sensor detected on Serial0");
} else {
serial0Available = false;
pmInitSuccess = true;
Serial.println("Found PMS 1 on Serial0");
Serial.println("Detected PM 1 on Serial0");
}
}
if (pmInitSuccess == false) {
if (serial1Available) {
if (ag->pms5003t_1.begin(Serial1) == false) {
configuration.hasSensorPMS1 = false;
Serial.println("PMS1 sensor not found");
Serial.println("No PM sensor detected on Serial1");
} else {
serial1Available = false;
Serial.println("Found PMS 1 on Serial1");
Serial.println("Detected PM 1 on Serial1");
}
}
}
@ -809,15 +810,15 @@ static void openAirInit(void) {
} else {
if (ag->pms5003t_1.begin(Serial0) == false) {
configuration.hasSensorPMS1 = false;
Serial.println("PMS1 sensor not found");
Serial.println("No PM sensor detected on Serial0");
} else {
Serial.println("Found PMS 1 on Serial0");
Serial.println("Detected PM 1 on Serial0");
}
if (ag->pms5003t_2.begin(Serial1) == false) {
configuration.hasSensorPMS2 = false;
Serial.println("PMS2 sensor not found");
Serial.println("No PM sensor detected on Serial1");
} else {
Serial.println("Found PMS 2 on Serial1");
Serial.println("Detected PM 2 on Serial1");
}
if (fwMode == FW_MODE_O_1PP) {
@ -941,58 +942,45 @@ static void configUpdateHandle() {
stateMachine.executeLedBarTest();
}
else if(ag->isOpenAir()) {
stateMachine.executeLedBarTest();
}
appDispHandler();
appLedHandler();
// Update display and led bar notification based on updated configuration
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;
if (configuration.isOfflineMode() == false) {
if (wifiConnector.isConnected() == false) {
state = AgStateMachineWiFiLost;
} else if (apiClient.isFetchConfigureFailed()) {
state = AgStateMachineSensorConfigFailed;
} else if (apiClient.isPostToServerFailed()) {
state = AgStateMachineServerLost;
if (wifiConnector.isConnected() == false) {
state = AgStateMachineWiFiLost;
} else if (apiClient.isFetchConfigureFailed()) {
state = AgStateMachineSensorConfigFailed;
if (apiClient.isNotAvailableOnDashboard()) {
stateMachine.displaySetAddToDashBoard();
} else {
stateMachine.displayClearAddToDashBoard();
}
} else if (apiClient.isPostToServerFailed() && configuration.isPostDataToAirGradient()) {
state = AgStateMachineServerLost;
}
stateMachine.displayHandle(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) {
measurements.TVOC = ag->sgp41.getTvocIndex();
measurements.TVOCRaw = ag->sgp41.getTvocRaw();
@ -1007,8 +995,9 @@ static void updateTvoc(void) {
}
static void updatePm(void) {
bool restart = false;
if (ag->isOne()) {
if (ag->pms5003.isFailed() == false) {
if (ag->pms5003.connected()) {
measurements.pm01_1 = ag->pms5003.getPm01Ae();
measurements.pm25_1 = ag->pms5003.getPm25Ae();
measurements.pm10_1 = ag->pms5003.getPm10Ae();
@ -1019,21 +1008,26 @@ static void updatePm(void) {
Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_1);
Serial.printf("PM10 ug/m3: %d\r\n", measurements.pm10_1);
Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1);
pmFailCount = 0;
Serial.printf("PM firmware version: %d\r\n", ag->pms5003.getFirmwareVersion());
ag->pms5003.resetFailCount();
} else {
pmFailCount++;
Serial.printf("PMS read failed: %d\r\n", pmFailCount);
if (pmFailCount >= 3) {
measurements.pm01_1 = -1;
measurements.pm25_1 = -1;
measurements.pm10_1 = -1;
measurements.pm03PCount_1 = -1;
ag->pms5003.updateFailCount();
Serial.printf("PMS read failed %d times\r\n", ag->pms5003.getFailCount());
if (ag->pms5003.getFailCount() >= PMS_FAIL_COUNT_SET_INVALID) {
measurements.pm01_1 = utils::getInvalidPmValue();
measurements.pm25_1 = utils::getInvalidPmValue();
measurements.pm10_1 = utils::getInvalidPmValue();
measurements.pm03PCount_1 = utils::getInvalidPmValue();
}
if (ag->pms5003.getFailCount() >= ag->pms5003.getFailCountMax()) {
restart = true;
}
}
} else {
bool pmsResult_1 = false;
bool pmsResult_2 = false;
if (configuration.hasSensorPMS1 && (ag->pms5003t_1.isFailed() == false)) {
if (configuration.hasSensorPMS1 && ag->pms5003t_1.connected()) {
measurements.pm01_1 = ag->pms5003t_1.getPm01Ae();
measurements.pm25_1 = ag->pms5003t_1.getPm25Ae();
measurements.pm10_1 = ag->pms5003t_1.getPm10Ae();
@ -1051,19 +1045,33 @@ static void updatePm(void) {
Serial.printf("[1] Temperature in C: %0.2f\r\n", measurements.temp_1);
Serial.printf("[1] Relative Humidity: %d\r\n", measurements.hum_1);
Serial.printf("[1] Temperature compensated in C: %0.2f\r\n",
ag->pms5003t_1.temperatureCompensated(measurements.temp_1));
Serial.printf("[1] Relative Humidity compensated: %f\r\n",
ag->pms5003t_1.humidityCompensated(measurements.hum_1));
ag->pms5003t_1.compensateTemp(measurements.temp_1));
Serial.printf("[1] Relative Humidity compensated: %0.2f\r\n",
ag->pms5003t_1.compensateHum(measurements.hum_1));
Serial.printf("[1] PM firmware version: %d\r\n", ag->pms5003t_1.getFirmwareVersion());
ag->pms5003t_1.resetFailCount();
} else {
measurements.pm01_1 = -1;
measurements.pm25_1 = -1;
measurements.pm10_1 = -1;
measurements.pm03PCount_1 = -1;
measurements.temp_1 = -1001;
measurements.hum_1 = -1;
if (configuration.hasSensorPMS1) {
ag->pms5003t_1.updateFailCount();
Serial.printf("[1] PMS read failed %d times\r\n", ag->pms5003t_1.getFailCount());
if (ag->pms5003t_1.getFailCount() >= PMS_FAIL_COUNT_SET_INVALID) {
measurements.pm01_1 = utils::getInvalidPmValue();
measurements.pm25_1 = utils::getInvalidPmValue();
measurements.pm10_1 = utils::getInvalidPmValue();
measurements.pm03PCount_1 = utils::getInvalidPmValue();
measurements.temp_1 = utils::getInvalidTemperature();
measurements.hum_1 = utils::getInvalidHumidity();
}
if (ag->pms5003t_1.getFailCount() >= ag->pms5003t_1.getFailCountMax()) {
restart = true;
}
}
}
if (configuration.hasSensorPMS2 && (ag->pms5003t_2.isFailed() == false)) {
if (configuration.hasSensorPMS2 && ag->pms5003t_2.connected()) {
measurements.pm01_2 = ag->pms5003t_2.getPm01Ae();
measurements.pm25_2 = ag->pms5003t_2.getPm25Ae();
measurements.pm10_2 = ag->pms5003t_2.getPm10Ae();
@ -1081,16 +1089,30 @@ static void updatePm(void) {
Serial.printf("[2] Temperature in C: %0.2f\r\n", measurements.temp_2);
Serial.printf("[2] Relative Humidity: %d\r\n", measurements.hum_2);
Serial.printf("[2] Temperature compensated in C: %0.2f\r\n",
ag->pms5003t_1.temperatureCompensated(measurements.temp_2));
Serial.printf("[2] Relative Humidity compensated: %d\r\n",
ag->pms5003t_1.humidityCompensated(measurements.hum_2));
ag->pms5003t_1.compensateTemp(measurements.temp_2));
Serial.printf("[2] Relative Humidity compensated: %0.2f\r\n",
ag->pms5003t_1.compensateHum(measurements.hum_2));
Serial.printf("[2] PM firmware version: %d\r\n", ag->pms5003t_2.getFirmwareVersion());
ag->pms5003t_2.resetFailCount();
} else {
measurements.pm01_2 = -1;
measurements.pm25_2 = -1;
measurements.pm10_2 = -1;
measurements.pm03PCount_2 = -1;
measurements.temp_2 = -1001;
measurements.hum_2 = -1;
if (configuration.hasSensorPMS2) {
ag->pms5003t_2.updateFailCount();
Serial.printf("[2] PMS read failed %d times\r\n", ag->pms5003t_2.getFailCount());
if (ag->pms5003t_2.getFailCount() >= PMS_FAIL_COUNT_SET_INVALID) {
measurements.pm01_2 = utils::getInvalidPmValue();
measurements.pm25_2 = utils::getInvalidPmValue();
measurements.pm10_2 = utils::getInvalidPmValue();
measurements.pm03PCount_2 = utils::getInvalidPmValue();
measurements.temp_2 = utils::getInvalidTemperature();
measurements.hum_2 = utils::getInvalidHumidity();
}
if (ag->pms5003t_2.getFailCount() >= ag->pms5003t_2.getFailCountMax()) {
restart = true;
}
}
}
if (configuration.hasSensorPMS1 && configuration.hasSensorPMS2 &&
@ -1190,6 +1212,11 @@ static void updatePm(void) {
ag->sgp41.setCompensationTemperatureHumidity(temp, hum);
}
}
if (restart) {
Serial.printf("PMS failure count reach to max set %d, restarting...", ag->pms5003.getFailCountMax());
ESP.restart();
}
}
static void sendDataToServer(void) {
@ -1201,13 +1228,11 @@ static void sendDataToServer(void) {
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(),
ag, &configuration);
if (apiClient.postToServer(syncData)) {
ag->watchdog.reset();
Serial.println();
Serial.println(
"Online mode and isPostToAirGradient = true: watchdog reset");
Serial.println();
}
measurements.bootCount++;
}
@ -1230,6 +1255,8 @@ static void tempHumUpdate(void) {
measurements.Humidity);
}
} else {
measurements.Temperature = utils::getInvalidTemperature();
measurements.Humidity = utils::getInvalidHumidity();
Serial.println("SHT read failed");
}
}

View File

@ -65,14 +65,14 @@ String OpenMetrics::getPayload(void) {
add_metric_point("", String(measure.CO2));
}
float _temp = -1001;
float _hum = -1;
int pm01 = -1;
int pm25 = -1;
int pm10 = -1;
int pm03PCount = -1;
int atmpCompensated = -1;
int ahumCompensated = -1;
float _temp = utils::getInvalidTemperature();
float _hum = utils::getInvalidHumidity();
int pm01 = utils::getInvalidPmValue();
int pm25 = utils::getInvalidPmValue();
int pm10 = utils::getInvalidPmValue();
int pm03PCount = utils::getInvalidPmValue();
int atmpCompensated = utils::getInvalidTemperature();
int ahumCompensated = utils::getInvalidHumidity();
if (config.hasSensorPMS1 && config.hasSensorPMS2) {
_temp = (measure.temp_1 + measure.temp_2) / 2.0f;
_hum = (measure.hum_1 + measure.hum_2) / 2.0f;
@ -118,33 +118,33 @@ String OpenMetrics::getPayload(void) {
atmpCompensated = _temp;
ahumCompensated = _hum;
} else {
atmpCompensated = ag->pms5003t_1.temperatureCompensated(_temp);
ahumCompensated = ag->pms5003t_1.humidityCompensated(_hum);
atmpCompensated = ag->pms5003t_1.compensateTemp(_temp);
ahumCompensated = ag->pms5003t_1.compensateHum(_hum);
}
if (config.hasSensorPMS1 || config.hasSensorPMS2) {
if (pm01 >= 0) {
if (utils::isValidPm(pm01)) {
add_metric("pm1",
"PM1.0 concentration as measured by the AirGradient PMS "
"sensor, in micrograms per cubic meter",
"gauge", "ugm3");
add_metric_point("", String(pm01));
}
if (pm25 >= 0) {
if (utils::isValidPm(pm25)) {
add_metric("pm2d5",
"PM2.5 concentration as measured by the AirGradient PMS "
"sensor, in micrograms per cubic meter",
"gauge", "ugm3");
add_metric_point("", String(pm25));
}
if (pm10 >= 0) {
if (utils::isValidPm(pm10)) {
add_metric("pm10",
"PM10 concentration as measured by the AirGradient PMS "
"sensor, in micrograms per cubic meter",
"gauge", "ugm3");
add_metric_point("", String(pm10));
}
if (pm03PCount >= 0) {
if (utils::isValidPm03Count(pm03PCount)) {
add_metric("pm0d3",
"PM0.3 concentration as measured by the AirGradient PMS "
"sensor, in number of particules per 100 milliliters",
@ -154,28 +154,28 @@ String OpenMetrics::getPayload(void) {
}
if (config.hasSensorSGP) {
if (measure.TVOC >= 0) {
if (utils::isValidVOC(measure.TVOC)) {
add_metric("tvoc_index",
"The processed Total Volatile Organic Compounds (TVOC) index "
"as measured by the AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.TVOC));
}
if (measure.TVOCRaw >= 0) {
if (utils::isValidVOC(measure.TVOCRaw)) {
add_metric("tvoc_raw",
"The raw input value to the Total Volatile Organic Compounds "
"(TVOC) index as measured by the AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.TVOCRaw));
}
if (measure.NOx >= 0) {
if (utils::isValidNOx(measure.NOx)) {
add_metric("nox_index",
"The processed Nitrous Oxide (NOx) index as measured by the "
"AirGradient SGP sensor",
"gauge");
add_metric_point("", String(measure.NOx));
}
if (measure.NOxRaw >= 0) {
if (utils::isValidNOx(measure.NOxRaw)) {
add_metric("nox_raw",
"The raw input value to the Nitrous Oxide (NOx) index as "
"measured by the AirGradient SGP sensor",
@ -184,14 +184,14 @@ String OpenMetrics::getPayload(void) {
}
}
if (_temp > -1001) {
if (utils::isValidTemperature(_temp)) {
add_metric("temperature",
"The ambient temperature as measured by the AirGradient SHT / PMS "
"sensor, in degrees Celsius",
"gauge", "celsius");
add_metric_point("", String(_temp));
}
if (atmpCompensated > -1001) {
if (utils::isValidTemperature(atmpCompensated)) {
add_metric(
"temperature_compensated",
"The compensated ambient temperature as measured by the AirGradient SHT / PMS "
@ -199,14 +199,14 @@ String OpenMetrics::getPayload(void) {
"gauge", "celsius");
add_metric_point("", String(atmpCompensated));
}
if (_hum >= 0) {
if (utils::isValidHumidity(_hum)) {
add_metric(
"humidity",
"The relative humidity as measured by the AirGradient SHT sensor",
"gauge", "percent");
add_metric_point("", String(_hum));
}
if (ahumCompensated >= 0) {
if (utils::isValidHumidity(ahumCompensated)) {
add_metric(
"humidity_compensated",
"The compensated relative humidity as measured by the AirGradient SHT / PMS sensor",

View File

@ -44,7 +44,7 @@ void loop() {
if (ms >= 5000) {
lastRead = millis();
#ifdef ESP8266
if (ag.pms5003.isFailed() == false) {
if (ag.pms5003.connected()) {
PM2 = ag.pms5003.getPm25Ae();
Serial.printf("PM2.5 in ug/m3: %d\r\n", PM2);
Serial.printf("PM2.5 in US AQI: %d\r\n",
@ -54,12 +54,12 @@ void loop() {
}
#else
if (ag.getBoardType() == OPEN_AIR_OUTDOOR) {
if (ag.pms5003t_1.isFailed() == false) {
if (ag.pms5003t_1.connected()) {
PM2 = ag.pms5003t_1.getPm25Ae();
readResul = true;
}
} else {
if (ag.pms5003.isFailed() == false) {
if (ag.pms5003.connected()) {
PM2 = ag.pms5003.getPm25Ae();
readResul = true;
}

View File

@ -1,5 +1,5 @@
name=AirGradient Air Quality Sensor
version=3.1.4
version=3.1.9
author=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.

View File

@ -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,
# 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 ,

1 # Name # Name Type Type SubType SubType Offset Offset Size Size Flags
2 nvs nvs data data nvs nvs 0x9000 0x9000 0x5000 0x5000
3 otadata otadata data data ota ota 0xe000 0xe000 0x2000 0x2000
4 app0 app0 app app ota_0 ota_0 0x10000 0x10000 0x1E0000 0x1E0000
5 app1 app1 app app ota_1 ota_1 0x1F0000 0x1F0000 0x1E0000 0x1E0000
6 spiffs spiffs data data spiffs spiffs 0x3D0000 0x3D0000 0x20000 0x20000
7 coredump coredump data data coredump coredump 0x3F0000 0x3F0000 0x10000 0x10000

View File

@ -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
@ -58,6 +58,7 @@ bool AgApiClient::fetchServerConfiguration(void) {
}
#else
HTTPClient client;
client.setTimeout(timeoutMs);
if (client.begin(uri) == false) {
getConfigFailed = true;
return false;
@ -66,6 +67,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;
@ -109,21 +114,25 @@ bool AgApiClient::postToServer(String data) {
return false;
}
String uri =
"http://hw.airgradient.com/sensors/airgradient:" + ag->deviceId() +
"/measures";
logInfo("Post uri: " + uri);
logInfo("Post data: " + data);
String uri = apiRoot + "/sensors/airgradient:" + ag->deviceId() + "/measures";
// logInfo("Post uri: " + uri);
// logInfo("Post data: " + data);
WiFiClient wifiClient;
HTTPClient client;
client.setTimeout(timeoutMs);
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("DATA: ") + data);
logInfo(String("Return code: ") + String(retCode));
if ((retCode == 200) || (retCode == 429)) {
postToServerFailed = false;
return true;
@ -177,3 +186,16 @@ 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; }
/**
* @brief Set http request timeout. (Default: 10s)
*
* @param timeoutMs
*/
void AgApiClient::setTimeout(uint16_t timeoutMs) {
this->timeoutMs = timeoutMs;
}

View File

@ -20,10 +20,12 @@ class AgApiClient : public PrintLog {
private:
Configuration &config;
AirGradient *ag;
String apiRoot = "http://hw.airgradient.com";
bool getConfigFailed;
bool postToServerFailed;
bool notAvailableOnDashboard = false; // Device not setup on Airgradient cloud dashboard.
uint16_t timeoutMs = 10000; // Default set to 10s
public:
AgApiClient(Stream &stream, Configuration &config);
@ -37,6 +39,9 @@ public:
bool isNotAvailableOnDashboard(void);
void setAirGradient(AirGradient *ag);
bool sendPing(int rssi, int bootCount);
String getApiRoot() const;
void setApiRoot(const String &apiRoot);
void setTimeout(uint16_t timeoutMs);
};
#endif /** _AG_API_CLIENT_H_ */

View File

@ -41,21 +41,23 @@ JSON_PROP_DEF(displayBrightness);
JSON_PROP_DEF(co2CalibrationRequested);
JSON_PROP_DEF(ledBarTestRequested);
JSON_PROP_DEF(offlineMode);
JSON_PROP_DEF(monitorDisplayCompensatedValues);
#define jprop_model_default ""
#define jprop_country_default "TH"
#define jprop_pmStandard_default getPMStandardString(false)
#define jprop_ledBarMode_default getLedBarModeName(LedBarMode::LedBarModeCO2)
#define jprop_abcDays_default 8
#define jprop_tvocLearningOffset_default 12
#define jprop_noxLearningOffset_default 12
#define jprop_mqttBrokerUrl_default ""
#define jprop_temperatureUnit_default "c"
#define jprop_configurationControl_default String(CONFIGURATION_CONTROL_NAME[ConfigurationControl::ConfigurationControlBoth])
#define jprop_postDataToAirGradient_default true
#define jprop_ledBarBrightness_default 100
#define jprop_displayBrightness_default 100
#define jprop_offlineMode_default false
#define jprop_model_default ""
#define jprop_country_default "TH"
#define jprop_pmStandard_default getPMStandardString(false)
#define jprop_ledBarMode_default getLedBarModeName(LedBarMode::LedBarModeCO2)
#define jprop_abcDays_default 8
#define jprop_tvocLearningOffset_default 12
#define jprop_noxLearningOffset_default 12
#define jprop_mqttBrokerUrl_default ""
#define jprop_temperatureUnit_default "c"
#define jprop_configurationControl_default String(CONFIGURATION_CONTROL_NAME[ConfigurationControl::ConfigurationControlBoth])
#define jprop_postDataToAirGradient_default true
#define jprop_ledBarBrightness_default 100
#define jprop_displayBrightness_default 100
#define jprop_offlineMode_default false
#define jprop_monitorDisplayCompensatedValues_default false
JSONVar jconfig;
@ -167,6 +169,7 @@ void Configuration::defaultConfig(void) {
jconfig[jprop_abcDays] = jprop_abcDays_default;
jconfig[jprop_model] = jprop_model_default;
jconfig[jprop_offlineMode] = jprop_offlineMode_default;
jconfig[jprop_monitorDisplayCompensatedValues] = jprop_monitorDisplayCompensatedValues_default;
saveConfig();
}
@ -264,12 +267,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 =
@ -626,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 ||
ag->getBoardType() == OPEN_AIR_OUTDOOR) {
if (JSON.typeof_(root["targetFirmware"]) == "string") {
@ -1080,12 +1106,16 @@ void Configuration::toConfig(const char *buf) {
}
if (JSON.typeof_(jconfig[jprop_offlineMode]) != "boolean") {
isInvalid = true;
} else {
isInvalid = false;
changed = true;
jconfig[jprop_offlineMode] = jprop_offlineMode_default;
}
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) {
@ -1171,6 +1201,10 @@ bool Configuration::isLedBarModeChanged(void) {
return changed;
}
bool Configuration::isMonitorDisplayCompensatedValues(void) {
return jconfig[jprop_monitorDisplayCompensatedValues];
}
bool Configuration::isDisplayBrightnessChanged(void) {
bool changed = displayBrightnessChanged;
displayBrightnessChanged = false;

View File

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

View File

@ -1,5 +1,6 @@
#include "AgOledDisplay.h"
#include "Libraries/U8g2/src/U8g2lib.h"
#include "Main/utils.h"
/** Cast U8G2 */
#define DISP() ((U8G2_SH1106_128X64_NONAME_F_HW_I2C *)(this->u8g2))
@ -9,37 +10,43 @@
*
* @param hasStatus
*/
void OledDisplay::showTempHum(bool hasStatus) {
char buf[10];
if (value.Temperature > -1001) {
void OledDisplay::showTempHum(bool hasStatus, char *buf, int buf_size) {
/** 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()) {
float tempF = (value.Temperature * 9) / 5 + 32;
if (hasStatus) {
snprintf(buf, sizeof(buf), "%0.1f", tempF);
snprintf(buf, buf_size, "%0.1f", t);
} else {
snprintf(buf, sizeof(buf), "%0.1f°F", tempF);
snprintf(buf, buf_size, "%0.1f°F", t);
}
} else {
if (hasStatus) {
snprintf(buf, sizeof(buf), "%.1f", value.Temperature);
snprintf(buf, buf_size, "%.1f", t);
} else {
snprintf(buf, sizeof(buf), "%.1f°C", value.Temperature);
snprintf(buf, buf_size, "%.1f°C", t);
}
}
} else {
} else { /** Show invalid value */
if (config.isTemperatureUnitInF()) {
snprintf(buf, sizeof(buf), "-°F");
snprintf(buf, buf_size, "-°F");
} else {
snprintf(buf, sizeof(buf), "-°C");
snprintf(buf, buf_size, "-°C");
}
}
DISP()->drawUTF8(1, 10, buf);
/** Show humidty */
if (value.Humidity >= 0) {
snprintf(buf, sizeof(buf), "%d%%", value.Humidity);
/** Show humidity */
if (utils::isValidHumidity(value.Humidity)) {
snprintf(buf, buf_size, "%d%%", value.Humidity);
} else {
snprintf(buf, sizeof(buf), "%-%%");
snprintf(buf, buf_size, "-%%");
}
if (value.Humidity > 99) {
@ -260,7 +267,7 @@ void OledDisplay::showDashboard(const char *status) {
do {
DISP()->setFont(u8g2_font_t0_16_tf);
if ((status == NULL) || (strlen(status) == 0)) {
showTempHum(false);
showTempHum(false, strBuf, sizeof(strBuf));
} else {
String strStatus = "Show status: " + String(status);
logInfo(strStatus);
@ -271,7 +278,7 @@ void OledDisplay::showDashboard(const char *status) {
/** Show WiFi NA*/
if (strcmp(status, "WiFi N/A") == 0) {
DISP()->setFont(u8g2_font_t0_12_tf);
showTempHum(true);
showTempHum(true, strBuf, sizeof(strBuf));
}
}
@ -283,12 +290,8 @@ void OledDisplay::showDashboard(const char *status) {
DISP()->drawUTF8(1, 27, "CO2");
DISP()->setFont(u8g2_font_t0_22b_tf);
if (value.CO2 > 0) {
int val = 9999;
if (value.CO2 < 10000) {
val = value.CO2;
}
sprintf(strBuf, "%d", val);
if (utils::isValidCO2(value.CO2)) {
sprintf(strBuf, "%d", value.CO2);
} else {
sprintf(strBuf, "%s", "-");
}
@ -299,88 +302,115 @@ void OledDisplay::showDashboard(const char *status) {
DISP()->drawStr(1, 61, "ppm");
/** Draw vertical line */
DISP()->drawLine(45, 14, 45, 64);
DISP()->drawLine(82, 14, 82, 64);
DISP()->drawLine(52, 14, 52, 64);
DISP()->drawLine(97, 14, 97, 64);
/** Draw PM2.5 label */
DISP()->setFont(u8g2_font_t0_12_tf);
DISP()->drawStr(48, 27, "PM2.5");
DISP()->drawStr(55, 27, "PM2.5");
/** Draw PM2.5 value */
if (utils::isValidPm(value.pm25_1)) {
int pm25 = value.pm25_1;
/** Compensate PM2.5 value. */
if (config.hasSensorSHT && config.isMonitorDisplayCompensatedValues()) {
pm25 = ag->pms5003.compensate(pm25, value.Humidity);
logInfo("PM2.5 compensate: " + String(pm25));
}
if (config.isPmStandardInUSAQI()) {
sprintf(strBuf, "%d", ag->pms5003.convertPm25ToUsAqi(pm25));
} else {
sprintf(strBuf, "%d", pm25);
}
} else { /** Show invalid value. */
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()) {
if (value.pm25_1 >= 0) {
sprintf(strBuf, "%d", ag->pms5003.convertPm25ToUsAqi(value.pm25_1));
} else {
sprintf(strBuf, "%s", "-");
}
DISP()->drawStr(48, 48, strBuf);
DISP()->setFont(u8g2_font_t0_12_tf);
DISP()->drawUTF8(48, 61, "AQI");
DISP()->drawUTF8(55, 61, "AQI");
} else {
if (value.pm25_1 >= 0) {
sprintf(strBuf, "%d", value.pm25_1);
} else {
sprintf(strBuf, "%s", "-");
}
DISP()->drawStr(48, 48, strBuf);
DISP()->setFont(u8g2_font_t0_12_tf);
DISP()->drawUTF8(48, 61, "ug/m³");
DISP()->drawUTF8(55, 61, "ug/m³");
}
/** Draw tvocIndexlabel */
DISP()->setFont(u8g2_font_t0_12_tf);
DISP()->drawStr(85, 27, "tvoc:");
DISP()->drawStr(100, 27, "VOC:");
/** Draw tvocIndexvalue */
if (value.TVOC >= 0) {
if (utils::isValidVOC(value.TVOC)) {
sprintf(strBuf, "%d", value.TVOC);
} else {
sprintf(strBuf, "%s", "-");
}
DISP()->drawStr(85, 39, strBuf);
DISP()->drawStr(100, 39, strBuf);
/** Draw NOx label */
DISP()->drawStr(85, 53, "NOx:");
if (value.NOx >= 0) {
DISP()->drawStr(100, 53, "NOx:");
if (utils::isValidNOx(value.NOx)) {
sprintf(strBuf, "%d", value.NOx);
} else {
sprintf(strBuf, "%s", "-");
}
DISP()->drawStr(85, 63, strBuf);
DISP()->drawStr(100, 63, strBuf);
} while (DISP()->nextPage());
} else if (ag->isBasic()) {
ag->display.clear();
/** Set CO2 */
snprintf(strBuf, sizeof(strBuf), "CO2:%d", value.CO2);
if (utils::isValidCO2(value.CO2)) {
snprintf(strBuf, sizeof(strBuf), "CO2:%d", value.CO2);
} else {
snprintf(strBuf, sizeof(strBuf), "CO2:-");
}
ag->display.setCursor(0, 0);
ag->display.setText(strBuf);
/** Set PM */
int pm25 = value.pm25_1;
if (config.hasSensorSHT && config.isMonitorDisplayCompensatedValues()) {
pm25 = (int)ag->pms5003.compensate(pm25, value.Humidity);
}
ag->display.setCursor(0, 12);
snprintf(strBuf, sizeof(strBuf), "PM2.5:%d", value.pm25_1);
if (utils::isValidPm(pm25)) {
snprintf(strBuf, sizeof(strBuf), "PM2.5:%d", pm25);
} else {
snprintf(strBuf, sizeof(strBuf), "PM2.5:-");
}
ag->display.setText(strBuf);
/** Set temperature and humidity */
if (value.Temperature <= -1001.0f) {
if (utils::isValidTemperature(value.Temperature)) {
if (config.isTemperatureUnitInF()) {
snprintf(strBuf, sizeof(strBuf), "T:%0.1f F",
utils::degreeC_To_F(value.Temperature));
} else {
snprintf(strBuf, sizeof(strBuf), "T:%0.f1 C", value.Temperature);
}
} else {
if (config.isTemperatureUnitInF()) {
snprintf(strBuf, sizeof(strBuf), "T:-F");
} else {
snprintf(strBuf, sizeof(strBuf), "T:-C");
}
} else {
if (config.isTemperatureUnitInF()) {
float tempF = (value.Temperature * 9) / 5 + 32;
snprintf(strBuf, sizeof(strBuf), "T:%d F", (int)tempF);
} else {
snprintf(strBuf, sizeof(strBuf), "T:%d C", (int)value.Temperature);
}
}
ag->display.setCursor(0, 24);
ag->display.setText(strBuf);
snprintf(strBuf, sizeof(strBuf), "H:%d %%", (int)value.Humidity);
if (utils::isValidHumidity(value.Humidity)) {
snprintf(strBuf, sizeof(strBuf), "H:%d %%", (int)value.Humidity);
} else {
snprintf(strBuf, sizeof(strBuf), "H:- %%");
}
ag->display.setCursor(0, 36);
ag->display.setText(strBuf);
@ -403,7 +433,17 @@ void OledDisplay::setBrightness(int percent) {
DISP()->setContrast((127 * percent) / 100);
}
} 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);
}
}
}
@ -498,7 +538,7 @@ void OledDisplay::showRebooting(void) {
do {
DISP()->setFont(u8g2_font_t0_16_tf);
// setCentralText(20, "Firmware Update");
setCentralText(40, "Reboot...");
setCentralText(40, "Rebooting...");
// setCentralText(60, String("Retry after 24h"));
} while (DISP()->nextPage());
} else if (ag->isBasic()) {

View File

@ -16,7 +16,7 @@ private:
Measurements &value;
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, const char *text);

View File

@ -1,5 +1,6 @@
#include "AgStateMachine.h"
#define LED_TEST_BLINK_DELAY 50 /** ms */
#define LED_FAST_BLINK_DELAY 250 /** ms */
#define LED_SLOW_BLINK_DELAY 1000 /** ms */
#define LED_SHORT_BLINK_DELAY 500 /** ms */
@ -9,9 +10,9 @@
#define RGB_COLOR_R 255, 0, 0 /** Red */
#define RGB_COLOR_G 0, 255, 0 /** Green */
#define RGB_COLOR_Y 255, 255, 0 /** Yellow */
#define RGB_COLOR_O 255, 165, 0 /** Organge */
#define RGB_COLOR_P 160, 32, 240 /** Purple */
#define RGB_COLOR_Y 255, 150, 0 /** Yellow */
#define RGB_COLOR_O 255, 40, 0 /** Orange */
#define RGB_COLOR_P 180, 0, 255 /** Purple */
/**
* @brief Animation LED bar with color
@ -141,6 +142,10 @@ void StateMachine::co2handleLeds(void) {
*/
void StateMachine::pm25handleLeds(void) {
int pm25Value = value.pm25_1;
if (config.isMonitorDisplayCompensatedValues() && config.hasSensorSHT) {
pm25Value = ag->pms5003.compensate(value.pm25_1, value.Humidity);
}
if (pm25Value < 5) {
/** G; 1 */
ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1);
@ -305,18 +310,23 @@ void StateMachine::co2Calibration(void) {
void StateMachine::ledBarTest(void) {
if (config.isLedBarTestRequested()) {
if (config.getCountry() == "TH") {
uint32_t tstart = millis();
logInfo("Start run LED test for 2 min");
while (1) {
ledBarRunTest();
uint32_t ms = (uint32_t)(millis() - tstart);
if (ms >= (60 * 1000 * 2)) {
logInfo("LED test after 2 min finish");
break;
if (ag->isOne()) {
if (config.getCountry() == "TH") {
uint32_t tstart = millis();
logInfo("Start run LED test for 2 min");
while (1) {
ledBarRunTest();
uint32_t ms = (uint32_t)(millis() - tstart);
if (ms >= (60 * 1000 * 2)) {
logInfo("LED test after 2 min finish");
break;
}
}
} else {
ledBarRunTest();
}
} else {
}
else if(ag->isOpenAir()) {
ledBarRunTest();
}
}
@ -325,22 +335,31 @@ void StateMachine::ledBarTest(void) {
void StateMachine::ledBarPowerUpTest(void) { ledBarRunTest(); }
void StateMachine::ledBarRunTest(void) {
disp.setText("LED Test", "running", ".....");
runLedTest('r');
ag->ledBar.show();
delay(1000);
runLedTest('g');
ag->ledBar.show();
delay(1000);
runLedTest('b');
ag->ledBar.show();
delay(1000);
runLedTest('w');
ag->ledBar.show();
delay(1000);
runLedTest('n');
ag->ledBar.show();
delay(1000);
if (ag->isOne()) {
disp.setText("LED Test", "running", ".....");
runLedTest('r');
ag->ledBar.show();
delay(1000);
runLedTest('g');
ag->ledBar.show();
delay(1000);
runLedTest('b');
ag->ledBar.show();
delay(1000);
runLedTest('w');
ag->ledBar.show();
delay(1000);
runLedTest('n');
ag->ledBar.show();
delay(1000);
} else if (ag->isOpenAir()) {
for (int i = 0; i < 100; i++) {
ag->statusLed.setOn();
delay(LED_TEST_BLINK_DELAY);
ag->statusLed.setOff();
delay(LED_TEST_BLINK_DELAY);
}
}
}
void StateMachine::runLedTest(char color) {
@ -480,7 +499,7 @@ void StateMachine::displayHandle(AgStateMachineState state) {
break;
}
case AgStateMachineServerLost: {
disp.showDashboard("Server N/A");
disp.showDashboard("AG Server N/A");
break;
}
case AgStateMachineSensorConfigFailed: {
@ -489,7 +508,7 @@ void StateMachine::displayHandle(AgStateMachineState state) {
if (ms >= 5000) {
addToDashboardTime = millis();
if (addToDashBoardToggle) {
disp.showDashboard("Add to Dashboard");
disp.showDashboard("Add to AG Dashb.");
} else {
disp.showDashboard(ag->deviceId().c_str());
}
@ -573,7 +592,7 @@ void StateMachine::handleLeds(AgStateMachineState state) {
case AgStateMachineWiFiManagerMode: {
/** In WiFi Manager Mode */
/** Turn LED OFF */
/** Turn midle LED Color */
/** Turn middle LED Color */
if (ag->isOne()) {
ag->ledBar.setColor(0, 0, 255, ag->ledBar.getNumberOfLeds() / 2);
} else {
@ -713,7 +732,7 @@ void StateMachine::handleLeds(AgStateMachineState state) {
break;
}
case AgStateMachineSensorConfigFailed: {
/** Server is reachable but there is some conguration issue to be fixed on
/** Server is reachable but there is some configuration issue to be fixed on
* the server side */
if (ag->isOne()) {
ag->ledBar.setColor(139, 24, 248, 0);

View File

@ -1,8 +1,42 @@
#include "AgValue.h"
#include "AgConfigure.h"
#include "AirGradient.h"
#include "Main/utils.h"
#include "Libraries/Arduino_JSON/src/Arduino_JSON.h"
#define json_prop_pmFirmware "firmware"
/**
* @brief Get PMS5003 firmware version string
*
* @param fwCode
* @return String
*/
String Measurements::pms5003FirmwareVersion(int fwCode) {
return pms5003FirmwareVersionBase("PMS5003x", fwCode);
}
/**
* @brief Get PMS5003T firmware version string
*
* @param fwCode
* @return String
*/
String Measurements::pms5003TFirmwareVersion(int fwCode) {
return pms5003FirmwareVersionBase("PMS5003x", fwCode);
}
/**
* @brief Get firmware version string
*
* @param prefix Prefix firmware string
* @param fwCode Version code
* @return string
*/
String Measurements::pms5003FirmwareVersionBase(String prefix, int fwCode) {
return prefix + String("-") + String(fwCode);
}
String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
void *_ag, void *_config) {
AirGradient *ag = (AirGradient *)_ag;
@ -13,36 +47,40 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
if (localServer) {
root["serialno"] = ag->deviceId();
}
if (config->hasSensorS8) {
if (this->CO2 >= 0) {
root["rco2"] = this->CO2;
}
if (config->hasSensorS8 && utils::isValidCO2(this->CO2)) {
root["rco2"] = this->CO2;
}
if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_3() || ag->isBasic()) {
if (config->hasSensorPMS1) {
if (this->pm01_1 >= 0) {
if (utils::isValidPm(this->pm01_1)) {
root["pm01"] = this->pm01_1;
}
if (this->pm25_1 >= 0) {
if (utils::isValidPm(this->pm25_1)) {
root["pm02"] = this->pm25_1;
}
if (this->pm10_1 >= 0) {
if (utils::isValidPm(this->pm10_1)) {
root["pm10"] = this->pm10_1;
}
if (this->pm03PCount_1 >= 0) {
if (utils::isValidPm03Count(this->pm03PCount_1)) {
root["pm003Count"] = this->pm03PCount_1;
}
if (!localServer) {
root[json_prop_pmFirmware] =
this->pms5003FirmwareVersion(ag->pms5003.getFirmwareVersion());
}
}
if (config->hasSensorSHT) {
if (this->Temperature > -1001) {
if (utils::isValidTemperature(this->Temperature)) {
root["atmp"] = ag->round2(this->Temperature);
if (localServer) {
root["atmpCompensated"] = ag->round2(this->Temperature);
}
}
if (this->Humidity >= 0) {
if (utils::isValidHumidity(this->Humidity)) {
root["rhum"] = this->Humidity;
if (localServer) {
root["rhumCompensated"] = this->Humidity;
@ -50,109 +88,297 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
}
}
} else {
if (config->hasSensorPMS1 && config->hasSensorPMS2) {
root["pm01"] = ag->round2((this->pm01_1 + this->pm01_2) / 2.0);
root["pm02"] = ag->round2((this->pm25_1 + this->pm25_2) / 2.0);
root["pm10"] = ag->round2((this->pm10_1 + this->pm10_2) / 2.0);
root["pm003Count"] =
ag->round2((this->pm03PCount_1 + this->pm03PCount_2) / 2.0);
root["atmp"] = ag->round2((this->temp_1 + this->temp_2) / 2.0f);
root["rhum"] = ag->round2((this->hum_1 + this->hum_2) / 2.0f);
if (localServer) {
root["atmpCompensated"] =
ag->round2(ag->pms5003t_2.temperatureCompensated(
(this->temp_1 + this->temp_2) / 2.0f));
root["rhumCompensated"] = (int)ag->pms5003t_2.humidityCompensated(
(this->hum_1 + this->hum_2) / 2.0f);
if (config->hasSensorSHT && config->hasSensorPMS1) {
int pm25 = ag->pms5003.compensate(this->pm25_1, this->Humidity);
if (pm25 >= 0) {
root["pm02Compensated"] = pm25;
}
}
if (fwMode == FW_MODE_O_1PS || fwMode == FW_MODE_O_1PST) {
if (config->hasSensorPMS1) {
root["pm01"] = this->pm01_1;
root["pm02"] = this->pm25_1;
root["pm10"] = this->pm10_1;
root["pm003Count"] = this->pm03PCount_1;
root["atmp"] = ag->round2(this->temp_1);
root["rhum"] = this->hum_1;
} else {
if (config->hasSensorPMS1 && config->hasSensorPMS2) {
if (utils::isValidPm(this->pm01_1) && utils::isValidPm(this->pm01_2)) {
root["pm01"] = ag->round2((this->pm01_1 + this->pm01_2) / 2.0f);
}
if (utils::isValidPm(this->pm25_1) && utils::isValidPm(this->pm25_2)) {
root["pm02"] = ag->round2((this->pm25_1 + this->pm25_2) / 2.0f);
}
if (utils::isValidPm(this->pm10_1) && utils::isValidPm(this->pm10_2)) {
root["pm10"] = ag->round2((this->pm10_1 + this->pm10_2) / 2.0f);
}
if (utils::isValidPm(this->pm03PCount_1) && utils::isValidPm(this->pm03PCount_2)) {
root["pm003Count"] = ag->round2((this->pm03PCount_1 + this->pm03PCount_2) / 2.0f);
}
float val;
if (utils::isValidTemperature(this->temp_1) && utils::isValidTemperature(this->temp_1)) {
root["atmp"] = ag->round2((this->temp_1 + this->temp_2) / 2.0f);
if (localServer) {
root["atmpCompensated"] =
ag->round2(ag->pms5003t_1.temperatureCompensated(this->temp_1));
root["rhumCompensated"] =
(int)ag->pms5003t_1.humidityCompensated(this->hum_1);
val = ag->pms5003t_2.compensateTemp((this->temp_1 + this->temp_2) / 2.0f);
if (utils::isValidTemperature(val)) {
root["atmpCompensated"] = ag->round2(val);
}
}
}
if (utils::isValidHumidity(this->hum_1) && utils::isValidHumidity(this->hum_1)) {
root["rhum"] = ag->round2((this->hum_1 + this->hum_2) / 2.0f);
if (localServer) {
val = ag->pms5003t_2.compensateHum((this->hum_1 + this->hum_2) / 2.0f);
if (utils::isValidHumidity(val)) {
root["rhumCompensated"] = (int)val;
}
}
}
int pm25 = (ag->pms5003t_1.compensate(this->pm25_1, this->hum_1) +
ag->pms5003t_2.compensate(this->pm25_2, this->hum_2)) /
2;
root["pm02Compensated"] = pm25;
}
if (fwMode == FW_MODE_O_1PS || fwMode == FW_MODE_O_1PST) {
float val;
if (config->hasSensorPMS1) {
if (utils::isValidPm(this->pm01_1)) {
root["pm01"] = this->pm01_1;
}
if (utils::isValidPm(this->pm25_1)) {
root["pm02"] = this->pm25_1;
}
if (utils::isValidPm(this->pm10_1)) {
root["pm10"] = this->pm10_1;
}
if (utils::isValidPm03Count(this->pm03PCount_1)) {
root["pm003Count"] = this->pm03PCount_1;
}
if (utils::isValidTemperature(this->temp_1)) {
root["atmp"] = ag->round2(this->temp_1);
if (localServer) {
val = ag->pms5003t_1.compensateTemp(this->temp_1);
if (utils::isValidTemperature(val)) {
root["atmpCompensated"] = ag->round2(val);
}
}
}
if (utils::isValidHumidity(this->hum_1)) {
root["rhum"] = this->hum_1;
if (localServer) {
val = ag->pms5003t_1.compensateHum(this->hum_1);
if (utils::isValidHumidity(val)) {
root["rhumCompensated"] = (int)val;
}
}
}
root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1);
if (!localServer) {
root[json_prop_pmFirmware] =
pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion());
}
}
if (config->hasSensorPMS2) {
root["pm01"] = this->pm01_2;
root["pm02"] = this->pm25_2;
root["pm10"] = this->pm10_2;
root["pm003Count"] = this->pm03PCount_2;
root["atmp"] = ag->round2(this->temp_2);
root["rhum"] = this->hum_2;
if (localServer) {
root["atmpCompensated"] =
ag->round2(ag->pms5003t_2.temperatureCompensated(this->temp_2));
root["rhumCompensated"] =
(int)ag->pms5003t_2.humidityCompensated(this->hum_2);
if(utils::isValidPm(this->pm01_2)) {
root["pm01"] = this->pm01_2;
}
if(utils::isValidPm(this->pm25_2)) {
root["pm02"] = this->pm25_2;
}
if(utils::isValidPm(this->pm10_2)) {
root["pm10"] = this->pm10_2;
}
if(utils::isValidPm03Count(this->pm03PCount_2)) {
root["pm003Count"] = this->pm03PCount_2;
}
float val;
if (utils::isValidTemperature(this->temp_2)) {
root["atmp"] = ag->round2(this->temp_2);
if (localServer) {
val = ag->pms5003t_2.compensateTemp(this->temp_2);
if (utils::isValidTemperature(val)) {
root["atmpCompensated"] = ag->round2(val);
}
}
}
if(utils::isValidHumidity(this->hum_2)) {
root["rhum"] = this->hum_2;
if (localServer) {
val = ag->pms5003t_2.compensateHum(this->hum_2);
if (utils::isValidHumidity(val)) {
root["rhumCompensated"] = (int)val;
}
}
}
root["pm02Compensated"] = ag->pms5003t_2.compensate(this->pm25_2, this->hum_2);
if(!localServer) {
root[json_prop_pmFirmware] =
pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion());
}
}
} else {
if (fwMode == FW_MODE_O_1P) {
float val;
if (config->hasSensorPMS1) {
root["pm01"] = this->pm01_1;
root["pm02"] = this->pm25_1;
root["pm10"] = this->pm10_1;
root["pm003Count"] = this->pm03PCount_1;
root["atmp"] = ag->round2(this->temp_1);
root["rhum"] = this->hum_1;
if (localServer) {
root["atmpCompensated"] =
ag->round2(ag->pms5003t_1.temperatureCompensated(this->temp_1));
root["rhumCompensated"] =
(int)ag->pms5003t_1.humidityCompensated(this->hum_1);
if (utils::isValidPm(this->pm01_1)) {
root["pm01"] = this->pm01_1;
}
if (utils::isValidPm(this->pm25_1)) {
root["pm02"] = this->pm25_1;
}
if (utils::isValidPm(this->pm10_1)) {
root["pm10"] = this->pm10_1;
}
if (utils::isValidPm03Count(this->pm03PCount_1)) {
root["pm003Count"] = this->pm03PCount_1;
}
if (utils::isValidTemperature(this->temp_1)) {
root["atmp"] = ag->round2(this->temp_1);
if (localServer) {
val = ag->pms5003t_1.compensateTemp(this->temp_1);
if (utils::isValidTemperature(val)) {
root["atmpCompensated"] = ag->round2(val);
}
}
}
if (utils::isValidHumidity(this->hum_1)) {
root["rhum"] = this->hum_1;
if(localServer) {
val = ag->pms5003t_1.compensateHum(this->hum_1);
if(utils::isValidHumidity(val)) {
root["rhumCompensated"] = (int)val;
}
}
}
root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1);
if(!localServer) {
root[json_prop_pmFirmware] =
pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion());
}
} else if (config->hasSensorPMS2) {
root["pm01"] = this->pm01_2;
root["pm02"] = this->pm25_2;
root["pm10"] = this->pm10_2;
root["pm003Count"] = this->pm03PCount_2;
root["atmp"] = ag->round2(this->temp_2);
root["rhum"] = this->hum_2;
if (localServer) {
root["atmpCompensated"] =
ag->round2(ag->pms5003t_1.temperatureCompensated(this->temp_2));
root["rhumCompensated"] =
(int)ag->pms5003t_1.humidityCompensated(this->hum_2);
if(utils::isValidPm(this->pm01_2)) {
root["pm01"] = this->pm01_2;
}
if(utils::isValidPm(this->pm25_2)) {
root["pm02"] = this->pm25_2;
}
if(utils::isValidPm(this->pm10_2)) {
root["pm10"] = this->pm10_2;
}
if(utils::isValidPm03Count(this->pm03PCount_2)) {
root["pm003Count"] = this->pm03PCount_2;
}
if (utils::isValidTemperature(this->temp_2)) {
root["atmp"] = ag->round2(this->temp_2);
if (localServer) {
val = ag->pms5003t_1.compensateTemp(this->temp_2);
if (utils::isValidTemperature(val)) {
root["atmpCompensated"] = ag->round2(val);
}
}
}
if (utils::isValidHumidity(this->hum_2)) {
root["rhum"] = this->hum_2;
if(localServer) {
val = ag->pms5003t_1.compensateHum(this->hum_2);
if(utils::isValidHumidity(val)) {
root["rhumCompensated"] = (int)val;
}
}
}
root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1);
if(!localServer) {
root[json_prop_pmFirmware] =
pms5003TFirmwareVersion(ag->pms5003t_2.getFirmwareVersion());
}
}
} else {
float val;
if (config->hasSensorPMS1) {
root["channels"]["1"]["pm01"] = this->pm01_1;
root["channels"]["1"]["pm02"] = this->pm25_1;
root["channels"]["1"]["pm10"] = this->pm10_1;
root["channels"]["1"]["pm003Count"] = this->pm03PCount_1;
root["channels"]["1"]["atmp"] = ag->round2(this->temp_1);
root["channels"]["1"]["rhum"] = this->hum_1;
if (localServer) {
root["channels"]["1"]["atmpCompensated"] =
ag->round2(ag->pms5003t_1.temperatureCompensated(this->temp_1));
root["channels"]["1"]["rhumCompensated"] =
(int)ag->pms5003t_1.humidityCompensated(this->hum_1);
if(utils::isValidPm(this->pm01_1)) {
root["channels"]["1"]["pm01"] = this->pm01_1;
}
if(utils::isValidPm(this->pm25_1)) {
root["channels"]["1"]["pm02"] = this->pm25_1;
}
if(utils::isValidPm(this->pm10_1)) {
root["channels"]["1"]["pm10"] = this->pm10_1;
}
if (utils::isValidPm03Count(this->pm03PCount_1)) {
root["channels"]["1"]["pm003Count"] = this->pm03PCount_1;
}
if(utils::isValidTemperature(this->temp_1)) {
root["channels"]["1"]["atmp"] = ag->round2(this->temp_1);
if (localServer) {
val = ag->pms5003t_1.compensateTemp(this->temp_1);
if (utils::isValidTemperature(val)) {
root["channels"]["1"]["atmpCompensated"] = ag->round2(val);
}
}
}
if (utils::isValidHumidity(this->hum_1)) {
root["channels"]["1"]["rhum"] = this->hum_1;
if (localServer) {
val = ag->pms5003t_1.compensateHum(this->hum_1);
if (utils::isValidHumidity(val)) {
root["channels"]["1"]["rhumCompensated"] = (int)val;
}
}
}
root["channels"]["1"]["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1);
// PMS5003T version
if(!localServer) {
root["channels"]["1"][json_prop_pmFirmware] =
pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion());
}
}
if (config->hasSensorPMS2) {
root["channels"]["2"]["pm01"] = this->pm01_2;
root["channels"]["2"]["pm02"] = this->pm25_2;
root["channels"]["2"]["pm10"] = this->pm10_2;
root["channels"]["2"]["pm003Count"] = this->pm03PCount_2;
root["channels"]["2"]["atmp"] = ag->round2(this->temp_2);
root["channels"]["2"]["rhum"] = this->hum_2;
if (localServer) {
root["channels"]["2"]["atmpCompensated"] =
ag->round2(ag->pms5003t_1.temperatureCompensated(this->temp_2));
root["channels"]["2"]["rhumCompensated"] =
(int)ag->pms5003t_1.humidityCompensated(this->hum_2);
float val;
if (utils::isValidPm(this->pm01_2)) {
root["channels"]["2"]["pm01"] = this->pm01_2;
}
if (utils::isValidPm(this->pm25_2)) {
root["channels"]["2"]["pm02"] = this->pm25_2;
}
if (utils::isValidPm(this->pm10_2)) {
root["channels"]["2"]["pm10"] = this->pm10_2;
}
if (utils::isValidPm03Count(this->pm03PCount_2)) {
root["channels"]["2"]["pm003Count"] = this->pm03PCount_2;
}
if (utils::isValidTemperature(this->temp_2)) {
root["channels"]["2"]["atmp"] = ag->round2(this->temp_2);
if (localServer) {
val = ag->pms5003t_1.compensateTemp(this->temp_2);
if (utils::isValidTemperature(val)) {
root["channels"]["2"]["atmpCompensated"] = ag->round2(val);
}
}
}
if (utils::isValidHumidity(this->hum_2)) {
root["channels"]["2"]["rhum"] = this->hum_2;
if (localServer) {
val = ag->pms5003t_1.compensateHum(this->hum_2);
if (utils::isValidHumidity(val)) {
root["channels"]["2"]["rhumCompensated"] = (int)val;
}
}
}
root["channels"]["2"]["pm02Compensated"] = ag->pms5003t_2.compensate(this->pm25_2, this->hum_2);
// PMS5003T version
if(!localServer) {
root["channels"]["2"][json_prop_pmFirmware] =
pms5003TFirmwareVersion(ag->pms5003t_2.getFirmwareVersion());
}
}
}
@ -160,16 +386,16 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
}
if (config->hasSensorSGP) {
if (this->TVOC >= 0) {
if (utils::isValidVOC(this->TVOC)) {
root["tvocIndex"] = this->TVOC;
}
if (this->TVOCRaw >= 0) {
if (utils::isValidVOC(this->TVOCRaw)) {
root["tvocRaw"] = this->TVOCRaw;
}
if (this->NOx >= 0) {
if (utils::isValidNOx(this->NOx)) {
root["noxIndex"] = this->NOx;
}
if (this->NOxRaw >= 0) {
if (utils::isValidNOx(this->NOxRaw)) {
root["noxRaw"] = this->NOxRaw;
}
}

View File

@ -6,6 +6,9 @@
class Measurements {
private:
String pms5003FirmwareVersion(int fwCode);
String pms5003TFirmwareVersion(int fwCode);
String pms5003FirmwareVersionBase(String prefix, int fwCode);
public:
Measurements() {
pm25_1 = -1;

View File

@ -41,6 +41,28 @@ bool WifiConnector::connect(void) {
}
}
WiFi.begin();
String wifiSSID = WIFI()->getWiFiSSID(true);
if (wifiSSID.isEmpty()) {
logInfo("Connected WiFi is empty, connect to default wifi \"" +
String(this->defaultSsid) + String("\""));
/** Set wifi connect */
WiFi.begin(this->defaultSsid, this->defaultPassword);
/** Wait for wifi connect to AP */
int count = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
count++;
if (count >= 15) {
logError("Try connect to default wifi \"" + String(this->defaultSsid) +
String("\" failed"));
break;
}
}
}
WIFI()->setConfigPortalBlocking(false);
WIFI()->setConnectTimeout(15);
WIFI()->setTimeout(WIFI_CONNECT_COUNTDOWN_MAX);
@ -50,7 +72,7 @@ bool WifiConnector::connect(void) {
WIFI()->setSaveParamsCallback([this]() { _wifiSaveParamCallback(); });
WIFI()->setConfigPortalTimeoutCallback([this]() {_wifiTimeoutCallback();});
if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_3() || ag->isBasic()) {
disp.setText("Connect to", "WiFi", "...");
disp.setText("Connecting to", "WiFi", "...");
} else {
logInfo("Connecting to WiFi...");
}
@ -383,3 +405,11 @@ bool WifiConnector::hasConfigurated(void) {
* @return false
*/
bool WifiConnector::isConfigurePorttalTimeout(void) { return connectorTimeout; }
/**
* @brief Set wifi connect to default WiFi
*
*/
void WifiConnector::setDefault(void) {
WiFi.begin("airgradient", "cleanair");
}

View File

@ -46,6 +46,10 @@ public:
String localIpStr(void);
bool hasConfigurated(void);
bool isConfigurePorttalTimeout(void);
const char* defaultSsid = "airgradient";
const char* defaultPassword = "cleanair";
void setDefault(void);
};
#endif /** _AG_WIFI_CONNECTOR_H_ */

View File

@ -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) {
@ -58,6 +65,10 @@ bool AirGradient::isOne(void) {
return boardType == BoardType::ONE_INDOOR;
}
bool AirGradient::isOpenAir(void) {
return boardType == BoardType::OPEN_AIR_OUTDOOR;
}
bool AirGradient::isPro4_2(void) {
return boardType == BoardType::DIY_PRO_INDOOR_V4_2;
}

View File

@ -12,9 +12,10 @@
#include "S8/S8.h"
#include "Sgp41/Sgp41.h"
#include "Sht/Sht.h"
#include "Main/utils.h"
#ifndef GIT_VERSION
#define GIT_VERSION "snapshot"
#define GIT_VERSION "3.1.9-snap"
#endif
/**
@ -134,6 +135,14 @@ public:
*/
bool isOne(void);
/**
* @brief Check that Airgradient object is OPEN_AIR
*
* @return true
* @return false
*/
bool isOpenAir(void);
/**
* @brief Check that Airgradient object is DIY_PRO 4.2 indoor
*

94
src/Main/utils.cpp Normal file
View File

@ -0,0 +1,94 @@
#include "utils.h"
#define VALID_TEMPERATURE_MAX (125)
#define VALID_TEMPERATURE_MIN (-40)
#define INVALID_TEMPERATURE (-1000)
#define VALID_HUMIDITY_MAX (100)
#define VALID_HUMIDITY_MIN (0)
#define INVALID_HUMIDITY (-1)
#define VALID_PMS_MAX (1000)
#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)
utils::utils(/* args */) {}
utils::~utils() {}
bool utils::isValidTemperature(float value) {
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)) {
return true;
}
return false;
}
bool utils::isValidCO2(int16_t value) {
if ((value >= VALID_CO2_MIN) && (value <= VALID_CO2_MAX)) {
return true;
}
return false;
}
bool utils::isValidPm(int value) {
if ((value >= VALID_PMS_MIN) && (value <= VALID_PMS_MAX)) {
return true;
}
return false;
}
bool utils::isValidPm03Count(int value) {
if (value >= VALID_PMS03COUNT_MIN) {
return true;
}
return false;
}
bool utils::isValidNOx(int value) {
if (value >= VALID_NOX_MIN) {
return true;
}
return false;
}
bool utils::isValidVOC(int value) {
if (value >= VALID_VOC_MIN) {
return true;
}
return false;
}
float utils::getInvalidTemperature(void) { return INVALID_TEMPERATURE; }
float utils::getInvalidHumidity(void) { return INVALID_HUMIDITY; }
int utils::getInvalidCO2(void) { return INVALID_CO2; }
int utils::getInvalidPmValue(void) { return INVALID_PMS; }
int utils::getInvalidNOx(void) { return INVALID_NOX; }
int utils::getInvalidVOC(void) { return INVALID_VOC; }
float utils::degreeC_To_F(float t) {
/** (t * 9)/5 + 32 */
return t * 1.8f + 32.0f;
}

31
src/Main/utils.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef _UTILS_H_
#define _UTILS_H_
#include <Arduino.h>
class utils
{
private:
/* data */
public:
utils(/* args */);
~utils();
static bool isValidTemperature(float value);
static bool isValidHumidity(float value);
static bool isValidCO2(int16_t value);
static bool isValidPm(int value);
static bool isValidPm03Count(int value);
static bool isValidNOx(int value);
static bool isValidVOC(int value);
static float getInvalidTemperature(void);
static float getInvalidHumidity(void);
static int getInvalidCO2(void);
static int getInvalidPmValue(void);
static int getInvalidNOx(void);
static int getInvalidVOC(void);
static float degreeC_To_F(float t);
};
#endif /** _UTILS_H_ */

View File

@ -2,248 +2,293 @@
#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
* @return true Sucecss
* @return false Failure
*/
bool PMSBase::begin(Stream *stream) {
this->stream = stream;
Serial.printf("initializing PM sensor\n");
failed = true;
lastRead = 0; // To read buffer on handle without wait after 1.5sec
failCount = 0;
_connected = false;
this->stream->flush();
// empty first
int bytesCleared = 0;
while (stream->read() != -1) {
bytesCleared++;
}
Serial.printf("cleared %d byte(s)\n", bytesCleared);
// explicitly put the sensor into active mode, this seems to be be needed for the Cubic PM2009X
Serial.printf("setting active mode\n");
uint8_t activeModeCommand[] = { 0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71 };
size_t bytesWritten = stream->write(activeModeCommand, sizeof(activeModeCommand));
Serial.printf("%d byte(s) written\n", bytesWritten);
// Run and check sensor data for 4sec
while (1) {
handle();
if (failed == false) {
return true;
unsigned long lastInit = millis();
while (true) {
readPackage(stream);
if (_connected) {
break;
}
delay(1);
uint32_t ms = (uint32_t)(millis() - lastRead);
unsigned long ms = (unsigned long)(millis() - lastInit);
if (ms >= 4000) {
break;
}
}
return false;
return _connected;
}
/**
* @brief Check and read sensor data then update variable.
* Check result from method @isFailed before get value
* @brief Read PMS package send to device each 1sec
*
* @param serial
*/
void PMSBase::handle() {
uint32_t ms;
if (lastRead == 0) {
lastRead = millis();
if (lastRead == 0) {
lastRead = 1;
void PMSBase::readPackage(Stream *serial) {
/** If readPackage has process as period larger than READ_PACKAGE_TIMEOUT,
* should be clear the lastPackage and readBufferIndex */
if (lastReadPackage) {
unsigned long ms = (unsigned long)(millis() - lastReadPackage);
if (ms >= READ_PACKGE_TIMEOUT) {
/** Clear buffer */
readBufferIndex = 0;
/** Disable check read package timeout */
lastPackage = 0;
Serial.println("Last process timeout, clear buffer and last handle package");
}
lastReadPackage = millis();
if (!lastReadPackage) {
lastReadPackage = 1;
}
} else {
ms = (uint32_t)(millis() - lastRead);
/**
* The PMS in Active mode sends an update data every 1 second. If we read
* exactly every 1 sec then we may or may not get an update (depending on
* timing tolerances). Hence we read every 2.5 seconds and expect 2 ..3
* updates,
*/
if (ms < 2500) {
return;
lastReadPackage = millis();
if (!lastReadPackage) {
lastReadPackage = 1;
}
}
bool result = false;
char buf[32];
int bufIndex;
int step = 0;
int len = 0;
int bcount = 0;
while (stream->available()) {
char value = stream->read();
switch (step) {
case 0: {
/** Count to call delay() to release the while loop MCU resource for avoid the
* watchdog time reset */
uint8_t delayCount = 0;
while (serial->available()) {
/** Get value */
uint8_t value = (uint8_t)serial->read();
/** Process receiving package... */
switch (readBufferIndex) {
case 0: /** Start byte 1 */
if (value == 0x42) {
step = 1;
bufIndex = 0;
buf[bufIndex++] = value;
readBuffer[readBufferIndex++] = value;
}
break;
}
case 1: {
case 1: /** Start byte 2 */
if (value == 0x4d) {
step = 2;
buf[bufIndex++] = value;
// Serial.println("Got 0x4d");
readBuffer[readBufferIndex++] = value;
} else {
step = 0;
readBufferIndex = 0;
}
break;
}
case 2: {
buf[bufIndex++] = value;
if (bufIndex >= 4) {
len = toValue(&buf[2]);
if (len != 28) {
// Serial.printf("Got good bad len %d\r\n", len);
len += 4;
step = 3;
} else {
// Serial.println("Got good len");
step = 4;
case 2: /** Frame length */
if (value == 0x00) {
readBuffer[readBufferIndex++] = value;
} else {
readBufferIndex = 0;
}
break;
case 3: /** Frame length */
if (value == 0x1C) {
readBuffer[readBufferIndex++] = value;
} else {
readBufferIndex = 0;
}
break;
default: /** Data */
{
readBuffer[readBufferIndex++] = value;
/** Check that received full bufer */
if (readBufferIndex >= sizeof(readBuffer)) {
/** validata package */
if (validate(readBuffer)) {
_connected = true; /** Set connected status */
/** Parse data */
parse(readBuffer);
/** Set last received package */
lastPackage = millis();
if (lastPackage == 0) {
lastPackage = 1;
}
}
/** Clear buffer index */
readBufferIndex = 0;
}
break;
}
case 3: {
bufIndex++;
if (bufIndex >= len) {
step = 0;
// Serial.println("Bad lengh read all buffer");
}
break;
}
case 4: {
buf[bufIndex++] = value;
if (bufIndex >= 32) {
result |= validate(buf);
step = 0;
// Serial.println("Got data");
}
break;
}
default:
break;
}
// Reduce core panic: delay 1 ms each 32bytes data
bcount++;
if ((bcount % 32) == 0) {
/** Avoid task watchdog timer reset... */
delayCount++;
if (delayCount >= 32) {
delayCount = 0;
delay(1);
}
}
if (result) {
lastRead = millis();
if (lastRead == 0) {
lastRead = 1;
}
failed = false;
} else {
if (ms > 5000) {
failed = true;
/** Check that sensor removed */
if (lastPackage) {
unsigned long ms = (unsigned long)(millis() - lastPackage);
if (ms >= READ_PACKGE_TIMEOUT) {
lastPackage = 0;
_connected = false;
}
}
}
/**
* @brief Check that PMS send is failed or disconnected
* @brief Increate number of fail
*
* @return true Failed
* @return false No problem
*/
bool PMSBase::isFailed(void) { return failed; }
void PMSBase::updateFailCount(void) {
if (failCount < failCountMax) {
failCount++;
}
}
void PMSBase::resetFailCount(void) { failCount = 0; }
/**
* @brief Get number of fail
*
* @return int
*/
int PMSBase::getFailCount(void) { return failCount; }
int PMSBase::getFailCountMax(void) { return failCountMax; }
/**
* @brief Read PMS 0.1 ug/m3 with CF = 1 PM estimates
*
* @return uint16_t
*/
uint16_t PMSBase::getRaw0_1(void) { return toValue(&package[4]); }
uint16_t PMSBase::getRaw0_1(void) { return pms_raw0_1; }
/**
* @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 pms_raw2_5; }
/**
* @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 pms_raw10; }
/**
* @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 pms_pm0_1; }
/**
* @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 pms_pm2_5; }
/**
* @brief Read PMS 10 ug/m3
*
* @return uint16_t
*/
uint16_t PMSBase::getPM10(void) { return toValue(&package[14]); }
uint16_t PMSBase::getPM10(void) { return pms_pm10; }
/**
* @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 pms_count0_3; }
/**
* @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 pms_count0_5; }
/**
* @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 pms_count1_0; }
/**
* @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 pms_count2_5; }
bool PMSBase::connected(void) { return _connected; }
/**
* @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 pms_count5_0; }
/**
* @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 pms_count10; }
/**
* @brief Get temperature (only PMS5003T)
*
* @return uint16_t
*/
uint16_t PMSBase::getTemp(void) { return toValue(&package[24]); }
int16_t PMSBase::getTemp(void) { return pms_temp; }
/**
* @brief Get humidity (only PMS5003T)
*
* @return uint16_t
*/
uint16_t PMSBase::getHum(void) { return toValue(&package[26]); }
uint16_t PMSBase::getHum(void) { return pms_hum; }
/**
* @brief Get firmware version code
*
* @return uint8_t
*/
uint8_t PMSBase::getFirmwareVersion(void) { return pms_firmwareVersion; }
/**
* @brief Ge PMS5003 error code
*
* @return uint8_t
*/
uint8_t PMSBase::getErrorCode(void) { return pms_errorCode; }
/**
* @brief Convert PMS2.5 to US AQI unit
@ -270,13 +315,61 @@ int PMSBase::pm25ToAQI(int pm02) {
return 500;
}
/**
* @brief Correction PM2.5
*
* Formula: https://www.airgradient.com/documentation/correction-algorithms/
*
* @param pm25 Raw PM2.5 value
* @param humidity Humidity value (%)
* @return int
*/
int PMSBase::compensate(int pm25, float humidity) {
float value;
float fpm25 = pm25;
if (humidity < 0) {
humidity = 0;
}
if (humidity > 100) {
humidity = 100.0f;
}
if(pm25 < 30) { /** pm2.5 < 30 */
value = (fpm25 * 0.524f) - (humidity * 0.0862f) + 5.75f;
} else if(pm25 < 50) { /** 30 <= pm2.5 < 50 */
value = (0.786f * (fpm25 * 0.05f - 1.5f) + 0.524f * (1.0f - (fpm25 * 0.05f - 1.5f))) * fpm25 - (0.0862f * humidity) + 5.75f;
} else if(pm25 < 210) { /** 50 <= pm2.5 < 210 */
value = (0.786f * fpm25) - (0.0862f * humidity) + 5.75f;
} else if(pm25 < 260) { /** 210 <= pm2.5 < 260 */
value = (0.69f * (fpm25 * 0.02f - 4.2f) + 0.786f * (1.0f - (fpm25 * 0.02f - 4.2f))) * fpm25 - (0.0862f * humidity * (1.0f - (fpm25 * 0.02f - 4.2f))) + (2.966f * (fpm25 * 0.02f - 4.2f)) + (5.75f * (1.0f - (fpm25 * 0.02f - 4.2f))) + (8.84f * (1.e-4) * fpm25 * fpm25 * (fpm25 * 0.02f - 4.2f));
} else { /** 260 <= pm2.5 */
value = 2.966f + (0.69f * fpm25) + (8.84f * (1.e-4) * fpm25 * fpm25);
}
if (value < 0) {
value = 0;
}
return (int)value;
}
/**
* @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(const uint8_t *buf) {
int16_t value = buf[0];
value = (value << 8) | buf[1];
return value;
}
uint16_t PMSBase::toU16(const uint8_t *buf) {
uint16_t value = buf[0];
value = (value << 8) | buf[1];
return value;
}
/**
* @brief Validate package data
@ -285,16 +378,32 @@ uint16_t PMSBase::toValue(char *buf) { return (buf[0] << 8) | buf[1]; }
* @return true Success
* @return false Failed
*/
bool PMSBase::validate(char *buf) {
bool PMSBase::validate(const uint8_t *buf) {
uint16_t sum = 0;
for (int i = 0; i < 30; i++) {
sum += buf[i];
}
if (sum == toValue(&buf[30])) {
for (int i = 0; i < 32; i++) {
package[i] = buf[i];
}
if (sum == toU16(&buf[30])) {
return true;
}
return false;
}
void PMSBase::parse(const uint8_t *buf) {
pms_raw0_1 = toU16(&buf[4]);
pms_raw2_5 = toU16(&buf[6]);
pms_raw10 = toU16(&buf[8]);
pms_pm0_1 = toU16(&buf[10]);
pms_pm2_5 = toU16(&buf[12]);
pms_pm10 = toU16(&buf[14]);
pms_count0_3 = toU16(&buf[16]);
pms_count0_5 = toU16(&buf[18]);
pms_count1_0 = toU16(&buf[20]);
pms_count2_5 = toU16(&buf[22]);
pms_count5_0 = toU16(&buf[24]);
pms_count10 = toU16(&buf[26]);
pms_temp = toU16(&buf[24]);
pms_hum = toU16(&buf[26]);
pms_firmwareVersion = buf[28];
pms_errorCode = buf[29];
}

View File

@ -3,11 +3,19 @@
#include <Arduino.h>
#define PMS_FAIL_COUNT_SET_INVALID 3
/**
* Known to work with these sensors: Plantower PMS5003, Plantower PMS5003, Cubic PM2009X
*/
class PMSBase {
public:
bool begin(Stream *stream);
void handle();
bool isFailed(void);
void readPackage(Stream *stream);
void updateFailCount(void);
void resetFailCount(void);
int getFailCount(void);
int getFailCountMax(void);
uint16_t getRaw0_1(void);
uint16_t getRaw2_5(void);
uint16_t getRaw10(void);
@ -18,26 +26,64 @@ public:
uint16_t getCount0_5(void);
uint16_t getCount1_0(void);
uint16_t getCount2_5(void);
bool connected(void);
/** For PMS5003 */
uint16_t getCount5_0(void);
uint16_t getCount10(void);
/** For PMS5003T*/
uint16_t getTemp(void);
int16_t getTemp(void);
uint16_t getHum(void);
uint8_t getFirmwareVersion(void);
uint8_t getErrorCode(void);
int pm25ToAQI(int pm02);
int compensate(int pm25, float humidity);
private:
Stream *stream;
char package[32];
int packageIndex;
bool failed = false;
uint32_t lastRead;
static const uint8_t package_size = 32;
uint16_t toValue(char *buf);
bool validate(char *buf);
/** In normal package interval is 200-800ms, In case small changed on sensor
* it's will interval reach to 2.3sec
*/
const uint16_t READ_PACKGE_TIMEOUT = 3000; /** ms */
const int failCountMax = 10;
int failCount = 0;
uint8_t readBuffer[package_size];
uint8_t readBufferIndex = 0;
/**
* Save last time received package success. 0 to disable check package
* timeout.
*/
unsigned long lastPackage = 0;
bool _connected;
unsigned long lastReadPackage = 0;
uint16_t pms_raw0_1;
uint16_t pms_raw2_5;
uint16_t pms_raw10;
uint16_t pms_pm0_1;
uint16_t pms_pm2_5;
uint16_t pms_pm10;
uint16_t pms_count0_3;
uint16_t pms_count0_5;
uint16_t pms_count1_0;
uint16_t pms_count2_5;
uint16_t pms_count5_0;
uint16_t pms_count10;
int16_t pms_temp;
uint16_t pms_hum;
uint8_t pms_errorCode;
uint8_t pms_firmwareVersion;
int16_t toI16(const uint8_t *buf);
uint16_t toU16(const uint8_t *buf);
bool validate(const uint8_t *buf);
void parse(const uint8_t* buf);
};
#endif /** _PMS5003_BASE_H_ */

View File

@ -1,8 +1,8 @@
#include "PMS5003.h"
#include "Arduino.h"
#include "../Main/utils.h"
#if defined(ESP8266)
#include <SoftwareSerial.h>
/**
* @brief Init sensor
*
@ -37,14 +37,11 @@ bool PMS5003::begin(HardwareSerial &serial) {
PMS5003::PMS5003(BoardType def) : _boardDef(def) {}
/**
* @brief Init sensor
*
* @return true Success
* @return false Failure
* Initializes the sensor.
*/
bool PMS5003::begin(void) {
if (this->_isBegin) {
AgLog("Initialized, call end() then try again");
AgLog("Already initialized, call end() then try again");
return true;
}
@ -62,11 +59,10 @@ bool PMS5003::begin(void) {
}
#if defined(ESP8266)
bsp->Pms5003.uart_tx_pin;
SoftwareSerial *uart =
this->_serial =
new SoftwareSerial(bsp->Pms5003.uart_tx_pin, bsp->Pms5003.uart_rx_pin);
uart->begin(9600);
if (pms.begin(uart) == false) {
this->_serial->begin(9600);
if (pms.begin(this->_serial) == false) {
AgLog("PMS failed");
return false;
}
@ -77,7 +73,7 @@ bool PMS5003::begin(void) {
return false;
}
#endif
_ver = pms.getFirmwareVersion();
this->_isBegin = true;
return true;
}
@ -108,7 +104,9 @@ int PMS5003::getPm10Ae(void) { return pms.getPM10(); }
*
* @return int PM0.3 index
*/
int PMS5003::getPm03ParticleCount(void) { return pms.getCount0_3(); }
int PMS5003::getPm03ParticleCount(void) {
return pms.getCount0_3();
}
/**
* @brief Convert PM2.5 to US AQI
@ -118,6 +116,41 @@ int PMS5003::getPm03ParticleCount(void) { return pms.getCount0_3(); }
*/
int PMS5003::convertPm25ToUsAqi(int pm25) { return pms.pm25ToAQI(pm25); }
/**
* @brief Correct PM2.5
*
* Reference formula: https://www.airgradient.com/documentation/correction-algorithms/
*
* @param pm25 PM2.5 raw value
* @param humidity Humidity value
* @return int
*/
int PMS5003::compensate(int pm25, float humidity) {
return pms.compensate(pm25, humidity);
}
/**
* @brief Get sensor firmware version
*
* @return int
*/
int PMS5003::getFirmwareVersion(void) { return _ver; }
/**
* @brief Get sensor error code
*
* @return uint8_t
*/
uint8_t PMS5003::getErrorCode(void) { return pms.getErrorCode(); }
/**
* @brief Is sensor connect with device
*
* @return true Connected
* @return false Removed
*/
bool PMS5003::connected(void) { return pms.connected(); }
/**
* @brief Check device initialized or not
*
@ -152,12 +185,26 @@ void PMS5003::end(void) {
* @brief Check and read PMS sensor data. This method should be callack from
* loop process to continoue check sensor data if it's available
*/
void PMS5003::handle(void) { pms.handle(); }
void PMS5003::handle(void) { pms.readPackage(this->_serial); }
void PMS5003::updateFailCount(void) {
pms.updateFailCount();
}
void PMS5003::resetFailCount(void) {
pms.resetFailCount();
}
/**
* @brief Get sensor status
* @brief Get number of fail count
*
* @return true No problem
* @return false Communication timeout or sensor has removed
* @return int
*/
bool PMS5003::isFailed(void) { return pms.isFailed(); }
int PMS5003::getFailCount(void) { return pms.getFailCount(); }
/**
* @brief Get number of fail count max
*
* @return int
*/
int PMS5003::getFailCountMax(void) { return pms.getFailCountMax(); }

View File

@ -4,6 +4,9 @@
#include "../Main/BoardDef.h"
#include "PMS.h"
#include "Stream.h"
#ifdef ESP8266
#include <SoftwareSerial.h>
#endif
/**
* @brief The class define how to handle PMS5003 sensor bas on @ref PMS class
@ -18,21 +21,30 @@ public:
#endif
void end(void);
void handle(void);
bool isFailed(void);
void updateFailCount(void);
void resetFailCount(void);
int getFailCount(void);
int getFailCountMax(void);
int getPm01Ae(void);
int getPm25Ae(void);
int getPm10Ae(void);
int getPm03ParticleCount(void);
int convertPm25ToUsAqi(int pm25);
int compensate(int pm25, float humidity);
int getFirmwareVersion(void);
uint8_t getErrorCode(void);
bool connected(void);
private:
bool _isBegin = false;
int _ver;
BoardType _boardDef;
PMSBase pms;
const BoardDef *bsp;
#if defined(ESP8266)
Stream *_debugStream;
const char *TAG = "PMS5003";
SoftwareSerial *_serial;
#else
HardwareSerial *_serial;
#endif

View File

@ -1,5 +1,6 @@
#include "PMS5003T.h"
#include "Arduino.h"
#include "../Main/utils.h"
#if defined(ESP8266)
#include <SoftwareSerial.h>
@ -66,11 +67,10 @@ bool PMS5003T::begin(void) {
}
#if defined(ESP8266)
bsp->Pms5003.uart_tx_pin;
SoftwareSerial *uart =
this->_serial =
new SoftwareSerial(bsp->Pms5003.uart_tx_pin, bsp->Pms5003.uart_rx_pin);
uart->begin(9600);
if (pms.begin(uart) == false) {
this->_serial->begin(9600);
if (pms.begin(this->_serial) == false) {
AgLog("PMS failed");
return false;
}
@ -102,7 +102,7 @@ bool PMS5003T::begin(void) {
return false;
}
#endif
_ver = pms.getFirmwareVersion();
this->_isBegin = true;
return true;
}
@ -133,7 +133,9 @@ int PMS5003T::getPm10Ae(void) { return pms.getPM10(); }
*
* @return int PM 0.3 Count index
*/
int PMS5003T::getPm03ParticleCount(void) { return pms.getCount0_3(); }
int PMS5003T::getPm03ParticleCount(void) {
return pms.getCount0_3();
}
/**
* @brief Convert PM2.5 to US AQI
@ -149,7 +151,7 @@ int PMS5003T::convertPm25ToUsAqi(int pm25) { return pms.pm25ToAQI(pm25); }
* @return float Degree Celcius
*/
float PMS5003T::getTemperature(void) {
return pms.getTemp()/10.0f;
return pms.getTemp() / 10.0f;
}
/**
@ -158,9 +160,44 @@ float PMS5003T::getTemperature(void) {
* @return float Percent (%)
*/
float PMS5003T::getRelativeHumidity(void) {
return pms.getHum()/10.0f;
return pms.getHum() / 10.0f;
}
/**
* @brief Correct PM2.5
*
* Reference formula: https://www.airgradient.com/documentation/correction-algorithms/
*
* @param pm25 PM2.5 raw value
* @param humidity Humidity value
* @return int
*/
int PMS5003T::compensate(int pm25, float humidity) {
return pms.compensate(pm25, humidity);
}
/**
* @brief Get module(s) firmware version
*
* @return int Version code
*/
int PMS5003T::getFirmwareVersion(void) { return _ver; }
/**
* @brief Get sensor error code
*
* @return uint8_t
*/
uint8_t PMS5003T::getErrorCode(void) { return pms.getErrorCode(); }
/**
* @brief Is sensor connect to device
*
* @return true Connected
* @return false Removed
*/
bool PMS5003T::connected(void) { return pms.connected(); }
/**
* @brief Check device initialized or not
*
@ -192,13 +229,26 @@ void PMS5003T::end(void) {
* @brief Check and read PMS sensor data. This method should be callack from
* loop process to continoue check sensor data if it's available
*/
void PMS5003T::handle(void) { pms.handle(); }
void PMS5003T::handle(void) { pms.readPackage(this->_serial); }
void PMS5003T::updateFailCount(void) {
pms.updateFailCount();
}
void PMS5003T::resetFailCount(void) {
pms.resetFailCount();
}
/**
* @brief Get sensor status
* @brief Get fail count
*
* @return true No problem
* @return false Communication timeout or sensor has removed
* @return int
*/
bool PMS5003T::isFailed(void) { return pms.isFailed(); }
int PMS5003T::getFailCount(void) { return pms.getFailCount(); }
/**
* @brief Get fail count max
*
* @return int
*/
int PMS5003T::getFailCountMax(void) { return pms.getFailCountMax(); }

View File

@ -6,6 +6,9 @@
#include "PMS5003TBase.h"
#include "Stream.h"
#include <HardwareSerial.h>
#ifdef ESP8266
#include <SoftwareSerial.h>
#endif
/**
* @brief The class define how to handle PMS5003T sensor bas on @ref PMS class
@ -21,7 +24,10 @@ public:
void end(void);
void handle(void);
bool isFailed(void);
void updateFailCount(void);
void resetFailCount(void);
int getFailCount(void);
int getFailCountMax(void);
int getPm01Ae(void);
int getPm25Ae(void);
int getPm10Ae(void);
@ -29,16 +35,22 @@ public:
int convertPm25ToUsAqi(int pm25);
float getTemperature(void);
float getRelativeHumidity(void);
int compensate(int pm25, float humidity);
int getFirmwareVersion(void);
uint8_t getErrorCode(void);
bool connected(void);
private:
bool _isBegin = false;
bool _isSleep = false;
int _ver; /** Firmware version code */
BoardType _boardDef;
const BoardDef *bsp;
#if defined(ESP8266)
Stream *_debugStream;
const char *TAG = "PMS5003T";
SoftwareSerial *_serial;
#else
HardwareSerial *_serial;
#endif

View File

@ -4,14 +4,30 @@ PMS5003TBase::PMS5003TBase() {}
PMS5003TBase::~PMS5003TBase() {}
float PMS5003TBase::temperatureCompensated(float temp) {
/**
* @brief Compensate the temperature
*
* Reference formula: https://www.airgradient.com/documentation/correction-algorithms/
*
* @param temp
* @return * float
*/
float PMS5003TBase::compensateTemp(float temp) {
if (temp < 10.0f) {
return temp * 1.327f - 6.738f;
}
return temp * 1.181f - 5.113f;
}
float PMS5003TBase::humidityCompensated(float hum) {
/**
* @brief Compensate the humidity
*
* Reference formula: https://www.airgradient.com/documentation/correction-algorithms/
*
* @param temp
* @return * float
*/
float PMS5003TBase::compensateHum(float hum) {
hum = hum * 1.259f + 7.34f;
if (hum > 100.0f) {

View File

@ -8,8 +8,8 @@ private:
public:
PMS5003TBase();
~PMS5003TBase();
float temperatureCompensated(float temp);
float humidityCompensated(float hum);
float compensateTemp(float temp);
float compensateHum(float hum);
};
#endif

View File

@ -1,5 +1,6 @@
#include "S8.h"
#include "mb_crc.h"
#include "../Main/utils.h"
#if defined(ESP8266)
#include <SoftwareSerial.h>
#else
@ -103,7 +104,7 @@ void S8::getFirmwareVersion(char firmver[]) {
*/
int32_t S8::getSensorTypeId(void) {
if (this->isBegin() == false) {
return -1;
return utils::getInvalidCO2();
}
int32_t sensorType = 0;

View File

@ -2,6 +2,7 @@
#include "../Libraries/SensirionSGP41/src/SensirionI2CSgp41.h"
#include "../Libraries/Sensirion_Gas_Index_Algorithm/src/NOxGasIndexAlgorithm.h"
#include "../Libraries/Sensirion_Gas_Index_Algorithm/src/VOCGasIndexAlgorithm.h"
#include "../Main/utils.h"
#define sgpSensor() ((SensirionI2CSgp41 *)(this->_sensor))
#define vocAlgorithm() ((VOCGasIndexAlgorithm *)(this->_vocAlgorithm))
@ -66,6 +67,7 @@ bool Sgp41::begin(TwoWire &wire) {
}
onConditioning = true;
_handleFailCount = 0;
#ifdef ESP32
/** Create task */
xTaskCreate(
@ -108,7 +110,21 @@ void Sgp41::handle(void) {
noxRaw = srawNox;
nox = noxAlgorithm()->process(srawNox);
tvoc = vocAlgorithm()->process(srawVoc);
_handleFailCount = 0;
// AgLog("Polling SGP41 success: tvoc: %d, nox: %d", tvoc, nox);
} else {
if(_handleFailCount < 5) {
_handleFailCount++;
AgLog("Polling SGP41 failed: %d", _handleFailCount);
}
if (_handleFailCount >= 5) {
tvocRaw = utils::getInvalidVOC();
tvoc = utils::getInvalidVOC();
noxRaw = utils::getInvalidNOx();
nox = utils::getInvalidNOx();
}
}
}
}
@ -141,7 +157,21 @@ void Sgp41::_handle(void) {
noxRaw = srawNox;
nox = noxAlgorithm()->process(srawNox);
tvoc = vocAlgorithm()->process(srawVoc);
_handleFailCount = 0;
// AgLog("Polling SGP41 success: tvoc: %d, nox: %d", tvoc, nox);
} else {
if(_handleFailCount < 5) {
_handleFailCount++;
AgLog("Polling SGP41 failed: %d", _handleFailCount);
}
if (_handleFailCount >= 5) {
tvocRaw = utils::getInvalidVOC();
tvoc = utils::getInvalidVOC();
noxRaw = utils::getInvalidNOx();
nox = utils::getInvalidNOx();
}
}
}
}
@ -176,7 +206,7 @@ void Sgp41::end(void) {
*/
int Sgp41::getTvocIndex(void) {
if (onConditioning) {
return -1;
return utils::getInvalidVOC();
}
return tvoc;
}
@ -188,7 +218,7 @@ int Sgp41::getTvocIndex(void) {
*/
int Sgp41::getNoxIndex(void) {
if (onConditioning) {
return -1;
return utils::getInvalidNOx();
}
return nox;
}

View File

@ -35,6 +35,7 @@ private:
bool onConditioning = true;
bool ready = false;
bool _isBegin = false;
uint8_t _handleFailCount = 0;
void *_sensor;
void *_vocAlgorithm;
void *_noxAlgorithm;

View File

@ -1,6 +1,7 @@
#include "Sht.h"
#include "../Libraries/arduino-sht/SHTSensor.h"
#include "../Main/utils.h"
/** Cast _sensor to SHTSensor */
#define shtSensor() ((SHTSensor *)(this->_sensor))
@ -131,14 +132,18 @@ void Sht::end(void) {
*
* @return float
*/
float Sht::getTemperature(void) { return shtSensor()->getTemperature(); }
float Sht::getTemperature(void) {
return shtSensor()->getTemperature();
}
/**
* @brief Get humidity
*
* @return float
*/
float Sht::getRelativeHumidity(void) { return shtSensor()->getHumidity(); }
float Sht::getRelativeHumidity(void) {
return shtSensor()->getHumidity();
}
/**
* @brief Measure temperature and humidity