mirror of
https://github.com/airgradienthq/arduino.git
synced 2025-06-26 16:21:33 +02:00
Compare commits
167 Commits
Author | SHA1 | Date | |
---|---|---|---|
3ae5982380 | |||
db2c2ef052 | |||
593547cdbe | |||
673c46950d | |||
ae0b4038d4 | |||
cac0bd5355 | |||
3d6203dabf | |||
1db8fbefe9 | |||
d850d27dc1 | |||
f49e4a4b37 | |||
75f88b0009 | |||
c6961b3ca8 | |||
ade72ff3b8 | |||
9fbbea22ff | |||
7b0381dea3 | |||
5867d0f1d5 | |||
a98d77e0c3 | |||
641003f9d1 | |||
0275aee370 | |||
ea46b812c1 | |||
16c932962a | |||
f90b2e1a07 | |||
3a9bb16c09 | |||
bb754edc51 | |||
1d991b1004 | |||
3ebcc584a4 | |||
4d40ae421c | |||
3004a82e7e | |||
4af5ca2665 | |||
e6696f3d41 | |||
2b33823162 | |||
bf0768c7da | |||
33e2977eb4 | |||
85e779cfc2 | |||
4783684443 | |||
3b0c77ca4d | |||
eeba41f497 | |||
fd1f35f6d8 | |||
eb76eff403 | |||
4673999dda | |||
83aa6a4502 | |||
8a87b865e6 | |||
c3068be6e9 | |||
63bb5f8ddb | |||
8548d3e9f4 | |||
f7e1363da9 | |||
2ffe0a62aa | |||
2cda36ed0d | |||
7de2d0cc30 | |||
f478dd16c8 | |||
43ca0a2c2e | |||
84884d0c15 | |||
f36f860c2e | |||
e47a9057ea | |||
399b4ca1dc | |||
2e4f4643fa | |||
0ccf46c219 | |||
76a2f332d7 | |||
ed344d3e1a | |||
2082a2fa93 | |||
e145d32714 | |||
a2c19438c0 | |||
ac838efdb5 | |||
751d4e8380 | |||
6925b1ac9a | |||
77a23b4202 | |||
ea91cf9b6c | |||
467b3e8637 | |||
2a5cf78b68 | |||
9c09b82efd | |||
60d01c0d94 | |||
e7a91c53bc | |||
4e41fd5d71 | |||
fe4389bff4 | |||
9325830fad | |||
b86f0d45e3 | |||
210f0a5ff9 | |||
c841476ca4 | |||
359394af53 | |||
b8e10f473e | |||
cb511903ef | |||
ebb3f01dcd | |||
2e0ba26c97 | |||
c1a4758c6c | |||
0370a8aa15 | |||
863a37132a | |||
612317d976 | |||
8873bacf55 | |||
bf2388b121 | |||
b3918bd1fb | |||
2a6fce674e | |||
2f0663ced0 | |||
3adf58537a | |||
e10c9ff854 | |||
12c6ec9910 | |||
d108b63a57 | |||
6e212714fc | |||
866684eb30 | |||
9d01479406 | |||
20245f2110 | |||
3890919f54 | |||
76e40fea8c | |||
c4024f49fb | |||
ca5fc8d65b | |||
fd2cef153e | |||
507b958fdf | |||
335c29ebb1 | |||
2907d6f14e | |||
c8d5b546ed | |||
b7cfdc4c4d | |||
994d281e02 | |||
39470384e4 | |||
c25ba764bf | |||
826ff00f42 | |||
520550037d | |||
90f336dee7 | |||
0d39643e76 | |||
21232ec49d | |||
b7339de31f | |||
013fb94307 | |||
e16373a64d | |||
f929623443 | |||
59587ce2b7 | |||
9ec74450a5 | |||
28096e9faf | |||
682378a47c | |||
a1861be7b7 | |||
99ddd24432 | |||
29491e4cbe | |||
87cc3fc45f | |||
7471d8079a | |||
8b0fe967f1 | |||
6f1cef4e67 | |||
02b63ff816 | |||
228bf83e92 | |||
d3534cda52 | |||
aafaa42a68 | |||
2e9ff0d7dd | |||
244b7814a6 | |||
28d27ee8fd | |||
753f22923c | |||
c45901706f | |||
663836e277 | |||
d39e10908d | |||
c52962d628 | |||
6b65efd3d6 | |||
8bb87a75ef | |||
1afcca25a1 | |||
17238cff86 | |||
03e2afbf54 | |||
104d58a8c0 | |||
7a988ea6c1 | |||
54ed83cb89 | |||
e461b92c9f | |||
db21648e91 | |||
a9654506f5 | |||
63f653d5cd | |||
b049a23657 | |||
d6766ef68b | |||
6c3259b94b | |||
2df78e9066 | |||
81b13134d2 | |||
6d01366887 | |||
040bd28038 | |||
01943f594d | |||
01a69668cc | |||
812c2ab803 |
@ -41,90 +41,161 @@ You get the following response:
|
|||||||
"bootCount": 6,
|
"bootCount": 6,
|
||||||
"ledMode": "pm",
|
"ledMode": "pm",
|
||||||
"firmware": "3.1.3",
|
"firmware": "3.1.3",
|
||||||
"model": "I-9PSL"
|
"model": "I-9PSL",
|
||||||
|
"monitorDisplayCompensatedValues": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
| Properties | Type | Explanation |
|
| Properties | Type | Explanation |
|
||||||
|------------------|--------|--------------------------------------------------------------------|
|
|-----------------------------------|---------|----------------------------------------------------------------------------------------|
|
||||||
| `serialno` | String | Serial Number of the monitor |
|
| `serialno` | String | Serial Number of the monitor |
|
||||||
| `wifi` | Number | WiFi signal strength |
|
| `wifi` | Number | WiFi signal strength |
|
||||||
| `pm01` | Number | PM1 in ug/m3 |
|
| `pm01` | Number | PM1.0 in ug/m3 (atmospheric environment) |
|
||||||
| `pm02` | Number | PM2.5 in ug/m3 |
|
| `pm02` | Number | PM2.5 in ug/m3 (atmospheric environment) |
|
||||||
| `pm10` | Number | PM10 in ug/m3 |
|
| `pm10` | Number | PM10 in ug/m3 (atmospheric environment) |
|
||||||
| `pm02Compensated` | Number | PM2.5 in ug/m3 with correction applied (from fw version 3.1.4 onwards) |
|
| `pm02Compensated` | Number | PM2.5 in ug/m3 with correction applied (from fw version 3.1.4 onwards) |
|
||||||
| `rco2` | Number | CO2 in ppm |
|
| `pm01Standard` | Number | PM1.0 in ug/m3 (standard particle) |
|
||||||
| `pm003Count` | Number | Particle count per dL |
|
| `pm02Standard` | Number | PM2.5 in ug/m3 (standard particle) |
|
||||||
| `atmp` | Number | Temperature in Degrees Celsius |
|
| `pm10Standard` | Number | PM10 in ug/m3 (standard particle) |
|
||||||
| `atmpCompensated` | Number | Temperature in Degrees Celsius with correction applied |
|
| `rco2` | Number | CO2 in ppm |
|
||||||
| `rhum` | Number | Relative Humidity |
|
| `pm003Count` | Number | Particle count 0.3um per dL |
|
||||||
| `rhumCompensated` | Number | Relative Humidity with correction applied |
|
| `pm005Count` | Number | Particle count 0.5um per dL |
|
||||||
| `tvocIndex` | Number | Senisiron VOC Index |
|
| `pm01Count` | Number | Particle count 1.0um per dL |
|
||||||
| `tvocRaw` | Number | VOC raw value |
|
| `pm02Count` | Number | Particle count 2.5um per dL |
|
||||||
| `noxIndex` | Number | Senisirion NOx Index |
|
| `pm50Count` | Number | Particle count 5.0um per dL (only for indoor monitor) |
|
||||||
| `noxRaw` | Number | NOx raw value |
|
| `pm10Count` | Number | Particle count 10um per dL (only for indoor monitor) |
|
||||||
| `boot` | Number | Counts every measurement cycle. Low boot counts indicate restarts. |
|
| `atmp` | Number | Temperature in Degrees Celsius |
|
||||||
| `bootCount` | Number | Same as boot property. Required for Home Assistant compatability. Will be depreciated. |
|
| `atmpCompensated` | Number | Temperature in Degrees Celsius with correction applied |
|
||||||
| `ledMode` | String | Current configuration of the LED mode |
|
| `rhum` | Number | Relative Humidity |
|
||||||
| `firmware` | String | Current firmware version |
|
| `rhumCompensated` | Number | Relative Humidity with correction applied |
|
||||||
| `model` | String | Current model name |
|
| `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. (deprecated soon!) |
|
||||||
|
| `ledMode` | String | Current configuration of the LED mode |
|
||||||
|
| `firmware` | String | Current firmware version |
|
||||||
|
| `model` | String | Current model name |
|
||||||
|
|
||||||
Compensated values apply correction algorithms to make the sensor values more accurate. Temperature and relative humidity correction is only applied on the outdoor monitor Open Air but the properties _compensated will still be send also for the indoor monitor AirGradient ONE.
|
Compensated values apply correction algorithms to make the sensor values more accurate. Temperature and relative humidity correction is only applied on the outdoor monitor Open Air but the properties _compensated will still be send also for the indoor monitor AirGradient ONE.
|
||||||
|
|
||||||
#### Get Configuration Parameters (GET)
|
#### Get Configuration Parameters (GET)
|
||||||
With the path "/config" you can get the current configuration.
|
|
||||||
|
"/config" path returns the current configuration of the monitor.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"country": "US",
|
"country": "TH",
|
||||||
"pmStandard": "ugm3",
|
"pmStandard": "ugm3",
|
||||||
"ledBarMode": "pm",
|
"ledBarMode": "pm",
|
||||||
"displayMode": "on",
|
"abcDays": 7,
|
||||||
"abcDays": 30,
|
|
||||||
"tvocLearningOffset": 12,
|
"tvocLearningOffset": 12,
|
||||||
"noxLearningOffset": 12,
|
"noxLearningOffset": 12,
|
||||||
"mqttBrokerUrl": "",
|
"mqttBrokerUrl": "",
|
||||||
"temperatureUnit": "f",
|
"temperatureUnit": "c",
|
||||||
"configurationControl": "both",
|
"configurationControl": "local",
|
||||||
"postDataToAirGradient": true
|
"postDataToAirGradient": true,
|
||||||
|
"ledBarBrightness": 100,
|
||||||
|
"displayBrightness": 100,
|
||||||
|
"offlineMode": false,
|
||||||
|
"model": "I-9PSL",
|
||||||
|
"monitorDisplayCompensatedValues": true,
|
||||||
|
"corrections": {
|
||||||
|
"pm02": {
|
||||||
|
"correctionAlgorithm": "epa_2021",
|
||||||
|
"slr": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Set Configuration Parameters (PUT)
|
#### Set Configuration Parameters (PUT)
|
||||||
|
|
||||||
Configuration parameters can be changed with a put request to the monitor, e.g.
|
Configuration parameters can be changed with a PUT request to the monitor, e.g.
|
||||||
|
|
||||||
Example to force CO2 calibration
|
Example to force CO2 calibration
|
||||||
|
|
||||||
```curl -X PUT -H "Content-Type: application/json" -d '{"co2CalibrationRequested":true}' http://airgradient_84fce612eff4.local/config ```
|
```bash
|
||||||
|
curl -X PUT -H "Content-Type: application/json" -d '{"co2CalibrationRequested":true}' http://airgradient_84fce612eff4.local/config
|
||||||
|
```
|
||||||
|
|
||||||
Example to set monitor to Celsius
|
Example to set monitor to Celsius
|
||||||
|
|
||||||
```curl -X PUT -H "Content-Type: application/json" -d '{"temperatureUnit":"c"}' http://airgradient_84fce612eff4.local/config ```
|
```bash
|
||||||
|
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:
|
If you use command prompt on Windows, you need to escape the quotes:
|
||||||
|
|
||||||
``` -d "{\"param\":\"value\"}" ```
|
``` -d "{\"param\":\"value\"}" ```
|
||||||
|
|
||||||
#### Avoiding Conflicts with Configuration on AirGradient Server
|
#### 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.
|
|
||||||
|
If the monitor is set up on the AirGradient dashboard, it will also receive the configuration parameters 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)
|
#### Configuration Parameters (GET/PUT)
|
||||||
|
|
||||||
| Properties | Description | Type | Accepted Values | Example |
|
| Properties | Description | Type | Accepted Values | Example |
|
||||||
|-------------------------|:-------------------------------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------|
|
|-----------------------------------|:-----------------------------------------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------|
|
||||||
| `country` | Country where the device is. | String | Country code as [ALPHA-2 notation](https://www.iban.com/country-codes) | {"country": "TH"} |
|
| `country` | Country where the device is. | String | Country code as [ALPHA-2 notation](https://www.iban.com/country-codes) | `{"country": "TH"}` |
|
||||||
| `model` | Hardware identifier (only GET). | String | I-9PSL-DE | {"model": "I-9PSL-DE"} |
|
| `model` | Hardware identifier (only GET). | String | I-9PSL-DE | `{"model": "I-9PSL-DE"}` |
|
||||||
| `pmStandard` | Particle matter standard used on the display. | String | `ugm3`: ug/m3 <br> `us-aqi`: USAQI | {"pmStandard": "ugm3"} |
|
| `pmStandard` | Particle matter standard used on the display. | String | `ugm3`: ug/m3 <br> `us-aqi`: USAQI | `{"pmStandard": "ugm3"}` |
|
||||||
| `ledBarMode` | Mode in which the led bar can be set. | String | `co2`: LED bar displays CO2 <br>`pm`: LED bar displays PM <br>`off`: Turn off LED bar | {"ledBarMode": "off"} |
|
| `ledBarMode` | Mode in which the led bar can be set. | String | `co2`: LED bar displays CO2 <br>`pm`: LED bar displays PM <br>`off`: Turn off LED bar | `{"ledBarMode": "off"}` |
|
||||||
| `displayBrightness` | Brightness of the Display. | Number | 0-100 | {"displayBrightness": 50} |
|
| `displayBrightness` | Brightness of the Display. | Number | 0-100 | `{"displayBrightness": 50}` |
|
||||||
| `ledBarBrightness` | Brightness of the LEDBar. | Number | 0-100 | {"ledBarBrightness": 40} |
|
| `ledBarBrightness` | Brightness of the LEDBar. | Number | 0-100 | `{"ledBarBrightness": 40}` |
|
||||||
| `abcDays` | Number of days for CO2 automatic baseline calibration. | Number | Maximum 200 days. Default 8 days. | {"abcDays": 8} |
|
| `abcDays` | Number of days for CO2 automatic baseline calibration. | Number | Maximum 200 days. Default 8 days. | `{"abcDays": 8}` |
|
||||||
| `mqttBrokerUrl` | MQTT broker URL. | String | | {"mqttBrokerUrl": "mqtt://192.168.0.18:1883"} |
|
| `mqttBrokerUrl` | MQTT broker URL. | String | | `{"mqttBrokerUrl": "mqtt://192.168.0.18:1883"}` |
|
||||||
| `temperatureUnit` | Temperature unit shown on the display. | String | `c` or `C`: Degree Celsius °C <br>`f` or `F`: Degree Fahrenheit °F | {"temperatureUnit": "c"} |
|
| `temperatureUnit` | Temperature unit shown on the display. | String | `c` or `C`: Degree Celsius °C <br>`f` or `F`: Degree Fahrenheit °F | `{"temperatureUnit": "c"}` |
|
||||||
| `configurationControl` | The configuration source of the device. | String | `both`: Accept local and cloud configuration <br>`local`: Accept only local configuration <br>`cloud`: Accept only cloud configuration | {"configurationControl": "both"} |
|
| `configurationControl` | The configuration source of the device. | String | `both`: Accept local and cloud configuration <br>`local`: Accept only local configuration <br>`cloud`: Accept only cloud configuration | `{"configurationControl": "both"}` |
|
||||||
| `postDataToAirGradient` | Send data to AirGradient cloud. | Boolean | `true`: Enabled <br>`false`: Disabled | {"postDataToAirGradient": true} |
|
| `postDataToAirGradient` | Send data to AirGradient cloud. | Boolean | `true`: Enabled <br>`false`: Disabled | `{"postDataToAirGradient": true}` |
|
||||||
| `co2CalibrationRequested` | Can be set to trigger a calibration. | Boolean | `true`: CO2 calibration (400ppm) will be triggered | {"co2CalibrationRequested": true} |
|
| `co2CalibrationRequested` | Can be set to trigger a calibration. | Boolean | `true`: CO2 calibration (400ppm) will be triggered | `{"co2CalibrationRequested": true}` |
|
||||||
| `ledBarTestRequested` | Can be set to trigger a test. | Boolean | `true` : LEDs will run test sequence | {"ledBarTestRequested": true} |
|
| `ledBarTestRequested` | Can be set to trigger a test. | Boolean | `true` : LEDs will run test sequence | `{"ledBarTestRequested": true}` |
|
||||||
| `noxLearningOffset` | Set NOx learning gain offset. | Number | 0-720 (default 12) | {"noxLearningOffset": 12} |
|
| `noxLearningOffset` | Set NOx learning gain offset. | Number | 0-720 (default 12) | `{"noxLearningOffset": 12}` |
|
||||||
| `tvocLearningOffset` | Set VOC learning gain offset. | Number | 0-720 (default 12) | {"tvocLearningOffset": 12} |
|
| `tvocLearningOffset` | Set VOC learning gain offset. | Number | 0-720 (default 12) | `{"tvocLearningOffset": 12}` |
|
||||||
| `offlineMode` | Set monitor to run without WiFi. | Boolean | `false`: Disabled (default) <br> `true`: Enabled | {"offlineMode": true} |
|
| `offlineMode` | Set monitor to run without WiFi. | Boolean | `false`: Disabled (default) <br> `true`: Enabled | `{"offlineMode": true}` |
|
||||||
|
| `monitorDisplayCompensatedValues` | Set the display show the PM value with/without compensate value (only on [3.1.9]()) | Boolean | `false`: Without compensate (default) <br> `true`: with compensate | `{"monitorDisplayCompensatedValues": false }` |
|
||||||
|
| `corrections` | Sets correction options to display and measurement values on local server response. | Object | _see corretions section_ | _see corretions section_ |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### Corrections
|
||||||
|
|
||||||
|
The `corrections` object allows configuring PM2.5 correction algorithms and parameters. This affects both the display and local server response values.
|
||||||
|
|
||||||
|
Example correction configuration:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"corrections": {
|
||||||
|
"pm02": {
|
||||||
|
"correctionAlgorithm": "<Option In String>",
|
||||||
|
"slr": {
|
||||||
|
"intercept": 0,
|
||||||
|
"scalingFactor": 0,
|
||||||
|
"useEpa2021": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Algorithm | Value | Description | SLR required |
|
||||||
|
|------------|-------------|------|---------|
|
||||||
|
| Raw | `"none"` | No correction (default) | No |
|
||||||
|
| EPA 2021 | `"epa_2021"` | Use EPA 2021 correction factors on top of raw value | No |
|
||||||
|
| PMS5003_20240104 | `"slr_PMS5003_20240104"` | Correction for PMS5003 sensor batch 20240104| Yes |
|
||||||
|
| PMS5003_20231218 | `"slr_PMS5003_20231218"` | Correction for PMS5003 sensor batch 20231218| Yes |
|
||||||
|
| PMS5003_20231030 | `"slr_PMS5003_20231030"` | Correction for PMS5003 sensor batch 20231030| Yes |
|
||||||
|
|
||||||
|
**Notes**:
|
||||||
|
|
||||||
|
- Set `useEpa2021` to true if want to apply EPA 2021 correction factors on top of SLR correction value.
|
||||||
|
- `intercept` and `scalingFactor` values can be obtained from [this article](https://www.airgradient.com/blog/low-readings-from-pms5003/)
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --location -X PUT 'http://airgradient_84fce612eff4.local/config' --header 'Content-Type: application/json' --data '{"corrections":{"pm02":{"correctionAlgorithm":"slr_PMS5003_20231030","slr":{"intercept":0,"scalingFactor":0.02838,"useEpa2021":false}}}}'
|
||||||
|
```
|
@ -49,9 +49,8 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|||||||
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */
|
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */
|
||||||
#define SENSOR_CO2_UPDATE_INTERVAL 4000 /** ms */
|
#define SENSOR_CO2_UPDATE_INTERVAL 4000 /** ms */
|
||||||
#define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */
|
#define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */
|
||||||
#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 2000 /** ms */
|
#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 6000 /** ms */
|
||||||
#define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */
|
#define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */
|
||||||
#define FIRMWARE_CHECK_FOR_UPDATE_MS (60 * 60 * 1000) /** ms */
|
|
||||||
|
|
||||||
static AirGradient ag(DIY_BASIC);
|
static AirGradient ag(DIY_BASIC);
|
||||||
static Configuration configuration(Serial);
|
static Configuration configuration(Serial);
|
||||||
@ -68,8 +67,6 @@ static LocalServer localServer(Serial, openMetrics, measurements, configuration,
|
|||||||
wifiConnector);
|
wifiConnector);
|
||||||
static MqttClient mqttClient(Serial);
|
static MqttClient mqttClient(Serial);
|
||||||
|
|
||||||
static int pmFailCount = 0;
|
|
||||||
static int getCO2FailCount = 0;
|
|
||||||
static AgFirmwareMode fwMode = FW_MODE_I_BASIC_40PS;
|
static AgFirmwareMode fwMode = FW_MODE_I_BASIC_40PS;
|
||||||
|
|
||||||
static String fwNewVersion;
|
static String fwNewVersion;
|
||||||
@ -91,6 +88,8 @@ static void wdgFeedUpdate(void);
|
|||||||
static bool sgp41Init(void);
|
static bool sgp41Init(void);
|
||||||
static void wifiFactoryConfigure(void);
|
static void wifiFactoryConfigure(void);
|
||||||
static void mqttHandle(void);
|
static void mqttHandle(void);
|
||||||
|
static int calculateMaxPeriod(int updateInterval);
|
||||||
|
static void setMeasurementMaxPeriod();
|
||||||
|
|
||||||
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplaySchedule);
|
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplaySchedule);
|
||||||
AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL,
|
AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL,
|
||||||
@ -131,6 +130,10 @@ void setup() {
|
|||||||
|
|
||||||
/** Init sensor */
|
/** Init sensor */
|
||||||
boardInit();
|
boardInit();
|
||||||
|
setMeasurementMaxPeriod();
|
||||||
|
|
||||||
|
// Uncomment below line to print every measurements reading update
|
||||||
|
// measurements.setDebug(true);
|
||||||
|
|
||||||
/** Connecting wifi */
|
/** Connecting wifi */
|
||||||
bool connectToWifi = false;
|
bool connectToWifi = false;
|
||||||
@ -207,11 +210,7 @@ void loop() {
|
|||||||
tvocSchedule.run();
|
tvocSchedule.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Auto reset watchdog timer if offline mode or postDataToAirGradient */
|
watchdogFeedSchedule.run();
|
||||||
if (configuration.isOfflineMode() ||
|
|
||||||
(configuration.isPostDataToAirGradient() == false)) {
|
|
||||||
watchdogFeedSchedule.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Check for handle WiFi reconnect */
|
/** Check for handle WiFi reconnect */
|
||||||
wifiConnector.handle();
|
wifiConnector.handle();
|
||||||
@ -235,17 +234,16 @@ void loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void co2Update(void) {
|
static void co2Update(void) {
|
||||||
|
if (!configuration.hasSensorS8) {
|
||||||
|
// Device don't have S8 sensor
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int value = ag.s8.getCo2();
|
int value = ag.s8.getCo2();
|
||||||
if (utils::isValidCO2(value)) {
|
if (utils::isValidCO2(value)) {
|
||||||
measurements.CO2 = value;
|
measurements.update(Measurements::CO2, value);
|
||||||
getCO2FailCount = 0;
|
|
||||||
Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2);
|
|
||||||
} else {
|
} else {
|
||||||
getCO2FailCount++;
|
measurements.update(Measurements::CO2, utils::getInvalidCO2());
|
||||||
Serial.printf("Get CO2 failed: %d\r\n", getCO2FailCount);
|
|
||||||
if (getCO2FailCount >= 3) {
|
|
||||||
measurements.CO2 = utils::getInvalidCO2();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,18 +265,23 @@ static void mdnsInit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void initMqtt(void) {
|
static void initMqtt(void) {
|
||||||
if (mqttClient.begin(configuration.getMqttBrokerUri())) {
|
String mqttUri = configuration.getMqttBrokerUri();
|
||||||
Serial.println("Setup connect to MQTT broker successful");
|
if (mqttUri.isEmpty()) {
|
||||||
|
Serial.println(
|
||||||
|
"MQTT is not configured, skipping initialization of MQTT client");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mqttClient.begin(mqttUri)) {
|
||||||
|
Serial.println("Successfully connected to MQTT broker");
|
||||||
} else {
|
} else {
|
||||||
Serial.println("setup Connect to MQTT broker failed");
|
Serial.println("Connection to MQTT broker failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wdgFeedUpdate(void) {
|
static void wdgFeedUpdate(void) {
|
||||||
ag.watchdog.reset();
|
ag.watchdog.reset();
|
||||||
Serial.println();
|
Serial.println("External watchdog feed!");
|
||||||
Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset");
|
|
||||||
Serial.println();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sgp41Init(void) {
|
static bool sgp41Init(void) {
|
||||||
@ -313,8 +316,7 @@ static void mqttHandle(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mqttClient.isConnected()) {
|
if (mqttClient.isConnected()) {
|
||||||
String payload = measurements.toString(true, fwMode, wifiConnector.RSSI(),
|
String payload = measurements.toString(true, fwMode, wifiConnector.RSSI(), ag, configuration);
|
||||||
&ag, &configuration);
|
|
||||||
String topic = "airgradient/readings/" + ag.deviceId();
|
String topic = "airgradient/readings/" + ag.deviceId();
|
||||||
if (mqttClient.publish(topic.c_str(), payload.c_str(), payload.length())) {
|
if (mqttClient.publish(topic.c_str(), payload.c_str(), payload.length())) {
|
||||||
Serial.println("MQTT sync success");
|
Serial.println("MQTT sync success");
|
||||||
@ -490,84 +492,98 @@ static void oledDisplaySchedule(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void updateTvoc(void) {
|
static void updateTvoc(void) {
|
||||||
measurements.TVOC = ag.sgp41.getTvocIndex();
|
if (!configuration.hasSensorSGP) {
|
||||||
measurements.TVOCRaw = ag.sgp41.getTvocRaw();
|
return;
|
||||||
measurements.NOx = ag.sgp41.getNoxIndex();
|
}
|
||||||
measurements.NOxRaw = ag.sgp41.getNoxRaw();
|
|
||||||
|
|
||||||
Serial.println();
|
measurements.update(Measurements::TVOC, ag.sgp41.getTvocIndex());
|
||||||
Serial.printf("TVOC index: %d\r\n", measurements.TVOC);
|
measurements.update(Measurements::TVOCRaw, ag.sgp41.getTvocRaw());
|
||||||
Serial.printf("TVOC raw: %d\r\n", measurements.TVOCRaw);
|
measurements.update(Measurements::NOx, ag.sgp41.getNoxIndex());
|
||||||
Serial.printf("NOx index: %d\r\n", measurements.NOx);
|
measurements.update(Measurements::NOxRaw, ag.sgp41.getNoxRaw());
|
||||||
Serial.printf("NOx raw: %d\r\n", measurements.NOxRaw);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void updatePm(void) {
|
static void updatePm(void) {
|
||||||
if (ag.pms5003.isFailed() == false) {
|
if (ag.pms5003.connected()) {
|
||||||
measurements.pm01_1 = ag.pms5003.getPm01Ae();
|
measurements.update(Measurements::PM01, ag.pms5003.getPm01Ae());
|
||||||
measurements.pm25_1 = ag.pms5003.getPm25Ae();
|
measurements.update(Measurements::PM25, ag.pms5003.getPm25Ae());
|
||||||
measurements.pm10_1 = ag.pms5003.getPm10Ae();
|
measurements.update(Measurements::PM10, ag.pms5003.getPm10Ae());
|
||||||
measurements.pm03PCount_1 = ag.pms5003.getPm03ParticleCount();
|
measurements.update(Measurements::PM03_PC, ag.pms5003.getPm03ParticleCount());
|
||||||
|
|
||||||
Serial.println();
|
|
||||||
Serial.printf("PM1 ug/m3: %d\r\n", measurements.pm01_1);
|
|
||||||
Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_1);
|
|
||||||
Serial.printf("PM10 ug/m3: %d\r\n", measurements.pm10_1);
|
|
||||||
Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1);
|
|
||||||
pmFailCount = 0;
|
|
||||||
} else {
|
} else {
|
||||||
pmFailCount++;
|
measurements.update(Measurements::PM01, utils::getInvalidPmValue());
|
||||||
Serial.printf("PMS read failed: %d\r\n", pmFailCount);
|
measurements.update(Measurements::PM25, utils::getInvalidPmValue());
|
||||||
if (pmFailCount >= 3) {
|
measurements.update(Measurements::PM10, utils::getInvalidPmValue());
|
||||||
measurements.pm01_1 = utils::getInvalidPMS();
|
measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue());
|
||||||
measurements.pm25_1 = utils::getInvalidPMS();
|
|
||||||
measurements.pm10_1 = utils::getInvalidPMS();
|
|
||||||
measurements.pm03PCount_1 = utils::getInvalidPMS();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sendDataToServer(void) {
|
static void sendDataToServer(void) {
|
||||||
|
/** Increment bootcount when send measurements data is scheduled */
|
||||||
|
measurements.bootCount++;
|
||||||
|
|
||||||
/** Ignore send data to server if postToAirGradient disabled */
|
/** Ignore send data to server if postToAirGradient disabled */
|
||||||
if (configuration.isPostDataToAirGradient() == false ||
|
if (configuration.isPostDataToAirGradient() == false ||
|
||||||
configuration.isOfflineMode()) {
|
configuration.isOfflineMode()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(),
|
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), ag, configuration);
|
||||||
&ag, &configuration);
|
|
||||||
if (apiClient.postToServer(syncData)) {
|
if (apiClient.postToServer(syncData)) {
|
||||||
ag.watchdog.reset();
|
|
||||||
Serial.println();
|
Serial.println();
|
||||||
Serial.println(
|
Serial.println(
|
||||||
"Online mode and isPostToAirGradient = true: watchdog reset");
|
"Online mode and isPostToAirGradient = true: watchdog reset");
|
||||||
Serial.println();
|
Serial.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
measurements.bootCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tempHumUpdate(void) {
|
static void tempHumUpdate(void) {
|
||||||
delay(100);
|
|
||||||
if (ag.sht.measure()) {
|
if (ag.sht.measure()) {
|
||||||
measurements.Temperature = ag.sht.getTemperature();
|
float temp = ag.sht.getTemperature();
|
||||||
measurements.Humidity = ag.sht.getRelativeHumidity();
|
float rhum = ag.sht.getRelativeHumidity();
|
||||||
|
|
||||||
Serial.printf("Temperature in C: %0.2f\r\n", measurements.Temperature);
|
measurements.update(Measurements::Temperature, temp);
|
||||||
Serial.printf("Relative Humidity: %d\r\n", measurements.Humidity);
|
measurements.update(Measurements::Humidity, rhum);
|
||||||
Serial.printf("Temperature compensated in C: %0.2f\r\n",
|
|
||||||
measurements.Temperature);
|
|
||||||
Serial.printf("Relative Humidity compensated: %d\r\n",
|
|
||||||
measurements.Humidity);
|
|
||||||
|
|
||||||
// Update compensation temperature and humidity for SGP41
|
// Update compensation temperature and humidity for SGP41
|
||||||
if (configuration.hasSensorSGP) {
|
if (configuration.hasSensorSGP) {
|
||||||
ag.sgp41.setCompensationTemperatureHumidity(measurements.Temperature,
|
ag.sgp41.setCompensationTemperatureHumidity(temp, rhum);
|
||||||
measurements.Humidity);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
measurements.update(Measurements::Temperature, utils::getInvalidTemperature());
|
||||||
|
measurements.update(Measurements::Humidity, utils::getInvalidHumidity());
|
||||||
Serial.println("SHT read failed");
|
Serial.println("SHT read failed");
|
||||||
measurements.Temperature = utils::getInvalidTemperature();
|
|
||||||
measurements.Humidity = utils::getInvalidHumidity();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set max period for each measurement type based on sensor update interval*/
|
||||||
|
void setMeasurementMaxPeriod() {
|
||||||
|
/// Max period for S8 sensors measurements
|
||||||
|
measurements.maxPeriod(Measurements::CO2, calculateMaxPeriod(SENSOR_CO2_UPDATE_INTERVAL));
|
||||||
|
/// Max period for SGP sensors measurements
|
||||||
|
measurements.maxPeriod(Measurements::TVOC, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::TVOCRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::NOx, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::NOxRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL));
|
||||||
|
/// Max period for PMS sensors measurements
|
||||||
|
measurements.maxPeriod(Measurements::PM25, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::PM01, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::PM10, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::PM03_PC, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
// Temperature and Humidity
|
||||||
|
if (configuration.hasSensorSHT) {
|
||||||
|
/// Max period for SHT sensors measurements
|
||||||
|
measurements.maxPeriod(Measurements::Temperature,
|
||||||
|
calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::Humidity,
|
||||||
|
calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL));
|
||||||
|
} else {
|
||||||
|
/// Temp and hum data retrieved from PMS5003T sensor
|
||||||
|
measurements.maxPeriod(Measurements::Temperature,
|
||||||
|
calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::Humidity, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int calculateMaxPeriod(int updateInterval) {
|
||||||
|
// 0.5 is 50% reduced interval for max period
|
||||||
|
return (SERVER_SYNC_INTERVAL - (SERVER_SYNC_INTERVAL * 0.5)) / updateInterval;
|
||||||
|
}
|
@ -53,9 +53,8 @@ void LocalServer::_GET_metrics(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LocalServer::_GET_measure(void) {
|
void LocalServer::_GET_measure(void) {
|
||||||
server.send(
|
String toSend = measure.toString(true, fwMode, wifiConnector.RSSI(), *ag, config);
|
||||||
200, "application/json",
|
server.send(200, "application/json", toSend);
|
||||||
measure.toString(true, fwMode, wifiConnector.RSSI(), ag, &config));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalServer::setFwMode(AgFirmwareMode fwMode) { this->fwMode = fwMode; }
|
void LocalServer::setFwMode(AgFirmwareMode fwMode) { this->fwMode = fwMode; }
|
||||||
|
@ -67,50 +67,61 @@ String OpenMetrics::getPayload(void) {
|
|||||||
|
|
||||||
float _temp = utils::getInvalidTemperature();
|
float _temp = utils::getInvalidTemperature();
|
||||||
float _hum = utils::getInvalidHumidity();
|
float _hum = utils::getInvalidHumidity();
|
||||||
int pm01 = utils::getInvalidPMS();
|
int pm01 = utils::getInvalidPmValue();
|
||||||
int pm25 = utils::getInvalidPMS();
|
int pm25 = utils::getInvalidPmValue();
|
||||||
int pm10 = utils::getInvalidPMS();
|
int pm10 = utils::getInvalidPmValue();
|
||||||
int pm03PCount = utils::getInvalidPMS();
|
int pm03PCount = utils::getInvalidPmValue();
|
||||||
int atmpCompensated = utils::getInvalidTemperature();
|
int atmpCompensated = utils::getInvalidTemperature();
|
||||||
int ahumCompensated = utils::getInvalidHumidity();
|
int ahumCompensated = utils::getInvalidHumidity();
|
||||||
|
int tvoc = utils::getInvalidVOC();
|
||||||
|
int tvoc_raw = utils::getInvalidVOC();
|
||||||
|
int nox = utils::getInvalidNOx();
|
||||||
|
int nox_raw = utils::getInvalidNOx();
|
||||||
|
|
||||||
if (config.hasSensorSHT) {
|
if (config.hasSensorSHT) {
|
||||||
_temp = measure.Temperature;
|
_temp = measure.getFloat(Measurements::Temperature);
|
||||||
_hum = measure.Humidity;
|
_hum = measure.getFloat(Measurements::Humidity);
|
||||||
atmpCompensated = _temp;
|
atmpCompensated = _temp;
|
||||||
ahumCompensated = _hum;
|
ahumCompensated = _hum;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.hasSensorPMS1) {
|
if (config.hasSensorPMS1) {
|
||||||
pm01 = measure.pm01_1;
|
pm01 = measure.get(Measurements::PM01);
|
||||||
pm25 = measure.pm25_1;
|
pm25 = measure.get(Measurements::PM25);
|
||||||
pm10 = measure.pm10_1;
|
pm10 = measure.get(Measurements::PM10);
|
||||||
pm03PCount = measure.pm03PCount_1;
|
pm03PCount = measure.get(Measurements::PM03_PC);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.hasSensorSGP) {
|
||||||
|
tvoc = measure.get(Measurements::TVOC);
|
||||||
|
tvoc_raw = measure.get(Measurements::TVOCRaw);
|
||||||
|
nox = measure.get(Measurements::NOx);
|
||||||
|
nox_raw = measure.get(Measurements::NOxRaw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.hasSensorPMS1) {
|
if (config.hasSensorPMS1) {
|
||||||
if (utils::isValidPMS(pm01)) {
|
if (utils::isValidPm(pm01)) {
|
||||||
add_metric("pm1",
|
add_metric("pm1",
|
||||||
"PM1.0 concentration as measured by the AirGradient PMS "
|
"PM1.0 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in micrograms per cubic meter",
|
"sensor, in micrograms per cubic meter",
|
||||||
"gauge", "ugm3");
|
"gauge", "ugm3");
|
||||||
add_metric_point("", String(pm01));
|
add_metric_point("", String(pm01));
|
||||||
}
|
}
|
||||||
if (utils::isValidPMS(pm25)) {
|
if (utils::isValidPm(pm25)) {
|
||||||
add_metric("pm2d5",
|
add_metric("pm2d5",
|
||||||
"PM2.5 concentration as measured by the AirGradient PMS "
|
"PM2.5 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in micrograms per cubic meter",
|
"sensor, in micrograms per cubic meter",
|
||||||
"gauge", "ugm3");
|
"gauge", "ugm3");
|
||||||
add_metric_point("", String(pm25));
|
add_metric_point("", String(pm25));
|
||||||
}
|
}
|
||||||
if (utils::isValidPMS(pm10)) {
|
if (utils::isValidPm(pm10)) {
|
||||||
add_metric("pm10",
|
add_metric("pm10",
|
||||||
"PM10 concentration as measured by the AirGradient PMS "
|
"PM10 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in micrograms per cubic meter",
|
"sensor, in micrograms per cubic meter",
|
||||||
"gauge", "ugm3");
|
"gauge", "ugm3");
|
||||||
add_metric_point("", String(pm10));
|
add_metric_point("", String(pm10));
|
||||||
}
|
}
|
||||||
if (utils::isValidPMS03Count(pm03PCount)) {
|
if (utils::isValidPm03Count(pm03PCount)) {
|
||||||
add_metric("pm0d3",
|
add_metric("pm0d3",
|
||||||
"PM0.3 concentration as measured by the AirGradient PMS "
|
"PM0.3 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in number of particules per 100 milliliters",
|
"sensor, in number of particules per 100 milliliters",
|
||||||
@ -120,33 +131,33 @@ String OpenMetrics::getPayload(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (config.hasSensorSGP) {
|
if (config.hasSensorSGP) {
|
||||||
if (utils::isValidVOC(measure.TVOC)) {
|
if (utils::isValidVOC(tvoc)) {
|
||||||
add_metric("tvoc_index",
|
add_metric("tvoc_index",
|
||||||
"The processed Total Volatile Organic Compounds (TVOC) index "
|
"The processed Total Volatile Organic Compounds (TVOC) index "
|
||||||
"as measured by the AirGradient SGP sensor",
|
"as measured by the AirGradient SGP sensor",
|
||||||
"gauge");
|
"gauge");
|
||||||
add_metric_point("", String(measure.TVOC));
|
add_metric_point("", String(tvoc));
|
||||||
}
|
}
|
||||||
if (utils::isValidVOC(measure.TVOCRaw)) {
|
if (utils::isValidVOC(tvoc_raw)) {
|
||||||
add_metric("tvoc_raw",
|
add_metric("tvoc_raw",
|
||||||
"The raw input value to the Total Volatile Organic Compounds "
|
"The raw input value to the Total Volatile Organic Compounds "
|
||||||
"(TVOC) index as measured by the AirGradient SGP sensor",
|
"(TVOC) index as measured by the AirGradient SGP sensor",
|
||||||
"gauge");
|
"gauge");
|
||||||
add_metric_point("", String(measure.TVOCRaw));
|
add_metric_point("", String(tvoc_raw));
|
||||||
}
|
}
|
||||||
if (utils::isValidNOx(measure.NOx)) {
|
if (utils::isValidNOx(nox)) {
|
||||||
add_metric("nox_index",
|
add_metric("nox_index",
|
||||||
"The processed Nitrous Oxide (NOx) index as measured by the "
|
"The processed Nitrous Oxide (NOx) index as measured by the "
|
||||||
"AirGradient SGP sensor",
|
"AirGradient SGP sensor",
|
||||||
"gauge");
|
"gauge");
|
||||||
add_metric_point("", String(measure.NOx));
|
add_metric_point("", String(nox));
|
||||||
}
|
}
|
||||||
if (utils::isValidNOx(measure.NOxRaw)) {
|
if (utils::isValidNOx(nox_raw)) {
|
||||||
add_metric("nox_raw",
|
add_metric("nox_raw",
|
||||||
"The raw input value to the Nitrous Oxide (NOx) index as "
|
"The raw input value to the Nitrous Oxide (NOx) index as "
|
||||||
"measured by the AirGradient SGP sensor",
|
"measured by the AirGradient SGP sensor",
|
||||||
"gauge");
|
"gauge");
|
||||||
add_metric_point("", String(measure.NOxRaw));
|
add_metric_point("", String(nox_raw));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,9 +49,8 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|||||||
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */
|
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */
|
||||||
#define SENSOR_CO2_UPDATE_INTERVAL 4000 /** ms */
|
#define SENSOR_CO2_UPDATE_INTERVAL 4000 /** ms */
|
||||||
#define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */
|
#define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */
|
||||||
#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 2000 /** ms */
|
#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 6000 /** ms */
|
||||||
#define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */
|
#define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */
|
||||||
#define FIRMWARE_CHECK_FOR_UPDATE_MS (60 * 60 * 1000) /** ms */
|
|
||||||
|
|
||||||
static AirGradient ag(DIY_PRO_INDOOR_V3_3);
|
static AirGradient ag(DIY_PRO_INDOOR_V3_3);
|
||||||
static Configuration configuration(Serial);
|
static Configuration configuration(Serial);
|
||||||
@ -68,8 +67,6 @@ static LocalServer localServer(Serial, openMetrics, measurements, configuration,
|
|||||||
wifiConnector);
|
wifiConnector);
|
||||||
static MqttClient mqttClient(Serial);
|
static MqttClient mqttClient(Serial);
|
||||||
|
|
||||||
static int pmFailCount = 0;
|
|
||||||
static int getCO2FailCount = 0;
|
|
||||||
static AgFirmwareMode fwMode = FW_MODE_I_33PS;
|
static AgFirmwareMode fwMode = FW_MODE_I_33PS;
|
||||||
|
|
||||||
static String fwNewVersion;
|
static String fwNewVersion;
|
||||||
@ -91,6 +88,8 @@ static void wdgFeedUpdate(void);
|
|||||||
static bool sgp41Init(void);
|
static bool sgp41Init(void);
|
||||||
static void wifiFactoryConfigure(void);
|
static void wifiFactoryConfigure(void);
|
||||||
static void mqttHandle(void);
|
static void mqttHandle(void);
|
||||||
|
static int calculateMaxPeriod(int updateInterval);
|
||||||
|
static void setMeasurementMaxPeriod();
|
||||||
|
|
||||||
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplaySchedule);
|
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplaySchedule);
|
||||||
AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL,
|
AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL,
|
||||||
@ -131,6 +130,10 @@ void setup() {
|
|||||||
|
|
||||||
/** Init sensor */
|
/** Init sensor */
|
||||||
boardInit();
|
boardInit();
|
||||||
|
setMeasurementMaxPeriod();
|
||||||
|
|
||||||
|
// Uncomment below line to print every measurements reading update
|
||||||
|
// measurements.setDebug(true);
|
||||||
|
|
||||||
/** Connecting wifi */
|
/** Connecting wifi */
|
||||||
bool connectToWifi = false;
|
bool connectToWifi = false;
|
||||||
@ -205,11 +208,7 @@ void loop() {
|
|||||||
tvocSchedule.run();
|
tvocSchedule.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Auto reset watchdog timer if offline mode or postDataToAirGradient */
|
watchdogFeedSchedule.run();
|
||||||
if (configuration.isOfflineMode() ||
|
|
||||||
(configuration.isPostDataToAirGradient() == false)) {
|
|
||||||
watchdogFeedSchedule.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Check for handle WiFi reconnect */
|
/** Check for handle WiFi reconnect */
|
||||||
wifiConnector.handle();
|
wifiConnector.handle();
|
||||||
@ -233,17 +232,16 @@ void loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void co2Update(void) {
|
static void co2Update(void) {
|
||||||
|
if (!configuration.hasSensorS8) {
|
||||||
|
// Device don't have S8 sensor
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int value = ag.s8.getCo2();
|
int value = ag.s8.getCo2();
|
||||||
if (utils::isValidCO2(value)) {
|
if (utils::isValidCO2(value)) {
|
||||||
measurements.CO2 = value;
|
measurements.update(Measurements::CO2, value);
|
||||||
getCO2FailCount = 0;
|
|
||||||
Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2);
|
|
||||||
} else {
|
} else {
|
||||||
getCO2FailCount++;
|
measurements.update(Measurements::CO2, utils::getInvalidCO2());
|
||||||
Serial.printf("Get CO2 failed: %d\r\n", getCO2FailCount);
|
|
||||||
if (getCO2FailCount >= 3) {
|
|
||||||
measurements.CO2 = utils::getInvalidCO2();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,10 +263,17 @@ static void mdnsInit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void initMqtt(void) {
|
static void initMqtt(void) {
|
||||||
if (mqttClient.begin(configuration.getMqttBrokerUri())) {
|
String mqttUri = configuration.getMqttBrokerUri();
|
||||||
Serial.println("Setup connect to MQTT broker successful");
|
if (mqttUri.isEmpty()) {
|
||||||
|
Serial.println(
|
||||||
|
"MQTT is not configured, skipping initialization of MQTT client");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mqttClient.begin(mqttUri)) {
|
||||||
|
Serial.println("Successfully connected to MQTT broker");
|
||||||
} else {
|
} else {
|
||||||
Serial.println("setup Connect to MQTT broker failed");
|
Serial.println("Connection to MQTT broker failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,9 +338,7 @@ static void factoryConfigReset(void) {
|
|||||||
|
|
||||||
static void wdgFeedUpdate(void) {
|
static void wdgFeedUpdate(void) {
|
||||||
ag.watchdog.reset();
|
ag.watchdog.reset();
|
||||||
Serial.println();
|
Serial.println("External watchdog feed!");
|
||||||
Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset");
|
|
||||||
Serial.println();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sgp41Init(void) {
|
static bool sgp41Init(void) {
|
||||||
@ -370,8 +373,7 @@ static void mqttHandle(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mqttClient.isConnected()) {
|
if (mqttClient.isConnected()) {
|
||||||
String payload = measurements.toString(true, fwMode, wifiConnector.RSSI(),
|
String payload = measurements.toString(true, fwMode, wifiConnector.RSSI(), ag, configuration);
|
||||||
&ag, &configuration);
|
|
||||||
String topic = "airgradient/readings/" + ag.deviceId();
|
String topic = "airgradient/readings/" + ag.deviceId();
|
||||||
if (mqttClient.publish(topic.c_str(), payload.c_str(), payload.length())) {
|
if (mqttClient.publish(topic.c_str(), payload.c_str(), payload.length())) {
|
||||||
Serial.println("MQTT sync success");
|
Serial.println("MQTT sync success");
|
||||||
@ -542,84 +544,98 @@ static void oledDisplaySchedule(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void updateTvoc(void) {
|
static void updateTvoc(void) {
|
||||||
measurements.TVOC = ag.sgp41.getTvocIndex();
|
if (!configuration.hasSensorSGP) {
|
||||||
measurements.TVOCRaw = ag.sgp41.getTvocRaw();
|
return;
|
||||||
measurements.NOx = ag.sgp41.getNoxIndex();
|
}
|
||||||
measurements.NOxRaw = ag.sgp41.getNoxRaw();
|
|
||||||
|
|
||||||
Serial.println();
|
measurements.update(Measurements::TVOC, ag.sgp41.getTvocIndex());
|
||||||
Serial.printf("TVOC index: %d\r\n", measurements.TVOC);
|
measurements.update(Measurements::TVOCRaw, ag.sgp41.getTvocRaw());
|
||||||
Serial.printf("TVOC raw: %d\r\n", measurements.TVOCRaw);
|
measurements.update(Measurements::NOx, ag.sgp41.getNoxIndex());
|
||||||
Serial.printf("NOx index: %d\r\n", measurements.NOx);
|
measurements.update(Measurements::NOxRaw, ag.sgp41.getNoxRaw());
|
||||||
Serial.printf("NOx raw: %d\r\n", measurements.NOxRaw);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void updatePm(void) {
|
static void updatePm(void) {
|
||||||
if (ag.pms5003.isFailed() == false) {
|
if (ag.pms5003.connected()) {
|
||||||
measurements.pm01_1 = ag.pms5003.getPm01Ae();
|
measurements.update(Measurements::PM01, ag.pms5003.getPm01Ae());
|
||||||
measurements.pm25_1 = ag.pms5003.getPm25Ae();
|
measurements.update(Measurements::PM25, ag.pms5003.getPm25Ae());
|
||||||
measurements.pm10_1 = ag.pms5003.getPm10Ae();
|
measurements.update(Measurements::PM10, ag.pms5003.getPm10Ae());
|
||||||
measurements.pm03PCount_1 = ag.pms5003.getPm03ParticleCount();
|
measurements.update(Measurements::PM03_PC, ag.pms5003.getPm03ParticleCount());
|
||||||
|
|
||||||
Serial.println();
|
|
||||||
Serial.printf("PM1 ug/m3: %d\r\n", measurements.pm01_1);
|
|
||||||
Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_1);
|
|
||||||
Serial.printf("PM10 ug/m3: %d\r\n", measurements.pm10_1);
|
|
||||||
Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1);
|
|
||||||
pmFailCount = 0;
|
|
||||||
} else {
|
} else {
|
||||||
pmFailCount++;
|
measurements.update(Measurements::PM01, utils::getInvalidPmValue());
|
||||||
Serial.printf("PMS read failed: %d\r\n", pmFailCount);
|
measurements.update(Measurements::PM25, utils::getInvalidPmValue());
|
||||||
if (pmFailCount >= 3) {
|
measurements.update(Measurements::PM10, utils::getInvalidPmValue());
|
||||||
measurements.pm01_1 = utils::getInvalidPMS();
|
measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue());
|
||||||
measurements.pm25_1 = utils::getInvalidPMS();
|
|
||||||
measurements.pm10_1 = utils::getInvalidPMS();
|
|
||||||
measurements.pm03PCount_1 = utils::getInvalidPMS();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sendDataToServer(void) {
|
static void sendDataToServer(void) {
|
||||||
|
/** Increment bootcount when send measurements data is scheduled */
|
||||||
|
measurements.bootCount++;
|
||||||
|
|
||||||
/** Ignore send data to server if postToAirGradient disabled */
|
/** Ignore send data to server if postToAirGradient disabled */
|
||||||
if (configuration.isPostDataToAirGradient() == false ||
|
if (configuration.isPostDataToAirGradient() == false ||
|
||||||
configuration.isOfflineMode()) {
|
configuration.isOfflineMode()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(),
|
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), ag, configuration);
|
||||||
&ag, &configuration);
|
|
||||||
if (apiClient.postToServer(syncData)) {
|
if (apiClient.postToServer(syncData)) {
|
||||||
ag.watchdog.reset();
|
|
||||||
Serial.println();
|
Serial.println();
|
||||||
Serial.println(
|
Serial.println(
|
||||||
"Online mode and isPostToAirGradient = true: watchdog reset");
|
"Online mode and isPostToAirGradient = true: watchdog reset");
|
||||||
Serial.println();
|
Serial.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
measurements.bootCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tempHumUpdate(void) {
|
static void tempHumUpdate(void) {
|
||||||
delay(100);
|
|
||||||
if (ag.sht.measure()) {
|
if (ag.sht.measure()) {
|
||||||
measurements.Temperature = ag.sht.getTemperature();
|
float temp = ag.sht.getTemperature();
|
||||||
measurements.Humidity = ag.sht.getRelativeHumidity();
|
float rhum = ag.sht.getRelativeHumidity();
|
||||||
|
|
||||||
Serial.printf("Temperature in C: %0.2f\r\n", measurements.Temperature);
|
measurements.update(Measurements::Temperature, temp);
|
||||||
Serial.printf("Relative Humidity: %d\r\n", measurements.Humidity);
|
measurements.update(Measurements::Humidity, rhum);
|
||||||
Serial.printf("Temperature compensated in C: %0.2f\r\n",
|
|
||||||
measurements.Temperature);
|
|
||||||
Serial.printf("Relative Humidity compensated: %d\r\n",
|
|
||||||
measurements.Humidity);
|
|
||||||
|
|
||||||
// Update compensation temperature and humidity for SGP41
|
// Update compensation temperature and humidity for SGP41
|
||||||
if (configuration.hasSensorSGP) {
|
if (configuration.hasSensorSGP) {
|
||||||
ag.sgp41.setCompensationTemperatureHumidity(measurements.Temperature,
|
ag.sgp41.setCompensationTemperatureHumidity(temp, rhum);
|
||||||
measurements.Humidity);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
measurements.update(Measurements::Temperature, utils::getInvalidTemperature());
|
||||||
|
measurements.update(Measurements::Humidity, utils::getInvalidHumidity());
|
||||||
Serial.println("SHT read failed");
|
Serial.println("SHT read failed");
|
||||||
measurements.Temperature = utils::getInvalidTemperature();
|
|
||||||
measurements.Humidity = utils::getInvalidHumidity();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set max period for each measurement type based on sensor update interval*/
|
||||||
|
void setMeasurementMaxPeriod() {
|
||||||
|
/// Max period for S8 sensors measurements
|
||||||
|
measurements.maxPeriod(Measurements::CO2, calculateMaxPeriod(SENSOR_CO2_UPDATE_INTERVAL));
|
||||||
|
/// Max period for SGP sensors measurements
|
||||||
|
measurements.maxPeriod(Measurements::TVOC, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::TVOCRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::NOx, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::NOxRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL));
|
||||||
|
/// Max period for PMS sensors measurements
|
||||||
|
measurements.maxPeriod(Measurements::PM25, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::PM01, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::PM10, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::PM03_PC, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
// Temperature and Humidity
|
||||||
|
if (configuration.hasSensorSHT) {
|
||||||
|
/// Max period for SHT sensors measurements
|
||||||
|
measurements.maxPeriod(Measurements::Temperature,
|
||||||
|
calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::Humidity,
|
||||||
|
calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL));
|
||||||
|
} else {
|
||||||
|
/// Temp and hum data retrieved from PMS5003T sensor
|
||||||
|
measurements.maxPeriod(Measurements::Temperature,
|
||||||
|
calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::Humidity, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int calculateMaxPeriod(int updateInterval) {
|
||||||
|
// 0.5 is 50% reduced interval for max period
|
||||||
|
return (SERVER_SYNC_INTERVAL - (SERVER_SYNC_INTERVAL * 0.5)) / updateInterval;
|
||||||
|
}
|
@ -53,9 +53,8 @@ void LocalServer::_GET_metrics(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LocalServer::_GET_measure(void) {
|
void LocalServer::_GET_measure(void) {
|
||||||
server.send(
|
String toSend = measure.toString(true, fwMode, wifiConnector.RSSI(), *ag, config);
|
||||||
200, "application/json",
|
server.send(200, "application/json", toSend);
|
||||||
measure.toString(true, fwMode, wifiConnector.RSSI(), ag, &config));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalServer::setFwMode(AgFirmwareMode fwMode) { this->fwMode = fwMode; }
|
void LocalServer::setFwMode(AgFirmwareMode fwMode) { this->fwMode = fwMode; }
|
||||||
|
@ -67,50 +67,61 @@ String OpenMetrics::getPayload(void) {
|
|||||||
|
|
||||||
float _temp = utils::getInvalidTemperature();
|
float _temp = utils::getInvalidTemperature();
|
||||||
float _hum = utils::getInvalidHumidity();
|
float _hum = utils::getInvalidHumidity();
|
||||||
int pm01 = utils::getInvalidPMS();
|
int pm01 = utils::getInvalidPmValue();
|
||||||
int pm25 = utils::getInvalidPMS();
|
int pm25 = utils::getInvalidPmValue();
|
||||||
int pm10 = utils::getInvalidPMS();
|
int pm10 = utils::getInvalidPmValue();
|
||||||
int pm03PCount = utils::getInvalidPMS();
|
int pm03PCount = utils::getInvalidPmValue();
|
||||||
int atmpCompensated = utils::getInvalidTemperature();
|
int atmpCompensated = utils::getInvalidTemperature();
|
||||||
int ahumCompensated = utils::getInvalidHumidity();
|
int ahumCompensated = utils::getInvalidHumidity();
|
||||||
|
int tvoc = utils::getInvalidVOC();
|
||||||
|
int tvoc_raw = utils::getInvalidVOC();
|
||||||
|
int nox = utils::getInvalidNOx();
|
||||||
|
int nox_raw = utils::getInvalidNOx();
|
||||||
|
|
||||||
if (config.hasSensorSHT) {
|
if (config.hasSensorSHT) {
|
||||||
_temp = measure.Temperature;
|
_temp = measure.getFloat(Measurements::Temperature);
|
||||||
_hum = measure.Humidity;
|
_hum = measure.getFloat(Measurements::Humidity);
|
||||||
atmpCompensated = _temp;
|
atmpCompensated = _temp;
|
||||||
ahumCompensated = _hum;
|
ahumCompensated = _hum;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.hasSensorPMS1) {
|
if (config.hasSensorPMS1) {
|
||||||
pm01 = measure.pm01_1;
|
pm01 = measure.get(Measurements::PM01);
|
||||||
pm25 = measure.pm25_1;
|
pm25 = measure.get(Measurements::PM25);
|
||||||
pm10 = measure.pm10_1;
|
pm10 = measure.get(Measurements::PM10);
|
||||||
pm03PCount = measure.pm03PCount_1;
|
pm03PCount = measure.get(Measurements::PM03_PC);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.hasSensorSGP) {
|
||||||
|
tvoc = measure.get(Measurements::TVOC);
|
||||||
|
tvoc_raw = measure.get(Measurements::TVOCRaw);
|
||||||
|
nox = measure.get(Measurements::NOx);
|
||||||
|
nox_raw = measure.get(Measurements::NOxRaw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.hasSensorPMS1) {
|
if (config.hasSensorPMS1) {
|
||||||
if (utils::isValidPMS(pm01)) {
|
if (utils::isValidPm(pm01)) {
|
||||||
add_metric("pm1",
|
add_metric("pm1",
|
||||||
"PM1.0 concentration as measured by the AirGradient PMS "
|
"PM1.0 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in micrograms per cubic meter",
|
"sensor, in micrograms per cubic meter",
|
||||||
"gauge", "ugm3");
|
"gauge", "ugm3");
|
||||||
add_metric_point("", String(pm01));
|
add_metric_point("", String(pm01));
|
||||||
}
|
}
|
||||||
if (utils::isValidPMS(pm25)) {
|
if (utils::isValidPm(pm25)) {
|
||||||
add_metric("pm2d5",
|
add_metric("pm2d5",
|
||||||
"PM2.5 concentration as measured by the AirGradient PMS "
|
"PM2.5 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in micrograms per cubic meter",
|
"sensor, in micrograms per cubic meter",
|
||||||
"gauge", "ugm3");
|
"gauge", "ugm3");
|
||||||
add_metric_point("", String(pm25));
|
add_metric_point("", String(pm25));
|
||||||
}
|
}
|
||||||
if (utils::isValidPMS(pm10)) {
|
if (utils::isValidPm(pm10)) {
|
||||||
add_metric("pm10",
|
add_metric("pm10",
|
||||||
"PM10 concentration as measured by the AirGradient PMS "
|
"PM10 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in micrograms per cubic meter",
|
"sensor, in micrograms per cubic meter",
|
||||||
"gauge", "ugm3");
|
"gauge", "ugm3");
|
||||||
add_metric_point("", String(pm10));
|
add_metric_point("", String(pm10));
|
||||||
}
|
}
|
||||||
if (utils::isValidPMS03Count(pm03PCount)) {
|
if (utils::isValidPm03Count(pm03PCount)) {
|
||||||
add_metric("pm0d3",
|
add_metric("pm0d3",
|
||||||
"PM0.3 concentration as measured by the AirGradient PMS "
|
"PM0.3 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in number of particules per 100 milliliters",
|
"sensor, in number of particules per 100 milliliters",
|
||||||
@ -120,33 +131,33 @@ String OpenMetrics::getPayload(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (config.hasSensorSGP) {
|
if (config.hasSensorSGP) {
|
||||||
if (utils::isValidVOC(measure.TVOC)) {
|
if (utils::isValidVOC(tvoc)) {
|
||||||
add_metric("tvoc_index",
|
add_metric("tvoc_index",
|
||||||
"The processed Total Volatile Organic Compounds (TVOC) index "
|
"The processed Total Volatile Organic Compounds (TVOC) index "
|
||||||
"as measured by the AirGradient SGP sensor",
|
"as measured by the AirGradient SGP sensor",
|
||||||
"gauge");
|
"gauge");
|
||||||
add_metric_point("", String(measure.TVOC));
|
add_metric_point("", String(tvoc));
|
||||||
}
|
}
|
||||||
if (utils::isValidVOC(measure.TVOCRaw)) {
|
if (utils::isValidVOC(tvoc_raw)) {
|
||||||
add_metric("tvoc_raw",
|
add_metric("tvoc_raw",
|
||||||
"The raw input value to the Total Volatile Organic Compounds "
|
"The raw input value to the Total Volatile Organic Compounds "
|
||||||
"(TVOC) index as measured by the AirGradient SGP sensor",
|
"(TVOC) index as measured by the AirGradient SGP sensor",
|
||||||
"gauge");
|
"gauge");
|
||||||
add_metric_point("", String(measure.TVOCRaw));
|
add_metric_point("", String(tvoc_raw));
|
||||||
}
|
}
|
||||||
if (utils::isValidNOx(measure.NOx)) {
|
if (utils::isValidNOx(nox)) {
|
||||||
add_metric("nox_index",
|
add_metric("nox_index",
|
||||||
"The processed Nitrous Oxide (NOx) index as measured by the "
|
"The processed Nitrous Oxide (NOx) index as measured by the "
|
||||||
"AirGradient SGP sensor",
|
"AirGradient SGP sensor",
|
||||||
"gauge");
|
"gauge");
|
||||||
add_metric_point("", String(measure.NOx));
|
add_metric_point("", String(nox));
|
||||||
}
|
}
|
||||||
if (utils::isValidNOx(measure.NOxRaw)) {
|
if (utils::isValidNOx(nox_raw)) {
|
||||||
add_metric("nox_raw",
|
add_metric("nox_raw",
|
||||||
"The raw input value to the Nitrous Oxide (NOx) index as "
|
"The raw input value to the Nitrous Oxide (NOx) index as "
|
||||||
"measured by the AirGradient SGP sensor",
|
"measured by the AirGradient SGP sensor",
|
||||||
"gauge");
|
"gauge");
|
||||||
add_metric_point("", String(measure.NOxRaw));
|
add_metric_point("", String(nox_raw));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,9 +49,8 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|||||||
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */
|
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */
|
||||||
#define SENSOR_CO2_UPDATE_INTERVAL 4000 /** ms */
|
#define SENSOR_CO2_UPDATE_INTERVAL 4000 /** ms */
|
||||||
#define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */
|
#define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */
|
||||||
#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 2000 /** ms */
|
#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 6000 /** ms */
|
||||||
#define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */
|
#define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */
|
||||||
#define FIRMWARE_CHECK_FOR_UPDATE_MS (60 * 60 * 1000) /** ms */
|
|
||||||
|
|
||||||
static AirGradient ag(DIY_PRO_INDOOR_V4_2);
|
static AirGradient ag(DIY_PRO_INDOOR_V4_2);
|
||||||
static Configuration configuration(Serial);
|
static Configuration configuration(Serial);
|
||||||
@ -68,9 +67,7 @@ static LocalServer localServer(Serial, openMetrics, measurements, configuration,
|
|||||||
wifiConnector);
|
wifiConnector);
|
||||||
static MqttClient mqttClient(Serial);
|
static MqttClient mqttClient(Serial);
|
||||||
|
|
||||||
static int pmFailCount = 0;
|
|
||||||
static uint32_t factoryBtnPressTime = 0;
|
static uint32_t factoryBtnPressTime = 0;
|
||||||
static int getCO2FailCount = 0;
|
|
||||||
static AgFirmwareMode fwMode = FW_MODE_I_42PS;
|
static AgFirmwareMode fwMode = FW_MODE_I_42PS;
|
||||||
|
|
||||||
static String fwNewVersion;
|
static String fwNewVersion;
|
||||||
@ -92,6 +89,8 @@ static void wdgFeedUpdate(void);
|
|||||||
static bool sgp41Init(void);
|
static bool sgp41Init(void);
|
||||||
static void wifiFactoryConfigure(void);
|
static void wifiFactoryConfigure(void);
|
||||||
static void mqttHandle(void);
|
static void mqttHandle(void);
|
||||||
|
static int calculateMaxPeriod(int updateInterval);
|
||||||
|
static void setMeasurementMaxPeriod();
|
||||||
|
|
||||||
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplaySchedule);
|
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplaySchedule);
|
||||||
AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL,
|
AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL,
|
||||||
@ -132,6 +131,10 @@ void setup() {
|
|||||||
|
|
||||||
/** Init sensor */
|
/** Init sensor */
|
||||||
boardInit();
|
boardInit();
|
||||||
|
setMeasurementMaxPeriod();
|
||||||
|
|
||||||
|
// Uncomment below line to print every measurements reading update
|
||||||
|
// measurements.setDebug(true);
|
||||||
|
|
||||||
/** Connecting wifi */
|
/** Connecting wifi */
|
||||||
bool connectToWifi = false;
|
bool connectToWifi = false;
|
||||||
@ -232,11 +235,7 @@ void loop() {
|
|||||||
tvocSchedule.run();
|
tvocSchedule.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Auto reset watchdog timer if offline mode or postDataToAirGradient */
|
watchdogFeedSchedule.run();
|
||||||
if (configuration.isOfflineMode() ||
|
|
||||||
(configuration.isPostDataToAirGradient() == false)) {
|
|
||||||
watchdogFeedSchedule.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Check for handle WiFi reconnect */
|
/** Check for handle WiFi reconnect */
|
||||||
wifiConnector.handle();
|
wifiConnector.handle();
|
||||||
@ -260,17 +259,16 @@ void loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void co2Update(void) {
|
static void co2Update(void) {
|
||||||
|
if (!configuration.hasSensorS8) {
|
||||||
|
// Device don't have S8 sensor
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int value = ag.s8.getCo2();
|
int value = ag.s8.getCo2();
|
||||||
if (utils::isValidCO2(value)) {
|
if (utils::isValidCO2(value)) {
|
||||||
measurements.CO2 = value;
|
measurements.update(Measurements::CO2, value);
|
||||||
getCO2FailCount = 0;
|
|
||||||
Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2);
|
|
||||||
} else {
|
} else {
|
||||||
getCO2FailCount++;
|
measurements.update(Measurements::CO2, utils::getInvalidCO2());
|
||||||
Serial.printf("Get CO2 failed: %d\r\n", getCO2FailCount);
|
|
||||||
if (getCO2FailCount >= 3) {
|
|
||||||
measurements.CO2 = utils::getInvalidCO2();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,10 +290,17 @@ static void mdnsInit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void initMqtt(void) {
|
static void initMqtt(void) {
|
||||||
if (mqttClient.begin(configuration.getMqttBrokerUri())) {
|
String mqttUri = configuration.getMqttBrokerUri();
|
||||||
Serial.println("Setup connect to MQTT broker successful");
|
if (mqttUri.isEmpty()) {
|
||||||
|
Serial.println(
|
||||||
|
"MQTT is not configured, skipping initialization of MQTT client");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mqttClient.begin(mqttUri)) {
|
||||||
|
Serial.println("Successfully connected to MQTT broker");
|
||||||
} else {
|
} else {
|
||||||
Serial.println("setup Connect to MQTT broker failed");
|
Serial.println("Connection to MQTT broker failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,8 +333,6 @@ static void factoryConfigReset(void) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
/** Reset WIFI */
|
/** Reset WIFI */
|
||||||
// WiFi.enableSTA(true); // Incase offline mode
|
|
||||||
// WiFi.disconnect(true, true);
|
|
||||||
wifiConnector.reset();
|
wifiConnector.reset();
|
||||||
|
|
||||||
/** Reset local config */
|
/** Reset local config */
|
||||||
@ -358,9 +361,7 @@ static void factoryConfigReset(void) {
|
|||||||
|
|
||||||
static void wdgFeedUpdate(void) {
|
static void wdgFeedUpdate(void) {
|
||||||
ag.watchdog.reset();
|
ag.watchdog.reset();
|
||||||
Serial.println();
|
Serial.println("External watchdog feed!");
|
||||||
Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset");
|
|
||||||
Serial.println();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sgp41Init(void) {
|
static bool sgp41Init(void) {
|
||||||
@ -395,8 +396,7 @@ static void mqttHandle(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mqttClient.isConnected()) {
|
if (mqttClient.isConnected()) {
|
||||||
String payload = measurements.toString(true, fwMode, wifiConnector.RSSI(),
|
String payload = measurements.toString(true, fwMode, wifiConnector.RSSI(), ag, configuration);
|
||||||
&ag, &configuration);
|
|
||||||
String topic = "airgradient/readings/" + ag.deviceId();
|
String topic = "airgradient/readings/" + ag.deviceId();
|
||||||
if (mqttClient.publish(topic.c_str(), payload.c_str(), payload.length())) {
|
if (mqttClient.publish(topic.c_str(), payload.c_str(), payload.length())) {
|
||||||
Serial.println("MQTT sync success");
|
Serial.println("MQTT sync success");
|
||||||
@ -585,84 +585,98 @@ static void oledDisplaySchedule(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void updateTvoc(void) {
|
static void updateTvoc(void) {
|
||||||
measurements.TVOC = ag.sgp41.getTvocIndex();
|
if (!configuration.hasSensorSGP) {
|
||||||
measurements.TVOCRaw = ag.sgp41.getTvocRaw();
|
return;
|
||||||
measurements.NOx = ag.sgp41.getNoxIndex();
|
}
|
||||||
measurements.NOxRaw = ag.sgp41.getNoxRaw();
|
|
||||||
|
|
||||||
Serial.println();
|
measurements.update(Measurements::TVOC, ag.sgp41.getTvocIndex());
|
||||||
Serial.printf("TVOC index: %d\r\n", measurements.TVOC);
|
measurements.update(Measurements::TVOCRaw, ag.sgp41.getTvocRaw());
|
||||||
Serial.printf("TVOC raw: %d\r\n", measurements.TVOCRaw);
|
measurements.update(Measurements::NOx, ag.sgp41.getNoxIndex());
|
||||||
Serial.printf("NOx index: %d\r\n", measurements.NOx);
|
measurements.update(Measurements::NOxRaw, ag.sgp41.getNoxRaw());
|
||||||
Serial.printf("NOx raw: %d\r\n", measurements.NOxRaw);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void updatePm(void) {
|
static void updatePm(void) {
|
||||||
if (ag.pms5003.isFailed() == false) {
|
if (ag.pms5003.connected()) {
|
||||||
measurements.pm01_1 = ag.pms5003.getPm01Ae();
|
measurements.update(Measurements::PM01, ag.pms5003.getPm01Ae());
|
||||||
measurements.pm25_1 = ag.pms5003.getPm25Ae();
|
measurements.update(Measurements::PM25, ag.pms5003.getPm25Ae());
|
||||||
measurements.pm10_1 = ag.pms5003.getPm10Ae();
|
measurements.update(Measurements::PM10, ag.pms5003.getPm10Ae());
|
||||||
measurements.pm03PCount_1 = ag.pms5003.getPm03ParticleCount();
|
measurements.update(Measurements::PM03_PC, ag.pms5003.getPm03ParticleCount());
|
||||||
|
|
||||||
Serial.println();
|
|
||||||
Serial.printf("PM1 ug/m3: %d\r\n", measurements.pm01_1);
|
|
||||||
Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_1);
|
|
||||||
Serial.printf("PM10 ug/m3: %d\r\n", measurements.pm10_1);
|
|
||||||
Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1);
|
|
||||||
pmFailCount = 0;
|
|
||||||
} else {
|
} else {
|
||||||
pmFailCount++;
|
measurements.update(Measurements::PM01, utils::getInvalidPmValue());
|
||||||
Serial.printf("PMS read failed: %d\r\n", pmFailCount);
|
measurements.update(Measurements::PM25, utils::getInvalidPmValue());
|
||||||
if (pmFailCount >= 3) {
|
measurements.update(Measurements::PM10, utils::getInvalidPmValue());
|
||||||
measurements.pm01_1 = utils::getInvalidPMS();
|
measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue());
|
||||||
measurements.pm25_1 = utils::getInvalidPMS();
|
|
||||||
measurements.pm10_1 = utils::getInvalidPMS();
|
|
||||||
measurements.pm03PCount_1 = utils::getInvalidPMS();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sendDataToServer(void) {
|
static void sendDataToServer(void) {
|
||||||
|
/** Increment bootcount when send measurements data is scheduled */
|
||||||
|
measurements.bootCount++;
|
||||||
|
|
||||||
/** Ignore send data to server if postToAirGradient disabled */
|
/** Ignore send data to server if postToAirGradient disabled */
|
||||||
if (configuration.isPostDataToAirGradient() == false ||
|
if (configuration.isPostDataToAirGradient() == false ||
|
||||||
configuration.isOfflineMode()) {
|
configuration.isOfflineMode()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(),
|
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), ag, configuration);
|
||||||
&ag, &configuration);
|
|
||||||
if (apiClient.postToServer(syncData)) {
|
if (apiClient.postToServer(syncData)) {
|
||||||
ag.watchdog.reset();
|
|
||||||
Serial.println();
|
Serial.println();
|
||||||
Serial.println(
|
Serial.println(
|
||||||
"Online mode and isPostToAirGradient = true: watchdog reset");
|
"Online mode and isPostToAirGradient = true: watchdog reset");
|
||||||
Serial.println();
|
Serial.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
measurements.bootCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tempHumUpdate(void) {
|
static void tempHumUpdate(void) {
|
||||||
delay(100);
|
|
||||||
if (ag.sht.measure()) {
|
if (ag.sht.measure()) {
|
||||||
measurements.Temperature = ag.sht.getTemperature();
|
float temp = ag.sht.getTemperature();
|
||||||
measurements.Humidity = ag.sht.getRelativeHumidity();
|
float rhum = ag.sht.getRelativeHumidity();
|
||||||
|
|
||||||
Serial.printf("Temperature in C: %0.2f\r\n", measurements.Temperature);
|
measurements.update(Measurements::Temperature, temp);
|
||||||
Serial.printf("Relative Humidity: %d\r\n", measurements.Humidity);
|
measurements.update(Measurements::Humidity, rhum);
|
||||||
Serial.printf("Temperature compensated in C: %0.2f\r\n",
|
|
||||||
measurements.Temperature);
|
|
||||||
Serial.printf("Relative Humidity compensated: %d\r\n",
|
|
||||||
measurements.Humidity);
|
|
||||||
|
|
||||||
// Update compensation temperature and humidity for SGP41
|
// Update compensation temperature and humidity for SGP41
|
||||||
if (configuration.hasSensorSGP) {
|
if (configuration.hasSensorSGP) {
|
||||||
ag.sgp41.setCompensationTemperatureHumidity(measurements.Temperature,
|
ag.sgp41.setCompensationTemperatureHumidity(temp, rhum);
|
||||||
measurements.Humidity);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
measurements.update(Measurements::Temperature, utils::getInvalidTemperature());
|
||||||
|
measurements.update(Measurements::Humidity, utils::getInvalidHumidity());
|
||||||
Serial.println("SHT read failed");
|
Serial.println("SHT read failed");
|
||||||
measurements.Temperature = utils::getInvalidTemperature();
|
|
||||||
measurements.Humidity = utils::getInvalidHumidity();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set max period for each measurement type based on sensor update interval*/
|
||||||
|
void setMeasurementMaxPeriod() {
|
||||||
|
/// Max period for S8 sensors measurements
|
||||||
|
measurements.maxPeriod(Measurements::CO2, calculateMaxPeriod(SENSOR_CO2_UPDATE_INTERVAL));
|
||||||
|
/// Max period for SGP sensors measurements
|
||||||
|
measurements.maxPeriod(Measurements::TVOC, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::TVOCRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::NOx, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::NOxRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL));
|
||||||
|
/// Max period for PMS sensors measurements
|
||||||
|
measurements.maxPeriod(Measurements::PM25, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::PM01, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::PM10, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::PM03_PC, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
// Temperature and Humidity
|
||||||
|
if (configuration.hasSensorSHT) {
|
||||||
|
/// Max period for SHT sensors measurements
|
||||||
|
measurements.maxPeriod(Measurements::Temperature,
|
||||||
|
calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::Humidity,
|
||||||
|
calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL));
|
||||||
|
} else {
|
||||||
|
/// Temp and hum data retrieved from PMS5003T sensor
|
||||||
|
measurements.maxPeriod(Measurements::Temperature,
|
||||||
|
calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::Humidity, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int calculateMaxPeriod(int updateInterval) {
|
||||||
|
// 0.5 is 50% reduced interval for max period
|
||||||
|
return (SERVER_SYNC_INTERVAL - (SERVER_SYNC_INTERVAL * 0.5)) / updateInterval;
|
||||||
|
}
|
@ -53,9 +53,8 @@ void LocalServer::_GET_metrics(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LocalServer::_GET_measure(void) {
|
void LocalServer::_GET_measure(void) {
|
||||||
server.send(
|
String toSend = measure.toString(true, fwMode, wifiConnector.RSSI(), *ag, config);
|
||||||
200, "application/json",
|
server.send(200, "application/json", toSend);
|
||||||
measure.toString(true, fwMode, wifiConnector.RSSI(), ag, &config));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalServer::setFwMode(AgFirmwareMode fwMode) { this->fwMode = fwMode; }
|
void LocalServer::setFwMode(AgFirmwareMode fwMode) { this->fwMode = fwMode; }
|
||||||
|
@ -67,50 +67,61 @@ String OpenMetrics::getPayload(void) {
|
|||||||
|
|
||||||
float _temp = utils::getInvalidTemperature();
|
float _temp = utils::getInvalidTemperature();
|
||||||
float _hum = utils::getInvalidHumidity();
|
float _hum = utils::getInvalidHumidity();
|
||||||
int pm01 = utils::getInvalidPMS();
|
int pm01 = utils::getInvalidPmValue();
|
||||||
int pm25 = utils::getInvalidPMS();
|
int pm25 = utils::getInvalidPmValue();
|
||||||
int pm10 = utils::getInvalidPMS();
|
int pm10 = utils::getInvalidPmValue();
|
||||||
int pm03PCount = utils::getInvalidPMS();
|
int pm03PCount = utils::getInvalidPmValue();
|
||||||
int atmpCompensated = utils::getInvalidTemperature();
|
int atmpCompensated = utils::getInvalidTemperature();
|
||||||
int ahumCompensated = utils::getInvalidHumidity();
|
int ahumCompensated = utils::getInvalidHumidity();
|
||||||
|
int tvoc = utils::getInvalidVOC();
|
||||||
|
int tvoc_raw = utils::getInvalidVOC();
|
||||||
|
int nox = utils::getInvalidNOx();
|
||||||
|
int nox_raw = utils::getInvalidNOx();
|
||||||
|
|
||||||
if (config.hasSensorSHT) {
|
if (config.hasSensorSHT) {
|
||||||
_temp = measure.Temperature;
|
_temp = measure.getFloat(Measurements::Temperature);
|
||||||
_hum = measure.Humidity;
|
_hum = measure.getFloat(Measurements::Humidity);
|
||||||
atmpCompensated = _temp;
|
atmpCompensated = _temp;
|
||||||
ahumCompensated = _hum;
|
ahumCompensated = _hum;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.hasSensorPMS1) {
|
if (config.hasSensorPMS1) {
|
||||||
pm01 = measure.pm01_1;
|
pm01 = measure.get(Measurements::PM01);
|
||||||
pm25 = measure.pm25_1;
|
pm25 = measure.get(Measurements::PM25);
|
||||||
pm10 = measure.pm10_1;
|
pm10 = measure.get(Measurements::PM10);
|
||||||
pm03PCount = measure.pm03PCount_1;
|
pm03PCount = measure.get(Measurements::PM03_PC);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.hasSensorSGP) {
|
||||||
|
tvoc = measure.get(Measurements::TVOC);
|
||||||
|
tvoc_raw = measure.get(Measurements::TVOCRaw);
|
||||||
|
nox = measure.get(Measurements::NOx);
|
||||||
|
nox_raw = measure.get(Measurements::NOxRaw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.hasSensorPMS1) {
|
if (config.hasSensorPMS1) {
|
||||||
if (utils::isValidPMS(pm01)) {
|
if (utils::isValidPm(pm01)) {
|
||||||
add_metric("pm1",
|
add_metric("pm1",
|
||||||
"PM1.0 concentration as measured by the AirGradient PMS "
|
"PM1.0 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in micrograms per cubic meter",
|
"sensor, in micrograms per cubic meter",
|
||||||
"gauge", "ugm3");
|
"gauge", "ugm3");
|
||||||
add_metric_point("", String(pm01));
|
add_metric_point("", String(pm01));
|
||||||
}
|
}
|
||||||
if (utils::isValidPMS(pm25)) {
|
if (utils::isValidPm(pm25)) {
|
||||||
add_metric("pm2d5",
|
add_metric("pm2d5",
|
||||||
"PM2.5 concentration as measured by the AirGradient PMS "
|
"PM2.5 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in micrograms per cubic meter",
|
"sensor, in micrograms per cubic meter",
|
||||||
"gauge", "ugm3");
|
"gauge", "ugm3");
|
||||||
add_metric_point("", String(pm25));
|
add_metric_point("", String(pm25));
|
||||||
}
|
}
|
||||||
if (utils::isValidPMS(pm10)) {
|
if (utils::isValidPm(pm10)) {
|
||||||
add_metric("pm10",
|
add_metric("pm10",
|
||||||
"PM10 concentration as measured by the AirGradient PMS "
|
"PM10 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in micrograms per cubic meter",
|
"sensor, in micrograms per cubic meter",
|
||||||
"gauge", "ugm3");
|
"gauge", "ugm3");
|
||||||
add_metric_point("", String(pm10));
|
add_metric_point("", String(pm10));
|
||||||
}
|
}
|
||||||
if (utils::isValidPMS03Count(pm03PCount)) {
|
if (utils::isValidPm03Count(pm03PCount)) {
|
||||||
add_metric("pm0d3",
|
add_metric("pm0d3",
|
||||||
"PM0.3 concentration as measured by the AirGradient PMS "
|
"PM0.3 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in number of particules per 100 milliliters",
|
"sensor, in number of particules per 100 milliliters",
|
||||||
@ -120,33 +131,33 @@ String OpenMetrics::getPayload(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (config.hasSensorSGP) {
|
if (config.hasSensorSGP) {
|
||||||
if (utils::isValidVOC(measure.TVOC)) {
|
if (utils::isValidVOC(tvoc)) {
|
||||||
add_metric("tvoc_index",
|
add_metric("tvoc_index",
|
||||||
"The processed Total Volatile Organic Compounds (TVOC) index "
|
"The processed Total Volatile Organic Compounds (TVOC) index "
|
||||||
"as measured by the AirGradient SGP sensor",
|
"as measured by the AirGradient SGP sensor",
|
||||||
"gauge");
|
"gauge");
|
||||||
add_metric_point("", String(measure.TVOC));
|
add_metric_point("", String(tvoc));
|
||||||
}
|
}
|
||||||
if (utils::isValidVOC(measure.TVOCRaw)) {
|
if (utils::isValidVOC(tvoc_raw)) {
|
||||||
add_metric("tvoc_raw",
|
add_metric("tvoc_raw",
|
||||||
"The raw input value to the Total Volatile Organic Compounds "
|
"The raw input value to the Total Volatile Organic Compounds "
|
||||||
"(TVOC) index as measured by the AirGradient SGP sensor",
|
"(TVOC) index as measured by the AirGradient SGP sensor",
|
||||||
"gauge");
|
"gauge");
|
||||||
add_metric_point("", String(measure.TVOCRaw));
|
add_metric_point("", String(tvoc_raw));
|
||||||
}
|
}
|
||||||
if (utils::isValidNOx(measure.NOx)) {
|
if (utils::isValidNOx(nox)) {
|
||||||
add_metric("nox_index",
|
add_metric("nox_index",
|
||||||
"The processed Nitrous Oxide (NOx) index as measured by the "
|
"The processed Nitrous Oxide (NOx) index as measured by the "
|
||||||
"AirGradient SGP sensor",
|
"AirGradient SGP sensor",
|
||||||
"gauge");
|
"gauge");
|
||||||
add_metric_point("", String(measure.NOx));
|
add_metric_point("", String(nox));
|
||||||
}
|
}
|
||||||
if (utils::isValidNOx(measure.NOxRaw)) {
|
if (utils::isValidNOx(nox_raw)) {
|
||||||
add_metric("nox_raw",
|
add_metric("nox_raw",
|
||||||
"The raw input value to the Nitrous Oxide (NOx) index as "
|
"The raw input value to the Nitrous Oxide (NOx) index as "
|
||||||
"measured by the AirGradient SGP sensor",
|
"measured by the AirGradient SGP sensor",
|
||||||
"gauge");
|
"gauge");
|
||||||
add_metric_point("", String(measure.NOxRaw));
|
add_metric_point("", String(nox_raw));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,9 +64,8 @@ void LocalServer::_GET_metrics(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LocalServer::_GET_measure(void) {
|
void LocalServer::_GET_measure(void) {
|
||||||
server.send(
|
String toSend = measure.toString(true, fwMode, wifiConnector.RSSI(), *ag, config);
|
||||||
200, "application/json",
|
server.send(200, "application/json", toSend);
|
||||||
measure.toString(true, fwMode, wifiConnector.RSSI(), ag, &config));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalServer::setFwMode(AgFirmwareMode fwMode) { this->fwMode = fwMode; }
|
void LocalServer::setFwMode(AgFirmwareMode fwMode) { this->fwMode = fwMode; }
|
||||||
|
@ -62,7 +62,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|||||||
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */
|
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */
|
||||||
#define SENSOR_CO2_UPDATE_INTERVAL 4000 /** ms */
|
#define SENSOR_CO2_UPDATE_INTERVAL 4000 /** ms */
|
||||||
#define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */
|
#define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */
|
||||||
#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 2000 /** ms */
|
#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 6000 /** ms */
|
||||||
#define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */
|
#define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */
|
||||||
#define FIRMWARE_CHECK_FOR_UPDATE_MS (60*60*1000) /** ms */
|
#define FIRMWARE_CHECK_FOR_UPDATE_MS (60*60*1000) /** ms */
|
||||||
|
|
||||||
@ -88,9 +88,7 @@ static OtaHandler otaHandler;
|
|||||||
static LocalServer localServer(Serial, openMetrics, measurements, configuration,
|
static LocalServer localServer(Serial, openMetrics, measurements, configuration,
|
||||||
wifiConnector);
|
wifiConnector);
|
||||||
|
|
||||||
static int pmFailCount = 0;
|
|
||||||
static uint32_t factoryBtnPressTime = 0;
|
static uint32_t factoryBtnPressTime = 0;
|
||||||
static int getCO2FailCount = 0;
|
|
||||||
static AgFirmwareMode fwMode = FW_MODE_I_9PSL;
|
static AgFirmwareMode fwMode = FW_MODE_I_9PSL;
|
||||||
|
|
||||||
static bool ledBarButtonTest = false;
|
static bool ledBarButtonTest = false;
|
||||||
@ -99,9 +97,7 @@ static String fwNewVersion;
|
|||||||
static void boardInit(void);
|
static void boardInit(void);
|
||||||
static void failedHandler(String msg);
|
static void failedHandler(String msg);
|
||||||
static void configurationUpdateSchedule(void);
|
static void configurationUpdateSchedule(void);
|
||||||
static void appLedHandler(void);
|
static void updateDisplayAndLedBar(void);
|
||||||
static void appDispHandler(void);
|
|
||||||
static void oledDisplayLedBarSchedule(void);
|
|
||||||
static void updateTvoc(void);
|
static void updateTvoc(void);
|
||||||
static void updatePm(void);
|
static void updatePm(void);
|
||||||
static void sendDataToServer(void);
|
static void sendDataToServer(void);
|
||||||
@ -118,8 +114,10 @@ static void firmwareCheckForUpdate(void);
|
|||||||
static void otaHandlerCallback(OtaState state, String mesasge);
|
static void otaHandlerCallback(OtaState state, String mesasge);
|
||||||
static void displayExecuteOta(OtaState state, String msg,
|
static void displayExecuteOta(OtaState state, String msg,
|
||||||
int processing);
|
int processing);
|
||||||
|
static int calculateMaxPeriod(int updateInterval);
|
||||||
|
static void setMeasurementMaxPeriod();
|
||||||
|
|
||||||
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplayLedBarSchedule);
|
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, updateDisplayAndLedBar);
|
||||||
AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL,
|
AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL,
|
||||||
configurationUpdateSchedule);
|
configurationUpdateSchedule);
|
||||||
AgSchedule agApiPostSchedule(SERVER_SYNC_INTERVAL, sendDataToServer);
|
AgSchedule agApiPostSchedule(SERVER_SYNC_INTERVAL, sendDataToServer);
|
||||||
@ -168,6 +166,10 @@ void setup() {
|
|||||||
|
|
||||||
/** Init sensor */
|
/** Init sensor */
|
||||||
boardInit();
|
boardInit();
|
||||||
|
setMeasurementMaxPeriod();
|
||||||
|
|
||||||
|
// Uncomment below line to print every measurements reading update
|
||||||
|
measurements.setDebug(true);
|
||||||
|
|
||||||
/** Connecting wifi */
|
/** Connecting wifi */
|
||||||
bool connectToWifi = false;
|
bool connectToWifi = false;
|
||||||
@ -262,8 +264,8 @@ void setup() {
|
|||||||
oledDisplay.setBrightness(configuration.getDisplayBrightness());
|
oledDisplay.setBrightness(configuration.getDisplayBrightness());
|
||||||
}
|
}
|
||||||
|
|
||||||
appLedHandler();
|
// Update display and led bar after finishing setup to show dashboard
|
||||||
appDispHandler();
|
updateDisplayAndLedBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
@ -289,6 +291,11 @@ void loop() {
|
|||||||
if (ag->isOne()) {
|
if (ag->isOne()) {
|
||||||
if (configuration.hasSensorPMS1) {
|
if (configuration.hasSensorPMS1) {
|
||||||
ag->pms5003.handle();
|
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 {
|
} else {
|
||||||
if (configuration.hasSensorPMS1) {
|
if (configuration.hasSensorPMS1) {
|
||||||
@ -299,11 +306,7 @@ void loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Auto reset watchdog timer if offline mode or postDataToAirGradient */
|
watchdogFeedSchedule.run();
|
||||||
if (configuration.isOfflineMode() ||
|
|
||||||
(configuration.isPostDataToAirGradient() == false)) {
|
|
||||||
watchdogFeedSchedule.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Check for handle WiFi reconnect */
|
/** Check for handle WiFi reconnect */
|
||||||
wifiConnector.handle();
|
wifiConnector.handle();
|
||||||
@ -319,17 +322,16 @@ void loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void co2Update(void) {
|
static void co2Update(void) {
|
||||||
|
if (!configuration.hasSensorS8) {
|
||||||
|
// Device don't have S8 sensor
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int value = ag->s8.getCo2();
|
int value = ag->s8.getCo2();
|
||||||
if (utils::isValidCO2(value)) {
|
if (utils::isValidCO2(value)) {
|
||||||
measurements.CO2 = value;
|
measurements.update(Measurements::CO2, value);
|
||||||
getCO2FailCount = 0;
|
|
||||||
Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2);
|
|
||||||
} else {
|
} else {
|
||||||
getCO2FailCount++;
|
measurements.update(Measurements::CO2, utils::getInvalidCO2());
|
||||||
Serial.printf("Get CO2 failed: %d\r\n", getCO2FailCount);
|
|
||||||
if (getCO2FailCount >= 3) {
|
|
||||||
measurements.CO2 = utils::getInvalidCO2();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,8 +364,8 @@ static void createMqttTask(void) {
|
|||||||
|
|
||||||
/** Send data */
|
/** Send data */
|
||||||
if (mqttClient.isConnected()) {
|
if (mqttClient.isConnected()) {
|
||||||
String payload = measurements.toString(
|
String payload =
|
||||||
true, fwMode, wifiConnector.RSSI(), ag, &configuration);
|
measurements.toString(true, fwMode, wifiConnector.RSSI(), *ag, configuration);
|
||||||
String topic = "airgradient/readings/" + ag->deviceId();
|
String topic = "airgradient/readings/" + ag->deviceId();
|
||||||
|
|
||||||
if (mqttClient.publish(topic.c_str(), payload.c_str(),
|
if (mqttClient.publish(topic.c_str(), payload.c_str(),
|
||||||
@ -383,11 +385,18 @@ static void createMqttTask(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void initMqtt(void) {
|
static void initMqtt(void) {
|
||||||
if (mqttClient.begin(configuration.getMqttBrokerUri())) {
|
String mqttUri = configuration.getMqttBrokerUri();
|
||||||
Serial.println("Connect to MQTT broker successful");
|
if (mqttUri.isEmpty()) {
|
||||||
|
Serial.println(
|
||||||
|
"MQTT is not configured, skipping initialization of MQTT client");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mqttClient.begin(mqttUri)) {
|
||||||
|
Serial.println("Successfully connected to MQTT broker");
|
||||||
createMqttTask();
|
createMqttTask();
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Connect to MQTT broker failed");
|
Serial.println("Connection to MQTT broker failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,8 +433,7 @@ static void factoryConfigReset(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Reset WIFI */
|
/** Reset WIFI */
|
||||||
Serial.println("Set wifi connect to 'airgradient' as default");
|
WiFi.disconnect(true, true);
|
||||||
WiFi.begin("airgradient", "cleanair");
|
|
||||||
|
|
||||||
/** Reset local config */
|
/** Reset local config */
|
||||||
configuration.reset();
|
configuration.reset();
|
||||||
@ -444,7 +452,7 @@ static void factoryConfigReset(void) {
|
|||||||
/** Show current content cause reset ignore */
|
/** Show current content cause reset ignore */
|
||||||
factoryBtnPressTime = 0;
|
factoryBtnPressTime = 0;
|
||||||
if (ag->isOne()) {
|
if (ag->isOne()) {
|
||||||
appDispHandler();
|
updateDisplayAndLedBar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -452,7 +460,7 @@ static void factoryConfigReset(void) {
|
|||||||
if (factoryBtnPressTime != 0) {
|
if (factoryBtnPressTime != 0) {
|
||||||
if (ag->isOne()) {
|
if (ag->isOne()) {
|
||||||
/** Restore last display content */
|
/** Restore last display content */
|
||||||
appDispHandler();
|
updateDisplayAndLedBar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
factoryBtnPressTime = 0;
|
factoryBtnPressTime = 0;
|
||||||
@ -461,9 +469,7 @@ static void factoryConfigReset(void) {
|
|||||||
|
|
||||||
static void wdgFeedUpdate(void) {
|
static void wdgFeedUpdate(void) {
|
||||||
ag->watchdog.reset();
|
ag->watchdog.reset();
|
||||||
Serial.println();
|
Serial.println("External watchdog feed!");
|
||||||
Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset");
|
|
||||||
Serial.println();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ledBarEnabledUpdate(void) {
|
static void ledBarEnabledUpdate(void) {
|
||||||
@ -699,7 +705,7 @@ static void oneIndoorInit(void) {
|
|||||||
ledBarEnabledUpdate();
|
ledBarEnabledUpdate();
|
||||||
|
|
||||||
/** Show message init sensor */
|
/** Show message init sensor */
|
||||||
oledDisplay.setText("Sensor", "initializing...", "");
|
oledDisplay.setText("Monitor", "initializing...", "");
|
||||||
|
|
||||||
/** Init sensor SGP41 */
|
/** Init sensor SGP41 */
|
||||||
if (sgp41Init() == false) {
|
if (sgp41Init() == false) {
|
||||||
@ -780,27 +786,27 @@ static void openAirInit(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Try to find the PMS on other difference port with S8 */
|
/** Attempt to detect PM sensors */
|
||||||
if (fwMode == FW_MODE_O_1PST) {
|
if (fwMode == FW_MODE_O_1PST) {
|
||||||
bool pmInitSuccess = false;
|
bool pmInitSuccess = false;
|
||||||
if (serial0Available) {
|
if (serial0Available) {
|
||||||
if (ag->pms5003t_1.begin(Serial0) == false) {
|
if (ag->pms5003t_1.begin(Serial0) == false) {
|
||||||
configuration.hasSensorPMS1 = false;
|
configuration.hasSensorPMS1 = false;
|
||||||
Serial.println("PMS1 sensor not found");
|
Serial.println("No PM sensor detected on Serial0");
|
||||||
} else {
|
} else {
|
||||||
serial0Available = false;
|
serial0Available = false;
|
||||||
pmInitSuccess = true;
|
pmInitSuccess = true;
|
||||||
Serial.println("Found PMS 1 on Serial0");
|
Serial.println("Detected PM 1 on Serial0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pmInitSuccess == false) {
|
if (pmInitSuccess == false) {
|
||||||
if (serial1Available) {
|
if (serial1Available) {
|
||||||
if (ag->pms5003t_1.begin(Serial1) == false) {
|
if (ag->pms5003t_1.begin(Serial1) == false) {
|
||||||
configuration.hasSensorPMS1 = false;
|
configuration.hasSensorPMS1 = false;
|
||||||
Serial.println("PMS1 sensor not found");
|
Serial.println("No PM sensor detected on Serial1");
|
||||||
} else {
|
} else {
|
||||||
serial1Available = false;
|
serial1Available = false;
|
||||||
Serial.println("Found PMS 1 on Serial1");
|
Serial.println("Detected PM 1 on Serial1");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -808,15 +814,15 @@ static void openAirInit(void) {
|
|||||||
} else {
|
} else {
|
||||||
if (ag->pms5003t_1.begin(Serial0) == false) {
|
if (ag->pms5003t_1.begin(Serial0) == false) {
|
||||||
configuration.hasSensorPMS1 = false;
|
configuration.hasSensorPMS1 = false;
|
||||||
Serial.println("PMS1 sensor not found");
|
Serial.println("No PM sensor detected on Serial0");
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Found PMS 1 on Serial0");
|
Serial.println("Detected PM 1 on Serial0");
|
||||||
}
|
}
|
||||||
if (ag->pms5003t_2.begin(Serial1) == false) {
|
if (ag->pms5003t_2.begin(Serial1) == false) {
|
||||||
configuration.hasSensorPMS2 = false;
|
configuration.hasSensorPMS2 = false;
|
||||||
Serial.println("PMS2 sensor not found");
|
Serial.println("No PM sensor detected on Serial1");
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Found PMS 2 on Serial1");
|
Serial.println("Detected PM 2 on Serial1");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fwMode == FW_MODE_O_1PP) {
|
if (fwMode == FW_MODE_O_1PP) {
|
||||||
@ -940,297 +946,276 @@ static void configUpdateHandle() {
|
|||||||
|
|
||||||
stateMachine.executeLedBarTest();
|
stateMachine.executeLedBarTest();
|
||||||
}
|
}
|
||||||
|
else if(ag->isOpenAir()) {
|
||||||
appDispHandler();
|
stateMachine.executeLedBarTest();
|
||||||
appLedHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void appLedHandler(void) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update display and led bar notification based on updated configuration
|
||||||
|
updateDisplayAndLedBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (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);
|
stateMachine.handleLeds(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void appDispHandler(void) {
|
|
||||||
if (ag->isOne()) {
|
|
||||||
AgStateMachineState state = AgStateMachineNormal;
|
|
||||||
|
|
||||||
/** Only show display status on online mode. */
|
|
||||||
if (configuration.isOfflineMode() == false) {
|
|
||||||
if (wifiConnector.isConnected() == false) {
|
|
||||||
state = AgStateMachineWiFiLost;
|
|
||||||
} else if (apiClient.isFetchConfigureFailed()) {
|
|
||||||
state = AgStateMachineSensorConfigFailed;
|
|
||||||
if (apiClient.isNotAvailableOnDashboard()) {
|
|
||||||
stateMachine.displaySetAddToDashBoard();
|
|
||||||
} else {
|
|
||||||
stateMachine.displayClearAddToDashBoard();
|
|
||||||
}
|
|
||||||
} else if (apiClient.isPostToServerFailed()) {
|
|
||||||
state = AgStateMachineServerLost;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stateMachine.displayHandle(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void oledDisplayLedBarSchedule(void) {
|
|
||||||
if (ag->isOne()) {
|
|
||||||
if (factoryBtnPressTime == 0) {
|
|
||||||
appDispHandler();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
appLedHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void updateTvoc(void) {
|
static void updateTvoc(void) {
|
||||||
measurements.TVOC = ag->sgp41.getTvocIndex();
|
if (!configuration.hasSensorSGP) {
|
||||||
measurements.TVOCRaw = ag->sgp41.getTvocRaw();
|
return;
|
||||||
measurements.NOx = ag->sgp41.getNoxIndex();
|
}
|
||||||
measurements.NOxRaw = ag->sgp41.getNoxRaw();
|
|
||||||
|
|
||||||
Serial.println();
|
measurements.update(Measurements::TVOC, ag->sgp41.getTvocIndex());
|
||||||
Serial.printf("TVOC index: %d\r\n", measurements.TVOC);
|
measurements.update(Measurements::TVOCRaw, ag->sgp41.getTvocRaw());
|
||||||
Serial.printf("TVOC raw: %d\r\n", measurements.TVOCRaw);
|
measurements.update(Measurements::NOx, ag->sgp41.getNoxIndex());
|
||||||
Serial.printf("NOx index: %d\r\n", measurements.NOx);
|
measurements.update(Measurements::NOxRaw, ag->sgp41.getNoxRaw());
|
||||||
Serial.printf("NOx raw: %d\r\n", measurements.NOxRaw);
|
}
|
||||||
|
|
||||||
|
static void updatePMS5003() {
|
||||||
|
if (ag->pms5003.connected()) {
|
||||||
|
measurements.update(Measurements::PM01, ag->pms5003.getPm01Ae());
|
||||||
|
measurements.update(Measurements::PM25, ag->pms5003.getPm25Ae());
|
||||||
|
measurements.update(Measurements::PM10, ag->pms5003.getPm10Ae());
|
||||||
|
measurements.update(Measurements::PM01_SP, ag->pms5003.getPm01Sp());
|
||||||
|
measurements.update(Measurements::PM25_SP, ag->pms5003.getPm25Sp());
|
||||||
|
measurements.update(Measurements::PM10_SP, ag->pms5003.getPm10Sp());
|
||||||
|
measurements.update(Measurements::PM03_PC, ag->pms5003.getPm03ParticleCount());
|
||||||
|
measurements.update(Measurements::PM05_PC, ag->pms5003.getPm05ParticleCount());
|
||||||
|
measurements.update(Measurements::PM01_PC, ag->pms5003.getPm01ParticleCount());
|
||||||
|
measurements.update(Measurements::PM25_PC, ag->pms5003.getPm25ParticleCount());
|
||||||
|
measurements.update(Measurements::PM5_PC, ag->pms5003.getPm5ParticleCount());
|
||||||
|
measurements.update(Measurements::PM10_PC, ag->pms5003.getPm10ParticleCount());
|
||||||
|
} else {
|
||||||
|
measurements.update(Measurements::PM01, utils::getInvalidPmValue());
|
||||||
|
measurements.update(Measurements::PM25, utils::getInvalidPmValue());
|
||||||
|
measurements.update(Measurements::PM10, utils::getInvalidPmValue());
|
||||||
|
measurements.update(Measurements::PM01_SP, utils::getInvalidPmValue());
|
||||||
|
measurements.update(Measurements::PM25_SP, utils::getInvalidPmValue());
|
||||||
|
measurements.update(Measurements::PM10_SP, utils::getInvalidPmValue());
|
||||||
|
measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue());
|
||||||
|
measurements.update(Measurements::PM05_PC, utils::getInvalidPmValue());
|
||||||
|
measurements.update(Measurements::PM01_PC, utils::getInvalidPmValue());
|
||||||
|
measurements.update(Measurements::PM25_PC, utils::getInvalidPmValue());
|
||||||
|
measurements.update(Measurements::PM5_PC, utils::getInvalidPmValue());
|
||||||
|
measurements.update(Measurements::PM10_PC, utils::getInvalidPmValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void updatePm(void) {
|
static void updatePm(void) {
|
||||||
if (ag->isOne()) {
|
if (ag->isOne()) {
|
||||||
if (ag->pms5003.isFailed() == false) {
|
updatePMS5003();
|
||||||
measurements.pm01_1 = ag->pms5003.getPm01Ae();
|
return;
|
||||||
measurements.pm25_1 = ag->pms5003.getPm25Ae();
|
}
|
||||||
measurements.pm10_1 = ag->pms5003.getPm10Ae();
|
|
||||||
measurements.pm03PCount_1 = ag->pms5003.getPm03ParticleCount();
|
|
||||||
|
|
||||||
Serial.println();
|
// Open Air Monitor series, can have two PMS5003T sensor
|
||||||
Serial.printf("PM1 ug/m3: %d\r\n", measurements.pm01_1);
|
bool newPMS1Value = false;
|
||||||
Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_1);
|
bool newPMS2Value = false;
|
||||||
Serial.printf("PM10 ug/m3: %d\r\n", measurements.pm10_1);
|
|
||||||
Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1);
|
// Read PMS channel 1 if available
|
||||||
pmFailCount = 0;
|
int channel = 1;
|
||||||
|
if (configuration.hasSensorPMS1) {
|
||||||
|
if (ag->pms5003t_1.connected()) {
|
||||||
|
measurements.update(Measurements::PM01, ag->pms5003t_1.getPm01Ae(), channel);
|
||||||
|
measurements.update(Measurements::PM25, ag->pms5003t_1.getPm25Ae(), channel);
|
||||||
|
measurements.update(Measurements::PM10, ag->pms5003t_1.getPm10Ae(), channel);
|
||||||
|
measurements.update(Measurements::PM01_SP, ag->pms5003t_1.getPm01Sp(), channel);
|
||||||
|
measurements.update(Measurements::PM25_SP, ag->pms5003t_1.getPm25Sp(), channel);
|
||||||
|
measurements.update(Measurements::PM10_SP, ag->pms5003t_1.getPm10Sp(), channel);
|
||||||
|
measurements.update(Measurements::PM03_PC, ag->pms5003t_1.getPm03ParticleCount(), channel);
|
||||||
|
measurements.update(Measurements::PM05_PC, ag->pms5003t_1.getPm05ParticleCount(), channel);
|
||||||
|
measurements.update(Measurements::PM01_PC, ag->pms5003t_1.getPm01ParticleCount(), channel);
|
||||||
|
measurements.update(Measurements::PM25_PC, ag->pms5003t_1.getPm25ParticleCount(), channel);
|
||||||
|
measurements.update(Measurements::Temperature, ag->pms5003t_1.getTemperature(), channel);
|
||||||
|
measurements.update(Measurements::Humidity, ag->pms5003t_1.getRelativeHumidity(), channel);
|
||||||
|
|
||||||
|
// flag that new valid PMS value exists
|
||||||
|
newPMS1Value = true;
|
||||||
} else {
|
} else {
|
||||||
pmFailCount++;
|
// PMS channel 1 now is not connected, update using invalid value
|
||||||
Serial.printf("PMS read failed: %d\r\n", pmFailCount);
|
measurements.update(Measurements::PM01, utils::getInvalidPmValue(), channel);
|
||||||
if (pmFailCount >= 3) {
|
measurements.update(Measurements::PM25, utils::getInvalidPmValue(), channel);
|
||||||
measurements.pm01_1 = utils::getInvalidPMS();
|
measurements.update(Measurements::PM10, utils::getInvalidPmValue(), channel);
|
||||||
measurements.pm25_1 = utils::getInvalidPMS();
|
measurements.update(Measurements::PM01_SP, utils::getInvalidPmValue(), channel);
|
||||||
measurements.pm10_1 = utils::getInvalidPMS();
|
measurements.update(Measurements::PM25_SP, utils::getInvalidPmValue(), channel);
|
||||||
measurements.pm03PCount_1 = utils::getInvalidPMS();
|
measurements.update(Measurements::PM10_SP, utils::getInvalidPmValue(), channel);
|
||||||
}
|
measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue(), channel);
|
||||||
|
measurements.update(Measurements::PM05_PC, utils::getInvalidPmValue(), channel);
|
||||||
|
measurements.update(Measurements::PM01_PC, utils::getInvalidPmValue(), channel);
|
||||||
|
measurements.update(Measurements::PM25_PC, utils::getInvalidPmValue(), channel);
|
||||||
|
measurements.update(Measurements::Temperature, utils::getInvalidTemperature(), channel);
|
||||||
|
measurements.update(Measurements::Humidity, utils::getInvalidHumidity(), channel);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
bool pmsResult_1 = false;
|
|
||||||
bool pmsResult_2 = false;
|
|
||||||
if (configuration.hasSensorPMS1 && (ag->pms5003t_1.isFailed() == false)) {
|
|
||||||
measurements.pm01_1 = ag->pms5003t_1.getPm01Ae();
|
|
||||||
measurements.pm25_1 = ag->pms5003t_1.getPm25Ae();
|
|
||||||
measurements.pm10_1 = ag->pms5003t_1.getPm10Ae();
|
|
||||||
measurements.pm03PCount_1 = ag->pms5003t_1.getPm03ParticleCount();
|
|
||||||
measurements.temp_1 = ag->pms5003t_1.getTemperature();
|
|
||||||
measurements.hum_1 = ag->pms5003t_1.getRelativeHumidity();
|
|
||||||
|
|
||||||
pmsResult_1 = true;
|
// Read PMS channel 2 if available
|
||||||
|
channel = 2;
|
||||||
|
if (configuration.hasSensorPMS2) {
|
||||||
|
if (ag->pms5003t_2.connected()) {
|
||||||
|
measurements.update(Measurements::PM01, ag->pms5003t_2.getPm01Ae(), channel);
|
||||||
|
measurements.update(Measurements::PM25, ag->pms5003t_2.getPm25Ae(), channel);
|
||||||
|
measurements.update(Measurements::PM10, ag->pms5003t_2.getPm10Ae(), channel);
|
||||||
|
measurements.update(Measurements::PM01_SP, ag->pms5003t_2.getPm01Sp(), channel);
|
||||||
|
measurements.update(Measurements::PM25_SP, ag->pms5003t_2.getPm25Sp(), channel);
|
||||||
|
measurements.update(Measurements::PM10_SP, ag->pms5003t_2.getPm10Sp(), channel);
|
||||||
|
measurements.update(Measurements::PM03_PC, ag->pms5003t_2.getPm03ParticleCount(), channel);
|
||||||
|
measurements.update(Measurements::PM05_PC, ag->pms5003t_2.getPm05ParticleCount(), channel);
|
||||||
|
measurements.update(Measurements::PM01_PC, ag->pms5003t_2.getPm01ParticleCount(), channel);
|
||||||
|
measurements.update(Measurements::PM25_PC, ag->pms5003t_2.getPm25ParticleCount(), channel);
|
||||||
|
measurements.update(Measurements::Temperature, ag->pms5003t_2.getTemperature(), channel);
|
||||||
|
measurements.update(Measurements::Humidity, ag->pms5003t_2.getRelativeHumidity(), channel);
|
||||||
|
|
||||||
Serial.println();
|
// flag that new valid PMS value exists
|
||||||
Serial.printf("[1] PM1 ug/m3: %d\r\n", measurements.pm01_1);
|
newPMS2Value = true;
|
||||||
Serial.printf("[1] PM2.5 ug/m3: %d\r\n", measurements.pm25_1);
|
|
||||||
Serial.printf("[1] PM10 ug/m3: %d\r\n", measurements.pm10_1);
|
|
||||||
Serial.printf("[1] PM3.0 Count: %d\r\n", measurements.pm03PCount_1);
|
|
||||||
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));
|
|
||||||
} else {
|
} else {
|
||||||
measurements.pm01_1 = utils::getInvalidPMS();
|
// PMS channel 2 now is not connected, update using invalid value
|
||||||
measurements.pm25_1 = utils::getInvalidPMS();
|
measurements.update(Measurements::PM01, utils::getInvalidPmValue(), channel);
|
||||||
measurements.pm10_1 = utils::getInvalidPMS();
|
measurements.update(Measurements::PM25, utils::getInvalidPmValue(), channel);
|
||||||
measurements.pm03PCount_1 = utils::getInvalidPMS();
|
measurements.update(Measurements::PM10, utils::getInvalidPmValue(), channel);
|
||||||
measurements.temp_1 = utils::getInvalidTemperature();
|
measurements.update(Measurements::PM01_SP, utils::getInvalidPmValue(), channel);
|
||||||
measurements.hum_1 = utils::getInvalidHumidity();
|
measurements.update(Measurements::PM25_SP, utils::getInvalidPmValue(), channel);
|
||||||
|
measurements.update(Measurements::PM10_SP, utils::getInvalidPmValue(), channel);
|
||||||
|
measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue(), channel);
|
||||||
|
measurements.update(Measurements::PM05_PC, utils::getInvalidPmValue(), channel);
|
||||||
|
measurements.update(Measurements::PM01_PC, utils::getInvalidPmValue(), channel);
|
||||||
|
measurements.update(Measurements::PM25_PC, utils::getInvalidPmValue(), channel);
|
||||||
|
measurements.update(Measurements::Temperature, utils::getInvalidTemperature(), channel);
|
||||||
|
measurements.update(Measurements::Humidity, utils::getInvalidHumidity(), channel);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (configuration.hasSensorPMS2 && (ag->pms5003t_2.isFailed() == false)) {
|
if (configuration.hasSensorSGP) {
|
||||||
measurements.pm01_2 = ag->pms5003t_2.getPm01Ae();
|
float temp, hum;
|
||||||
measurements.pm25_2 = ag->pms5003t_2.getPm25Ae();
|
if (newPMS1Value && newPMS2Value) {
|
||||||
measurements.pm10_2 = ag->pms5003t_2.getPm10Ae();
|
// Both PMS has new valid value
|
||||||
measurements.pm03PCount_2 = ag->pms5003t_2.getPm03ParticleCount();
|
temp = (measurements.getFloat(Measurements::Temperature, 1) +
|
||||||
measurements.temp_2 = ag->pms5003t_2.getTemperature();
|
measurements.getFloat(Measurements::Temperature, 2)) /
|
||||||
measurements.hum_2 = ag->pms5003t_2.getRelativeHumidity();
|
2.0f;
|
||||||
|
hum = (measurements.getFloat(Measurements::Humidity, 1) +
|
||||||
pmsResult_2 = true;
|
measurements.getFloat(Measurements::Humidity, 2)) /
|
||||||
|
2.0f;
|
||||||
Serial.println();
|
} else if (newPMS1Value) {
|
||||||
Serial.printf("[2] PM1 ug/m3: %d\r\n", measurements.pm01_2);
|
// Only PMS1 has new valid value
|
||||||
Serial.printf("[2] PM2.5 ug/m3: %d\r\n", measurements.pm25_2);
|
temp = measurements.getFloat(Measurements::Temperature, 1);
|
||||||
Serial.printf("[2] PM10 ug/m3: %d\r\n", measurements.pm10_2);
|
hum = measurements.getFloat(Measurements::Humidity, 1);
|
||||||
Serial.printf("[2] PM3.0 Count: %d\r\n", measurements.pm03PCount_2);
|
|
||||||
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));
|
|
||||||
} else {
|
} else {
|
||||||
measurements.pm01_2 = utils::getInvalidPMS();
|
// Only PMS2 has new valid value
|
||||||
measurements.pm25_2 = utils::getInvalidPMS();
|
temp = measurements.getFloat(Measurements::Temperature, 2);
|
||||||
measurements.pm10_2 = utils::getInvalidPMS();
|
hum = measurements.getFloat(Measurements::Humidity, 2);
|
||||||
measurements.pm03PCount_2 = utils::getInvalidPMS();
|
|
||||||
measurements.temp_2 = utils::getInvalidTemperature();
|
|
||||||
measurements.hum_2 = utils::getInvalidHumidity();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (configuration.hasSensorPMS1 && configuration.hasSensorPMS2 &&
|
// Update compensation temperature and humidity for SGP41
|
||||||
pmsResult_1 && pmsResult_2) {
|
ag->sgp41.setCompensationTemperatureHumidity(temp, hum);
|
||||||
/** Get total of PMS1*/
|
|
||||||
measurements.pm1Value01 = measurements.pm1Value01 + measurements.pm01_1;
|
|
||||||
measurements.pm1Value25 = measurements.pm1Value25 + measurements.pm25_1;
|
|
||||||
measurements.pm1Value10 = measurements.pm1Value10 + measurements.pm10_1;
|
|
||||||
measurements.pm1PCount =
|
|
||||||
measurements.pm1PCount + measurements.pm03PCount_1;
|
|
||||||
measurements.pm1temp = measurements.pm1temp + measurements.temp_1;
|
|
||||||
measurements.pm1hum = measurements.pm1hum + measurements.hum_1;
|
|
||||||
|
|
||||||
/** Get total of PMS2 */
|
|
||||||
measurements.pm2Value01 = measurements.pm2Value01 + measurements.pm01_2;
|
|
||||||
measurements.pm2Value25 = measurements.pm2Value25 + measurements.pm25_2;
|
|
||||||
measurements.pm2Value10 = measurements.pm2Value10 + measurements.pm10_2;
|
|
||||||
measurements.pm2PCount =
|
|
||||||
measurements.pm2PCount + measurements.pm03PCount_2;
|
|
||||||
measurements.pm2temp = measurements.pm2temp + measurements.temp_2;
|
|
||||||
measurements.pm2hum = measurements.pm2hum + measurements.hum_2;
|
|
||||||
|
|
||||||
measurements.countPosition++;
|
|
||||||
|
|
||||||
/** Get average */
|
|
||||||
if (measurements.countPosition == measurements.targetCount) {
|
|
||||||
measurements.pm01_1 =
|
|
||||||
measurements.pm1Value01 / measurements.targetCount;
|
|
||||||
measurements.pm25_1 =
|
|
||||||
measurements.pm1Value25 / measurements.targetCount;
|
|
||||||
measurements.pm10_1 =
|
|
||||||
measurements.pm1Value10 / measurements.targetCount;
|
|
||||||
measurements.pm03PCount_1 =
|
|
||||||
measurements.pm1PCount / measurements.targetCount;
|
|
||||||
measurements.temp_1 = measurements.pm1temp / measurements.targetCount;
|
|
||||||
measurements.hum_1 = measurements.pm1hum / measurements.targetCount;
|
|
||||||
|
|
||||||
measurements.pm01_2 =
|
|
||||||
measurements.pm2Value01 / measurements.targetCount;
|
|
||||||
measurements.pm25_2 =
|
|
||||||
measurements.pm2Value25 / measurements.targetCount;
|
|
||||||
measurements.pm10_2 =
|
|
||||||
measurements.pm2Value10 / measurements.targetCount;
|
|
||||||
measurements.pm03PCount_2 =
|
|
||||||
measurements.pm2PCount / measurements.targetCount;
|
|
||||||
measurements.temp_2 = measurements.pm2temp / measurements.targetCount;
|
|
||||||
measurements.hum_2 = measurements.pm2hum / measurements.targetCount;
|
|
||||||
|
|
||||||
measurements.countPosition = 0;
|
|
||||||
|
|
||||||
measurements.pm1Value01 = 0;
|
|
||||||
measurements.pm1Value25 = 0;
|
|
||||||
measurements.pm1Value10 = 0;
|
|
||||||
measurements.pm1PCount = 0;
|
|
||||||
measurements.pm1temp = 0;
|
|
||||||
measurements.pm1hum = 0;
|
|
||||||
measurements.pm2Value01 = 0;
|
|
||||||
measurements.pm2Value25 = 0;
|
|
||||||
measurements.pm2Value10 = 0;
|
|
||||||
measurements.pm2PCount = 0;
|
|
||||||
measurements.pm2temp = 0;
|
|
||||||
measurements.pm2hum = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pmsResult_1 && pmsResult_2) {
|
|
||||||
measurements.Temperature =
|
|
||||||
(measurements.temp_1 + measurements.temp_2) / 2;
|
|
||||||
measurements.Humidity = (measurements.hum_1 + measurements.hum_2) / 2;
|
|
||||||
} else {
|
|
||||||
if (pmsResult_1) {
|
|
||||||
measurements.Temperature = measurements.temp_1;
|
|
||||||
measurements.Humidity = measurements.hum_1;
|
|
||||||
}
|
|
||||||
if (pmsResult_2) {
|
|
||||||
measurements.Temperature = measurements.temp_2;
|
|
||||||
measurements.Humidity = measurements.hum_2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configuration.hasSensorSGP) {
|
|
||||||
float temp;
|
|
||||||
float hum;
|
|
||||||
if (pmsResult_1 && pmsResult_2) {
|
|
||||||
temp = (measurements.temp_1 + measurements.temp_2) / 2.0f;
|
|
||||||
hum = (measurements.hum_1 + measurements.hum_2) / 2.0f;
|
|
||||||
} else {
|
|
||||||
if (pmsResult_1) {
|
|
||||||
temp = measurements.temp_1;
|
|
||||||
hum = measurements.hum_1;
|
|
||||||
}
|
|
||||||
if (pmsResult_2) {
|
|
||||||
temp = measurements.temp_2;
|
|
||||||
hum = measurements.hum_2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ag->sgp41.setCompensationTemperatureHumidity(temp, hum);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sendDataToServer(void) {
|
static void sendDataToServer(void) {
|
||||||
|
/** Increment bootcount when send measurements data is scheduled */
|
||||||
|
measurements.bootCount++;
|
||||||
|
|
||||||
/** Ignore send data to server if postToAirGradient disabled */
|
/** Ignore send data to server if postToAirGradient disabled */
|
||||||
if (configuration.isPostDataToAirGradient() == false || configuration.isOfflineMode()) {
|
if (configuration.isPostDataToAirGradient() == false || configuration.isOfflineMode()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(),
|
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), *ag, configuration);
|
||||||
ag, &configuration);
|
|
||||||
if (apiClient.postToServer(syncData)) {
|
if (apiClient.postToServer(syncData)) {
|
||||||
ag->watchdog.reset();
|
|
||||||
Serial.println();
|
Serial.println();
|
||||||
Serial.println(
|
Serial.println(
|
||||||
"Online mode and isPostToAirGradient = true: watchdog reset");
|
"Online mode and isPostToAirGradient = true: watchdog reset");
|
||||||
Serial.println();
|
Serial.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
measurements.bootCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tempHumUpdate(void) {
|
static void tempHumUpdate(void) {
|
||||||
delay(100);
|
delay(100);
|
||||||
if (ag->sht.measure()) {
|
if (ag->sht.measure()) {
|
||||||
measurements.Temperature = ag->sht.getTemperature();
|
float temp = ag->sht.getTemperature();
|
||||||
measurements.Humidity = ag->sht.getRelativeHumidity();
|
float rhum = ag->sht.getRelativeHumidity();
|
||||||
|
|
||||||
Serial.printf("Temperature in C: %0.2f\r\n", measurements.Temperature);
|
measurements.update(Measurements::Temperature, temp);
|
||||||
Serial.printf("Relative Humidity: %d\r\n", measurements.Humidity);
|
measurements.update(Measurements::Humidity, rhum);
|
||||||
Serial.printf("Temperature compensated in C: %0.2f\r\n",
|
|
||||||
measurements.Temperature);
|
|
||||||
Serial.printf("Relative Humidity compensated: %d\r\n",
|
|
||||||
measurements.Humidity);
|
|
||||||
|
|
||||||
// Update compensation temperature and humidity for SGP41
|
// Update compensation temperature and humidity for SGP41
|
||||||
if (configuration.hasSensorSGP) {
|
if (configuration.hasSensorSGP) {
|
||||||
ag->sgp41.setCompensationTemperatureHumidity(measurements.Temperature,
|
ag->sgp41.setCompensationTemperatureHumidity(temp, rhum);
|
||||||
measurements.Humidity);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
measurements.Temperature = utils::getInvalidTemperature();
|
measurements.update(Measurements::Temperature, utils::getInvalidTemperature());
|
||||||
measurements.Humidity = utils::getInvalidHumidity();
|
measurements.update(Measurements::Humidity, utils::getInvalidHumidity());
|
||||||
Serial.println("SHT read failed");
|
Serial.println("SHT read failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set max period for each measurement type based on sensor update interval*/
|
||||||
|
void setMeasurementMaxPeriod() {
|
||||||
|
int max;
|
||||||
|
|
||||||
|
/// Max period for S8 sensors measurements
|
||||||
|
measurements.maxPeriod(Measurements::CO2, calculateMaxPeriod(SENSOR_CO2_UPDATE_INTERVAL));
|
||||||
|
|
||||||
|
/// Max period for SGP sensors measurements
|
||||||
|
max = calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL);
|
||||||
|
measurements.maxPeriod(Measurements::TVOC, max);
|
||||||
|
measurements.maxPeriod(Measurements::TVOCRaw, max);
|
||||||
|
measurements.maxPeriod(Measurements::NOx, max);
|
||||||
|
measurements.maxPeriod(Measurements::NOxRaw, max);
|
||||||
|
|
||||||
|
/// Max period for PMS sensors measurements
|
||||||
|
max = calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL);
|
||||||
|
measurements.maxPeriod(Measurements::PM25, max);
|
||||||
|
measurements.maxPeriod(Measurements::PM01, max);
|
||||||
|
measurements.maxPeriod(Measurements::PM10, max);
|
||||||
|
measurements.maxPeriod(Measurements::PM25_SP, max);
|
||||||
|
measurements.maxPeriod(Measurements::PM01_SP, max);
|
||||||
|
measurements.maxPeriod(Measurements::PM10_SP, max);
|
||||||
|
measurements.maxPeriod(Measurements::PM03_PC, max);
|
||||||
|
measurements.maxPeriod(Measurements::PM05_PC, max);
|
||||||
|
measurements.maxPeriod(Measurements::PM01_PC, max);
|
||||||
|
measurements.maxPeriod(Measurements::PM25_PC, max);
|
||||||
|
measurements.maxPeriod(Measurements::PM5_PC, max);
|
||||||
|
measurements.maxPeriod(Measurements::PM10_PC, max);
|
||||||
|
|
||||||
|
// Temperature and Humidity
|
||||||
|
if (configuration.hasSensorSHT) {
|
||||||
|
/// Max period for SHT sensors measurements
|
||||||
|
measurements.maxPeriod(Measurements::Temperature,
|
||||||
|
calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::Humidity,
|
||||||
|
calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL));
|
||||||
|
} else {
|
||||||
|
/// Temp and hum data retrieved from PMS5003T sensor
|
||||||
|
measurements.maxPeriod(Measurements::Temperature,
|
||||||
|
calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
measurements.maxPeriod(Measurements::Humidity, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int calculateMaxPeriod(int updateInterval) {
|
||||||
|
// 0.5 is 50% reduced interval for max period
|
||||||
|
return (SERVER_SYNC_INTERVAL - (SERVER_SYNC_INTERVAL * 0.5)) / updateInterval;
|
||||||
|
}
|
||||||
|
@ -67,48 +67,53 @@ String OpenMetrics::getPayload(void) {
|
|||||||
|
|
||||||
float _temp = utils::getInvalidTemperature();
|
float _temp = utils::getInvalidTemperature();
|
||||||
float _hum = utils::getInvalidHumidity();
|
float _hum = utils::getInvalidHumidity();
|
||||||
int pm01 = utils::getInvalidPMS();
|
int pm01 = utils::getInvalidPmValue();
|
||||||
int pm25 = utils::getInvalidPMS();
|
int pm25 = utils::getInvalidPmValue();
|
||||||
int pm10 = utils::getInvalidPMS();
|
int pm10 = utils::getInvalidPmValue();
|
||||||
int pm03PCount = utils::getInvalidPMS();
|
int pm03PCount = utils::getInvalidPmValue();
|
||||||
int atmpCompensated = utils::getInvalidTemperature();
|
int atmpCompensated = utils::getInvalidTemperature();
|
||||||
int ahumCompensated = utils::getInvalidHumidity();
|
int ahumCompensated = utils::getInvalidHumidity();
|
||||||
if (config.hasSensorPMS1 && config.hasSensorPMS2) {
|
if (config.hasSensorPMS1 && config.hasSensorPMS2) {
|
||||||
_temp = (measure.temp_1 + measure.temp_2) / 2.0f;
|
_temp = (measure.getFloat(Measurements::Temperature, 1) +
|
||||||
_hum = (measure.hum_1 + measure.hum_2) / 2.0f;
|
measure.getFloat(Measurements::Temperature, 2)) /
|
||||||
pm01 = (measure.pm01_1 + measure.pm01_2) / 2;
|
2.0f;
|
||||||
pm25 = (measure.pm25_1 + measure.pm25_2) / 2;
|
_hum = (measure.getFloat(Measurements::Humidity, 1) +
|
||||||
pm10 = (measure.pm10_1 + measure.pm10_2) / 2;
|
measure.getFloat(Measurements::Humidity, 2)) /
|
||||||
pm03PCount = (measure.pm03PCount_1 + measure.pm03PCount_2) / 2;
|
2.0f;
|
||||||
|
pm01 = (measure.get(Measurements::PM01, 1) + measure.get(Measurements::PM01, 2)) / 2.0f;
|
||||||
|
pm25 = (measure.get(Measurements::PM25, 1) + measure.get(Measurements::PM25, 2)) / 2.0f;
|
||||||
|
pm10 = (measure.get(Measurements::PM10, 1) + measure.get(Measurements::PM10, 2)) / 2.0f;
|
||||||
|
pm03PCount =
|
||||||
|
(measure.get(Measurements::PM03_PC, 1) + measure.get(Measurements::PM03_PC, 2)) / 2.0f;
|
||||||
} else {
|
} else {
|
||||||
if (ag->isOne()) {
|
if (ag->isOne()) {
|
||||||
if (config.hasSensorSHT) {
|
if (config.hasSensorSHT) {
|
||||||
_temp = measure.Temperature;
|
_temp = measure.getFloat(Measurements::Temperature);
|
||||||
_hum = measure.Humidity;
|
_hum = measure.getFloat(Measurements::Humidity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.hasSensorPMS1) {
|
if (config.hasSensorPMS1) {
|
||||||
pm01 = measure.pm01_1;
|
pm01 = measure.get(Measurements::PM01);
|
||||||
pm25 = measure.pm25_1;
|
pm25 = measure.get(Measurements::PM25);
|
||||||
pm10 = measure.pm10_1;
|
pm10 = measure.get(Measurements::PM10);
|
||||||
pm03PCount = measure.pm03PCount_1;
|
pm03PCount = measure.get(Measurements::PM03_PC);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (config.hasSensorPMS1) {
|
if (config.hasSensorPMS1) {
|
||||||
_temp = measure.temp_1;
|
_temp = measure.getFloat(Measurements::Temperature, 1);
|
||||||
_hum = measure.hum_1;
|
_hum = measure.getFloat(Measurements::Humidity, 1);
|
||||||
pm01 = measure.pm01_1;
|
pm01 = measure.get(Measurements::PM01, 1);
|
||||||
pm25 = measure.pm25_1;
|
pm25 = measure.get(Measurements::PM25, 1);
|
||||||
pm10 = measure.pm10_1;
|
pm10 = measure.get(Measurements::PM10, 1);
|
||||||
pm03PCount = measure.pm03PCount_1;
|
pm03PCount = measure.get(Measurements::PM03_PC, 1);
|
||||||
}
|
}
|
||||||
if (config.hasSensorPMS2) {
|
if (config.hasSensorPMS2) {
|
||||||
_temp = measure.temp_2;
|
_temp = measure.getFloat(Measurements::Temperature, 2);
|
||||||
_hum = measure.hum_2;
|
_hum = measure.getFloat(Measurements::Humidity, 2);
|
||||||
pm01 = measure.pm01_2;
|
pm01 = measure.get(Measurements::PM01, 2);
|
||||||
pm25 = measure.pm25_2;
|
pm25 = measure.get(Measurements::PM25, 2);
|
||||||
pm10 = measure.pm10_2;
|
pm10 = measure.get(Measurements::PM10, 2);
|
||||||
pm03PCount = measure.pm03PCount_2;
|
pm03PCount = measure.get(Measurements::PM03_PC, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,33 +123,33 @@ String OpenMetrics::getPayload(void) {
|
|||||||
atmpCompensated = _temp;
|
atmpCompensated = _temp;
|
||||||
ahumCompensated = _hum;
|
ahumCompensated = _hum;
|
||||||
} else {
|
} else {
|
||||||
atmpCompensated = ag->pms5003t_1.temperatureCompensated(_temp);
|
atmpCompensated = ag->pms5003t_1.compensateTemp(_temp);
|
||||||
ahumCompensated = ag->pms5003t_1.humidityCompensated(_hum);
|
ahumCompensated = ag->pms5003t_1.compensateHum(_hum);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.hasSensorPMS1 || config.hasSensorPMS2) {
|
if (config.hasSensorPMS1 || config.hasSensorPMS2) {
|
||||||
if (utils::isValidPMS(pm01)) {
|
if (utils::isValidPm(pm01)) {
|
||||||
add_metric("pm1",
|
add_metric("pm1",
|
||||||
"PM1.0 concentration as measured by the AirGradient PMS "
|
"PM1.0 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in micrograms per cubic meter",
|
"sensor, in micrograms per cubic meter",
|
||||||
"gauge", "ugm3");
|
"gauge", "ugm3");
|
||||||
add_metric_point("", String(pm01));
|
add_metric_point("", String(pm01));
|
||||||
}
|
}
|
||||||
if (utils::isValidPMS(pm25)) {
|
if (utils::isValidPm(pm25)) {
|
||||||
add_metric("pm2d5",
|
add_metric("pm2d5",
|
||||||
"PM2.5 concentration as measured by the AirGradient PMS "
|
"PM2.5 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in micrograms per cubic meter",
|
"sensor, in micrograms per cubic meter",
|
||||||
"gauge", "ugm3");
|
"gauge", "ugm3");
|
||||||
add_metric_point("", String(pm25));
|
add_metric_point("", String(pm25));
|
||||||
}
|
}
|
||||||
if (utils::isValidPMS(pm10)) {
|
if (utils::isValidPm(pm10)) {
|
||||||
add_metric("pm10",
|
add_metric("pm10",
|
||||||
"PM10 concentration as measured by the AirGradient PMS "
|
"PM10 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in micrograms per cubic meter",
|
"sensor, in micrograms per cubic meter",
|
||||||
"gauge", "ugm3");
|
"gauge", "ugm3");
|
||||||
add_metric_point("", String(pm10));
|
add_metric_point("", String(pm10));
|
||||||
}
|
}
|
||||||
if (utils::isValidPMS03Count(pm03PCount)) {
|
if (utils::isValidPm03Count(pm03PCount)) {
|
||||||
add_metric("pm0d3",
|
add_metric("pm0d3",
|
||||||
"PM0.3 concentration as measured by the AirGradient PMS "
|
"PM0.3 concentration as measured by the AirGradient PMS "
|
||||||
"sensor, in number of particules per 100 milliliters",
|
"sensor, in number of particules per 100 milliliters",
|
||||||
|
@ -44,7 +44,7 @@ void loop() {
|
|||||||
if (ms >= 5000) {
|
if (ms >= 5000) {
|
||||||
lastRead = millis();
|
lastRead = millis();
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
if (ag.pms5003.isFailed() == false) {
|
if (ag.pms5003.connected()) {
|
||||||
PM2 = ag.pms5003.getPm25Ae();
|
PM2 = ag.pms5003.getPm25Ae();
|
||||||
Serial.printf("PM2.5 in ug/m3: %d\r\n", PM2);
|
Serial.printf("PM2.5 in ug/m3: %d\r\n", PM2);
|
||||||
Serial.printf("PM2.5 in US AQI: %d\r\n",
|
Serial.printf("PM2.5 in US AQI: %d\r\n",
|
||||||
@ -54,12 +54,12 @@ void loop() {
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (ag.getBoardType() == OPEN_AIR_OUTDOOR) {
|
if (ag.getBoardType() == OPEN_AIR_OUTDOOR) {
|
||||||
if (ag.pms5003t_1.isFailed() == false) {
|
if (ag.pms5003t_1.connected()) {
|
||||||
PM2 = ag.pms5003t_1.getPm25Ae();
|
PM2 = ag.pms5003t_1.getPm25Ae();
|
||||||
readResul = true;
|
readResul = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (ag.pms5003.isFailed() == false) {
|
if (ag.pms5003.connected()) {
|
||||||
PM2 = ag.pms5003.getPm25Ae();
|
PM2 = ag.pms5003.getPm25Ae();
|
||||||
readResul = true;
|
readResul = true;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
name=AirGradient Air Quality Sensor
|
name=AirGradient Air Quality Sensor
|
||||||
version=3.1.5
|
version=3.1.10
|
||||||
author=AirGradient <support@airgradient.com>
|
author=AirGradient <support@airgradient.com>
|
||||||
maintainer=AirGradient <support@airgradient.com>
|
maintainer=AirGradient <support@airgradient.com>
|
||||||
sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display.
|
sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display.
|
||||||
|
@ -58,6 +58,7 @@ bool AgApiClient::fetchServerConfiguration(void) {
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
HTTPClient client;
|
HTTPClient client;
|
||||||
|
client.setTimeout(timeoutMs);
|
||||||
if (client.begin(uri) == false) {
|
if (client.begin(uri) == false) {
|
||||||
getConfigFailed = true;
|
getConfigFailed = true;
|
||||||
return false;
|
return false;
|
||||||
@ -113,14 +114,13 @@ bool AgApiClient::postToServer(String data) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String uri =
|
String uri = apiRoot + "/sensors/airgradient:" + ag->deviceId() + "/measures";
|
||||||
"http://hw.airgradient.com/sensors/airgradient:" + ag->deviceId() +
|
|
||||||
"/measures";
|
|
||||||
// logInfo("Post uri: " + uri);
|
// logInfo("Post uri: " + uri);
|
||||||
// logInfo("Post data: " + data);
|
// logInfo("Post data: " + data);
|
||||||
|
|
||||||
WiFiClient wifiClient;
|
WiFiClient wifiClient;
|
||||||
HTTPClient client;
|
HTTPClient client;
|
||||||
|
client.setTimeout(timeoutMs);
|
||||||
if (client.begin(wifiClient, uri.c_str()) == false) {
|
if (client.begin(wifiClient, uri.c_str()) == false) {
|
||||||
logError("Init client failed");
|
logError("Init client failed");
|
||||||
return false;
|
return false;
|
||||||
@ -130,7 +130,7 @@ bool AgApiClient::postToServer(String data) {
|
|||||||
client.end();
|
client.end();
|
||||||
|
|
||||||
logInfo(String("POST: ") + uri);
|
logInfo(String("POST: ") + uri);
|
||||||
logInfo(String("DATA: ") + data);
|
// logInfo(String("DATA: ") + data);
|
||||||
logInfo(String("Return code: ") + String(retCode));
|
logInfo(String("Return code: ") + String(retCode));
|
||||||
|
|
||||||
if ((retCode == 200) || (retCode == 429)) {
|
if ((retCode == 200) || (retCode == 429)) {
|
||||||
@ -190,3 +190,12 @@ bool AgApiClient::sendPing(int rssi, int bootCount) {
|
|||||||
String AgApiClient::getApiRoot() const { return apiRoot; }
|
String AgApiClient::getApiRoot() const { return apiRoot; }
|
||||||
|
|
||||||
void AgApiClient::setApiRoot(const String &apiRoot) { this->apiRoot = apiRoot; }
|
void AgApiClient::setApiRoot(const String &apiRoot) { this->apiRoot = apiRoot; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set http request timeout. (Default: 10s)
|
||||||
|
*
|
||||||
|
* @param timeoutMs
|
||||||
|
*/
|
||||||
|
void AgApiClient::setTimeout(uint16_t timeoutMs) {
|
||||||
|
this->timeoutMs = timeoutMs;
|
||||||
|
}
|
@ -25,6 +25,7 @@ private:
|
|||||||
bool getConfigFailed;
|
bool getConfigFailed;
|
||||||
bool postToServerFailed;
|
bool postToServerFailed;
|
||||||
bool notAvailableOnDashboard = false; // Device not setup on Airgradient cloud dashboard.
|
bool notAvailableOnDashboard = false; // Device not setup on Airgradient cloud dashboard.
|
||||||
|
uint16_t timeoutMs = 10000; // Default set to 10s
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AgApiClient(Stream &stream, Configuration &config);
|
AgApiClient(Stream &stream, Configuration &config);
|
||||||
@ -40,6 +41,7 @@ public:
|
|||||||
bool sendPing(int rssi, int bootCount);
|
bool sendPing(int rssi, int bootCount);
|
||||||
String getApiRoot() const;
|
String getApiRoot() const;
|
||||||
void setApiRoot(const String &apiRoot);
|
void setApiRoot(const String &apiRoot);
|
||||||
|
void setTimeout(uint16_t timeoutMs);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /** _AG_API_CLIENT_H_ */
|
#endif /** _AG_API_CLIENT_H_ */
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#include "AgConfigure.h"
|
#include "AgConfigure.h"
|
||||||
#include "Libraries/Arduino_JSON/src/Arduino_JSON.h"
|
|
||||||
#if ESP32
|
#if ESP32
|
||||||
#include "FS.h"
|
#include "FS.h"
|
||||||
#include "SPIFFS.h"
|
#include "SPIFFS.h"
|
||||||
@ -22,6 +21,18 @@ const char *LED_BAR_MODE_NAMES[] = {
|
|||||||
[LedBarModeCO2] = "co2",
|
[LedBarModeCO2] = "co2",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char *PM_CORRECTION_ALGORITHM_NAMES[] = {
|
||||||
|
[Unknown] = "-", // This is only to pass "non-trivial designated initializers" error
|
||||||
|
[None] = "none",
|
||||||
|
[EPA_2021] = "epa_2021",
|
||||||
|
[SLR_PMS5003_20220802] = "slr_PMS5003_20220802",
|
||||||
|
[SLR_PMS5003_20220803] = "slr_PMS5003_20220803",
|
||||||
|
[SLR_PMS5003_20220824] = "slr_PMS5003_20220824",
|
||||||
|
[SLR_PMS5003_20231030] = "slr_PMS5003_20231030",
|
||||||
|
[SLR_PMS5003_20231218] = "slr_PMS5003_20231218",
|
||||||
|
[SLR_PMS5003_20240104] = "slr_PMS5003_20240104",
|
||||||
|
};
|
||||||
|
|
||||||
#define JSON_PROP_NAME(name) jprop_##name
|
#define JSON_PROP_NAME(name) jprop_##name
|
||||||
#define JSON_PROP_DEF(name) const char *JSON_PROP_NAME(name) = #name
|
#define JSON_PROP_DEF(name) const char *JSON_PROP_NAME(name) = #name
|
||||||
|
|
||||||
@ -41,21 +52,24 @@ JSON_PROP_DEF(displayBrightness);
|
|||||||
JSON_PROP_DEF(co2CalibrationRequested);
|
JSON_PROP_DEF(co2CalibrationRequested);
|
||||||
JSON_PROP_DEF(ledBarTestRequested);
|
JSON_PROP_DEF(ledBarTestRequested);
|
||||||
JSON_PROP_DEF(offlineMode);
|
JSON_PROP_DEF(offlineMode);
|
||||||
|
JSON_PROP_DEF(monitorDisplayCompensatedValues);
|
||||||
|
JSON_PROP_DEF(corrections);
|
||||||
|
|
||||||
#define jprop_model_default ""
|
#define jprop_model_default ""
|
||||||
#define jprop_country_default "TH"
|
#define jprop_country_default "TH"
|
||||||
#define jprop_pmStandard_default getPMStandardString(false)
|
#define jprop_pmStandard_default getPMStandardString(false)
|
||||||
#define jprop_ledBarMode_default getLedBarModeName(LedBarMode::LedBarModeCO2)
|
#define jprop_ledBarMode_default getLedBarModeName(LedBarMode::LedBarModeCO2)
|
||||||
#define jprop_abcDays_default 8
|
#define jprop_abcDays_default 8
|
||||||
#define jprop_tvocLearningOffset_default 12
|
#define jprop_tvocLearningOffset_default 12
|
||||||
#define jprop_noxLearningOffset_default 12
|
#define jprop_noxLearningOffset_default 12
|
||||||
#define jprop_mqttBrokerUrl_default ""
|
#define jprop_mqttBrokerUrl_default ""
|
||||||
#define jprop_temperatureUnit_default "c"
|
#define jprop_temperatureUnit_default "c"
|
||||||
#define jprop_configurationControl_default String(CONFIGURATION_CONTROL_NAME[ConfigurationControl::ConfigurationControlBoth])
|
#define jprop_configurationControl_default String(CONFIGURATION_CONTROL_NAME[ConfigurationControl::ConfigurationControlBoth])
|
||||||
#define jprop_postDataToAirGradient_default true
|
#define jprop_postDataToAirGradient_default true
|
||||||
#define jprop_ledBarBrightness_default 100
|
#define jprop_ledBarBrightness_default 100
|
||||||
#define jprop_displayBrightness_default 100
|
#define jprop_displayBrightness_default 100
|
||||||
#define jprop_offlineMode_default false
|
#define jprop_offlineMode_default false
|
||||||
|
#define jprop_monitorDisplayCompensatedValues_default false
|
||||||
|
|
||||||
JSONVar jconfig;
|
JSONVar jconfig;
|
||||||
|
|
||||||
@ -85,6 +99,112 @@ String Configuration::getLedBarModeName(LedBarMode mode) {
|
|||||||
return String("unknown");
|
return String("unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PMCorrectionAlgorithm Configuration::matchPmAlgorithm(String algorithm) {
|
||||||
|
// Loop through all algorithm names in the PM_CORRECTION_ALGORITHM_NAMES array
|
||||||
|
// If the input string matches an algorithm name, return the corresponding enum value
|
||||||
|
// Else return Unknown
|
||||||
|
|
||||||
|
const size_t enumSize = SLR_PMS5003_20240104 + 1; // Get the actual size of the enum
|
||||||
|
PMCorrectionAlgorithm result = PMCorrectionAlgorithm::Unknown;
|
||||||
|
|
||||||
|
// Loop through enum values
|
||||||
|
for (size_t enumVal = 0; enumVal < enumSize; enumVal++) {
|
||||||
|
if (algorithm == PM_CORRECTION_ALGORITHM_NAMES[enumVal]) {
|
||||||
|
result = static_cast<PMCorrectionAlgorithm>(enumVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Configuration::updatePmCorrection(JSONVar &json) {
|
||||||
|
if (!json.hasOwnProperty("corrections")) {
|
||||||
|
// TODO: need to response message?
|
||||||
|
Serial.println("corrections not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONVar corrections = json["corrections"];
|
||||||
|
if (!corrections.hasOwnProperty("pm02")) {
|
||||||
|
Serial.println("pm02 not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONVar pm02 = corrections["pm02"];
|
||||||
|
if (!pm02.hasOwnProperty("correctionAlgorithm")) {
|
||||||
|
Serial.println("correctionAlgorithm not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Need to have data type check, with error message response if invalid
|
||||||
|
|
||||||
|
// Check algorithm
|
||||||
|
String algorithm = pm02["correctionAlgorithm"];
|
||||||
|
PMCorrectionAlgorithm algo = matchPmAlgorithm(algorithm);
|
||||||
|
if (algo == Unknown) {
|
||||||
|
logInfo("Unknown algorithm");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
logInfo("Correction algorithm: " + algorithm);
|
||||||
|
|
||||||
|
// If algo is None or EPA_2021, no need to check slr
|
||||||
|
// But first check if pmCorrection different from algo
|
||||||
|
if (algo == None || algo == EPA_2021) {
|
||||||
|
if (pmCorrection.algorithm != algo) {
|
||||||
|
// Deep copy corrections from root to jconfig, so it will be saved later
|
||||||
|
jconfig[jprop_corrections]["pm02"]["correctionAlgorithm"] = algorithm;
|
||||||
|
jconfig[jprop_corrections]["pm02"]["slr"] = JSON.parse("{}"); // Clear slr
|
||||||
|
// Update pmCorrection with new values
|
||||||
|
pmCorrection.algorithm = algo;
|
||||||
|
pmCorrection.changed = true;
|
||||||
|
logInfo("PM2.5 correction updated");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if pm02 has slr object
|
||||||
|
if (!pm02.hasOwnProperty("slr")) {
|
||||||
|
Serial.println("slr not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONVar slr = pm02["slr"];
|
||||||
|
|
||||||
|
// Validate required slr properties exist
|
||||||
|
if (!slr.hasOwnProperty("intercept") || !slr.hasOwnProperty("scalingFactor") ||
|
||||||
|
!slr.hasOwnProperty("useEpa2021")) {
|
||||||
|
Serial.println("Missing required slr properties");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// arduino_json doesn't support float type, need to cast to double first
|
||||||
|
float intercept = (float)((double)slr["intercept"]);
|
||||||
|
float scalingFactor = (float)((double)slr["scalingFactor"]);
|
||||||
|
|
||||||
|
// Compare with current pmCorrection
|
||||||
|
if (pmCorrection.algorithm == algo && pmCorrection.intercept == intercept &&
|
||||||
|
pmCorrection.scalingFactor == scalingFactor &&
|
||||||
|
pmCorrection.useEPA == (bool)slr["useEpa2021"]) {
|
||||||
|
return false; // No changes needed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deep copy corrections from root to jconfig, so it will be saved later
|
||||||
|
jconfig[jprop_corrections] = corrections;
|
||||||
|
|
||||||
|
// Update pmCorrection with new values
|
||||||
|
pmCorrection.algorithm = algo;
|
||||||
|
pmCorrection.intercept = intercept;
|
||||||
|
pmCorrection.scalingFactor = scalingFactor;
|
||||||
|
pmCorrection.useEPA = (bool)slr["useEpa2021"];
|
||||||
|
pmCorrection.changed = true;
|
||||||
|
|
||||||
|
// Correction values were updated
|
||||||
|
logInfo("PM2.5 correction updated");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Save configure to device storage (EEPROM)
|
* @brief Save configure to device storage (EEPROM)
|
||||||
*
|
*
|
||||||
@ -160,13 +280,21 @@ void Configuration::defaultConfig(void) {
|
|||||||
jconfig[jprop_displayBrightness] = jprop_displayBrightness_default;
|
jconfig[jprop_displayBrightness] = jprop_displayBrightness_default;
|
||||||
}
|
}
|
||||||
if (ag->isOne()) {
|
if (ag->isOne()) {
|
||||||
jconfig[jprop_ledBarMode] = jprop_ledBarBrightness_default;
|
jconfig[jprop_ledBarMode] = jprop_ledBarMode_default;
|
||||||
}
|
}
|
||||||
jconfig[jprop_tvocLearningOffset] = jprop_tvocLearningOffset_default;
|
jconfig[jprop_tvocLearningOffset] = jprop_tvocLearningOffset_default;
|
||||||
jconfig[jprop_noxLearningOffset] = jprop_noxLearningOffset_default;
|
jconfig[jprop_noxLearningOffset] = jprop_noxLearningOffset_default;
|
||||||
jconfig[jprop_abcDays] = jprop_abcDays_default;
|
jconfig[jprop_abcDays] = jprop_abcDays_default;
|
||||||
jconfig[jprop_model] = jprop_model_default;
|
jconfig[jprop_model] = jprop_model_default;
|
||||||
jconfig[jprop_offlineMode] = jprop_offlineMode_default;
|
jconfig[jprop_offlineMode] = jprop_offlineMode_default;
|
||||||
|
jconfig[jprop_monitorDisplayCompensatedValues] = jprop_monitorDisplayCompensatedValues_default;
|
||||||
|
|
||||||
|
// PM2.5 correction
|
||||||
|
pmCorrection.algorithm = None;
|
||||||
|
pmCorrection.changed = false;
|
||||||
|
pmCorrection.intercept = -1;
|
||||||
|
pmCorrection.scalingFactor = -1;
|
||||||
|
pmCorrection.useEPA = false;
|
||||||
|
|
||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
@ -628,6 +756,27 @@ bool Configuration::parse(String data, bool isLocal) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (JSON.typeof_(root[jprop_monitorDisplayCompensatedValues]) == "boolean") {
|
||||||
|
bool value = root[jprop_monitorDisplayCompensatedValues];
|
||||||
|
bool oldValue = jconfig[jprop_monitorDisplayCompensatedValues];
|
||||||
|
if (value != oldValue) {
|
||||||
|
changed = true;
|
||||||
|
jconfig[jprop_monitorDisplayCompensatedValues] = value;
|
||||||
|
|
||||||
|
configLogInfo(String(jprop_monitorDisplayCompensatedValues),
|
||||||
|
String(oldValue ? "true" : "false"),
|
||||||
|
String(value ? "true" : "false"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (jsonTypeInvalid(root[jprop_monitorDisplayCompensatedValues],
|
||||||
|
"boolean")) {
|
||||||
|
failedMessage = jsonTypeInvalidMessage(
|
||||||
|
String(jprop_monitorDisplayCompensatedValues), "boolean");
|
||||||
|
jsonInvalid();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ag->getBoardType() == ONE_INDOOR ||
|
if (ag->getBoardType() == ONE_INDOOR ||
|
||||||
ag->getBoardType() == OPEN_AIR_OUTDOOR) {
|
ag->getBoardType() == OPEN_AIR_OUTDOOR) {
|
||||||
if (JSON.typeof_(root["targetFirmware"]) == "string") {
|
if (JSON.typeof_(root["targetFirmware"]) == "string") {
|
||||||
@ -636,20 +785,25 @@ bool Configuration::parse(String data, bool isLocal) {
|
|||||||
if (curVer != newVer) {
|
if (curVer != newVer) {
|
||||||
logInfo("Detected new firmware version: " + newVer);
|
logInfo("Detected new firmware version: " + newVer);
|
||||||
otaNewFirmwareVersion = newVer;
|
otaNewFirmwareVersion = newVer;
|
||||||
udpated = true;
|
updated = true;
|
||||||
} else {
|
} else {
|
||||||
otaNewFirmwareVersion = String("");
|
otaNewFirmwareVersion = String("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Corrections
|
||||||
|
if (updatePmCorrection(root)) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
udpated = true;
|
updated = true;
|
||||||
saveConfig();
|
saveConfig();
|
||||||
printConfig();
|
printConfig();
|
||||||
} else {
|
} else {
|
||||||
if (ledBarTestRequested || co2CalibrationRequested) {
|
if (ledBarTestRequested || co2CalibrationRequested) {
|
||||||
udpated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -836,8 +990,8 @@ String Configuration::getModel(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Configuration::isUpdated(void) {
|
bool Configuration::isUpdated(void) {
|
||||||
bool updated = this->udpated;
|
bool updated = this->updated;
|
||||||
this->udpated = false;
|
this->updated = false;
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1082,14 +1236,27 @@ void Configuration::toConfig(const char *buf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (JSON.typeof_(jconfig[jprop_offlineMode]) != "boolean") {
|
if (JSON.typeof_(jconfig[jprop_offlineMode]) != "boolean") {
|
||||||
isInvalid = true;
|
changed = true;
|
||||||
} else {
|
jconfig[jprop_offlineMode] = jprop_offlineMode_default;
|
||||||
isInvalid = false;
|
|
||||||
}
|
}
|
||||||
if (isInvalid) {
|
|
||||||
jconfig[jprop_offlineMode] = false;
|
/** Validate monitorDisplayCompensatedValues */
|
||||||
|
if (JSON.typeof_(jconfig[jprop_monitorDisplayCompensatedValues]) !=
|
||||||
|
"boolean") {
|
||||||
|
changed = true;
|
||||||
|
jconfig[jprop_monitorDisplayCompensatedValues] =
|
||||||
|
jprop_monitorDisplayCompensatedValues_default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Set default first before parsing local config
|
||||||
|
pmCorrection.algorithm = PMCorrectionAlgorithm::None;
|
||||||
|
pmCorrection.intercept = 0;
|
||||||
|
pmCorrection.scalingFactor = 0;
|
||||||
|
pmCorrection.useEPA = false;
|
||||||
|
// Load correction from saved config
|
||||||
|
updatePmCorrection(jconfig);
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
@ -1173,6 +1340,10 @@ bool Configuration::isLedBarModeChanged(void) {
|
|||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Configuration::isMonitorDisplayCompensatedValues(void) {
|
||||||
|
return jconfig[jprop_monitorDisplayCompensatedValues];
|
||||||
|
}
|
||||||
|
|
||||||
bool Configuration::isDisplayBrightnessChanged(void) {
|
bool Configuration::isDisplayBrightnessChanged(void) {
|
||||||
bool changed = displayBrightnessChanged;
|
bool changed = displayBrightnessChanged;
|
||||||
displayBrightnessChanged = false;
|
displayBrightnessChanged = false;
|
||||||
@ -1184,3 +1355,23 @@ String Configuration::newFirmwareVersion(void) {
|
|||||||
otaNewFirmwareVersion = String("");
|
otaNewFirmwareVersion = String("");
|
||||||
return newFw;
|
return newFw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Configuration::isPMCorrectionChanged(void) {
|
||||||
|
bool changed = pmCorrection.changed;
|
||||||
|
pmCorrection.changed = false;
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if PM correction is enabled
|
||||||
|
*
|
||||||
|
* @return true if PM correction algorithm is not None, otherwise false
|
||||||
|
*/
|
||||||
|
bool Configuration::isPMCorrectionEnabled(void) {
|
||||||
|
PMCorrection pmCorrection = getPMCorrection();
|
||||||
|
return pmCorrection.algorithm != PMCorrectionAlgorithm::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Configuration::PMCorrection Configuration::getPMCorrection(void) {
|
||||||
|
return pmCorrection;
|
||||||
|
}
|
||||||
|
@ -5,12 +5,22 @@
|
|||||||
#include "Main/PrintLog.h"
|
#include "Main/PrintLog.h"
|
||||||
#include "AirGradient.h"
|
#include "AirGradient.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include "Libraries/Arduino_JSON/src/Arduino_JSON.h"
|
||||||
|
|
||||||
class Configuration : public PrintLog {
|
class Configuration : public PrintLog {
|
||||||
|
public:
|
||||||
|
struct PMCorrection {
|
||||||
|
PMCorrectionAlgorithm algorithm;
|
||||||
|
float intercept;
|
||||||
|
float scalingFactor;
|
||||||
|
bool useEPA; // EPA 2021
|
||||||
|
bool changed;
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool co2CalibrationRequested;
|
bool co2CalibrationRequested;
|
||||||
bool ledBarTestRequested;
|
bool ledBarTestRequested;
|
||||||
bool udpated;
|
bool updated;
|
||||||
String failedMessage;
|
String failedMessage;
|
||||||
bool _noxLearnOffsetChanged;
|
bool _noxLearnOffsetChanged;
|
||||||
bool _tvocLearningOffsetChanged;
|
bool _tvocLearningOffsetChanged;
|
||||||
@ -19,10 +29,13 @@ private:
|
|||||||
String otaNewFirmwareVersion;
|
String otaNewFirmwareVersion;
|
||||||
bool _offlineMode = false;
|
bool _offlineMode = false;
|
||||||
bool _ledBarModeChanged = false;
|
bool _ledBarModeChanged = false;
|
||||||
|
PMCorrection pmCorrection;
|
||||||
|
|
||||||
AirGradient* ag;
|
AirGradient* ag;
|
||||||
|
|
||||||
String getLedBarModeName(LedBarMode mode);
|
String getLedBarModeName(LedBarMode mode);
|
||||||
|
PMCorrectionAlgorithm matchPmAlgorithm(String algorithm);
|
||||||
|
bool updatePmCorrection(JSONVar &json);
|
||||||
void saveConfig(void);
|
void saveConfig(void);
|
||||||
void loadConfig(void);
|
void loadConfig(void);
|
||||||
void defaultConfig(void);
|
void defaultConfig(void);
|
||||||
@ -82,6 +95,10 @@ public:
|
|||||||
void setOfflineMode(bool offline);
|
void setOfflineMode(bool offline);
|
||||||
void setOfflineModeWithoutSave(bool offline);
|
void setOfflineModeWithoutSave(bool offline);
|
||||||
bool isLedBarModeChanged(void);
|
bool isLedBarModeChanged(void);
|
||||||
|
bool isMonitorDisplayCompensatedValues(void);
|
||||||
|
bool isPMCorrectionChanged(void);
|
||||||
|
bool isPMCorrectionEnabled(void);
|
||||||
|
PMCorrection getPMCorrection(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /** _AG_CONFIG_H_ */
|
#endif /** _AG_CONFIG_H_ */
|
||||||
|
@ -10,40 +10,48 @@
|
|||||||
*
|
*
|
||||||
* @param hasStatus
|
* @param hasStatus
|
||||||
*/
|
*/
|
||||||
void OledDisplay::showTempHum(bool hasStatus) {
|
void OledDisplay::showTempHum(bool hasStatus, char *buf, int buf_size) {
|
||||||
char buf[16];
|
/** Temperature */
|
||||||
if (utils::isValidTemperature(value.Temperature)) {
|
float temp = value.getFloat(Measurements::Temperature);
|
||||||
|
if (utils::isValidTemperature(temp)) {
|
||||||
|
float t = 0.0f;
|
||||||
|
if (config.isTemperatureUnitInF()) {
|
||||||
|
t = utils::degreeC_To_F(temp);
|
||||||
|
} else {
|
||||||
|
t = temp;
|
||||||
|
}
|
||||||
|
|
||||||
if (config.isTemperatureUnitInF()) {
|
if (config.isTemperatureUnitInF()) {
|
||||||
float tempF = (value.Temperature * 9) / 5 + 32;
|
|
||||||
if (hasStatus) {
|
if (hasStatus) {
|
||||||
snprintf(buf, sizeof(buf), "%0.1f", tempF);
|
snprintf(buf, buf_size, "%0.1f", t);
|
||||||
} else {
|
} else {
|
||||||
snprintf(buf, sizeof(buf), "%0.1f°F", tempF);
|
snprintf(buf, buf_size, "%0.1f°F", t);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (hasStatus) {
|
if (hasStatus) {
|
||||||
snprintf(buf, sizeof(buf), "%.1f", value.Temperature);
|
snprintf(buf, buf_size, "%.1f", t);
|
||||||
} else {
|
} else {
|
||||||
snprintf(buf, sizeof(buf), "%.1f°C", value.Temperature);
|
snprintf(buf, buf_size, "%.1f°C", t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else { /** Show invalid value */
|
||||||
if (config.isTemperatureUnitInF()) {
|
if (config.isTemperatureUnitInF()) {
|
||||||
snprintf(buf, sizeof(buf), "-°F");
|
snprintf(buf, buf_size, "-°F");
|
||||||
} else {
|
} else {
|
||||||
snprintf(buf, sizeof(buf), "-°C");
|
snprintf(buf, buf_size, "-°C");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DISP()->drawUTF8(1, 10, buf);
|
DISP()->drawUTF8(1, 10, buf);
|
||||||
|
|
||||||
/** Show humidty */
|
/** Show humidity */
|
||||||
if (utils::isValidHumidity(value.Humidity)) {
|
int rhum = (int)value.getFloat(Measurements::Humidity);
|
||||||
snprintf(buf, sizeof(buf), "%d%%", value.Humidity);
|
if (utils::isValidHumidity(rhum)) {
|
||||||
|
snprintf(buf, buf_size, "%d%%", rhum);
|
||||||
} else {
|
} else {
|
||||||
snprintf(buf, sizeof(buf), "-%%");
|
snprintf(buf, buf_size, "-%%");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.Humidity > 99) {
|
if (rhum > 99.0) {
|
||||||
DISP()->drawStr(97, 10, buf);
|
DISP()->drawStr(97, 10, buf);
|
||||||
} else {
|
} else {
|
||||||
DISP()->drawStr(105, 10, buf);
|
DISP()->drawStr(105, 10, buf);
|
||||||
@ -261,7 +269,7 @@ void OledDisplay::showDashboard(const char *status) {
|
|||||||
do {
|
do {
|
||||||
DISP()->setFont(u8g2_font_t0_16_tf);
|
DISP()->setFont(u8g2_font_t0_16_tf);
|
||||||
if ((status == NULL) || (strlen(status) == 0)) {
|
if ((status == NULL) || (strlen(status) == 0)) {
|
||||||
showTempHum(false);
|
showTempHum(false, strBuf, sizeof(strBuf));
|
||||||
} else {
|
} else {
|
||||||
String strStatus = "Show status: " + String(status);
|
String strStatus = "Show status: " + String(status);
|
||||||
logInfo(strStatus);
|
logInfo(strStatus);
|
||||||
@ -272,7 +280,7 @@ void OledDisplay::showDashboard(const char *status) {
|
|||||||
/** Show WiFi NA*/
|
/** Show WiFi NA*/
|
||||||
if (strcmp(status, "WiFi N/A") == 0) {
|
if (strcmp(status, "WiFi N/A") == 0) {
|
||||||
DISP()->setFont(u8g2_font_t0_12_tf);
|
DISP()->setFont(u8g2_font_t0_12_tf);
|
||||||
showTempHum(true);
|
showTempHum(true, strBuf, sizeof(strBuf));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,8 +292,9 @@ void OledDisplay::showDashboard(const char *status) {
|
|||||||
DISP()->drawUTF8(1, 27, "CO2");
|
DISP()->drawUTF8(1, 27, "CO2");
|
||||||
|
|
||||||
DISP()->setFont(u8g2_font_t0_22b_tf);
|
DISP()->setFont(u8g2_font_t0_22b_tf);
|
||||||
if (utils::isValidCO2(value.CO2)) {
|
int co2 = value.get(Measurements::CO2);
|
||||||
sprintf(strBuf, "%d", value.CO2);
|
if (utils::isValidCO2(co2)) {
|
||||||
|
sprintf(strBuf, "%d", co2);
|
||||||
} else {
|
} else {
|
||||||
sprintf(strBuf, "%s", "-");
|
sprintf(strBuf, "%s", "-");
|
||||||
}
|
}
|
||||||
@ -304,28 +313,28 @@ void OledDisplay::showDashboard(const char *status) {
|
|||||||
DISP()->drawStr(55, 27, "PM2.5");
|
DISP()->drawStr(55, 27, "PM2.5");
|
||||||
|
|
||||||
/** Draw PM2.5 value */
|
/** Draw PM2.5 value */
|
||||||
int pm25 = value.pm25_1;
|
|
||||||
if (config.hasSensorSHT) {
|
int pm25 = value.get(Measurements::PM25);
|
||||||
pm25 = ag->pms5003.compensated(pm25, value.Humidity);
|
if (utils::isValidPm(pm25)) {
|
||||||
|
if (config.hasSensorSHT && config.isPMCorrectionEnabled()) {
|
||||||
|
pm25 = (int)value.getCorrectedPM25(*ag, config);
|
||||||
|
}
|
||||||
|
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()->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 (config.isPmStandardInUSAQI()) {
|
||||||
if (utils::isValidPMS(value.pm25_1)) {
|
|
||||||
sprintf(strBuf, "%d", ag->pms5003.convertPm25ToUsAqi(value.pm25_1));
|
|
||||||
} else {
|
|
||||||
sprintf(strBuf, "%s", "-");
|
|
||||||
}
|
|
||||||
DISP()->drawStr(55, 48, strBuf);
|
|
||||||
DISP()->setFont(u8g2_font_t0_12_tf);
|
|
||||||
DISP()->drawUTF8(55, 61, "AQI");
|
DISP()->drawUTF8(55, 61, "AQI");
|
||||||
} else {
|
} else {
|
||||||
if (utils::isValidPMS(value.pm25_1)) {
|
|
||||||
sprintf(strBuf, "%d", value.pm25_1);
|
|
||||||
} else {
|
|
||||||
sprintf(strBuf, "%s", "-");
|
|
||||||
}
|
|
||||||
DISP()->drawStr(55, 48, strBuf);
|
|
||||||
DISP()->setFont(u8g2_font_t0_12_tf);
|
|
||||||
DISP()->drawUTF8(55, 61, "ug/m³");
|
DISP()->drawUTF8(55, 61, "ug/m³");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,17 +343,19 @@ void OledDisplay::showDashboard(const char *status) {
|
|||||||
DISP()->drawStr(100, 27, "VOC:");
|
DISP()->drawStr(100, 27, "VOC:");
|
||||||
|
|
||||||
/** Draw tvocIndexvalue */
|
/** Draw tvocIndexvalue */
|
||||||
if (utils::isValidVOC(value.TVOC)) {
|
int tvoc = value.get(Measurements::TVOC);
|
||||||
sprintf(strBuf, "%d", value.TVOC);
|
if (utils::isValidVOC(tvoc)) {
|
||||||
|
sprintf(strBuf, "%d", tvoc);
|
||||||
} else {
|
} else {
|
||||||
sprintf(strBuf, "%s", "-");
|
sprintf(strBuf, "%s", "-");
|
||||||
}
|
}
|
||||||
DISP()->drawStr(100, 39, strBuf);
|
DISP()->drawStr(100, 39, strBuf);
|
||||||
|
|
||||||
/** Draw NOx label */
|
/** Draw NOx label */
|
||||||
|
int nox = value.get(Measurements::NOx);
|
||||||
DISP()->drawStr(100, 53, "NOx:");
|
DISP()->drawStr(100, 53, "NOx:");
|
||||||
if (utils::isValidNOx(value.NOx)) {
|
if (utils::isValidNOx(nox)) {
|
||||||
sprintf(strBuf, "%d", value.NOx);
|
sprintf(strBuf, "%d", nox);
|
||||||
} else {
|
} else {
|
||||||
sprintf(strBuf, "%s", "-");
|
sprintf(strBuf, "%s", "-");
|
||||||
}
|
}
|
||||||
@ -354,35 +365,37 @@ void OledDisplay::showDashboard(const char *status) {
|
|||||||
ag->display.clear();
|
ag->display.clear();
|
||||||
|
|
||||||
/** Set CO2 */
|
/** Set CO2 */
|
||||||
if(utils::isValidCO2(value.CO2)) {
|
int co2 = value.get(Measurements::CO2);
|
||||||
snprintf(strBuf, sizeof(strBuf), "CO2:%d", value.CO2);
|
if (utils::isValidCO2(co2)) {
|
||||||
|
snprintf(strBuf, sizeof(strBuf), "CO2:%d", co2);
|
||||||
} else {
|
} else {
|
||||||
snprintf(strBuf, sizeof(strBuf), "CO2:-");
|
snprintf(strBuf, sizeof(strBuf), "CO2:-");
|
||||||
}
|
}
|
||||||
|
|
||||||
ag->display.setCursor(0, 0);
|
ag->display.setCursor(0, 0);
|
||||||
ag->display.setText(strBuf);
|
ag->display.setText(strBuf);
|
||||||
|
|
||||||
/** Set PM */
|
/** Set PM */
|
||||||
int pm25 = value.pm25_1;
|
int pm25 = value.get(Measurements::PM25);
|
||||||
if(config.hasSensorSHT) {
|
if (config.hasSensorSHT && config.isPMCorrectionEnabled()) {
|
||||||
pm25 = (int)ag->pms5003.compensated(pm25, value.Humidity);
|
pm25 = (int)value.getCorrectedPM25(*ag, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
ag->display.setCursor(0, 12);
|
ag->display.setCursor(0, 12);
|
||||||
if (utils::isValidPMS(value.pm25_1)) {
|
if (utils::isValidPm(pm25)) {
|
||||||
snprintf(strBuf, sizeof(strBuf), "PM2.5:%d", value.pm25_1);
|
snprintf(strBuf, sizeof(strBuf), "PM2.5:%d", pm25);
|
||||||
} else {
|
} else {
|
||||||
snprintf(strBuf, sizeof(strBuf), "PM2.5:-");
|
snprintf(strBuf, sizeof(strBuf), "PM2.5:-");
|
||||||
}
|
}
|
||||||
ag->display.setText(strBuf);
|
ag->display.setText(strBuf);
|
||||||
|
|
||||||
/** Set temperature and humidity */
|
/** Set temperature and humidity */
|
||||||
if (utils::isValidTemperature(value.Temperature)) {
|
float temp = value.getFloat(Measurements::Temperature);
|
||||||
|
if (utils::isValidTemperature(temp)) {
|
||||||
if (config.isTemperatureUnitInF()) {
|
if (config.isTemperatureUnitInF()) {
|
||||||
float tempF = (value.Temperature * 9) / 5 + 32;
|
snprintf(strBuf, sizeof(strBuf), "T:%0.1f F", utils::degreeC_To_F(temp));
|
||||||
snprintf(strBuf, sizeof(strBuf), "T:%0.1f F", tempF);
|
|
||||||
} else {
|
} else {
|
||||||
snprintf(strBuf, sizeof(strBuf), "T:%0.f1 C", value.Temperature);
|
snprintf(strBuf, sizeof(strBuf), "T:%0.f1 C", temp);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (config.isTemperatureUnitInF()) {
|
if (config.isTemperatureUnitInF()) {
|
||||||
@ -395,8 +408,9 @@ void OledDisplay::showDashboard(const char *status) {
|
|||||||
ag->display.setCursor(0, 24);
|
ag->display.setCursor(0, 24);
|
||||||
ag->display.setText(strBuf);
|
ag->display.setText(strBuf);
|
||||||
|
|
||||||
if (utils::isValidHumidity(value.Humidity)) {
|
int rhum = (int)value.getFloat(Measurements::Humidity);
|
||||||
snprintf(strBuf, sizeof(strBuf), "H:%d %%", (int)value.Humidity);
|
if (utils::isValidHumidity(rhum)) {
|
||||||
|
snprintf(strBuf, sizeof(strBuf), "H:%d %%", rhum);
|
||||||
} else {
|
} else {
|
||||||
snprintf(strBuf, sizeof(strBuf), "H:- %%");
|
snprintf(strBuf, sizeof(strBuf), "H:- %%");
|
||||||
}
|
}
|
||||||
@ -423,7 +437,17 @@ void OledDisplay::setBrightness(int percent) {
|
|||||||
DISP()->setContrast((127 * percent) / 100);
|
DISP()->setContrast((127 * percent) / 100);
|
||||||
}
|
}
|
||||||
} else if (ag->isBasic()) {
|
} else if (ag->isBasic()) {
|
||||||
ag->display.setContrast((255 * percent) / 100);
|
if (percent == 0) {
|
||||||
|
isDisplayOff = true;
|
||||||
|
|
||||||
|
// Clear display.
|
||||||
|
ag->display.clear();
|
||||||
|
ag->display.show();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
isDisplayOff = false;
|
||||||
|
ag->display.setContrast((255 * percent) / 100);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -518,7 +542,7 @@ void OledDisplay::showRebooting(void) {
|
|||||||
do {
|
do {
|
||||||
DISP()->setFont(u8g2_font_t0_16_tf);
|
DISP()->setFont(u8g2_font_t0_16_tf);
|
||||||
// setCentralText(20, "Firmware Update");
|
// setCentralText(20, "Firmware Update");
|
||||||
setCentralText(40, "Reboot...");
|
setCentralText(40, "Rebooting...");
|
||||||
// setCentralText(60, String("Retry after 24h"));
|
// setCentralText(60, String("Retry after 24h"));
|
||||||
} while (DISP()->nextPage());
|
} while (DISP()->nextPage());
|
||||||
} else if (ag->isBasic()) {
|
} else if (ag->isBasic()) {
|
||||||
|
@ -16,7 +16,7 @@ private:
|
|||||||
Measurements &value;
|
Measurements &value;
|
||||||
bool isDisplayOff = false;
|
bool isDisplayOff = false;
|
||||||
|
|
||||||
void showTempHum(bool hasStatus);
|
void showTempHum(bool hasStatus, char* buf, int buf_size);
|
||||||
void setCentralText(int y, String text);
|
void setCentralText(int y, String text);
|
||||||
void setCentralText(int y, const char *text);
|
void setCentralText(int y, const char *text);
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "AgStateMachine.h"
|
#include "AgStateMachine.h"
|
||||||
|
|
||||||
|
#define LED_TEST_BLINK_DELAY 50 /** ms */
|
||||||
#define LED_FAST_BLINK_DELAY 250 /** ms */
|
#define LED_FAST_BLINK_DELAY 250 /** ms */
|
||||||
#define LED_SLOW_BLINK_DELAY 1000 /** ms */
|
#define LED_SLOW_BLINK_DELAY 1000 /** ms */
|
||||||
#define LED_SHORT_BLINK_DELAY 500 /** ms */
|
#define LED_SHORT_BLINK_DELAY 500 /** ms */
|
||||||
@ -68,33 +69,33 @@ void StateMachine::sensorhandleLeds(void) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void StateMachine::co2handleLeds(void) {
|
void StateMachine::co2handleLeds(void) {
|
||||||
int co2Value = value.CO2;
|
int co2Value = value.get(Measurements::CO2);
|
||||||
if (co2Value <= 600) {
|
if (co2Value <= 700) {
|
||||||
/** G; 1 */
|
/** G; 1 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
} else if (co2Value <= 800) {
|
} else if (co2Value <= 1000) {
|
||||||
/** GG; 2 */
|
/** GG; 2 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
} else if (co2Value <= 1000) {
|
} else if (co2Value <= 1333) {
|
||||||
/** YYY; 3 */
|
/** YYY; 3 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 3);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 3);
|
||||||
} else if (co2Value <= 1250) {
|
} else if (co2Value <= 1666) {
|
||||||
/** OOOO; 4 */
|
/** OOOO; 4 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 3);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 3);
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 4);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 4);
|
||||||
} else if (co2Value <= 1500) {
|
} else if (co2Value <= 2000) {
|
||||||
/** OOOOO; 5 */
|
/** OOOOO; 5 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 3);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 3);
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 4);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 4);
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 5);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 5);
|
||||||
} else if (co2Value <= 1750) {
|
} else if (co2Value <= 2666) {
|
||||||
/** RRRRRR; 6 */
|
/** RRRRRR; 6 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
@ -102,7 +103,7 @@ void StateMachine::co2handleLeds(void) {
|
|||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 4);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 4);
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 5);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 5);
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6);
|
||||||
} else if (co2Value <= 2000) {
|
} else if (co2Value <= 3333) {
|
||||||
/** RRRRRRR; 7 */
|
/** RRRRRRR; 7 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
@ -111,17 +112,17 @@ void StateMachine::co2handleLeds(void) {
|
|||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 5);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 5);
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6);
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 7);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 7);
|
||||||
} else if (co2Value <= 3000) {
|
} else if (co2Value <= 4000) {
|
||||||
/** PPPPPPPP; 8 */
|
/** RRRRRRRR; 8 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 3);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 3);
|
||||||
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 4);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 4);
|
||||||
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 5);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 5);
|
||||||
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 6);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6);
|
||||||
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 7);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 7);
|
||||||
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 8);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 8);
|
||||||
} else { /** > 3000 */
|
} else { /** > 4000 */
|
||||||
/* PRPRPRPRP; 9 */
|
/* PRPRPRPRP; 9 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
@ -140,33 +141,37 @@ void StateMachine::co2handleLeds(void) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void StateMachine::pm25handleLeds(void) {
|
void StateMachine::pm25handleLeds(void) {
|
||||||
int pm25Value = value.pm25_1;
|
int pm25Value = value.get(Measurements::PM25);
|
||||||
if (pm25Value < 5) {
|
if (config.hasSensorSHT && config.isPMCorrectionEnabled()) {
|
||||||
|
pm25Value = (int)value.getCorrectedPM25(*ag, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pm25Value <= 5) {
|
||||||
/** G; 1 */
|
/** G; 1 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
} else if (pm25Value < 10) {
|
} else if (pm25Value <= 9) {
|
||||||
/** GG; 2 */
|
/** GG; 2 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
} else if (pm25Value < 20) {
|
} else if (pm25Value <= 20) {
|
||||||
/** YYY; 3 */
|
/** YYY; 3 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 3);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 3);
|
||||||
} else if (pm25Value < 35) {
|
} else if (pm25Value <= 35) {
|
||||||
/** YYYY; 4 */
|
/** YYYY; 4 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 3);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 3);
|
||||||
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 4);
|
ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 4);
|
||||||
} else if (pm25Value < 45) {
|
} else if (pm25Value <= 45) {
|
||||||
/** OOOOO; 5 */
|
/** OOOOO; 5 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 3);
|
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 3);
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 4);
|
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 4);
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 5);
|
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 5);
|
||||||
} else if (pm25Value < 55) {
|
} else if (pm25Value <= 55) {
|
||||||
/** OOOOOO; 6 */
|
/** OOOOOO; 6 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
@ -174,7 +179,7 @@ void StateMachine::pm25handleLeds(void) {
|
|||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 4);
|
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 4);
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 5);
|
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 5);
|
||||||
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 6);
|
ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 6);
|
||||||
} else if (pm25Value < 100) {
|
} else if (pm25Value <= 100) {
|
||||||
/** RRRRRRR; 7 */
|
/** RRRRRRR; 7 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
@ -183,7 +188,7 @@ void StateMachine::pm25handleLeds(void) {
|
|||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 5);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 5);
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6);
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 7);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 7);
|
||||||
} else if (pm25Value < 200) {
|
} else if (pm25Value <= 125) {
|
||||||
/** RRRRRRRR; 8 */
|
/** RRRRRRRR; 8 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
@ -193,7 +198,7 @@ void StateMachine::pm25handleLeds(void) {
|
|||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6);
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 7);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 7);
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 8);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 8);
|
||||||
} else if (pm25Value < 250) {
|
} else if (pm25Value <= 225) {
|
||||||
/** PPPPPPPPP; 9 */
|
/** PPPPPPPPP; 9 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
@ -204,7 +209,7 @@ void StateMachine::pm25handleLeds(void) {
|
|||||||
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 7);
|
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 7);
|
||||||
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 8);
|
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 8);
|
||||||
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 9);
|
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 9);
|
||||||
} else { /** > 250 */
|
} else { /** > 225 */
|
||||||
/* PRPRPRPRP; 9 */
|
/* PRPRPRPRP; 9 */
|
||||||
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 1);
|
ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 1);
|
||||||
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2);
|
ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2);
|
||||||
@ -305,18 +310,23 @@ void StateMachine::co2Calibration(void) {
|
|||||||
|
|
||||||
void StateMachine::ledBarTest(void) {
|
void StateMachine::ledBarTest(void) {
|
||||||
if (config.isLedBarTestRequested()) {
|
if (config.isLedBarTestRequested()) {
|
||||||
if (config.getCountry() == "TH") {
|
if (ag->isOne()) {
|
||||||
uint32_t tstart = millis();
|
if (config.getCountry() == "TH") {
|
||||||
logInfo("Start run LED test for 2 min");
|
uint32_t tstart = millis();
|
||||||
while (1) {
|
logInfo("Start run LED test for 2 min");
|
||||||
ledBarRunTest();
|
while (1) {
|
||||||
uint32_t ms = (uint32_t)(millis() - tstart);
|
ledBarRunTest();
|
||||||
if (ms >= (60 * 1000 * 2)) {
|
uint32_t ms = (uint32_t)(millis() - tstart);
|
||||||
logInfo("LED test after 2 min finish");
|
if (ms >= (60 * 1000 * 2)) {
|
||||||
break;
|
logInfo("LED test after 2 min finish");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ledBarRunTest();
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else if(ag->isOpenAir()) {
|
||||||
ledBarRunTest();
|
ledBarRunTest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -325,22 +335,31 @@ void StateMachine::ledBarTest(void) {
|
|||||||
void StateMachine::ledBarPowerUpTest(void) { ledBarRunTest(); }
|
void StateMachine::ledBarPowerUpTest(void) { ledBarRunTest(); }
|
||||||
|
|
||||||
void StateMachine::ledBarRunTest(void) {
|
void StateMachine::ledBarRunTest(void) {
|
||||||
disp.setText("LED Test", "running", ".....");
|
if (ag->isOne()) {
|
||||||
runLedTest('r');
|
disp.setText("LED Test", "running", ".....");
|
||||||
ag->ledBar.show();
|
runLedTest('r');
|
||||||
delay(1000);
|
ag->ledBar.show();
|
||||||
runLedTest('g');
|
delay(1000);
|
||||||
ag->ledBar.show();
|
runLedTest('g');
|
||||||
delay(1000);
|
ag->ledBar.show();
|
||||||
runLedTest('b');
|
delay(1000);
|
||||||
ag->ledBar.show();
|
runLedTest('b');
|
||||||
delay(1000);
|
ag->ledBar.show();
|
||||||
runLedTest('w');
|
delay(1000);
|
||||||
ag->ledBar.show();
|
runLedTest('w');
|
||||||
delay(1000);
|
ag->ledBar.show();
|
||||||
runLedTest('n');
|
delay(1000);
|
||||||
ag->ledBar.show();
|
runLedTest('n');
|
||||||
delay(1000);
|
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) {
|
void StateMachine::runLedTest(char color) {
|
||||||
@ -480,7 +499,7 @@ void StateMachine::displayHandle(AgStateMachineState state) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AgStateMachineServerLost: {
|
case AgStateMachineServerLost: {
|
||||||
disp.showDashboard("Server N/A");
|
disp.showDashboard("AG Server N/A");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AgStateMachineSensorConfigFailed: {
|
case AgStateMachineSensorConfigFailed: {
|
||||||
@ -489,7 +508,7 @@ void StateMachine::displayHandle(AgStateMachineState state) {
|
|||||||
if (ms >= 5000) {
|
if (ms >= 5000) {
|
||||||
addToDashboardTime = millis();
|
addToDashboardTime = millis();
|
||||||
if (addToDashBoardToggle) {
|
if (addToDashBoardToggle) {
|
||||||
disp.showDashboard("Add to Dashboard");
|
disp.showDashboard("Add to AG Dashb.");
|
||||||
} else {
|
} else {
|
||||||
disp.showDashboard(ag->deviceId().c_str());
|
disp.showDashboard(ag->deviceId().c_str());
|
||||||
}
|
}
|
||||||
|
1298
src/AgValue.cpp
1298
src/AgValue.cpp
File diff suppressed because it is too large
Load Diff
247
src/AgValue.h
247
src/AgValue.h
@ -1,76 +1,207 @@
|
|||||||
#ifndef _AG_VALUE_H_
|
#ifndef _AG_VALUE_H_
|
||||||
#define _AG_VALUE_H_
|
#define _AG_VALUE_H_
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include "AgConfigure.h"
|
||||||
|
#include "AirGradient.h"
|
||||||
#include "App/AppDef.h"
|
#include "App/AppDef.h"
|
||||||
|
#include "Libraries/Arduino_JSON/src/Arduino_JSON.h"
|
||||||
|
#include "Main/utils.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class Measurements {
|
class Measurements {
|
||||||
private:
|
private:
|
||||||
|
// Generic struct for update indication for respective value
|
||||||
|
struct Update {
|
||||||
|
int invalidCounter; // Counting on how many invalid value that are passed to update function
|
||||||
|
int max; // Maximum length of the period of the moving average
|
||||||
|
float avg; // Moving average value, updated every update function called
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reading type for sensor value that outputs float
|
||||||
|
struct FloatValue {
|
||||||
|
float sumValues; // Total value from each update
|
||||||
|
std::vector<float> listValues; // List of update value that are kept
|
||||||
|
Update update;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reading type for sensor value that outputs integer
|
||||||
|
struct IntegerValue {
|
||||||
|
unsigned long sumValues; // Total value from each update; unsigned long to accomodate TVOx and
|
||||||
|
// NOx raw data
|
||||||
|
std::vector<int> listValues; // List of update value that are kept
|
||||||
|
Update update;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Measurements() {
|
Measurements() {}
|
||||||
pm25_1 = -1;
|
|
||||||
pm01_1 = -1;
|
|
||||||
pm10_1 = -1;
|
|
||||||
pm03PCount_1 = -1;
|
|
||||||
temp_1 = -1001;
|
|
||||||
hum_1 = -1;
|
|
||||||
|
|
||||||
pm25_2 = -1;
|
|
||||||
pm01_2 = -1;
|
|
||||||
pm10_2 = -1;
|
|
||||||
pm03PCount_2 = -1;
|
|
||||||
temp_2 = -1001;
|
|
||||||
hum_2 = -1;
|
|
||||||
|
|
||||||
Temperature = -1001;
|
|
||||||
Humidity = -1;
|
|
||||||
CO2 = -1;
|
|
||||||
TVOC = -1;
|
|
||||||
TVOCRaw = -1;
|
|
||||||
NOx = -1;
|
|
||||||
NOxRaw = -1;
|
|
||||||
}
|
|
||||||
~Measurements() {}
|
~Measurements() {}
|
||||||
|
|
||||||
float Temperature;
|
// Enumeration for every AG measurements
|
||||||
int Humidity;
|
enum MeasurementType {
|
||||||
int CO2;
|
Temperature,
|
||||||
int TVOC;
|
Humidity,
|
||||||
int TVOCRaw;
|
CO2,
|
||||||
int NOx;
|
TVOC, // index value
|
||||||
int NOxRaw;
|
TVOCRaw,
|
||||||
|
NOx, // index value
|
||||||
|
NOxRaw,
|
||||||
|
PM01, // PM1.0 under atmospheric environment
|
||||||
|
PM25, // PM2.5 under athompheric environment
|
||||||
|
PM10, // PM10 under atmospheric environment
|
||||||
|
PM01_SP, // PM1.0 standard particle
|
||||||
|
PM25_SP, // PM2.5 standard particle
|
||||||
|
PM10_SP, // PM10 standard particle
|
||||||
|
PM03_PC, // Particle 0.3 count
|
||||||
|
PM05_PC, // Particle 0.5 count
|
||||||
|
PM01_PC, // Particle 1.0 count
|
||||||
|
PM25_PC, // Particle 2.5 count
|
||||||
|
PM5_PC, // Particle 5.0 count
|
||||||
|
PM10_PC, // Particle 10 count
|
||||||
|
};
|
||||||
|
|
||||||
int pm25_1;
|
/**
|
||||||
int pm01_1;
|
* @brief Set each MeasurementType maximum period length for moving average
|
||||||
int pm10_1;
|
*
|
||||||
int pm03PCount_1;
|
* @param type the target measurement type to set
|
||||||
float temp_1;
|
* @param max the maximum period length
|
||||||
int hum_1;
|
*/
|
||||||
|
void maxPeriod(MeasurementType, int max);
|
||||||
|
|
||||||
int pm25_2;
|
/**
|
||||||
int pm01_2;
|
* @brief update target measurement type with new value.
|
||||||
int pm10_2;
|
* Each MeasurementType has last raw value and moving average value based on max period
|
||||||
int pm03PCount_2;
|
* This function is for MeasurementType that use INT as the data type
|
||||||
float temp_2;
|
*
|
||||||
int hum_2;
|
* @param type measurement type that will be updated
|
||||||
|
* @param val (int) the new value
|
||||||
|
* @param ch (int) the MeasurementType channel, not every MeasurementType has more than 1 channel.
|
||||||
|
* Currently maximum channel is 2. Default: 1 (channel 1)
|
||||||
|
* @return false if new value invalid consecutively reach threshold (max period)
|
||||||
|
* @return true otherwise
|
||||||
|
*/
|
||||||
|
bool update(MeasurementType type, int val, int ch = 1);
|
||||||
|
|
||||||
int pm1Value01;
|
/**
|
||||||
int pm1Value25;
|
* @brief update target measurement type with new value.
|
||||||
int pm1Value10;
|
* Each MeasurementType has last raw value and moving average value based on max period
|
||||||
int pm1PCount;
|
* This function is for MeasurementType that use FLOAT as the data type
|
||||||
int pm1temp;
|
*
|
||||||
int pm1hum;
|
* @param type measurement type that will be updated
|
||||||
int pm2Value01;
|
* @param val (float) the new value
|
||||||
int pm2Value25;
|
* @param ch (int) the MeasurementType channel, not every MeasurementType has more than 1 channel.
|
||||||
int pm2Value10;
|
* Currently maximum channel is 2. Default: 1 (channel 1)
|
||||||
int pm2PCount;
|
* @return false if new value invalid consecutively reach threshold (max period)
|
||||||
int pm2temp;
|
* @return true otherwise
|
||||||
int pm2hum;
|
*/
|
||||||
int countPosition;
|
bool update(MeasurementType type, float val, int ch = 1);
|
||||||
const int targetCount = 20;
|
|
||||||
|
/**
|
||||||
|
* @brief Get the target measurement latest value
|
||||||
|
*
|
||||||
|
* @param type measurement type that will be retrieve
|
||||||
|
* @param ch target type value channel
|
||||||
|
* @return int measurement type value
|
||||||
|
*/
|
||||||
|
int get(MeasurementType type, int ch = 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the target measurement latest value
|
||||||
|
*
|
||||||
|
* @param type measurement type that will be retrieve
|
||||||
|
* @param ch target type value channel
|
||||||
|
* @return float measurement type value
|
||||||
|
*/
|
||||||
|
float getFloat(MeasurementType type, int ch = 1);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the Corrected PM25 object based on the correction algorithm from configuration
|
||||||
|
*
|
||||||
|
* @param ag AirGradient instance
|
||||||
|
* @param config Configuration instance
|
||||||
|
* @param useAvg Use moving average value if true, otherwise use latest value
|
||||||
|
* @param ch MeasurementType channel
|
||||||
|
* @return float Corrected PM2.5 value
|
||||||
|
*/
|
||||||
|
float getCorrectedPM25(AirGradient &ag, Configuration &config, bool useAvg = false, int ch = 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* build json payload for every measurements
|
||||||
|
*/
|
||||||
|
String toString(bool localServer, AgFirmwareMode fwMode, int rssi, AirGradient &ag,
|
||||||
|
Configuration &config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to true if want to debug every update value
|
||||||
|
*/
|
||||||
|
void setDebug(bool debug);
|
||||||
|
|
||||||
|
// TODO: update this to use setter
|
||||||
int bootCount;
|
int bootCount;
|
||||||
|
|
||||||
String toString(bool isLocal, AgFirmwareMode fwMode, int rssi, void* _ag, void* _config);
|
private:
|
||||||
|
// Some declared as an array (channel), because FW_MODE_O_1PPx has two PMS5003T
|
||||||
|
FloatValue _temperature[2];
|
||||||
|
FloatValue _humidity[2];
|
||||||
|
IntegerValue _co2;
|
||||||
|
IntegerValue _tvoc; // Index value
|
||||||
|
IntegerValue _tvoc_raw;
|
||||||
|
IntegerValue _nox; // Index value
|
||||||
|
IntegerValue _nox_raw;
|
||||||
|
IntegerValue _pm_01[2]; // pm 1.0 atmospheric environment
|
||||||
|
IntegerValue _pm_25[2]; // pm 2.5 atmospheric environment
|
||||||
|
IntegerValue _pm_10[2]; // pm 10 atmospheric environment
|
||||||
|
IntegerValue _pm_01_sp[2]; // pm 1.0 standard particle
|
||||||
|
IntegerValue _pm_25_sp[2]; // pm 2.5 standard particle
|
||||||
|
IntegerValue _pm_10_sp[2]; // pm 10 standard particle
|
||||||
|
IntegerValue _pm_03_pc[2]; // particle count 0.3
|
||||||
|
IntegerValue _pm_05_pc[2]; // particle count 0.5
|
||||||
|
IntegerValue _pm_01_pc[2]; // particle count 1.0
|
||||||
|
IntegerValue _pm_25_pc[2]; // particle count 2.5
|
||||||
|
IntegerValue _pm_5_pc[2]; // particle count 5.0
|
||||||
|
IntegerValue _pm_10_pc[2]; // particle count 10
|
||||||
|
|
||||||
|
bool _debug = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get PMS5003 firmware version string
|
||||||
|
*
|
||||||
|
* @param fwCode
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
String pms5003FirmwareVersion(int fwCode);
|
||||||
|
/**
|
||||||
|
* @brief Get PMS5003T firmware version string
|
||||||
|
*
|
||||||
|
* @param fwCode
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
String pms5003TFirmwareVersion(int fwCode);
|
||||||
|
/**
|
||||||
|
* @brief Get firmware version string
|
||||||
|
*
|
||||||
|
* @param prefix Prefix firmware string
|
||||||
|
* @param fwCode Version code
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
String pms5003FirmwareVersionBase(String prefix, int fwCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert AgValue Type to string representation of the value
|
||||||
|
*/
|
||||||
|
String measurementTypeStr(MeasurementType type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief check if provided channel is a valid channel or not
|
||||||
|
* abort program if invalid
|
||||||
|
*/
|
||||||
|
void validateChannel(int ch);
|
||||||
|
|
||||||
|
JSONVar buildOutdoor(bool localServer, AgFirmwareMode fwMode, AirGradient &ag,
|
||||||
|
Configuration &config);
|
||||||
|
JSONVar buildIndoor(bool localServer, AirGradient &ag, Configuration &config);
|
||||||
|
JSONVar buildPMS(AirGradient &ag, int ch, bool allCh, bool withTempHum, bool compensate);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /** _AG_VALUE_H_ */
|
#endif /** _AG_VALUE_H_ */
|
||||||
|
@ -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()->setConfigPortalBlocking(false);
|
||||||
WIFI()->setConnectTimeout(15);
|
WIFI()->setConnectTimeout(15);
|
||||||
WIFI()->setTimeout(WIFI_CONNECT_COUNTDOWN_MAX);
|
WIFI()->setTimeout(WIFI_CONNECT_COUNTDOWN_MAX);
|
||||||
@ -50,7 +72,7 @@ bool WifiConnector::connect(void) {
|
|||||||
WIFI()->setSaveParamsCallback([this]() { _wifiSaveParamCallback(); });
|
WIFI()->setSaveParamsCallback([this]() { _wifiSaveParamCallback(); });
|
||||||
WIFI()->setConfigPortalTimeoutCallback([this]() {_wifiTimeoutCallback();});
|
WIFI()->setConfigPortalTimeoutCallback([this]() {_wifiTimeoutCallback();});
|
||||||
if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_3() || ag->isBasic()) {
|
if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_3() || ag->isBasic()) {
|
||||||
disp.setText("Connect to", "WiFi", "...");
|
disp.setText("Connecting to", "WiFi", "...");
|
||||||
} else {
|
} else {
|
||||||
logInfo("Connecting to WiFi...");
|
logInfo("Connecting to WiFi...");
|
||||||
}
|
}
|
||||||
@ -383,3 +405,11 @@ bool WifiConnector::hasConfigurated(void) {
|
|||||||
* @return false
|
* @return false
|
||||||
*/
|
*/
|
||||||
bool WifiConnector::isConfigurePorttalTimeout(void) { return connectorTimeout; }
|
bool WifiConnector::isConfigurePorttalTimeout(void) { return connectorTimeout; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set wifi connect to default WiFi
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void WifiConnector::setDefault(void) {
|
||||||
|
WiFi.begin("airgradient", "cleanair");
|
||||||
|
}
|
||||||
|
@ -46,6 +46,10 @@ public:
|
|||||||
String localIpStr(void);
|
String localIpStr(void);
|
||||||
bool hasConfigurated(void);
|
bool hasConfigurated(void);
|
||||||
bool isConfigurePorttalTimeout(void);
|
bool isConfigurePorttalTimeout(void);
|
||||||
|
|
||||||
|
const char* defaultSsid = "airgradient";
|
||||||
|
const char* defaultPassword = "cleanair";
|
||||||
|
void setDefault(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /** _AG_WIFI_CONNECTOR_H_ */
|
#endif /** _AG_WIFI_CONNECTOR_H_ */
|
||||||
|
@ -65,6 +65,10 @@ bool AirGradient::isOne(void) {
|
|||||||
return boardType == BoardType::ONE_INDOOR;
|
return boardType == BoardType::ONE_INDOOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AirGradient::isOpenAir(void) {
|
||||||
|
return boardType == BoardType::OPEN_AIR_OUTDOOR;
|
||||||
|
}
|
||||||
|
|
||||||
bool AirGradient::isPro4_2(void) {
|
bool AirGradient::isPro4_2(void) {
|
||||||
return boardType == BoardType::DIY_PRO_INDOOR_V4_2;
|
return boardType == BoardType::DIY_PRO_INDOOR_V4_2;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
#include "Main/utils.h"
|
#include "Main/utils.h"
|
||||||
|
|
||||||
#ifndef GIT_VERSION
|
#ifndef GIT_VERSION
|
||||||
#define GIT_VERSION "3.1.5-snap"
|
#define GIT_VERSION "3.1.10-snap"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -135,6 +135,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool isOne(void);
|
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
|
* @brief Check that Airgradient object is DIY_PRO 4.2 indoor
|
||||||
*
|
*
|
||||||
|
@ -94,6 +94,18 @@ enum ConfigurationControl {
|
|||||||
ConfigurationControlBoth
|
ConfigurationControlBoth
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum PMCorrectionAlgorithm {
|
||||||
|
Unknown, // Unknown algorithm
|
||||||
|
None, // No PM correction
|
||||||
|
EPA_2021,
|
||||||
|
SLR_PMS5003_20220802,
|
||||||
|
SLR_PMS5003_20220803,
|
||||||
|
SLR_PMS5003_20220824,
|
||||||
|
SLR_PMS5003_20231030,
|
||||||
|
SLR_PMS5003_20231218,
|
||||||
|
SLR_PMS5003_20240104,
|
||||||
|
};
|
||||||
|
|
||||||
enum AgFirmwareMode {
|
enum AgFirmwareMode {
|
||||||
FW_MODE_I_9PSL, /** ONE_INDOOR */
|
FW_MODE_I_9PSL, /** ONE_INDOOR */
|
||||||
FW_MODE_O_1PST, /** PMS5003T, S8 and SGP41 */
|
FW_MODE_O_1PST, /** PMS5003T, S8 and SGP41 */
|
||||||
|
@ -48,14 +48,14 @@ bool utils::isValidCO2(int16_t value) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool utils::isValidPMS(int value) {
|
bool utils::isValidPm(int value) {
|
||||||
if ((value >= VALID_PMS_MIN) && (value <= VALID_PMS_MAX)) {
|
if ((value >= VALID_PMS_MIN) && (value <= VALID_PMS_MAX)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool utils::isValidPMS03Count(int value) {
|
bool utils::isValidPm03Count(int value) {
|
||||||
if (value >= VALID_PMS03COUNT_MIN) {
|
if (value >= VALID_PMS03COUNT_MIN) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -82,8 +82,13 @@ float utils::getInvalidHumidity(void) { return INVALID_HUMIDITY; }
|
|||||||
|
|
||||||
int utils::getInvalidCO2(void) { return INVALID_CO2; }
|
int utils::getInvalidCO2(void) { return INVALID_CO2; }
|
||||||
|
|
||||||
int utils::getInvalidPMS(void) { return INVALID_PMS; }
|
int utils::getInvalidPmValue(void) { return INVALID_PMS; }
|
||||||
|
|
||||||
int utils::getInvalidNOx(void) { return INVALID_NOX; }
|
int utils::getInvalidNOx(void) { return INVALID_NOX; }
|
||||||
|
|
||||||
int utils::getInvalidVOC(void) { return INVALID_VOC; }
|
int utils::getInvalidVOC(void) { return INVALID_VOC; }
|
||||||
|
|
||||||
|
float utils::degreeC_To_F(float t) {
|
||||||
|
/** (t * 9)/5 + 32 */
|
||||||
|
return t * 1.8f + 32.0f;
|
||||||
|
}
|
||||||
|
@ -14,16 +14,17 @@ public:
|
|||||||
static bool isValidTemperature(float value);
|
static bool isValidTemperature(float value);
|
||||||
static bool isValidHumidity(float value);
|
static bool isValidHumidity(float value);
|
||||||
static bool isValidCO2(int16_t value);
|
static bool isValidCO2(int16_t value);
|
||||||
static bool isValidPMS(int value);
|
static bool isValidPm(int value);
|
||||||
static bool isValidPMS03Count(int value);
|
static bool isValidPm03Count(int value);
|
||||||
static bool isValidNOx(int value);
|
static bool isValidNOx(int value);
|
||||||
static bool isValidVOC(int value);
|
static bool isValidVOC(int value);
|
||||||
static float getInvalidTemperature(void);
|
static float getInvalidTemperature(void);
|
||||||
static float getInvalidHumidity(void);
|
static float getInvalidHumidity(void);
|
||||||
static int getInvalidCO2(void);
|
static int getInvalidCO2(void);
|
||||||
static int getInvalidPMS(void);
|
static int getInvalidPmValue(void);
|
||||||
static int getInvalidNOx(void);
|
static int getInvalidNOx(void);
|
||||||
static int getInvalidVOC(void);
|
static int getInvalidVOC(void);
|
||||||
|
static float degreeC_To_F(float t);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
392
src/PMS/PMS.cpp
392
src/PMS/PMS.cpp
@ -2,248 +2,294 @@
|
|||||||
#include "../Main/BoardDef.h"
|
#include "../Main/BoardDef.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Init and check that sensor has connected
|
* @brief Initializes the sensor and attempts to read data.
|
||||||
*
|
*
|
||||||
* @param stream UART stream
|
* @param stream UART stream
|
||||||
* @return true Sucecss
|
* @return true Sucecss
|
||||||
* @return false Failure
|
* @return false Failure
|
||||||
*/
|
*/
|
||||||
bool PMSBase::begin(Stream *stream) {
|
bool PMSBase::begin(Stream *stream) {
|
||||||
this->stream = stream;
|
Serial.printf("initializing PM sensor\n");
|
||||||
|
|
||||||
failed = true;
|
failCount = 0;
|
||||||
lastRead = 0; // To read buffer on handle without wait after 1.5sec
|
_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
|
// Run and check sensor data for 4sec
|
||||||
while (1) {
|
unsigned long lastInit = millis();
|
||||||
handle();
|
while (true) {
|
||||||
if (failed == false) {
|
readPackage(stream);
|
||||||
return true;
|
if (_connected) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
delay(1);
|
delay(1);
|
||||||
uint32_t ms = (uint32_t)(millis() - lastRead);
|
unsigned long ms = (unsigned long)(millis() - lastInit);
|
||||||
if (ms >= 4000) {
|
if (ms >= 4000) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return _connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check and read sensor data then update variable.
|
* @brief Read PMS package send to device each 1sec
|
||||||
* Check result from method @isFailed before get value
|
*
|
||||||
|
* @param serial
|
||||||
*/
|
*/
|
||||||
void PMSBase::handle() {
|
void PMSBase::readPackage(Stream *serial) {
|
||||||
uint32_t ms;
|
/** If readPackage has process as period larger than READ_PACKAGE_TIMEOUT,
|
||||||
if (lastRead == 0) {
|
* should be clear the lastPackage and readBufferIndex */
|
||||||
lastRead = millis();
|
if (lastReadPackage) {
|
||||||
if (lastRead == 0) {
|
unsigned long ms = (unsigned long)(millis() - lastReadPackage);
|
||||||
lastRead = 1;
|
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 {
|
} else {
|
||||||
ms = (uint32_t)(millis() - lastRead);
|
lastReadPackage = millis();
|
||||||
/**
|
if (!lastReadPackage) {
|
||||||
* The PMS in Active mode sends an update data every 1 second. If we read
|
lastReadPackage = 1;
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool result = false;
|
|
||||||
char buf[32];
|
|
||||||
int bufIndex;
|
|
||||||
int step = 0;
|
|
||||||
int len = 0;
|
|
||||||
int bcount = 0;
|
|
||||||
|
|
||||||
while (stream->available()) {
|
/** Count to call delay() to release the while loop MCU resource for avoid the
|
||||||
char value = stream->read();
|
* watchdog time reset */
|
||||||
switch (step) {
|
uint8_t delayCount = 0;
|
||||||
case 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) {
|
if (value == 0x42) {
|
||||||
step = 1;
|
readBuffer[readBufferIndex++] = value;
|
||||||
bufIndex = 0;
|
|
||||||
buf[bufIndex++] = value;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
case 1: /** Start byte 2 */
|
||||||
case 1: {
|
|
||||||
if (value == 0x4d) {
|
if (value == 0x4d) {
|
||||||
step = 2;
|
readBuffer[readBufferIndex++] = value;
|
||||||
buf[bufIndex++] = value;
|
|
||||||
// Serial.println("Got 0x4d");
|
|
||||||
} else {
|
} else {
|
||||||
step = 0;
|
readBufferIndex = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
case 2: /** Frame length */
|
||||||
case 2: {
|
if (value == 0x00) {
|
||||||
buf[bufIndex++] = value;
|
readBuffer[readBufferIndex++] = value;
|
||||||
if (bufIndex >= 4) {
|
} else {
|
||||||
len = toI16(&buf[2]);
|
readBufferIndex = 0;
|
||||||
if (len != 28) {
|
}
|
||||||
// Serial.printf("Got good bad len %d\r\n", len);
|
break;
|
||||||
len += 4;
|
case 3: /** Frame length */
|
||||||
step = 3;
|
if (value == 0x1C) {
|
||||||
} else {
|
readBuffer[readBufferIndex++] = value;
|
||||||
// Serial.println("Got good len");
|
} else {
|
||||||
step = 4;
|
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;
|
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
|
/** Avoid task watchdog timer reset... */
|
||||||
bcount++;
|
delayCount++;
|
||||||
if ((bcount % 32) == 0) {
|
if (delayCount >= 32) {
|
||||||
|
delayCount = 0;
|
||||||
delay(1);
|
delay(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
/** Check that sensor removed */
|
||||||
lastRead = millis();
|
if (lastPackage) {
|
||||||
if (lastRead == 0) {
|
unsigned long ms = (unsigned long)(millis() - lastPackage);
|
||||||
lastRead = 1;
|
if (ms >= READ_PACKGE_TIMEOUT) {
|
||||||
}
|
lastPackage = 0;
|
||||||
failed = false;
|
_connected = false;
|
||||||
} else {
|
Serial.println("PMS disconnected");
|
||||||
if (ms > 5000) {
|
|
||||||
failed = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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
|
* @brief Read PMS 0.1 ug/m3 with CF = 1 PM estimates
|
||||||
*
|
*
|
||||||
* @return uint16_t
|
* @return uint16_t
|
||||||
*/
|
*/
|
||||||
uint16_t PMSBase::getRaw0_1(void) { return toU16(&package[4]); }
|
uint16_t PMSBase::getRaw0_1(void) { return pms_raw0_1; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read PMS 2.5 ug/m3 with CF = 1 PM estimates
|
* @brief Read PMS 2.5 ug/m3 with CF = 1 PM estimates
|
||||||
*
|
*
|
||||||
* @return uint16_t
|
* @return uint16_t
|
||||||
*/
|
*/
|
||||||
uint16_t PMSBase::getRaw2_5(void) { return toU16(&package[6]); }
|
uint16_t PMSBase::getRaw2_5(void) { return pms_raw2_5; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read PMS 10 ug/m3 with CF = 1 PM estimates
|
* @brief Read PMS 10 ug/m3 with CF = 1 PM estimates
|
||||||
*
|
*
|
||||||
* @return uint16_t
|
* @return uint16_t
|
||||||
*/
|
*/
|
||||||
uint16_t PMSBase::getRaw10(void) { return toU16(&package[8]); }
|
uint16_t PMSBase::getRaw10(void) { return pms_raw10; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read PMS 0.1 ug/m3
|
* @brief Read PMS 0.1 ug/m3
|
||||||
*
|
*
|
||||||
* @return uint16_t
|
* @return uint16_t
|
||||||
*/
|
*/
|
||||||
uint16_t PMSBase::getPM0_1(void) { return toU16(&package[10]); }
|
uint16_t PMSBase::getPM0_1(void) { return pms_pm0_1; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read PMS 2.5 ug/m3
|
* @brief Read PMS 2.5 ug/m3
|
||||||
*
|
*
|
||||||
* @return uint16_t
|
* @return uint16_t
|
||||||
*/
|
*/
|
||||||
uint16_t PMSBase::getPM2_5(void) { return toU16(&package[12]); }
|
uint16_t PMSBase::getPM2_5(void) { return pms_pm2_5; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read PMS 10 ug/m3
|
* @brief Read PMS 10 ug/m3
|
||||||
*
|
*
|
||||||
* @return uint16_t
|
* @return uint16_t
|
||||||
*/
|
*/
|
||||||
uint16_t PMSBase::getPM10(void) { return toU16(&package[14]); }
|
uint16_t PMSBase::getPM10(void) { return pms_pm10; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get numnber concentrations over 0.3 um/0.1L
|
* @brief Get numnber concentrations over 0.3 um/0.1L
|
||||||
*
|
*
|
||||||
* @return uint16_t
|
* @return uint16_t
|
||||||
*/
|
*/
|
||||||
uint16_t PMSBase::getCount0_3(void) { return toU16(&package[16]); }
|
uint16_t PMSBase::getCount0_3(void) { return pms_count0_3; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get numnber concentrations over 0.5 um/0.1L
|
* @brief Get numnber concentrations over 0.5 um/0.1L
|
||||||
*
|
*
|
||||||
* @return uint16_t
|
* @return uint16_t
|
||||||
*/
|
*/
|
||||||
uint16_t PMSBase::getCount0_5(void) { return toU16(&package[18]); }
|
uint16_t PMSBase::getCount0_5(void) { return pms_count0_5; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get numnber concentrations over 1.0 um/0.1L
|
* @brief Get numnber concentrations over 1.0 um/0.1L
|
||||||
*
|
*
|
||||||
* @return uint16_t
|
* @return uint16_t
|
||||||
*/
|
*/
|
||||||
uint16_t PMSBase::getCount1_0(void) { return toU16(&package[20]); }
|
uint16_t PMSBase::getCount1_0(void) { return pms_count1_0; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get numnber concentrations over 2.5 um/0.1L
|
* @brief Get numnber concentrations over 2.5 um/0.1L
|
||||||
*
|
*
|
||||||
* @return uint16_t
|
* @return uint16_t
|
||||||
*/
|
*/
|
||||||
uint16_t PMSBase::getCount2_5(void) { return toU16(&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)
|
* @brief Get numnber concentrations over 5.0 um/0.1L (only PMS5003)
|
||||||
*
|
*
|
||||||
* @return uint16_t
|
* @return uint16_t
|
||||||
*/
|
*/
|
||||||
uint16_t PMSBase::getCount5_0(void) { return toU16(&package[24]); }
|
uint16_t PMSBase::getCount5_0(void) { return pms_count5_0; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get numnber concentrations over 10.0 um/0.1L (only PMS5003)
|
* @brief Get numnber concentrations over 10.0 um/0.1L (only PMS5003)
|
||||||
*
|
*
|
||||||
* @return uint16_t
|
* @return uint16_t
|
||||||
*/
|
*/
|
||||||
uint16_t PMSBase::getCount10(void) { return toU16(&package[26]); }
|
uint16_t PMSBase::getCount10(void) { return pms_count10; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get temperature (only PMS5003T)
|
* @brief Get temperature (only PMS5003T)
|
||||||
*
|
*
|
||||||
* @return uint16_t
|
* @return uint16_t
|
||||||
*/
|
*/
|
||||||
int16_t PMSBase::getTemp(void) { return toI16(&package[24]); }
|
int16_t PMSBase::getTemp(void) { return pms_temp; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get humidity (only PMS5003T)
|
* @brief Get humidity (only PMS5003T)
|
||||||
*
|
*
|
||||||
* @return uint16_t
|
* @return uint16_t
|
||||||
*/
|
*/
|
||||||
uint16_t PMSBase::getHum(void) { return toU16(&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
|
* @brief Convert PMS2.5 to US AQI unit
|
||||||
@ -252,57 +298,99 @@ uint16_t PMSBase::getHum(void) { return toU16(&package[26]); }
|
|||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
int PMSBase::pm25ToAQI(int pm02) {
|
int PMSBase::pm25ToAQI(int pm02) {
|
||||||
if (pm02 <= 12.0)
|
if (pm02 <= 9.0)
|
||||||
return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0);
|
return ((50 - 0) / (9.0 - .0) * (pm02 - .0) + 0);
|
||||||
else if (pm02 <= 35.4)
|
else if (pm02 <= 35.4)
|
||||||
return ((100 - 50) / (35.4 - 12.0) * (pm02 - 12.0) + 50);
|
return ((100 - 51) / (35.4 - 9.1) * (pm02 - 9.0) + 51);
|
||||||
else if (pm02 <= 55.4)
|
else if (pm02 <= 55.4)
|
||||||
return ((150 - 100) / (55.4 - 35.4) * (pm02 - 35.4) + 100);
|
return ((150 - 101) / (55.4 - 35.5) * (pm02 - 35.5) + 101);
|
||||||
else if (pm02 <= 150.4)
|
else if (pm02 <= 125.4)
|
||||||
return ((200 - 150) / (150.4 - 55.4) * (pm02 - 55.4) + 150);
|
return ((200 - 151) / (125.4 - 55.5) * (pm02 - 55.5) + 151);
|
||||||
else if (pm02 <= 250.4)
|
else if (pm02 <= 225.4)
|
||||||
return ((300 - 200) / (250.4 - 150.4) * (pm02 - 150.4) + 200);
|
return ((300 - 201) / (225.4 - 125.5) * (pm02 - 125.5) + 201);
|
||||||
else if (pm02 <= 350.4)
|
else if (pm02 <= 325.4)
|
||||||
return ((400 - 300) / (350.4 - 250.4) * (pm02 - 250.4) + 300);
|
return ((500 - 301) / (325.4 - 225.5) * (pm02 - 225.5) + 301);
|
||||||
else if (pm02 <= 500.4)
|
|
||||||
return ((500 - 400) / (500.4 - 350.4) * (pm02 - 350.4) + 400);
|
|
||||||
else
|
else
|
||||||
return 500;
|
return 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SLR correction for PM2.5
|
||||||
|
*
|
||||||
|
* Reference: https://www.airgradient.com/blog/low-readings-from-pms5003/
|
||||||
|
*
|
||||||
|
* @param pm25 PM2.5 raw value
|
||||||
|
* @param pm003Count PM0.3 count
|
||||||
|
* @param scalingFactor Scaling factor
|
||||||
|
* @param intercept Intercept
|
||||||
|
* @return float Calibrated PM2.5 value
|
||||||
|
*/
|
||||||
|
float PMSBase::slrCorrection(float pm25, float pm003Count, float scalingFactor, float intercept) {
|
||||||
|
float calibrated;
|
||||||
|
|
||||||
|
float lowCalibrated = (scalingFactor * pm003Count) + intercept;
|
||||||
|
if (lowCalibrated < 31) {
|
||||||
|
calibrated = lowCalibrated;
|
||||||
|
} else {
|
||||||
|
calibrated = pm25;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No negative value for pm2.5
|
||||||
|
if (calibrated < 0) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return calibrated;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Correction PM2.5
|
* @brief Correction PM2.5
|
||||||
*
|
*
|
||||||
|
* Formula: https://www.airgradient.com/documentation/correction-algorithms/
|
||||||
|
*
|
||||||
* @param pm25 Raw PM2.5 value
|
* @param pm25 Raw PM2.5 value
|
||||||
* @param humidity Humidity value (%)
|
* @param humidity Humidity value (%)
|
||||||
* @return int
|
* @return compensated pm25 value
|
||||||
*/
|
*/
|
||||||
int PMSBase::compensated(int pm25, float humidity) {
|
float PMSBase::compensate(float pm25, float humidity) {
|
||||||
float value;
|
float value;
|
||||||
|
|
||||||
|
// Correct invalid humidity value
|
||||||
if (humidity < 0) {
|
if (humidity < 0) {
|
||||||
humidity = 0;
|
humidity = 0;
|
||||||
}
|
}
|
||||||
if (humidity > 100) {
|
if (humidity > 100) {
|
||||||
humidity = 100;
|
humidity = 100.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pm25 < 30) {
|
// If its already 0, do not proceed
|
||||||
|
if (pm25 == 0) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pm25 < 30) { /** pm2.5 < 30 */
|
||||||
value = (pm25 * 0.524f) - (humidity * 0.0862f) + 5.75f;
|
value = (pm25 * 0.524f) - (humidity * 0.0862f) + 5.75f;
|
||||||
} else if(pm25 < 50) {
|
} else if (pm25 < 50) { /** 30 <= pm2.5 < 50 */
|
||||||
value = (0.786f * (pm25 / 20 - 3 / 2) + 0.524f * (1 - (pm25 / 20 - 3 / 2))) * pm25 - (0.0862f * humidity) + 5.75f;
|
value = (0.786f * (pm25 * 0.05f - 1.5f) + 0.524f * (1.0f - (pm25 * 0.05f - 1.5f))) * pm25 -
|
||||||
} else if(pm25 < 210) {
|
(0.0862f * humidity) + 5.75f;
|
||||||
|
} else if (pm25 < 210) { /** 50 <= pm2.5 < 210 */
|
||||||
value = (0.786f * pm25) - (0.0862f * humidity) + 5.75f;
|
value = (0.786f * pm25) - (0.0862f * humidity) + 5.75f;
|
||||||
} else if(pm25 < 260) {
|
} else if (pm25 < 260) { /** 210 <= pm2.5 < 260 */
|
||||||
value = (0.69f * (pm25/50 - 21/5) + 0.786f * (1 - (pm25/50 - 21/5))) * pm25 - (0.0862f * humidity * (1 - (pm25/50 - 21/5))) + (2.966f * (pm25/50 -21/5)) + (5.75f * (1 - (pm25/50 - 21/5))) + (8.84f * (1.e-4) * pm25* (pm25/50 - 21/5));
|
value = (0.69f * (pm25 * 0.02f - 4.2f) + 0.786f * (1.0f - (pm25 * 0.02f - 4.2f))) * pm25 -
|
||||||
} else {
|
(0.0862f * humidity * (1.0f - (pm25 * 0.02f - 4.2f))) +
|
||||||
value = 2.966f + (0.69f * pm25) + (8.84f * (1.e-4) * pm25);
|
(2.966f * (pm25 * 0.02f - 4.2f)) + (5.75f * (1.0f - (pm25 * 0.02f - 4.2f))) +
|
||||||
|
(8.84f * (1.e-4) * pm25 * pm25 * (pm25 * 0.02f - 4.2f));
|
||||||
|
} else { /** 260 <= pm2.5 */
|
||||||
|
value = 2.966f + (0.69f * pm25) + (8.84f * (1.e-4) * pm25 * pm25);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(value < 0) {
|
// No negative value for pm2.5
|
||||||
value = 0;
|
if (value < 0) {
|
||||||
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (int)value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -311,13 +399,13 @@ int PMSBase::compensated(int pm25, float humidity) {
|
|||||||
* @param buf bytes array (must be >= 2)
|
* @param buf bytes array (must be >= 2)
|
||||||
* @return int16_t
|
* @return int16_t
|
||||||
*/
|
*/
|
||||||
int16_t PMSBase::toI16(char *buf) {
|
int16_t PMSBase::toI16(const uint8_t *buf) {
|
||||||
int16_t value = buf[0];
|
int16_t value = buf[0];
|
||||||
value = (value << 8) | buf[1];
|
value = (value << 8) | buf[1];
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t PMSBase::toU16(char *buf) {
|
uint16_t PMSBase::toU16(const uint8_t *buf) {
|
||||||
uint16_t value = buf[0];
|
uint16_t value = buf[0];
|
||||||
value = (value << 8) | buf[1];
|
value = (value << 8) | buf[1];
|
||||||
return value;
|
return value;
|
||||||
@ -330,16 +418,38 @@ uint16_t PMSBase::toU16(char *buf) {
|
|||||||
* @return true Success
|
* @return true Success
|
||||||
* @return false Failed
|
* @return false Failed
|
||||||
*/
|
*/
|
||||||
bool PMSBase::validate(char *buf) {
|
bool PMSBase::validate(const uint8_t *buf) {
|
||||||
uint16_t sum = 0;
|
uint16_t sum = 0;
|
||||||
for (int i = 0; i < 30; i++) {
|
for (int i = 0; i < 30; i++) {
|
||||||
sum += buf[i];
|
sum += buf[i];
|
||||||
}
|
}
|
||||||
if (sum == toU16(&buf[30])) {
|
if (sum == toU16(&buf[30])) {
|
||||||
for (int i = 0; i < 32; i++) {
|
|
||||||
package[i] = buf[i];
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PMSBase::parse(const uint8_t *buf) {
|
||||||
|
// Standard particle
|
||||||
|
pms_raw0_1 = toU16(&buf[4]);
|
||||||
|
pms_raw2_5 = toU16(&buf[6]);
|
||||||
|
pms_raw10 = toU16(&buf[8]);
|
||||||
|
// atmospheric
|
||||||
|
pms_pm0_1 = toU16(&buf[10]);
|
||||||
|
pms_pm2_5 = toU16(&buf[12]);
|
||||||
|
pms_pm10 = toU16(&buf[14]);
|
||||||
|
|
||||||
|
// particle count
|
||||||
|
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]); // PMS5003 only
|
||||||
|
pms_count10 = toU16(&buf[26]); // PMS5003 only
|
||||||
|
|
||||||
|
// Others
|
||||||
|
pms_temp = toU16(&buf[24]); // PMS5003T only
|
||||||
|
pms_hum = toU16(&buf[26]); // PMS5003T only
|
||||||
|
pms_firmwareVersion = buf[28];
|
||||||
|
pms_errorCode = buf[29];
|
||||||
|
}
|
||||||
|
@ -3,11 +3,19 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#define PMS_FAIL_COUNT_SET_INVALID 3
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Known to work with these sensors: Plantower PMS5003, Plantower PMS5003, Cubic PM2009X
|
||||||
|
*/
|
||||||
class PMSBase {
|
class PMSBase {
|
||||||
public:
|
public:
|
||||||
bool begin(Stream *stream);
|
bool begin(Stream *stream);
|
||||||
void handle();
|
void readPackage(Stream *stream);
|
||||||
bool isFailed(void);
|
void updateFailCount(void);
|
||||||
|
void resetFailCount(void);
|
||||||
|
int getFailCount(void);
|
||||||
|
int getFailCountMax(void);
|
||||||
uint16_t getRaw0_1(void);
|
uint16_t getRaw0_1(void);
|
||||||
uint16_t getRaw2_5(void);
|
uint16_t getRaw2_5(void);
|
||||||
uint16_t getRaw10(void);
|
uint16_t getRaw10(void);
|
||||||
@ -18,6 +26,7 @@ public:
|
|||||||
uint16_t getCount0_5(void);
|
uint16_t getCount0_5(void);
|
||||||
uint16_t getCount1_0(void);
|
uint16_t getCount1_0(void);
|
||||||
uint16_t getCount2_5(void);
|
uint16_t getCount2_5(void);
|
||||||
|
bool connected(void);
|
||||||
|
|
||||||
/** For PMS5003 */
|
/** For PMS5003 */
|
||||||
uint16_t getCount5_0(void);
|
uint16_t getCount5_0(void);
|
||||||
@ -26,20 +35,56 @@ public:
|
|||||||
/** For PMS5003T*/
|
/** For PMS5003T*/
|
||||||
int16_t getTemp(void);
|
int16_t getTemp(void);
|
||||||
uint16_t getHum(void);
|
uint16_t getHum(void);
|
||||||
|
uint8_t getFirmwareVersion(void);
|
||||||
|
uint8_t getErrorCode(void);
|
||||||
|
|
||||||
int pm25ToAQI(int pm02);
|
int pm25ToAQI(int pm02);
|
||||||
int compensated(int pm25, float humidity);
|
float slrCorrection(float pm25, float pm003Count, float scalingFactor, float intercept);
|
||||||
|
float compensate(float pm25, float humidity);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Stream *stream;
|
static const uint8_t package_size = 32;
|
||||||
char package[32];
|
|
||||||
int packageIndex;
|
|
||||||
bool failed = false;
|
|
||||||
uint32_t lastRead;
|
|
||||||
|
|
||||||
int16_t toI16(char *buf);
|
/** In normal package interval is 200-800ms, In case small changed on sensor
|
||||||
uint16_t toU16(char* buf);
|
* it's will interval reach to 2.3sec
|
||||||
bool validate(char *buf);
|
*/
|
||||||
|
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_ */
|
#endif /** _PMS5003_BASE_H_ */
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include "../Main/utils.h"
|
#include "../Main/utils.h"
|
||||||
|
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
#include <SoftwareSerial.h>
|
|
||||||
/**
|
/**
|
||||||
* @brief Init sensor
|
* @brief Init sensor
|
||||||
*
|
*
|
||||||
@ -38,14 +37,11 @@ bool PMS5003::begin(HardwareSerial &serial) {
|
|||||||
PMS5003::PMS5003(BoardType def) : _boardDef(def) {}
|
PMS5003::PMS5003(BoardType def) : _boardDef(def) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Init sensor
|
* Initializes the sensor.
|
||||||
*
|
|
||||||
* @return true Success
|
|
||||||
* @return false Failure
|
|
||||||
*/
|
*/
|
||||||
bool PMS5003::begin(void) {
|
bool PMS5003::begin(void) {
|
||||||
if (this->_isBegin) {
|
if (this->_isBegin) {
|
||||||
AgLog("Initialized, call end() then try again");
|
AgLog("Already initialized, call end() then try again");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,11 +59,10 @@ bool PMS5003::begin(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
bsp->Pms5003.uart_tx_pin;
|
this->_serial =
|
||||||
SoftwareSerial *uart =
|
|
||||||
new SoftwareSerial(bsp->Pms5003.uart_tx_pin, bsp->Pms5003.uart_rx_pin);
|
new SoftwareSerial(bsp->Pms5003.uart_tx_pin, bsp->Pms5003.uart_rx_pin);
|
||||||
uart->begin(9600);
|
this->_serial->begin(9600);
|
||||||
if (pms.begin(uart) == false) {
|
if (pms.begin(this->_serial) == false) {
|
||||||
AgLog("PMS failed");
|
AgLog("PMS failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -78,34 +73,55 @@ bool PMS5003::begin(void) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
_ver = pms.getFirmwareVersion();
|
||||||
this->_isBegin = true;
|
this->_isBegin = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read PM1.0 must call this function after @ref readData success
|
* @brief Read PM1.0
|
||||||
*
|
*
|
||||||
* @return int PM1.0 index
|
* @return int PM1.0 index (atmospheric environment)
|
||||||
*/
|
*/
|
||||||
int PMS5003::getPm01Ae(void) { return pms.getPM0_1(); }
|
int PMS5003::getPm01Ae(void) { return pms.getPM0_1(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read PM2.5 must call this function after @ref readData success
|
* @brief Read PM2.5
|
||||||
*
|
*
|
||||||
* @return int PM2.5 index
|
* @return int PM2.5 index (atmospheric environment)
|
||||||
*/
|
*/
|
||||||
int PMS5003::getPm25Ae(void) { return pms.getPM2_5(); }
|
int PMS5003::getPm25Ae(void) { return pms.getPM2_5(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read PM10.0 must call this function after @ref readData success
|
* @brief Read PM10.0
|
||||||
*
|
*
|
||||||
* @return int PM10.0 index
|
* @return int PM10.0 index (atmospheric environment)
|
||||||
*/
|
*/
|
||||||
int PMS5003::getPm10Ae(void) { return pms.getPM10(); }
|
int PMS5003::getPm10Ae(void) { return pms.getPM10(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read PM0.3 must call this function after @ref readData success
|
* @brief Read PM1.0
|
||||||
|
*
|
||||||
|
* @return int PM1.0 index (standard particle)
|
||||||
|
*/
|
||||||
|
int PMS5003::getPm01Sp(void) { return pms.getRaw0_1(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read PM2.5
|
||||||
|
*
|
||||||
|
* @return int PM2.5 index (standard particle)
|
||||||
|
*/
|
||||||
|
int PMS5003::getPm25Sp(void) { return pms.getRaw2_5(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read PM10
|
||||||
|
*
|
||||||
|
* @return int PM10 index (standard particle)
|
||||||
|
*/
|
||||||
|
int PMS5003::getPm10Sp(void) { return pms.getRaw10(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read particle 0.3 count
|
||||||
*
|
*
|
||||||
* @return int PM0.3 index
|
* @return int PM0.3 index
|
||||||
*/
|
*/
|
||||||
@ -113,6 +129,41 @@ int PMS5003::getPm03ParticleCount(void) {
|
|||||||
return pms.getCount0_3();
|
return pms.getCount0_3();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read particle 1.0 count
|
||||||
|
*
|
||||||
|
* @return int particle 1.0 count index
|
||||||
|
*/
|
||||||
|
int PMS5003::getPm01ParticleCount(void) { return pms.getCount1_0(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read particle 0.5 count
|
||||||
|
*
|
||||||
|
* @return int particle 0.5 count index
|
||||||
|
*/
|
||||||
|
int PMS5003::getPm05ParticleCount(void) { return pms.getCount0_5(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read particle 2.5 count
|
||||||
|
*
|
||||||
|
* @return int particle 2.5 count index
|
||||||
|
*/
|
||||||
|
int PMS5003::getPm25ParticleCount(void) { return pms.getCount2_5(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read particle 5.0 count
|
||||||
|
*
|
||||||
|
* @return int particle 5.0 count index
|
||||||
|
*/
|
||||||
|
int PMS5003::getPm5ParticleCount(void) { return pms.getCount5_0(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read particle 10 count
|
||||||
|
*
|
||||||
|
* @return int particle 10 count index
|
||||||
|
*/
|
||||||
|
int PMS5003::getPm10ParticleCount(void) { return pms.getCount10(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert PM2.5 to US AQI
|
* @brief Convert PM2.5 to US AQI
|
||||||
*
|
*
|
||||||
@ -121,16 +172,42 @@ int PMS5003::getPm03ParticleCount(void) {
|
|||||||
*/
|
*/
|
||||||
int PMS5003::convertPm25ToUsAqi(int pm25) { return pms.pm25ToAQI(pm25); }
|
int PMS5003::convertPm25ToUsAqi(int pm25) { return pms.pm25ToAQI(pm25); }
|
||||||
|
|
||||||
|
float PMS5003::slrCorrection(float pm25, float pm003Count, float scalingFactor, float intercept) {
|
||||||
|
return pms.slrCorrection(pm25, pm003Count, scalingFactor, intercept);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Correct PM2.5
|
* @brief Correct PM2.5
|
||||||
*
|
*
|
||||||
|
* Reference formula: https://www.airgradient.com/documentation/correction-algorithms/
|
||||||
|
*
|
||||||
* @param pm25 PM2.5 raw value
|
* @param pm25 PM2.5 raw value
|
||||||
* @param humidity Humidity value
|
* @param humidity Humidity value
|
||||||
* @return float
|
* @return compensated value in float
|
||||||
*/
|
*/
|
||||||
int PMS5003::compensated(int pm25, float humidity) {
|
float PMS5003::compensate(float pm25, float humidity) { return pms.compensate(pm25, humidity); }
|
||||||
return pms.compensated(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
|
* @brief Check device initialized or not
|
||||||
@ -166,12 +243,26 @@ void PMS5003::end(void) {
|
|||||||
* @brief Check and read PMS sensor data. This method should be callack from
|
* @brief Check and read PMS sensor data. This method should be callack from
|
||||||
* loop process to continoue check sensor data if it's available
|
* 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 int
|
||||||
* @return false Communication timeout or sensor has removed
|
|
||||||
*/
|
*/
|
||||||
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(); }
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
#include "../Main/BoardDef.h"
|
#include "../Main/BoardDef.h"
|
||||||
#include "PMS.h"
|
#include "PMS.h"
|
||||||
#include "Stream.h"
|
#include "Stream.h"
|
||||||
|
#ifdef ESP8266
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The class define how to handle PMS5003 sensor bas on @ref PMS class
|
* @brief The class define how to handle PMS5003 sensor bas on @ref PMS class
|
||||||
@ -18,22 +21,43 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
void end(void);
|
void end(void);
|
||||||
void handle(void);
|
void handle(void);
|
||||||
bool isFailed(void);
|
void updateFailCount(void);
|
||||||
|
void resetFailCount(void);
|
||||||
|
int getFailCount(void);
|
||||||
|
int getFailCountMax(void);
|
||||||
|
// Atmospheric environment
|
||||||
int getPm01Ae(void);
|
int getPm01Ae(void);
|
||||||
int getPm25Ae(void);
|
int getPm25Ae(void);
|
||||||
int getPm10Ae(void);
|
int getPm10Ae(void);
|
||||||
|
// Standard particle
|
||||||
|
int getPm01Sp(void);
|
||||||
|
int getPm25Sp(void);
|
||||||
|
int getPm10Sp(void);
|
||||||
|
// Particle count
|
||||||
int getPm03ParticleCount(void);
|
int getPm03ParticleCount(void);
|
||||||
|
int getPm05ParticleCount(void);
|
||||||
|
int getPm01ParticleCount(void);
|
||||||
|
int getPm25ParticleCount(void);
|
||||||
|
int getPm5ParticleCount(void);
|
||||||
|
int getPm10ParticleCount(void);
|
||||||
|
|
||||||
int convertPm25ToUsAqi(int pm25);
|
int convertPm25ToUsAqi(int pm25);
|
||||||
int compensated(int pm25, float humidity);
|
float slrCorrection(float pm25, float pm003Count, float scalingFactor, float intercept);
|
||||||
|
float compensate(float pm25, float humidity);
|
||||||
|
int getFirmwareVersion(void);
|
||||||
|
uint8_t getErrorCode(void);
|
||||||
|
bool connected(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _isBegin = false;
|
bool _isBegin = false;
|
||||||
|
int _ver;
|
||||||
BoardType _boardDef;
|
BoardType _boardDef;
|
||||||
PMSBase pms;
|
PMSBase pms;
|
||||||
const BoardDef *bsp;
|
const BoardDef *bsp;
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
Stream *_debugStream;
|
Stream *_debugStream;
|
||||||
const char *TAG = "PMS5003";
|
const char *TAG = "PMS5003";
|
||||||
|
SoftwareSerial *_serial;
|
||||||
#else
|
#else
|
||||||
HardwareSerial *_serial;
|
HardwareSerial *_serial;
|
||||||
#endif
|
#endif
|
||||||
|
@ -67,11 +67,10 @@ bool PMS5003T::begin(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
bsp->Pms5003.uart_tx_pin;
|
this->_serial =
|
||||||
SoftwareSerial *uart =
|
|
||||||
new SoftwareSerial(bsp->Pms5003.uart_tx_pin, bsp->Pms5003.uart_rx_pin);
|
new SoftwareSerial(bsp->Pms5003.uart_tx_pin, bsp->Pms5003.uart_rx_pin);
|
||||||
uart->begin(9600);
|
this->_serial->begin(9600);
|
||||||
if (pms.begin(uart) == false) {
|
if (pms.begin(this->_serial) == false) {
|
||||||
AgLog("PMS failed");
|
AgLog("PMS failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -103,41 +102,83 @@ bool PMS5003T::begin(void) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
_ver = pms.getFirmwareVersion();
|
||||||
this->_isBegin = true;
|
this->_isBegin = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read PM1.0 must call this function after @ref readData success
|
* @brief Read PM1.0
|
||||||
*
|
*
|
||||||
* @return int PM1.0 index
|
* @return int PM1.0 index (atmospheric environment)
|
||||||
*/
|
*/
|
||||||
int PMS5003T::getPm01Ae(void) { return pms.getPM0_1(); }
|
int PMS5003T::getPm01Ae(void) { return pms.getPM0_1(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read PM2.5 must call this function after @ref readData success
|
* @brief Read PM2.5
|
||||||
*
|
*
|
||||||
* @return int PM2.5 index
|
* @return int PM2.5 index (atmospheric environment)
|
||||||
*/
|
*/
|
||||||
int PMS5003T::getPm25Ae(void) { return pms.getPM2_5(); }
|
int PMS5003T::getPm25Ae(void) { return pms.getPM2_5(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read PM10.0 must call this function after @ref readData success
|
* @brief Read PM10.0
|
||||||
*
|
*
|
||||||
* @return int PM10.0 index
|
* @return int PM10.0 index (atmospheric environment)
|
||||||
*/
|
*/
|
||||||
int PMS5003T::getPm10Ae(void) { return pms.getPM10(); }
|
int PMS5003T::getPm10Ae(void) { return pms.getPM10(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read PM 0.3 Count must call this function after @ref readData success
|
* @brief Read PM1.0
|
||||||
*
|
*
|
||||||
* @return int PM 0.3 Count index
|
* @return int PM1.0 index (standard particle)
|
||||||
|
*/
|
||||||
|
int PMS5003T::getPm01Sp(void) { return pms.getRaw0_1(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read PM2.5
|
||||||
|
*
|
||||||
|
* @return int PM2.5 index (standard particle)
|
||||||
|
*/
|
||||||
|
int PMS5003T::getPm25Sp(void) { return pms.getRaw2_5(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read PM10
|
||||||
|
*
|
||||||
|
* @return int PM10 index (standard particle)
|
||||||
|
*/
|
||||||
|
int PMS5003T::getPm10Sp(void) { return pms.getRaw10(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read particle 0.3 count
|
||||||
|
*
|
||||||
|
* @return int particle 0.3 count index
|
||||||
*/
|
*/
|
||||||
int PMS5003T::getPm03ParticleCount(void) {
|
int PMS5003T::getPm03ParticleCount(void) {
|
||||||
return pms.getCount0_3();
|
return pms.getCount0_3();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read particle 0.5 count
|
||||||
|
*
|
||||||
|
* @return int particle 0.5 count index
|
||||||
|
*/
|
||||||
|
int PMS5003T::getPm05ParticleCount(void) { return pms.getCount0_5(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read particle 1.0 count
|
||||||
|
*
|
||||||
|
* @return int particle 1.0 count index
|
||||||
|
*/
|
||||||
|
int PMS5003T::getPm01ParticleCount(void) { return pms.getCount1_0(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read particle 2.5 count
|
||||||
|
*
|
||||||
|
* @return int particle 2.5 count index
|
||||||
|
*/
|
||||||
|
int PMS5003T::getPm25ParticleCount(void) { return pms.getCount2_5(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert PM2.5 to US AQI
|
* @brief Convert PM2.5 to US AQI
|
||||||
*
|
*
|
||||||
@ -166,14 +207,36 @@ float PMS5003T::getRelativeHumidity(void) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Correct PM2.5
|
* @brief Correct PM2.5
|
||||||
*
|
*
|
||||||
|
* Reference formula: https://www.airgradient.com/documentation/correction-algorithms/
|
||||||
|
*
|
||||||
* @param pm25 PM2.5 raw value
|
* @param pm25 PM2.5 raw value
|
||||||
* @param humidity Humidity value
|
* @param humidity Humidity value
|
||||||
* @return float
|
* @return compensated value
|
||||||
*/
|
*/
|
||||||
float PMS5003T::compensated(int pm25, float humidity) {
|
float PMS5003T::compensate(float pm25, float humidity) { return pms.compensate(pm25, humidity); }
|
||||||
return pms.compensated(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
|
* @brief Check device initialized or not
|
||||||
@ -206,13 +269,26 @@ void PMS5003T::end(void) {
|
|||||||
* @brief Check and read PMS sensor data. This method should be callack from
|
* @brief Check and read PMS sensor data. This method should be callack from
|
||||||
* loop process to continoue check sensor data if it's available
|
* 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 int
|
||||||
* @return false Communication timeout or sensor has removed
|
|
||||||
*/
|
*/
|
||||||
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(); }
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
#include "PMS5003TBase.h"
|
#include "PMS5003TBase.h"
|
||||||
#include "Stream.h"
|
#include "Stream.h"
|
||||||
#include <HardwareSerial.h>
|
#include <HardwareSerial.h>
|
||||||
|
#ifdef ESP8266
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The class define how to handle PMS5003T sensor bas on @ref PMS class
|
* @brief The class define how to handle PMS5003T sensor bas on @ref PMS class
|
||||||
@ -21,25 +24,43 @@ public:
|
|||||||
void end(void);
|
void end(void);
|
||||||
|
|
||||||
void handle(void);
|
void handle(void);
|
||||||
bool isFailed(void);
|
void updateFailCount(void);
|
||||||
|
void resetFailCount(void);
|
||||||
|
int getFailCount(void);
|
||||||
|
int getFailCountMax(void);
|
||||||
|
// Atmospheric environment
|
||||||
int getPm01Ae(void);
|
int getPm01Ae(void);
|
||||||
int getPm25Ae(void);
|
int getPm25Ae(void);
|
||||||
int getPm10Ae(void);
|
int getPm10Ae(void);
|
||||||
|
// Standard particle
|
||||||
|
int getPm01Sp(void);
|
||||||
|
int getPm25Sp(void);
|
||||||
|
int getPm10Sp(void);
|
||||||
|
// Particle count
|
||||||
int getPm03ParticleCount(void);
|
int getPm03ParticleCount(void);
|
||||||
|
int getPm05ParticleCount(void);
|
||||||
|
int getPm01ParticleCount(void);
|
||||||
|
int getPm25ParticleCount(void);
|
||||||
|
|
||||||
int convertPm25ToUsAqi(int pm25);
|
int convertPm25ToUsAqi(int pm25);
|
||||||
float getTemperature(void);
|
float getTemperature(void);
|
||||||
float getRelativeHumidity(void);
|
float getRelativeHumidity(void);
|
||||||
float compensated(int pm25, float humidity);
|
float compensate(float pm25, float humidity);
|
||||||
|
int getFirmwareVersion(void);
|
||||||
|
uint8_t getErrorCode(void);
|
||||||
|
bool connected(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _isBegin = false;
|
bool _isBegin = false;
|
||||||
bool _isSleep = false;
|
bool _isSleep = false;
|
||||||
|
int _ver; /** Firmware version code */
|
||||||
|
|
||||||
BoardType _boardDef;
|
BoardType _boardDef;
|
||||||
const BoardDef *bsp;
|
const BoardDef *bsp;
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
Stream *_debugStream;
|
Stream *_debugStream;
|
||||||
const char *TAG = "PMS5003T";
|
const char *TAG = "PMS5003T";
|
||||||
|
SoftwareSerial *_serial;
|
||||||
#else
|
#else
|
||||||
HardwareSerial *_serial;
|
HardwareSerial *_serial;
|
||||||
#endif
|
#endif
|
||||||
|
@ -4,14 +4,30 @@ PMS5003TBase::PMS5003TBase() {}
|
|||||||
|
|
||||||
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) {
|
if (temp < 10.0f) {
|
||||||
return temp * 1.327f - 6.738f;
|
return temp * 1.327f - 6.738f;
|
||||||
}
|
}
|
||||||
return temp * 1.181f - 5.113f;
|
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;
|
hum = hum * 1.259f + 7.34f;
|
||||||
|
|
||||||
if (hum > 100.0f) {
|
if (hum > 100.0f) {
|
||||||
|
@ -8,8 +8,8 @@ private:
|
|||||||
public:
|
public:
|
||||||
PMS5003TBase();
|
PMS5003TBase();
|
||||||
~PMS5003TBase();
|
~PMS5003TBase();
|
||||||
float temperatureCompensated(float temp);
|
float compensateTemp(float temp);
|
||||||
float humidityCompensated(float hum);
|
float compensateHum(float hum);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user