mirror of
https://github.com/airgradienthq/arduino.git
synced 2025-12-17 11:08:38 +01:00
Compare commits
111 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23f8c383fd | ||
|
|
c0ad1dbfad | ||
|
|
75be7d9fc5 | ||
|
|
309d322100 | ||
|
|
89ebe6c39f | ||
|
|
29db5469f5 | ||
|
|
5802cf17f6 | ||
|
|
4a4ce89f00 | ||
|
|
8985e08a00 | ||
|
|
e984aced18 | ||
|
|
73edf56c97 | ||
|
|
e54c62a2ef | ||
|
|
5c95f011e4 | ||
|
|
bed448e7d6 | ||
|
|
eb8378adfa | ||
|
|
f94ed5c5f5 | ||
|
|
9471f3e323 | ||
|
|
e6d90372c2 | ||
|
|
fabf0550fc | ||
|
|
ce0af5bf60 | ||
|
|
93bdbd85d5 | ||
|
|
29f583f20d | ||
|
|
0f6a2fe908 | ||
|
|
e3ce2c41be | ||
|
|
92b3c69b98 | ||
|
|
f4d518aa87 | ||
|
|
831c844c24 | ||
|
|
060a7f6815 | ||
|
|
d8eb6b3c1a | ||
|
|
77859bea22 | ||
|
|
969858b5cb | ||
|
|
09b5805686 | ||
|
|
b09b753339 | ||
|
|
ddb3dba131 | ||
|
|
e780b0ace6 | ||
|
|
e82da5401e | ||
|
|
50a98acde4 | ||
|
|
7049d21a41 | ||
|
|
d5cdeaa9f3 | ||
|
|
09207c6923 | ||
|
|
0a64424196 | ||
|
|
5b38ca222b | ||
|
|
9ee35341a5 | ||
|
|
cec0514444 | ||
|
|
21b9ddb2ed | ||
|
|
1ee05da5d1 | ||
|
|
626a2240fa | ||
|
|
174ec6568f | ||
|
|
6b55719399 | ||
|
|
e2084f0738 | ||
|
|
5e07923690 | ||
|
|
04049439b1 | ||
|
|
c148d256d7 | ||
|
|
02849a1938 | ||
|
|
074337a96d | ||
|
|
4daa817a0b | ||
|
|
81a4502952 | ||
|
|
764e2eae38 | ||
|
|
79bf9811be | ||
|
|
9475724d0c | ||
|
|
e7603a7659 | ||
|
|
9bba89722e | ||
|
|
81945a358e | ||
|
|
3d26a54d69 | ||
|
|
b70ee75d50 | ||
|
|
c6846c818a | ||
|
|
0b1c901a76 | ||
|
|
83504c8628 | ||
|
|
4487992748 | ||
|
|
3c8a65a329 | ||
|
|
673d564ddb | ||
|
|
423eb4808f | ||
|
|
18a710ffc2 | ||
|
|
040cb79a4d | ||
|
|
52d3dc03f1 | ||
|
|
1c6bc3ec55 | ||
|
|
34d7c93e14 | ||
|
|
fee1dc25d6 | ||
|
|
9fb01d42f4 | ||
|
|
7bb013939c | ||
|
|
0da21155e7 | ||
|
|
7a153cc0ea | ||
|
|
b079c35e6b | ||
|
|
6051e183b8 | ||
|
|
c95379b957 | ||
|
|
0cae8bc185 | ||
|
|
5902a4c8e4 | ||
|
|
66818cd075 | ||
|
|
c1a6ddc68f | ||
|
|
20a32dd22c | ||
|
|
263dc9934e | ||
|
|
61b863b7f1 | ||
|
|
e01c1029fe | ||
|
|
ba5d817739 | ||
|
|
a91747e379 | ||
|
|
029457c3fa | ||
|
|
55710dd4d9 | ||
|
|
4886163cda | ||
|
|
7c57477238 | ||
|
|
9ed58d1853 | ||
|
|
6c52b038e9 | ||
|
|
2f69932ef7 | ||
|
|
1d96a274a6 | ||
|
|
df9f6dfc95 | ||
|
|
3fc02b3f54 | ||
|
|
958ed0bd80 | ||
|
|
e9be9dcc83 | ||
|
|
7fbab82088 | ||
|
|
decdecdf22 | ||
|
|
145c612867 | ||
|
|
37de127887 |
32
.github/workflows/check.yml
vendored
32
.github/workflows/check.yml
vendored
@@ -1,5 +1,36 @@
|
|||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
jobs:
|
jobs:
|
||||||
|
trailing-whitespace:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4.2.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Check for trailing whitespace
|
||||||
|
run: |
|
||||||
|
set -u
|
||||||
|
|
||||||
|
# Don't enforce checks on vendored libraries.
|
||||||
|
readonly EXCLUDED_DIR='src/Libraries'
|
||||||
|
|
||||||
|
has_trailing_whitespace=false
|
||||||
|
|
||||||
|
while read -r line; do
|
||||||
|
if grep \
|
||||||
|
"\s$" \
|
||||||
|
--line-number \
|
||||||
|
--with-filename \
|
||||||
|
--binary-files=without-match \
|
||||||
|
"${line}"; then
|
||||||
|
has_trailing_whitespace=true
|
||||||
|
fi
|
||||||
|
done < <(git ls-files | grep --invert-match "^${EXCLUDED_DIR}/")
|
||||||
|
|
||||||
|
if [ "$has_trailing_whitespace" = true ]; then
|
||||||
|
echo "ERROR: Found trailing whitespace"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
compile:
|
compile:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -38,6 +69,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.2.2
|
- uses: actions/checkout@v4.2.2
|
||||||
with:
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
- uses: arduino/compile-sketches@v1.1.2
|
- uses: arduino/compile-sketches@v1.1.2
|
||||||
with:
|
with:
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,5 +4,7 @@ build
|
|||||||
/.idea/
|
/.idea/
|
||||||
.pio
|
.pio
|
||||||
.cache
|
.cache
|
||||||
|
.clangd
|
||||||
logs
|
logs
|
||||||
|
gen_compile_commands.py
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
|
|||||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -1,6 +1,6 @@
|
|||||||
[submodule "src/Libraries/airgradient-client"]
|
[submodule "src/Libraries/airgradient-client"]
|
||||||
path = src/Libraries/airgradient-client
|
path = src/Libraries/airgradient-client
|
||||||
url = git@github.com:airgradienthq/airgradient-client.git
|
url = ../../airgradienthq/airgradient-client.git
|
||||||
[submodule "src/Libraries/airgradient-ota"]
|
[submodule "src/Libraries/airgradient-ota"]
|
||||||
path = src/Libraries/airgradient-ota
|
path = src/Libraries/airgradient-ota
|
||||||
url = git@github.com:airgradienthq/airgradient-ota.git
|
url = ../../airgradienthq/airgradient-ota.git
|
||||||
|
|||||||
23
README.md
23
README.md
@@ -20,13 +20,32 @@ Make sure you have exactly the versions of libraries and boards installed as des
|
|||||||
|
|
||||||
If you have an older version of the AirGradient PCB not mentioned in the example files, please downgrade this library to version 2.4.15 to support these legacy boards.
|
If you have an older version of the AirGradient PCB not mentioned in the example files, please downgrade this library to version 2.4.15 to support these legacy boards.
|
||||||
|
|
||||||
|
### Release Process
|
||||||
|
|
||||||
|
Releases published on GitHub are **not immediately deployed to all devices in the market**. Each release first goes through internal testing, including limited deployments in select locations to verify stability and functionality.
|
||||||
|
|
||||||
|
If the tests pass, the firmware is then made available for:
|
||||||
|
- **FOTA (Firmware Over-The-Air) updates** from AirGradient dashboard
|
||||||
|
- **Manual flashing** via [Airgradient](https://www.airgradient.com/documentation/firmwares/) website
|
||||||
|
|
||||||
|
Each GitHub release note will also include the planned rollout date for wider availability.
|
||||||
|
|
||||||
## Help & Support
|
## Help & Support
|
||||||
|
|
||||||
If you have any questions or problems, check out [our forum](https://forum.airgradient.com/).
|
If you have any questions or problems, check out [our forum](https://forum.airgradient.com/).
|
||||||
|
|
||||||
## Documentation
|
## Development
|
||||||
|
|
||||||
Local server API documentation is available in [/docs/local-server.md](/docs/local-server.md) and AirGradient server API on [https://api.airgradient.com/public/docs/api/v1/](https://api.airgradient.com/public/docs/api/v1/).
|
* See [compilation instructions](/docs/howto-compile.md) for details about how to customize AirGradient's firmware and flash it to your device.
|
||||||
|
|
||||||
|
## Over the air (OTA) updates
|
||||||
|
|
||||||
|
* See the [OTA Updates documentation](/docs/ota-updates.md) for details about how AirGradient monitors receive over the air updates.
|
||||||
|
|
||||||
|
## API documentation
|
||||||
|
|
||||||
|
* [Local server API documentation](/docs/local-server.md)
|
||||||
|
* [AirGradient Cloud server API documentation](https://api.airgradient.com/public/docs/api/v1/).
|
||||||
|
|
||||||
## The following libraries have been integrated into this library for ease of use
|
## The following libraries have been integrated into this library for ease of use
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ Choose based on how python installed on your machine. But most user, using `apt`
|
|||||||
|
|
||||||
## How to contribute
|
## How to contribute
|
||||||
|
|
||||||
The instructions above are the instructions for how to build an official release of the AirGradient firmware using the Arduino IDE. If you intend to make changes that will you intent to contribute back to the main project, instead of installing the AirGradient library, check out the repo at `Documents/Arduino/libraries` (for Windows and Mac), or `~/Arduino/Libraries` (Linux). If you installed the library, you can remove it from the library manager in the Arduino IDE, or just delete the directory.
|
The instructions above are the instructions for how to build an official release of the AirGradient firmware using the Arduino IDE. If you intend to make changes which you plan to contribute back to the main project, instead of installing the AirGradient library, check out the repository at Documents/Arduino/libraries (for Windows and Mac) or ~/Arduino/libraries (for Linux). If you installed the library, you can remove it from the library manager in the Arduino IDE, or just delete the directory.
|
||||||
|
|
||||||
**NOTE:** When cloning the repository, for version >= 3.3.0 it has submodule, please use `--recursive` flag like this: `git clone --recursive https://github.com/airgradienthq/arduino.git AirGradient_Air_Quality_Sensor`
|
**NOTE:** When cloning the repository, for version >= 3.3.0 it has submodule, please use `--recursive` flag like this: `git clone --recursive https://github.com/airgradienthq/arduino.git AirGradient_Air_Quality_Sensor`
|
||||||
|
|
||||||
@@ -100,6 +100,3 @@ There are 2 environment options to compile this project, PlatformIO and ArduinoI
|
|||||||
|
|
||||||
- For PlatformIO, it should work out of the box
|
- For PlatformIO, it should work out of the box
|
||||||
- For arduino, files in `src` folder and also from `Examples` can be modified at `Documents/Arduino/libraries` for Windows and Mac, and `~/Arduino/Libraries` for Linux
|
- For arduino, files in `src` folder and also from `Examples` can be modified at `Documents/Arduino/libraries` for Windows and Mac, and `~/Arduino/Libraries` for Linux
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ Compensated values apply correction algorithms to make the sensor values more ac
|
|||||||
"tvocLearningOffset": 12,
|
"tvocLearningOffset": 12,
|
||||||
"noxLearningOffset": 12,
|
"noxLearningOffset": 12,
|
||||||
"mqttBrokerUrl": "",
|
"mqttBrokerUrl": "",
|
||||||
|
"httpDomain": "",
|
||||||
"temperatureUnit": "c",
|
"temperatureUnit": "c",
|
||||||
"configurationControl": "local",
|
"configurationControl": "local",
|
||||||
"postDataToAirGradient": true,
|
"postDataToAirGradient": true,
|
||||||
@@ -108,7 +109,6 @@ Compensated values apply correction algorithms to make the sensor values more ac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Set Configuration Parameters (PUT)
|
### Set Configuration Parameters (PUT)
|
||||||
@@ -146,7 +146,8 @@ If the monitor is set up on the AirGradient dashboard, it will also receive the
|
|||||||
| `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 | Maximum 255 characters. Set value to empty string to disable mqtt connection. | `{"mqttBrokerUrl": "mqtt://192.168.0.18:1883"}` |
|
||||||
|
| `httpDomain` | Domain name for http request. (version > 3.3.2) | String | Maximum 255 characters. Set value to empty string to set http domain to default airgradient | `{"httpDomain": "sub.domain.com"}` |
|
||||||
| `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}` |
|
||||||
@@ -154,8 +155,8 @@ If the monitor is set up on the AirGradient dashboard, it will also receive the
|
|||||||
| `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}` |
|
||||||
| `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 }` |
|
| `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. (version >= [3.1.11]()) | Object | _see corrections section_ | _see corrections section_ |
|
| `corrections` | Sets correction options to display and measurement values on local server response. (version >= 3.1.11) | Object | _see corrections section_ | _see corrections section_ |
|
||||||
|
|
||||||
|
|
||||||
**Notes**
|
**Notes**
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ static bool sgp41Init(void) {
|
|||||||
configuration.hasSensorSGP = true;
|
configuration.hasSensorSGP = true;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Init SGP41 failuire");
|
Serial.println("Init SGP41 failure");
|
||||||
configuration.hasSensorSGP = false;
|
configuration.hasSensorSGP = false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -351,7 +351,7 @@ static bool sgp41Init(void) {
|
|||||||
configuration.hasSensorSGP = true;
|
configuration.hasSensorSGP = true;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Init SGP41 failuire");
|
Serial.println("Init SGP41 failure");
|
||||||
configuration.hasSensorSGP = false;
|
configuration.hasSensorSGP = false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -374,7 +374,7 @@ static bool sgp41Init(void) {
|
|||||||
configuration.hasSensorSGP = true;
|
configuration.hasSensorSGP = true;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Init SGP41 failuire");
|
Serial.println("Init SGP41 failure");
|
||||||
configuration.hasSensorSGP = false;
|
configuration.hasSensorSGP = false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ https://forum.airgradient.com/
|
|||||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "AgConfigure.h"
|
#include "AgConfigure.h"
|
||||||
#include "AgSchedule.h"
|
#include "AgSchedule.h"
|
||||||
#include "AgStateMachine.h"
|
#include "AgStateMachine.h"
|
||||||
@@ -37,6 +36,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
#include "EEPROM.h"
|
#include "EEPROM.h"
|
||||||
#include "ESPmDNS.h"
|
#include "ESPmDNS.h"
|
||||||
|
#include "Libraries/airgradient-client/src/common.h"
|
||||||
#include "LocalServer.h"
|
#include "LocalServer.h"
|
||||||
#include "MqttClient.h"
|
#include "MqttClient.h"
|
||||||
#include "OpenMetrics.h"
|
#include "OpenMetrics.h"
|
||||||
@@ -45,6 +45,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|||||||
#include <HardwareSerial.h>
|
#include <HardwareSerial.h>
|
||||||
#include <WebServer.h>
|
#include <WebServer.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "Libraries/airgradient-client/src/agSerial.h"
|
#include "Libraries/airgradient-client/src/agSerial.h"
|
||||||
@@ -65,7 +66,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|||||||
#define WIFI_TRANSMISSION_INTERVAL 1 * 60000 /** ms */
|
#define WIFI_TRANSMISSION_INTERVAL 1 * 60000 /** ms */
|
||||||
#define CELLULAR_SERVER_CONFIG_SYNC_INTERVAL 30 * 60000 /** ms */
|
#define CELLULAR_SERVER_CONFIG_SYNC_INTERVAL 30 * 60000 /** ms */
|
||||||
#define CELLULAR_MEASUREMENT_INTERVAL 3 * 60000 /** ms */
|
#define CELLULAR_MEASUREMENT_INTERVAL 3 * 60000 /** ms */
|
||||||
#define CELLULAR_TRANSMISSION_INTERVAL 9 * 60000 /** ms */
|
#define CELLULAR_TRANSMISSION_INTERVAL 3 * 60000 /** ms */
|
||||||
#define MQTT_SYNC_INTERVAL 60000 /** ms */
|
#define MQTT_SYNC_INTERVAL 60000 /** ms */
|
||||||
#define SENSOR_CO2_CALIB_COUNTDOWN_MAX 5 /** sec */
|
#define SENSOR_CO2_CALIB_COUNTDOWN_MAX 5 /** sec */
|
||||||
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */
|
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */
|
||||||
@@ -74,7 +75,10 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|||||||
#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 6000 /** 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 */
|
||||||
|
#define TIME_TO_START_POWER_CYCLE_CELLULAR_MODULE (1 * 60) /** minutes */
|
||||||
|
#define TIMEOUT_WAIT_FOR_CELLULAR_MODULE_READY (2 * 60) /** minutes */
|
||||||
|
|
||||||
|
#define MEASUREMENT_TRANSMIT_CYCLE 3
|
||||||
#define MAXIMUM_MEASUREMENT_CYCLE_QUEUE 80
|
#define MAXIMUM_MEASUREMENT_CYCLE_QUEUE 80
|
||||||
#define RESERVED_MEASUREMENT_CYCLE_CAPACITY 10
|
#define RESERVED_MEASUREMENT_CYCLE_CAPACITY 10
|
||||||
|
|
||||||
@@ -88,6 +92,8 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|||||||
#define GPIO_EXPANSION_CARD_POWER 4
|
#define GPIO_EXPANSION_CARD_POWER 4
|
||||||
#define GPIO_IIC_RESET 3
|
#define GPIO_IIC_RESET 3
|
||||||
|
|
||||||
|
#define MINUTES() ((uint32_t)(esp_timer_get_time() / 1000 / 1000 / 60))
|
||||||
|
|
||||||
static MqttClient mqttClient(Serial);
|
static MqttClient mqttClient(Serial);
|
||||||
static TaskHandle_t mqttTask = NULL;
|
static TaskHandle_t mqttTask = NULL;
|
||||||
static Configuration configuration(Serial);
|
static Configuration configuration(Serial);
|
||||||
@@ -102,7 +108,7 @@ static OpenMetrics openMetrics(measurements, configuration, wifiConnector);
|
|||||||
static LocalServer localServer(Serial, openMetrics, measurements, configuration,
|
static LocalServer localServer(Serial, openMetrics, measurements, configuration,
|
||||||
wifiConnector);
|
wifiConnector);
|
||||||
static AgSerial *agSerial;
|
static AgSerial *agSerial;
|
||||||
static CellularModule *cell;
|
static CellularModule *cellularCard;
|
||||||
static AirgradientClient *agClient;
|
static AirgradientClient *agClient;
|
||||||
|
|
||||||
enum NetworkOption {
|
enum NetworkOption {
|
||||||
@@ -111,12 +117,17 @@ enum NetworkOption {
|
|||||||
};
|
};
|
||||||
NetworkOption networkOption;
|
NetworkOption networkOption;
|
||||||
TaskHandle_t handleNetworkTask = NULL;
|
TaskHandle_t handleNetworkTask = NULL;
|
||||||
static bool otaInProgress = false;
|
static bool firmwareUpdateInProgress = false;
|
||||||
|
|
||||||
static uint32_t factoryBtnPressTime = 0;
|
static uint32_t factoryBtnPressTime = 0;
|
||||||
static AgFirmwareMode fwMode = FW_MODE_I_9PSL;
|
static AgFirmwareMode fwMode = FW_MODE_I_9PSL;
|
||||||
static bool ledBarButtonTest = false;
|
static bool ledBarButtonTest = false;
|
||||||
static String fwNewVersion;
|
static String fwNewVersion;
|
||||||
|
static int lastCellSignalQuality = 99; // CSQ
|
||||||
|
|
||||||
|
// Default value is 0, indicate its not started yet
|
||||||
|
// In minutes
|
||||||
|
uint32_t agCeClientProblemDetectedTime = 0;
|
||||||
|
|
||||||
SemaphoreHandle_t mutexMeasurementCycleQueue;
|
SemaphoreHandle_t mutexMeasurementCycleQueue;
|
||||||
static std::vector<Measurements::Measures> measurementCycleQueue;
|
static std::vector<Measurements::Measures> measurementCycleQueue;
|
||||||
@@ -132,6 +143,7 @@ static void updatePm(void);
|
|||||||
static void sendDataToServer(void);
|
static void sendDataToServer(void);
|
||||||
static void tempHumUpdate(void);
|
static void tempHumUpdate(void);
|
||||||
static void co2Update(void);
|
static void co2Update(void);
|
||||||
|
static void printMeasurements();
|
||||||
static void mdnsInit(void);
|
static void mdnsInit(void);
|
||||||
static void createMqttTask(void);
|
static void createMqttTask(void);
|
||||||
static void initMqtt(void);
|
static void initMqtt(void);
|
||||||
@@ -145,6 +157,7 @@ static void displayExecuteOta(AirgradientOTA::OtaResult result, String msg, int
|
|||||||
static int calculateMaxPeriod(int updateInterval);
|
static int calculateMaxPeriod(int updateInterval);
|
||||||
static void setMeasurementMaxPeriod();
|
static void setMeasurementMaxPeriod();
|
||||||
static void newMeasurementCycle();
|
static void newMeasurementCycle();
|
||||||
|
static void restartIfCeClientIssueOverTwoHours();
|
||||||
static void networkSignalCheck();
|
static void networkSignalCheck();
|
||||||
static void networkingTask(void *args);
|
static void networkingTask(void *args);
|
||||||
|
|
||||||
@@ -160,6 +173,7 @@ AgSchedule tvocSchedule(SENSOR_TVOC_UPDATE_INTERVAL, updateTvoc);
|
|||||||
AgSchedule watchdogFeedSchedule(60000, wdgFeedUpdate);
|
AgSchedule watchdogFeedSchedule(60000, wdgFeedUpdate);
|
||||||
AgSchedule checkForUpdateSchedule(FIRMWARE_CHECK_FOR_UPDATE_MS, checkForFirmwareUpdate);
|
AgSchedule checkForUpdateSchedule(FIRMWARE_CHECK_FOR_UPDATE_MS, checkForFirmwareUpdate);
|
||||||
AgSchedule networkSignalCheckSchedule(10000, networkSignalCheck);
|
AgSchedule networkSignalCheckSchedule(10000, networkSignalCheck);
|
||||||
|
AgSchedule printMeasurementsSchedule(6000, printMeasurements);
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
/** Serial for print debug message */
|
/** Serial for print debug message */
|
||||||
@@ -179,6 +193,7 @@ void setup() {
|
|||||||
|
|
||||||
/** Initialize local configure */
|
/** Initialize local configure */
|
||||||
configuration.begin();
|
configuration.begin();
|
||||||
|
configuration.setConfigurationUpdatedCallback(configUpdateHandle);
|
||||||
|
|
||||||
/** Init I2C */
|
/** Init I2C */
|
||||||
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
|
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
|
||||||
@@ -198,7 +213,7 @@ void setup() {
|
|||||||
oledDisplay.setAirGradient(ag);
|
oledDisplay.setAirGradient(ag);
|
||||||
stateMachine.setAirGradient(ag);
|
stateMachine.setAirGradient(ag);
|
||||||
wifiConnector.setAirGradient(ag);
|
wifiConnector.setAirGradient(ag);
|
||||||
openMetrics.setAirGradient(ag, agClient);
|
openMetrics.setAirGradient(ag);
|
||||||
localServer.setAirGraident(ag);
|
localServer.setAirGraident(ag);
|
||||||
measurements.setAirGradient(ag);
|
measurements.setAirGradient(ag);
|
||||||
|
|
||||||
@@ -206,9 +221,6 @@ void setup() {
|
|||||||
boardInit();
|
boardInit();
|
||||||
setMeasurementMaxPeriod();
|
setMeasurementMaxPeriod();
|
||||||
|
|
||||||
// Comment below line to disable debug measurement readings
|
|
||||||
measurements.setDebug(true);
|
|
||||||
|
|
||||||
bool connectToNetwork = true;
|
bool connectToNetwork = true;
|
||||||
if (ag->isOne()) { // Offline mode only available for indoor monitor
|
if (ag->isOne()) { // Offline mode only available for indoor monitor
|
||||||
/** Show message confirm offline mode, should me perform if LED bar button
|
/** Show message confirm offline mode, should me perform if LED bar button
|
||||||
@@ -259,6 +271,7 @@ void setup() {
|
|||||||
|
|
||||||
Serial.println("Display brightness: " + String(configuration.getDisplayBrightness()));
|
Serial.println("Display brightness: " + String(configuration.getDisplayBrightness()));
|
||||||
oledDisplay.setBrightness(configuration.getDisplayBrightness());
|
oledDisplay.setBrightness(configuration.getDisplayBrightness());
|
||||||
|
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -297,11 +310,17 @@ void setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
if (networkOption == UseCellular) {
|
||||||
|
// Check if cellular client not ready until certain time
|
||||||
|
// Redundant check in both task to make sure its executed
|
||||||
|
restartIfCeClientIssueOverTwoHours();
|
||||||
|
}
|
||||||
|
|
||||||
// Schedule to feed external watchdog
|
// Schedule to feed external watchdog
|
||||||
watchdogFeedSchedule.run();
|
watchdogFeedSchedule.run();
|
||||||
|
|
||||||
if (otaInProgress) {
|
if (firmwareUpdateInProgress) {
|
||||||
// OTA currently in progress, temporarily disable running sensor schedules
|
// Firmare update currently in progress, temporarily disable running sensor schedules
|
||||||
delay(10000);
|
delay(10000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -334,7 +353,7 @@ void loop() {
|
|||||||
static bool pmsConnected = false;
|
static bool pmsConnected = false;
|
||||||
if (pmsConnected != ag->pms5003.connected()) {
|
if (pmsConnected != ag->pms5003.connected()) {
|
||||||
pmsConnected = ag->pms5003.connected();
|
pmsConnected = ag->pms5003.connected();
|
||||||
Serial.printf("PMS sensor %s ", pmsConnected?"connected":"removed");
|
Serial.printf("PMS sensor %s \n", pmsConnected?"connected":"removed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -346,11 +365,17 @@ void loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Run measurement schedule */
|
||||||
|
printMeasurementsSchedule.run();
|
||||||
|
|
||||||
/** factory reset handle */
|
/** factory reset handle */
|
||||||
factoryConfigReset();
|
factoryConfigReset();
|
||||||
|
|
||||||
/** check that local configuration changed then do some action */
|
if (configuration.isCommandRequested()) {
|
||||||
configUpdateHandle();
|
// Each state machine already has an independent request command check
|
||||||
|
stateMachine.executeCo2Calibration();
|
||||||
|
stateMachine.executeLedBarTest();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void co2Update(void) {
|
static void co2Update(void) {
|
||||||
@@ -367,6 +392,10 @@ static void co2Update(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void printMeasurements() {
|
||||||
|
measurements.printCurrentAverage();
|
||||||
|
}
|
||||||
|
|
||||||
static void mdnsInit(void) {
|
static void mdnsInit(void) {
|
||||||
if (!MDNS.begin(localServer.getHostname().c_str())) {
|
if (!MDNS.begin(localServer.getHostname().c_str())) {
|
||||||
Serial.println("Init mDNS failed");
|
Serial.println("Init mDNS failed");
|
||||||
@@ -423,6 +452,11 @@ static void initMqtt(void) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (networkOption == UseCellular) {
|
||||||
|
Serial.println("MQTT not available for cellular options");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (mqttClient.begin(mqttUri)) {
|
if (mqttClient.begin(mqttUri)) {
|
||||||
Serial.println("Successfully connected to MQTT broker");
|
Serial.println("Successfully connected to MQTT broker");
|
||||||
createMqttTask();
|
createMqttTask();
|
||||||
@@ -525,42 +559,44 @@ static bool sgp41Init(void) {
|
|||||||
configuration.hasSensorSGP = true;
|
configuration.hasSensorSGP = true;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Init SGP41 failuire");
|
Serial.println("Init SGP41 failure");
|
||||||
configuration.hasSensorSGP = false;
|
configuration.hasSensorSGP = false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkForFirmwareUpdate(void) {
|
void checkForFirmwareUpdate(void) {
|
||||||
|
if (configuration.isCloudConnectionDisabled()) {
|
||||||
|
Serial.println("Cloud connection is disabled, skip firmware update");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
AirgradientOTA *agOta;
|
AirgradientOTA *agOta;
|
||||||
if (networkOption == UseWifi) {
|
if (networkOption == UseWifi) {
|
||||||
agOta = new AirgradientOTAWifi;
|
agOta = new AirgradientOTAWifi;
|
||||||
} else {
|
} else {
|
||||||
agOta = new AirgradientOTACellular(cell);
|
agOta = new AirgradientOTACellular(cellularCard, agClient->getICCID());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indicate main task that ota is performing
|
// Indicate main task that firmware update is in progress
|
||||||
Serial.println("Check for firmware update, disabling main task");
|
firmwareUpdateInProgress = true;
|
||||||
otaInProgress = true;
|
|
||||||
if (configuration.hasSensorSGP && networkOption == UseCellular) {
|
|
||||||
// Only for cellular because it can disturb i2c line
|
|
||||||
Serial.println("Disable SGP41 task for cellular OTA");
|
|
||||||
ag->sgp41.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
agOta->setHandlerCallback(otaHandlerCallback);
|
agOta->setHandlerCallback(otaHandlerCallback);
|
||||||
agOta->updateIfAvailable(ag->deviceId().c_str(), GIT_VERSION);
|
|
||||||
|
|
||||||
// Only goes to this line if OTA is not success
|
String httpDomain = configuration.getHttpDomain();
|
||||||
|
if (httpDomain != "") {
|
||||||
|
Serial.printf("httpDomain configuration available, start OTA with custom domain\n",
|
||||||
|
httpDomain.c_str());
|
||||||
|
agOta->updateIfAvailable(ag->deviceId().c_str(), GIT_VERSION, httpDomain.c_str());
|
||||||
|
} else {
|
||||||
|
agOta->updateIfAvailable(ag->deviceId().c_str(), GIT_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only goes to this line if firmware update is not success
|
||||||
// Handled by otaHandlerCallback
|
// Handled by otaHandlerCallback
|
||||||
|
|
||||||
otaInProgress = false;
|
// Indicate main task that firmware update finish
|
||||||
if (configuration.hasSensorSGP && networkOption == UseCellular) {
|
firmwareUpdateInProgress = false;
|
||||||
// Re-start SGP41 task
|
|
||||||
if (!sgp41Init()) {
|
|
||||||
Serial.println("Failed re-start SGP41 task");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete agOta;
|
delete agOta;
|
||||||
Serial.println();
|
Serial.println();
|
||||||
@@ -568,14 +604,25 @@ void checkForFirmwareUpdate(void) {
|
|||||||
|
|
||||||
void otaHandlerCallback(AirgradientOTA::OtaResult result, const char *msg) {
|
void otaHandlerCallback(AirgradientOTA::OtaResult result, const char *msg) {
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case AirgradientOTA::Starting:
|
case AirgradientOTA::Starting: {
|
||||||
|
Serial.println("Firmware update starting...");
|
||||||
|
if (configuration.hasSensorSGP && networkOption == UseCellular) {
|
||||||
|
// Temporary pause SGP41 task while cellular firmware update is in progress
|
||||||
|
ag->sgp41.pause();
|
||||||
|
}
|
||||||
displayExecuteOta(result, fwNewVersion, 0);
|
displayExecuteOta(result, fwNewVersion, 0);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case AirgradientOTA::InProgress:
|
case AirgradientOTA::InProgress:
|
||||||
Serial.printf("OTA progress: %s\n", msg);
|
Serial.printf("OTA progress: %s\n", msg);
|
||||||
displayExecuteOta(result, "", std::stoi(msg));
|
displayExecuteOta(result, "", std::stoi(msg));
|
||||||
break;
|
break;
|
||||||
case AirgradientOTA::Failed:
|
case AirgradientOTA::Failed:
|
||||||
|
displayExecuteOta(result, "", 0);
|
||||||
|
if (configuration.hasSensorSGP && networkOption == UseCellular) {
|
||||||
|
ag->sgp41.resume();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case AirgradientOTA::Skipped:
|
case AirgradientOTA::Skipped:
|
||||||
case AirgradientOTA::AlreadyUpToDate:
|
case AirgradientOTA::AlreadyUpToDate:
|
||||||
displayExecuteOta(result, "", 0);
|
displayExecuteOta(result, "", 0);
|
||||||
@@ -642,7 +689,11 @@ static void displayExecuteOta(AirgradientOTA::OtaResult result, String msg, int
|
|||||||
}
|
}
|
||||||
delay(1000);
|
delay(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ag->isOne()) {
|
||||||
oledDisplay.setAirGradient(0);
|
oledDisplay.setAirGradient(0);
|
||||||
|
oledDisplay.setBrightness(0);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -808,8 +859,6 @@ static void openAirInit(void) {
|
|||||||
Serial.println("CO2 S8 sensor not found");
|
Serial.println("CO2 S8 sensor not found");
|
||||||
Serial.println("Can not detect S8 run mode 'PPT'");
|
Serial.println("Can not detect S8 run mode 'PPT'");
|
||||||
fwMode = FW_MODE_O_1PPT;
|
fwMode = FW_MODE_O_1PPT;
|
||||||
|
|
||||||
Serial0.end();
|
|
||||||
delay(200);
|
delay(200);
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Found S8 on Serial0");
|
Serial.println("Found S8 on Serial0");
|
||||||
@@ -903,6 +952,8 @@ static void boardInit(void) {
|
|||||||
} else {
|
} else {
|
||||||
Serial.println("Set S8 AbcDays failure");
|
Serial.println("Set S8 AbcDays failure");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ag->s8.printInformation();
|
||||||
}
|
}
|
||||||
|
|
||||||
localServer.setFwMode(fwMode);
|
localServer.setFwMode(fwMode);
|
||||||
@@ -922,8 +973,8 @@ void initializeNetwork() {
|
|||||||
if (agSerial->open()) {
|
if (agSerial->open()) {
|
||||||
Serial.println("Cellular module found");
|
Serial.println("Cellular module found");
|
||||||
// Initialize cellular module and use cellular as agClient
|
// Initialize cellular module and use cellular as agClient
|
||||||
cell = new CellularModuleA7672XX(agSerial, GPIO_POWER_MODULE_PIN);
|
cellularCard = new CellularModuleA7672XX(agSerial, GPIO_POWER_MODULE_PIN);
|
||||||
agClient = new AirgradientCellularClient(cell);
|
agClient = new AirgradientCellularClient(cellularCard);
|
||||||
networkOption = UseCellular;
|
networkOption = UseCellular;
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Cellular module not available, using wifi");
|
Serial.println("Cellular module not available, using wifi");
|
||||||
@@ -934,6 +985,19 @@ void initializeNetwork() {
|
|||||||
networkOption = UseWifi;
|
networkOption = UseWifi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (networkOption == UseCellular) {
|
||||||
|
// Enable serial stream debugging to check the AT command when doing registration
|
||||||
|
agSerial->setDebug(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
String httpDomain = configuration.getHttpDomain();
|
||||||
|
if (httpDomain != "") {
|
||||||
|
agClient->setHttpDomain(httpDomain.c_str());
|
||||||
|
Serial.printf("HTTP domain name is set to: %s\n", httpDomain.c_str());
|
||||||
|
oledDisplay.setText("HTTP domain name", "using local", "configuration");
|
||||||
|
delay(2500);
|
||||||
|
}
|
||||||
|
|
||||||
if (!agClient->begin(ag->deviceId().c_str())) {
|
if (!agClient->begin(ag->deviceId().c_str())) {
|
||||||
oledDisplay.setText("Client", "initialization", "failed");
|
oledDisplay.setText("Client", "initialization", "failed");
|
||||||
delay(5000);
|
delay(5000);
|
||||||
@@ -943,6 +1007,14 @@ void initializeNetwork() {
|
|||||||
ESP.restart();
|
ESP.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Provide openmetrics to have access to last transmission result
|
||||||
|
openMetrics.setAirgradientClient(agClient);
|
||||||
|
|
||||||
|
if (networkOption == UseCellular) {
|
||||||
|
// Disabling it again
|
||||||
|
agSerial->setDebug(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (networkOption == UseWifi) {
|
if (networkOption == UseWifi) {
|
||||||
if (!wifiConnector.connect()) {
|
if (!wifiConnector.connect()) {
|
||||||
Serial.println("Cannot initiate wifi connection");
|
Serial.println("Cannot initiate wifi connection");
|
||||||
@@ -973,10 +1045,17 @@ void initializeNetwork() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send data for the first time to AG server at boot
|
// Send data for the first time to AG server at boot only if postDataToAirgradient is enabled
|
||||||
|
if (configuration.isPostDataToAirGradient()) {
|
||||||
sendDataToAg();
|
sendDataToAg();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip fetch configuration if configuration control is set to "local" only
|
||||||
|
if (configuration.getConfigurationControl() == ConfigurationControl::ConfigurationControlLocal) {
|
||||||
|
ledBarEnabledUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string config = agClient->httpFetchConfig();
|
std::string config = agClient->httpFetchConfig();
|
||||||
configSchedule.update();
|
configSchedule.update();
|
||||||
@@ -1008,8 +1087,8 @@ static void configurationUpdateSchedule(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string config = agClient->httpFetchConfig();
|
std::string config = agClient->httpFetchConfig();
|
||||||
if (agClient->isLastFetchConfigSucceed() && configuration.parse(config.c_str(), false)) {
|
if (agClient->isLastFetchConfigSucceed()) {
|
||||||
configUpdateHandle();
|
configuration.parse(config.c_str(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1018,14 +1097,22 @@ static void configUpdateHandle() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
stateMachine.executeCo2Calibration();
|
|
||||||
|
|
||||||
String mqttUri = configuration.getMqttBrokerUri();
|
String mqttUri = configuration.getMqttBrokerUri();
|
||||||
if (mqttClient.isCurrentUri(mqttUri) == false) {
|
if (mqttClient.isCurrentUri(mqttUri) == false) {
|
||||||
mqttClient.end();
|
mqttClient.end();
|
||||||
initMqtt();
|
initMqtt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String httpDomain = configuration.getHttpDomain();
|
||||||
|
if (httpDomain != "") {
|
||||||
|
Serial.printf("HTTP domain name set to: %s\n", httpDomain.c_str());
|
||||||
|
agClient->setHttpDomain(httpDomain.c_str());
|
||||||
|
} else {
|
||||||
|
// Its empty, set to default
|
||||||
|
Serial.println("HTTP domain name from configuration empty, set to default");
|
||||||
|
agClient->setHttpDomainDefault();
|
||||||
|
}
|
||||||
|
|
||||||
if (configuration.hasSensorSGP) {
|
if (configuration.hasSensorSGP) {
|
||||||
if (configuration.noxLearnOffsetChanged() ||
|
if (configuration.noxLearnOffsetChanged() ||
|
||||||
configuration.tvocLearnOffsetChanged()) {
|
configuration.tvocLearnOffsetChanged()) {
|
||||||
@@ -1081,11 +1168,6 @@ static void configUpdateHandle() {
|
|||||||
if (configuration.isDisplayBrightnessChanged()) {
|
if (configuration.isDisplayBrightnessChanged()) {
|
||||||
oledDisplay.setBrightness(configuration.getDisplayBrightness());
|
oledDisplay.setBrightness(configuration.getDisplayBrightness());
|
||||||
}
|
}
|
||||||
|
|
||||||
stateMachine.executeLedBarTest();
|
|
||||||
}
|
|
||||||
else if(ag->isOpenAir()) {
|
|
||||||
stateMachine.executeLedBarTest();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update display and led bar notification based on updated configuration
|
// Update display and led bar notification based on updated configuration
|
||||||
@@ -1309,7 +1391,10 @@ void postUsingWifi() {
|
|||||||
Serial.printf("Free heap: %u\n", ESP.getFreeHeap());
|
Serial.printf("Free heap: %u\n", ESP.getFreeHeap());
|
||||||
}
|
}
|
||||||
|
|
||||||
void postUsingCellular() {
|
/**
|
||||||
|
* forcePost to force post without checking transmit cycle
|
||||||
|
*/
|
||||||
|
void postUsingCellular(bool forcePost) {
|
||||||
// Aquire queue mutex to get queue size
|
// Aquire queue mutex to get queue size
|
||||||
xSemaphoreTake(mutexMeasurementCycleQueue, portMAX_DELAY);
|
xSemaphoreTake(mutexMeasurementCycleQueue, portMAX_DELAY);
|
||||||
|
|
||||||
@@ -1321,6 +1406,14 @@ void postUsingCellular() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check queue size if its ready to transmit
|
||||||
|
// It is ready if size is divisible by 3
|
||||||
|
if (!forcePost && (queueSize % MEASUREMENT_TRANSMIT_CYCLE) > 0) {
|
||||||
|
Serial.printf("Not ready to transmit, queue size are %d\n", queueSize);
|
||||||
|
xSemaphoreGive(mutexMeasurementCycleQueue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Build payload include all measurements from queue
|
// Build payload include all measurements from queue
|
||||||
std::string payload;
|
std::string payload;
|
||||||
payload += std::to_string(CELLULAR_MEASUREMENT_INTERVAL / 1000); // Convert to seconds
|
payload += std::to_string(CELLULAR_MEASUREMENT_INTERVAL / 1000); // Convert to seconds
|
||||||
@@ -1342,13 +1435,17 @@ void postUsingCellular() {
|
|||||||
|
|
||||||
// Post success, remove the data that previously sent from queue
|
// Post success, remove the data that previously sent from queue
|
||||||
xSemaphoreTake(mutexMeasurementCycleQueue, portMAX_DELAY);
|
xSemaphoreTake(mutexMeasurementCycleQueue, portMAX_DELAY);
|
||||||
measurementCycleQueue.erase(measurementCycleQueue.begin(),
|
|
||||||
measurementCycleQueue.begin() + queueSize);
|
|
||||||
|
|
||||||
if (measurementCycleQueue.capacity() > RESERVED_MEASUREMENT_CYCLE_CAPACITY) {
|
if (measurementCycleQueue.capacity() > RESERVED_MEASUREMENT_CYCLE_CAPACITY) {
|
||||||
Serial.println("measurementCycleQueue capacity more than reserved space, resizing..");
|
Serial.println("measurementCycleQueue capacity more than reserved space, resizing..");
|
||||||
measurementCycleQueue.resize(RESERVED_MEASUREMENT_CYCLE_CAPACITY);
|
std::vector<Measurements::Measures> tmp;
|
||||||
|
tmp.reserve(RESERVED_MEASUREMENT_CYCLE_CAPACITY);
|
||||||
|
measurementCycleQueue.swap(tmp);
|
||||||
|
} else {
|
||||||
|
// If not more than the capacity, then just clear all the values
|
||||||
|
measurementCycleQueue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
xSemaphoreGive(mutexMeasurementCycleQueue);
|
xSemaphoreGive(mutexMeasurementCycleQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1362,7 +1459,7 @@ void sendDataToServer(void) {
|
|||||||
if (networkOption == UseWifi) {
|
if (networkOption == UseWifi) {
|
||||||
postUsingWifi();
|
postUsingWifi();
|
||||||
} else if (networkOption == UseCellular) {
|
} else if (networkOption == UseCellular) {
|
||||||
postUsingCellular();
|
postUsingCellular(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1432,12 +1529,8 @@ void setMeasurementMaxPeriod() {
|
|||||||
|
|
||||||
int calculateMaxPeriod(int updateInterval) {
|
int calculateMaxPeriod(int updateInterval) {
|
||||||
// 0.8 is 80% reduced interval for max period
|
// 0.8 is 80% reduced interval for max period
|
||||||
if (networkOption == UseWifi) {
|
// NOTE: Both network option use the same measurement interval
|
||||||
return (WIFI_MEASUREMENT_INTERVAL - (WIFI_MEASUREMENT_INTERVAL * 0.8)) / updateInterval;
|
return (WIFI_MEASUREMENT_INTERVAL - (WIFI_MEASUREMENT_INTERVAL * 0.8)) / updateInterval;
|
||||||
} else {
|
|
||||||
// Cellular
|
|
||||||
return (CELLULAR_MEASUREMENT_INTERVAL - (CELLULAR_MEASUREMENT_INTERVAL * 0.8)) / updateInterval;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1445,26 +1538,56 @@ void networkSignalCheck() {
|
|||||||
if (networkOption == UseWifi) {
|
if (networkOption == UseWifi) {
|
||||||
Serial.printf("WiFi RSSI %d\n", wifiConnector.RSSI());
|
Serial.printf("WiFi RSSI %d\n", wifiConnector.RSSI());
|
||||||
} else if (networkOption == UseCellular) {
|
} else if (networkOption == UseCellular) {
|
||||||
auto result = cell->retrieveSignal();
|
auto result = cellularCard->retrieveSignal();
|
||||||
if (result.status != CellReturnStatus::Ok) {
|
if (result.status != CellReturnStatus::Ok) {
|
||||||
agClient->setClientReady(false);
|
agClient->setClientReady(false);
|
||||||
|
lastCellSignalQuality = 99;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save last signal quality
|
||||||
|
lastCellSignalQuality = result.data;
|
||||||
|
|
||||||
if (result.data == 99) {
|
if (result.data == 99) {
|
||||||
// 99 indicate cellular not attached to network
|
// 99 indicate cellular not attached to network
|
||||||
agClient->setClientReady(false);
|
agClient->setClientReady(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Serial.printf("Cellular signal strength %d\n", result.data);
|
|
||||||
|
Serial.printf("Cellular signal quality %d\n", result.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If in 2 hours cellular client still not ready, then restart system
|
||||||
|
*/
|
||||||
|
void restartIfCeClientIssueOverTwoHours() {
|
||||||
|
if (agCeClientProblemDetectedTime > 0 &&
|
||||||
|
(MINUTES() - agCeClientProblemDetectedTime) >
|
||||||
|
TIMEOUT_WAIT_FOR_CELLULAR_MODULE_READY) {
|
||||||
|
// Give up wait
|
||||||
|
Serial.println("Rebooting because CE client issues for 2 hours detected");
|
||||||
|
int i = 3;
|
||||||
|
while (i != 0) {
|
||||||
|
if (ag->isOne()) {
|
||||||
|
String tmp = "Rebooting in " + String(i);
|
||||||
|
oledDisplay.setText("CE error", "since 2h", tmp.c_str());
|
||||||
|
} else {
|
||||||
|
Serial.println("Rebooting... " + String(i));
|
||||||
|
}
|
||||||
|
i = i - 1;
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
oledDisplay.setBrightness(0);
|
||||||
|
esp_restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void networkingTask(void *args) {
|
void networkingTask(void *args) {
|
||||||
|
// If cloud connection enabled, run first transmission to server at boot
|
||||||
|
if (configuration.isCloudConnectionDisabled() == false) {
|
||||||
// OTA check on boot
|
// OTA check on boot
|
||||||
#ifdef ESP8266
|
#ifndef ESP8266
|
||||||
// ota not supported
|
|
||||||
#else
|
|
||||||
// because cellular it takes too long, watchdog triggered
|
|
||||||
checkForFirmwareUpdate();
|
checkForFirmwareUpdate();
|
||||||
checkForUpdateSchedule.update();
|
checkForUpdateSchedule.update();
|
||||||
#endif
|
#endif
|
||||||
@@ -1474,14 +1597,15 @@ void networkingTask(void *args) {
|
|||||||
if (networkOption == UseCellular) {
|
if (networkOption == UseCellular) {
|
||||||
Serial.println("Prepare first measures cycle to send on boot for 20s");
|
Serial.println("Prepare first measures cycle to send on boot for 20s");
|
||||||
delay(20000);
|
delay(20000);
|
||||||
|
networkSignalCheck();
|
||||||
newMeasurementCycle();
|
newMeasurementCycle();
|
||||||
sendDataToServer();
|
postUsingCellular(true);
|
||||||
measurementSchedule.update();
|
measurementSchedule.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset scheduler
|
// Reset scheduler
|
||||||
configSchedule.update();
|
configSchedule.update();
|
||||||
transmissionSchedule.update();
|
transmissionSchedule.update();
|
||||||
|
}
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
// Handle reconnection based on mode
|
// Handle reconnection based on mode
|
||||||
@@ -1494,28 +1618,59 @@ void networkingTask(void *args) {
|
|||||||
}
|
}
|
||||||
else if (networkOption == UseCellular) {
|
else if (networkOption == UseCellular) {
|
||||||
if (agClient->isClientReady() == false) {
|
if (agClient->isClientReady() == false) {
|
||||||
|
// Start time if value still default
|
||||||
|
if (agCeClientProblemDetectedTime == 0) {
|
||||||
|
agCeClientProblemDetectedTime = MINUTES();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable at command debug
|
||||||
|
agSerial->setDebug(true);
|
||||||
|
|
||||||
|
// Check if cellular client not ready until certain time
|
||||||
|
// Redundant check in both task to make sure its executed
|
||||||
|
restartIfCeClientIssueOverTwoHours();
|
||||||
|
|
||||||
|
// Power cycling cellular module due to network issues for more than 1 hour
|
||||||
|
bool resetModule = true;
|
||||||
|
if ((MINUTES() - agCeClientProblemDetectedTime) >
|
||||||
|
TIME_TO_START_POWER_CYCLE_CELLULAR_MODULE) {
|
||||||
|
Serial.println("The CE client hasn't recovered in more than 1 hour, "
|
||||||
|
"performing a power cycle");
|
||||||
|
cellularCard->powerOff();
|
||||||
|
delay(2000);
|
||||||
|
cellularCard->powerOn();
|
||||||
|
delay(10000);
|
||||||
|
// no need to reset module when calling ensureClientConnection()
|
||||||
|
resetModule = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to reconnect
|
||||||
Serial.println("Cellular client not ready, ensuring connection...");
|
Serial.println("Cellular client not ready, ensuring connection...");
|
||||||
if (agClient->ensureClientConnection() == false) {
|
if (agClient->ensureClientConnection(resetModule) == false) {
|
||||||
Serial.println("Cellular client connection not ready, retry in 5s...");
|
Serial.println("Cellular client connection not ready, retry in 30s...");
|
||||||
delay(5000);
|
delay(30000); // before retry, wait for 30s
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Client is ready
|
||||||
|
agCeClientProblemDetectedTime = 0; // reset to default
|
||||||
|
agSerial->setDebug(false); // disable at command debug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If connection to AirGradient server disable don't run config and transmission schedule
|
// If connection to AirGradient server disable don't run config and transmission schedule
|
||||||
if (configuration.isCloudConnectionDisabled()) {
|
if (configuration.isCloudConnectionDisabled()) {
|
||||||
delay(1000);
|
delay(1000);
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run scheduler
|
// Run scheduler
|
||||||
networkSignalCheckSchedule.run();
|
networkSignalCheckSchedule.run();
|
||||||
configSchedule.run();
|
|
||||||
transmissionSchedule.run();
|
transmissionSchedule.run();
|
||||||
|
configSchedule.run();
|
||||||
checkForUpdateSchedule.run();
|
checkForUpdateSchedule.run();
|
||||||
|
|
||||||
delay(1000);
|
delay(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
vTaskDelete(handleNetworkTask);
|
vTaskDelete(handleNetworkTask);
|
||||||
@@ -1529,7 +1684,10 @@ void newMeasurementCycle() {
|
|||||||
measurementCycleQueue.erase(measurementCycleQueue.begin());
|
measurementCycleQueue.erase(measurementCycleQueue.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get current measures
|
||||||
auto mc = measurements.getMeasures();
|
auto mc = measurements.getMeasures();
|
||||||
|
mc.signal = cellularCard->csqToDbm(lastCellSignalQuality); // convert to RSSI
|
||||||
|
|
||||||
measurementCycleQueue.push_back(mc);
|
measurementCycleQueue.push_back(mc);
|
||||||
Serial.println("New measurement cycle added to queue");
|
Serial.println("New measurement cycle added to queue");
|
||||||
// Release mutex
|
// Release mutex
|
||||||
|
|||||||
@@ -6,8 +6,11 @@ OpenMetrics::OpenMetrics(Measurements &measure, Configuration &config,
|
|||||||
|
|
||||||
OpenMetrics::~OpenMetrics() {}
|
OpenMetrics::~OpenMetrics() {}
|
||||||
|
|
||||||
void OpenMetrics::setAirGradient(AirGradient *ag, AirgradientClient *client) {
|
void OpenMetrics::setAirGradient(AirGradient *ag) {
|
||||||
this->ag = ag;
|
this->ag = ag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenMetrics::setAirgradientClient(AirgradientClient *client) {
|
||||||
this->agClient = client;
|
this->agClient = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,14 +202,14 @@ String OpenMetrics::getPayload(void) {
|
|||||||
}
|
}
|
||||||
if (utils::isValidNOx(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 Nitrogen Oxide (NOx) index as measured by the "
|
||||||
"AirGradient SGP sensor",
|
"AirGradient SGP sensor",
|
||||||
"gauge");
|
"gauge");
|
||||||
add_metric_point("", String(nox));
|
add_metric_point("", String(nox));
|
||||||
}
|
}
|
||||||
if (utils::isValidNOx(noxRaw)) {
|
if (utils::isValidNOx(noxRaw)) {
|
||||||
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 Nitrogen Oxide (NOx) index as "
|
||||||
"measured by the AirGradient SGP sensor",
|
"measured by the AirGradient SGP sensor",
|
||||||
"gauge");
|
"gauge");
|
||||||
add_metric_point("", String(noxRaw));
|
add_metric_point("", String(noxRaw));
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ public:
|
|||||||
OpenMetrics(Measurements &measure, Configuration &config,
|
OpenMetrics(Measurements &measure, Configuration &config,
|
||||||
WifiConnector &wifiConnector);
|
WifiConnector &wifiConnector);
|
||||||
~OpenMetrics();
|
~OpenMetrics();
|
||||||
void setAirGradient(AirGradient *ag, AirgradientClient *client);
|
void setAirGradient(AirGradient *ag);
|
||||||
|
void setAirgradientClient(AirgradientClient *client);
|
||||||
const char *getApiContentType(void);
|
const char *getApiContentType(void);
|
||||||
const char* getApi(void);
|
const char* getApi(void);
|
||||||
String getPayload(void);
|
String getPayload(void);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name=AirGradient Air Quality Sensor
|
name=AirGradient Air Quality Sensor
|
||||||
version=3.3.0
|
version=3.3.9
|
||||||
author=AirGradient <support@airgradient.com>
|
author=AirGradient <support@airgradient.com>
|
||||||
maintainer=AirGradient <support@airgradient.com>
|
maintainer=AirGradient <support@airgradient.com>
|
||||||
sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display.
|
sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
platform = espressif32
|
platform = espressif32
|
||||||
board = esp32-c3-devkitm-1
|
board = esp32-c3-devkitm-1
|
||||||
framework = arduino
|
framework = arduino
|
||||||
build_flags = !echo '-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 -D CORE_DEBUG_LEVEL=3 -D GIT_VERSION=\\"'$(git describe --tags --always --dirty)'\\"'
|
build_flags = !echo '-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 -D AG_LOG_LEVEL=AG_LOG_LEVEL_INFO -D GIT_VERSION=\\"'$(git describe --tags --always --dirty)'\\"'
|
||||||
board_build.partitions = partitions.csv
|
board_build.partitions = partitions.csv
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
lib_deps =
|
lib_deps =
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ JSON_PROP_DEF(abcDays);
|
|||||||
JSON_PROP_DEF(tvocLearningOffset);
|
JSON_PROP_DEF(tvocLearningOffset);
|
||||||
JSON_PROP_DEF(noxLearningOffset);
|
JSON_PROP_DEF(noxLearningOffset);
|
||||||
JSON_PROP_DEF(mqttBrokerUrl);
|
JSON_PROP_DEF(mqttBrokerUrl);
|
||||||
|
JSON_PROP_DEF(httpDomain);
|
||||||
JSON_PROP_DEF(temperatureUnit);
|
JSON_PROP_DEF(temperatureUnit);
|
||||||
JSON_PROP_DEF(configurationControl);
|
JSON_PROP_DEF(configurationControl);
|
||||||
JSON_PROP_DEF(postDataToAirGradient);
|
JSON_PROP_DEF(postDataToAirGradient);
|
||||||
@@ -68,6 +69,7 @@ JSON_PROP_DEF(rhum);
|
|||||||
#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_httpDomain_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
|
||||||
@@ -240,7 +242,7 @@ bool Configuration::updateTempHumCorrection(JSONVar &json, TempHumCorrection &ta
|
|||||||
|
|
||||||
JSONVar corrections = json[jprop_corrections];
|
JSONVar corrections = json[jprop_corrections];
|
||||||
if (!corrections.hasOwnProperty(correctionName)) {
|
if (!corrections.hasOwnProperty(correctionName)) {
|
||||||
logWarning(String(correctionName) + " correction field not found on configuration");
|
logInfo(String(correctionName) + " correction field not found on configuration");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,6 +379,7 @@ void Configuration::defaultConfig(void) {
|
|||||||
|
|
||||||
jconfig[jprop_country] = jprop_country_default;
|
jconfig[jprop_country] = jprop_country_default;
|
||||||
jconfig[jprop_mqttBrokerUrl] = jprop_mqttBrokerUrl_default;
|
jconfig[jprop_mqttBrokerUrl] = jprop_mqttBrokerUrl_default;
|
||||||
|
jconfig[jprop_httpDomain] = jprop_httpDomain_default;
|
||||||
jconfig[jprop_configurationControl] = jprop_configurationControl_default;
|
jconfig[jprop_configurationControl] = jprop_configurationControl_default;
|
||||||
jconfig[jprop_pmStandard] = jprop_pmStandard_default;
|
jconfig[jprop_pmStandard] = jprop_pmStandard_default;
|
||||||
jconfig[jprop_temperatureUnit] = jprop_temperatureUnit_default;
|
jconfig[jprop_temperatureUnit] = jprop_temperatureUnit_default;
|
||||||
@@ -453,6 +456,10 @@ bool Configuration::begin(void) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Configuration::setConfigurationUpdatedCallback(ConfigurationUpdatedCallback_t callback) {
|
||||||
|
_callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Parse JSON configura string to local configure
|
* @brief Parse JSON configura string to local configure
|
||||||
*
|
*
|
||||||
@@ -735,11 +742,17 @@ bool Configuration::parse(String data, bool isLocal) {
|
|||||||
jconfig[jprop_mqttBrokerUrl] = broker;
|
jconfig[jprop_mqttBrokerUrl] = broker;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
failedMessage = "\"mqttBrokerUrl\" length should <= 255";
|
failedMessage = "\"mqttBrokerUrl\" length should less than 255 character";
|
||||||
jsonInvalid();
|
jsonInvalid();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else if (JSON.typeof_(root[jprop_mqttBrokerUrl]) == "null" and !isLocal) {
|
||||||
|
// So if its not available on the json and json comes from aigradient server
|
||||||
|
// then set its value to default (empty)
|
||||||
|
jconfig[jprop_mqttBrokerUrl] = jprop_mqttBrokerUrl_default;
|
||||||
|
}
|
||||||
|
else {
|
||||||
if (jsonTypeInvalid(root[jprop_mqttBrokerUrl], "string")) {
|
if (jsonTypeInvalid(root[jprop_mqttBrokerUrl], "string")) {
|
||||||
failedMessage =
|
failedMessage =
|
||||||
jsonTypeInvalidMessage(String(jprop_mqttBrokerUrl), "string");
|
jsonTypeInvalidMessage(String(jprop_mqttBrokerUrl), "string");
|
||||||
@@ -748,6 +761,32 @@ bool Configuration::parse(String data, bool isLocal) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isLocal) {
|
||||||
|
if (JSON.typeof_(root[jprop_httpDomain]) == "string") {
|
||||||
|
String httpDomain = root[jprop_httpDomain];
|
||||||
|
String oldHttpDomain = jconfig[jprop_httpDomain];
|
||||||
|
if (httpDomain.length() <= 255) {
|
||||||
|
if (httpDomain != oldHttpDomain) {
|
||||||
|
changed = true;
|
||||||
|
configLogInfo(String(jprop_httpDomain), oldHttpDomain, httpDomain);
|
||||||
|
jconfig[jprop_httpDomain] = httpDomain;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
failedMessage = "\"httpDomain\" length should less than 255 character";
|
||||||
|
jsonInvalid();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (jsonTypeInvalid(root[jprop_httpDomain], "string")) {
|
||||||
|
failedMessage =
|
||||||
|
jsonTypeInvalidMessage(String(jprop_httpDomain), "string");
|
||||||
|
jsonInvalid();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (JSON.typeof_(root[jprop_temperatureUnit]) == "string") {
|
if (JSON.typeof_(root[jprop_temperatureUnit]) == "string") {
|
||||||
String unit = root[jprop_temperatureUnit];
|
String unit = root[jprop_temperatureUnit];
|
||||||
String oldUnit = jconfig[jprop_temperatureUnit];
|
String oldUnit = jconfig[jprop_temperatureUnit];
|
||||||
@@ -916,15 +955,18 @@ bool Configuration::parse(String data, bool isLocal) {
|
|||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ledBarTestRequested || co2CalibrationRequested) {
|
||||||
|
commandRequested = true;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
updated = true;
|
updated = true;
|
||||||
saveConfig();
|
saveConfig();
|
||||||
printConfig();
|
printConfig();
|
||||||
} else {
|
_callback();
|
||||||
if (ledBarTestRequested || co2CalibrationRequested) {
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1030,6 +1072,16 @@ String Configuration::getMqttBrokerUri(void) {
|
|||||||
return broker;
|
return broker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get HTTP domain for post measures and get configuration
|
||||||
|
*
|
||||||
|
* @return String http domain, might be empty string
|
||||||
|
*/
|
||||||
|
String Configuration::getHttpDomain(void) {
|
||||||
|
String httpDomain = jconfig[jprop_httpDomain];
|
||||||
|
return httpDomain;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get configuratoin post data to AirGradient cloud
|
* @brief Get configuratoin post data to AirGradient cloud
|
||||||
*
|
*
|
||||||
@@ -1114,8 +1166,14 @@ bool Configuration::isUpdated(void) {
|
|||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Configuration::isCommandRequested(void) {
|
||||||
|
bool oldState = this->commandRequested;
|
||||||
|
this->commandRequested = false;
|
||||||
|
return oldState;
|
||||||
|
}
|
||||||
|
|
||||||
String Configuration::jsonTypeInvalidMessage(String name, String type) {
|
String Configuration::jsonTypeInvalidMessage(String name, String type) {
|
||||||
return "'" + name + "' type invalid, it's should '" + type + "'";
|
return "'" + name + "' type is invalid, expecting '" + type + "'";
|
||||||
}
|
}
|
||||||
|
|
||||||
String Configuration::jsonValueInvalidMessage(String name, String value) {
|
String Configuration::jsonValueInvalidMessage(String name, String value) {
|
||||||
@@ -1269,6 +1327,18 @@ void Configuration::toConfig(const char *buf) {
|
|||||||
logInfo("toConfig: mqttBroker changed");
|
logInfo("toConfig: mqttBroker changed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** validate http domain */
|
||||||
|
if (JSON.typeof_(jconfig[jprop_httpDomain]) != "string") {
|
||||||
|
isConfigFieldInvalid = true;
|
||||||
|
} else {
|
||||||
|
isConfigFieldInvalid = false;
|
||||||
|
}
|
||||||
|
if (isConfigFieldInvalid) {
|
||||||
|
changed = true;
|
||||||
|
jconfig[jprop_httpDomain] = jprop_httpDomain_default;
|
||||||
|
logInfo("toConfig: httpDomain changed");
|
||||||
|
}
|
||||||
|
|
||||||
/** Validate temperature unit */
|
/** Validate temperature unit */
|
||||||
if (JSON.typeof_(jconfig[jprop_temperatureUnit]) != "string") {
|
if (JSON.typeof_(jconfig[jprop_temperatureUnit]) != "string") {
|
||||||
isConfigFieldInvalid = true;
|
isConfigFieldInvalid = true;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ private:
|
|||||||
bool co2CalibrationRequested;
|
bool co2CalibrationRequested;
|
||||||
bool ledBarTestRequested;
|
bool ledBarTestRequested;
|
||||||
bool updated;
|
bool updated;
|
||||||
|
bool commandRequested = false;
|
||||||
String failedMessage;
|
String failedMessage;
|
||||||
bool _noxLearnOffsetChanged;
|
bool _noxLearnOffsetChanged;
|
||||||
bool _tvocLearningOffsetChanged;
|
bool _tvocLearningOffsetChanged;
|
||||||
@@ -70,6 +71,9 @@ public:
|
|||||||
bool hasSensorSGP = true;
|
bool hasSensorSGP = true;
|
||||||
bool hasSensorSHT = true;
|
bool hasSensorSHT = true;
|
||||||
|
|
||||||
|
typedef void (*ConfigurationUpdatedCallback_t)();
|
||||||
|
void setConfigurationUpdatedCallback(ConfigurationUpdatedCallback_t callback);
|
||||||
|
|
||||||
bool begin(void);
|
bool begin(void);
|
||||||
bool parse(String data, bool isLocal);
|
bool parse(String data, bool isLocal);
|
||||||
String toString(void);
|
String toString(void);
|
||||||
@@ -82,6 +86,7 @@ public:
|
|||||||
String getLedBarModeName(void);
|
String getLedBarModeName(void);
|
||||||
bool getDisplayMode(void);
|
bool getDisplayMode(void);
|
||||||
String getMqttBrokerUri(void);
|
String getMqttBrokerUri(void);
|
||||||
|
String getHttpDomain(void);
|
||||||
bool isPostDataToAirGradient(void);
|
bool isPostDataToAirGradient(void);
|
||||||
ConfigurationControl getConfigurationControl(void);
|
ConfigurationControl getConfigurationControl(void);
|
||||||
bool isCo2CalibrationRequested(void);
|
bool isCo2CalibrationRequested(void);
|
||||||
@@ -89,6 +94,7 @@ public:
|
|||||||
void reset(void);
|
void reset(void);
|
||||||
String getModel(void);
|
String getModel(void);
|
||||||
bool isUpdated(void);
|
bool isUpdated(void);
|
||||||
|
bool isCommandRequested(void);
|
||||||
String getFailedMesage(void);
|
String getFailedMesage(void);
|
||||||
void setPostToAirGradient(bool enable);
|
void setPostToAirGradient(bool enable);
|
||||||
bool noxLearnOffsetChanged(void);
|
bool noxLearnOffsetChanged(void);
|
||||||
@@ -115,6 +121,8 @@ public:
|
|||||||
PMCorrection getPMCorrection(void);
|
PMCorrection getPMCorrection(void);
|
||||||
TempHumCorrection getTempCorrection(void);
|
TempHumCorrection getTempCorrection(void);
|
||||||
TempHumCorrection getHumCorrection(void);
|
TempHumCorrection getHumCorrection(void);
|
||||||
|
private:
|
||||||
|
ConfigurationUpdatedCallback_t _callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /** _AG_CONFIG_H_ */
|
#endif /** _AG_CONFIG_H_ */
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ AgSchedule::~AgSchedule() {}
|
|||||||
void AgSchedule::run(void) {
|
void AgSchedule::run(void) {
|
||||||
uint32_t ms = (uint32_t)(millis() - count);
|
uint32_t ms = (uint32_t)(millis() - count);
|
||||||
if (ms >= period) {
|
if (ms >= period) {
|
||||||
handler();
|
|
||||||
count = millis();
|
count = millis();
|
||||||
|
handler();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
#define RGB_COLOR_R 255, 0, 0 /** Red */
|
#define RGB_COLOR_R 255, 0, 0 /** Red */
|
||||||
#define RGB_COLOR_G 0, 255, 0 /** Green */
|
#define RGB_COLOR_G 0, 255, 0 /** Green */
|
||||||
#define RGB_COLOR_Y 255, 150, 0 /** Yellow */
|
#define RGB_COLOR_Y 255, 255, 0 /** Yellow */
|
||||||
#define RGB_COLOR_O 255, 40, 0 /** Orange */
|
#define RGB_COLOR_O 255, 128, 0 /** Orange */
|
||||||
#define RGB_COLOR_P 180, 0, 255 /** Purple */
|
#define RGB_COLOR_P 180, 0, 255 /** Purple */
|
||||||
#define RGB_COLOR_CLEAR 0, 0, 0 /** No color */
|
#define RGB_COLOR_CLEAR 0, 0, 0 /** No color */
|
||||||
|
|
||||||
|
|||||||
171
src/AgValue.cpp
171
src/AgValue.cpp
@@ -76,6 +76,86 @@ Measurements::Measurements(Configuration &config) : config(config) {
|
|||||||
|
|
||||||
void Measurements::setAirGradient(AirGradient *ag) { this->ag = ag; }
|
void Measurements::setAirGradient(AirGradient *ag) { this->ag = ag; }
|
||||||
|
|
||||||
|
void Measurements::printCurrentAverage() {
|
||||||
|
Serial.println();
|
||||||
|
if (config.hasSensorS8) {
|
||||||
|
if (utils::isValidCO2(_co2.update.avg)) {
|
||||||
|
Serial.printf("CO2 = %.2f ppm\n", _co2.update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("CO2 = -\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.hasSensorSHT) {
|
||||||
|
if (utils::isValidTemperature(_temperature[0].update.avg)) {
|
||||||
|
Serial.printf("Temperature = %.2f C\n", _temperature[0].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("Temperature = -\n");
|
||||||
|
}
|
||||||
|
if (utils::isValidHumidity(_humidity[0].update.avg)) {
|
||||||
|
Serial.printf("Relative Humidity = %.2f\n", _humidity[0].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("Relative Humidity = -\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.hasSensorSGP) {
|
||||||
|
if (utils::isValidVOC(_tvoc.update.avg)) {
|
||||||
|
Serial.printf("TVOC Index = %.1f\n", _tvoc.update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("TVOC Index = -\n");
|
||||||
|
}
|
||||||
|
if (utils::isValidVOC(_tvoc_raw.update.avg)) {
|
||||||
|
Serial.printf("TVOC Raw = %.1f\n", _tvoc_raw.update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("TVOC Raw = -\n");
|
||||||
|
}
|
||||||
|
if (utils::isValidNOx(_nox.update.avg)) {
|
||||||
|
Serial.printf("NOx Index = %.1f\n", _nox.update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("NOx Index = -\n");
|
||||||
|
}
|
||||||
|
if (utils::isValidNOx(_nox_raw.update.avg)) {
|
||||||
|
Serial.printf("NOx Raw = %.1f\n", _nox_raw.update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("NOx Raw = -\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.hasSensorPMS1) {
|
||||||
|
printCurrentPMAverage(1);
|
||||||
|
if (!config.hasSensorSHT) {
|
||||||
|
if (utils::isValidTemperature(_temperature[0].update.avg)) {
|
||||||
|
Serial.printf("[1] Temperature = %.2f C\n", _temperature[0].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[1] Temperature = -\n");
|
||||||
|
}
|
||||||
|
if (utils::isValidHumidity(_humidity[0].update.avg)) {
|
||||||
|
Serial.printf("[1] Relative Humidity = %.2f\n", _humidity[0].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[1] Relative Humidity = -\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.hasSensorPMS2) {
|
||||||
|
printCurrentPMAverage(2);
|
||||||
|
if (!config.hasSensorSHT) {
|
||||||
|
if (utils::isValidTemperature(_temperature[1].update.avg)) {
|
||||||
|
Serial.printf("[2] Temperature = %.2f C\n", _temperature[1].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[2] Temperature = -\n");
|
||||||
|
}
|
||||||
|
if (utils::isValidHumidity(_humidity[1].update.avg)) {
|
||||||
|
Serial.printf("[2] Relative Humidity = %.2f\n", _humidity[1].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[2] Relative Humidity = -\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
|
||||||
void Measurements::maxPeriod(MeasurementType type, int max) {
|
void Measurements::maxPeriod(MeasurementType type, int max) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Temperature:
|
case Temperature:
|
||||||
@@ -570,6 +650,77 @@ String Measurements::measurementTypeStr(MeasurementType type) {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Measurements::printCurrentPMAverage(int ch) {
|
||||||
|
int idx = ch - 1;
|
||||||
|
|
||||||
|
if (utils::isValidPm(_pm_01[idx].update.avg)) {
|
||||||
|
Serial.printf("[%d] Atmospheric PM 1.0 = %.2f ug/m3\n", ch, _pm_01[idx].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[%d] Atmospheric PM 1.0 = -\n", ch);
|
||||||
|
}
|
||||||
|
if (utils::isValidPm(_pm_25[idx].update.avg)) {
|
||||||
|
Serial.printf("[%d] Atmospheric PM 2.5 = %.2f ug/m3\n", ch, _pm_25[idx].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[%d] Atmospheric PM 2.5 = -\n", ch);
|
||||||
|
}
|
||||||
|
if (utils::isValidPm(_pm_10[idx].update.avg)) {
|
||||||
|
Serial.printf("[%d] Atmospheric PM 10 = %.2f ug/m3\n", ch, _pm_10[idx].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[%d] Atmospheric PM 10 = -\n", ch);
|
||||||
|
}
|
||||||
|
if (utils::isValidPm(_pm_01_sp[idx].update.avg)) {
|
||||||
|
Serial.printf("[%d] Standard Particle PM 1.0 = %.2f ug/m3\n", ch, _pm_01_sp[idx].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[%d] Standard Particle PM 1.0 = -\n", ch);
|
||||||
|
}
|
||||||
|
if (utils::isValidPm(_pm_25_sp[idx].update.avg)) {
|
||||||
|
Serial.printf("[%d] Standard Particle PM 2.5 = %.2f ug/m3\n", ch, _pm_25_sp[idx].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[%d] Standard Particle PM 2.5 = -\n", ch);
|
||||||
|
}
|
||||||
|
if (utils::isValidPm(_pm_10_sp[idx].update.avg)) {
|
||||||
|
Serial.printf("[%d] Standard Particle PM 10 = %.2f ug/m3\n", ch, _pm_10_sp[idx].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[%d] Standard Particle PM 10 = -\n", ch);
|
||||||
|
}
|
||||||
|
if (utils::isValidPm03Count(_pm_03_pc[idx].update.avg)) {
|
||||||
|
Serial.printf("[%d] Particle Count 0.3 = %.1f\n", ch, _pm_03_pc[idx].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[%d] Particle Count 0.3 = -\n", ch);
|
||||||
|
}
|
||||||
|
if (utils::isValidPm03Count(_pm_05_pc[idx].update.avg)) {
|
||||||
|
Serial.printf("[%d] Particle Count 0.5 = %.1f\n", ch, _pm_05_pc[idx].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[%d] Particle Count 0.5 = -\n", ch);
|
||||||
|
}
|
||||||
|
if (utils::isValidPm03Count(_pm_01_pc[idx].update.avg)) {
|
||||||
|
Serial.printf("[%d] Particle Count 1.0 = %.1f\n", ch, _pm_01_pc[idx].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[%d] Particle Count 1.0 = -\n", ch);
|
||||||
|
}
|
||||||
|
if (utils::isValidPm03Count(_pm_25_pc[idx].update.avg)) {
|
||||||
|
Serial.printf("[%d] Particle Count 2.5 = %.1f\n", ch, _pm_25_pc[idx].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[%d] Particle Count 2.5 = -\n", ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_pm_5_pc[idx].listValues.empty() == false) {
|
||||||
|
if (utils::isValidPm03Count(_pm_5_pc[idx].update.avg)) {
|
||||||
|
Serial.printf("[%d] Particle Count 5.0 = %.1f\n", ch, _pm_5_pc[idx].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[%d] Particle Count 5.0 = -\n", ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_pm_10_pc[idx].listValues.empty() == false) {
|
||||||
|
if (utils::isValidPm03Count(_pm_10_pc[idx].update.avg)) {
|
||||||
|
Serial.printf("[%d] Particle Count 10 = %.1f\n", ch, _pm_10_pc[idx].update.avg);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[%d] Particle Count 10 = -\n", ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Measurements::validateChannel(int ch) {
|
void Measurements::validateChannel(int ch) {
|
||||||
if (ch != 1 && ch != 2) {
|
if (ch != 1 && ch != 2) {
|
||||||
Serial.printf("ERROR! Channel %d is undefined. Only channel 1 or 2 is the optional value!", ch);
|
Serial.printf("ERROR! Channel %d is undefined. Only channel 1 or 2 is the optional value!", ch);
|
||||||
@@ -804,16 +955,16 @@ std::string Measurements::buildMeasuresPayload(Measures &mc) {
|
|||||||
|
|
||||||
oss << ",";
|
oss << ",";
|
||||||
|
|
||||||
// NOx
|
// TVOC
|
||||||
if (utils::isValidNOx(mc.nox)) {
|
if (utils::isValidVOC(mc.tvoc)) {
|
||||||
oss << std::round(mc.nox);
|
oss << std::round(mc.tvoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
oss << ",";
|
oss << ",";
|
||||||
|
|
||||||
// TVOC
|
// NOx
|
||||||
if (utils::isValidVOC(mc.tvoc)) {
|
if (utils::isValidNOx(mc.nox)) {
|
||||||
oss << std::round(mc.tvoc);
|
oss << std::round(mc.nox);
|
||||||
}
|
}
|
||||||
|
|
||||||
oss << ",";
|
oss << ",";
|
||||||
@@ -827,9 +978,11 @@ std::string Measurements::buildMeasuresPayload(Measures &mc) {
|
|||||||
oss << std::round(mc.pm_03_pc[1]);
|
oss << std::round(mc.pm_03_pc[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// char datapoint[128] = {0};
|
oss << ",";
|
||||||
// snprintf(datapoint, 128, "%d,%.0f,%.0f,%.0f,%.0f,%.0f,%d,%d,%d", co2, temp * 10,
|
|
||||||
// hum * 10, pm01 * 10, pm25 * 10, pm10 * 10, tvoc, nox, pm003Count);
|
if (mc.signal < 0) {
|
||||||
|
oss << mc.signal;
|
||||||
|
}
|
||||||
|
|
||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ public:
|
|||||||
PM10_PC, // Particle 10 count
|
PM10_PC, // Particle 10 count
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void printCurrentAverage();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set each MeasurementType maximum period length for moving average
|
* @brief Set each MeasurementType maximum period length for moving average
|
||||||
*
|
*
|
||||||
@@ -258,6 +260,8 @@ private:
|
|||||||
*/
|
*/
|
||||||
void validateChannel(int ch);
|
void validateChannel(int ch);
|
||||||
|
|
||||||
|
void printCurrentPMAverage(int ch);
|
||||||
|
|
||||||
JSONVar buildOutdoor(bool localServer, AgFirmwareMode fwMode);
|
JSONVar buildOutdoor(bool localServer, AgFirmwareMode fwMode);
|
||||||
JSONVar buildIndoor(bool localServer);
|
JSONVar buildIndoor(bool localServer);
|
||||||
JSONVar buildPMS(int ch, bool allCh, bool withTempHum, bool compensate);
|
JSONVar buildPMS(int ch, bool allCh, bool withTempHum, bool compensate);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
#include "Main/utils.h"
|
#include "Main/utils.h"
|
||||||
|
|
||||||
#ifndef GIT_VERSION
|
#ifndef GIT_VERSION
|
||||||
#define GIT_VERSION "3.3.0-snap"
|
#define GIT_VERSION "3.3.9-snap"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Submodule src/Libraries/airgradient-client updated: 774f8b70e6...c23bb2ceac
Submodule src/Libraries/airgradient-ota updated: 24d2dc537c...7b103e9073
@@ -260,13 +260,14 @@ bool MqttClient::connect(String id) {
|
|||||||
connected = false;
|
connected = false;
|
||||||
if (user.isEmpty()) {
|
if (user.isEmpty()) {
|
||||||
logInfo("Connect without auth");
|
logInfo("Connect without auth");
|
||||||
if(CLIENT()->connect(id.c_str())) {
|
connected = CLIENT()->connect(id.c_str());
|
||||||
connected = true;
|
} else {
|
||||||
|
logInfo("Connect with auth");
|
||||||
|
connected = CLIENT()->connect(id.c_str(), user.c_str(), password.c_str());
|
||||||
}
|
}
|
||||||
return connected;
|
return connected;
|
||||||
}
|
}
|
||||||
return CLIENT()->connect(id.c_str(), user.c_str(), password.c_str());
|
|
||||||
}
|
|
||||||
void MqttClient::handle(void) {
|
void MqttClient::handle(void) {
|
||||||
if (isBegin == false) {
|
if (isBegin == false) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -835,3 +835,13 @@ bool S8::setAbcPeriod(int hours) {
|
|||||||
* @return int Hour
|
* @return int Hour
|
||||||
*/
|
*/
|
||||||
int S8::getAbcPeriod(void) { return getCalibPeriodABC(); }
|
int S8::getAbcPeriod(void) { return getCalibPeriodABC(); }
|
||||||
|
|
||||||
|
|
||||||
|
void S8::printInformation(void) {
|
||||||
|
Serial.print("S8 type ID: 0x");
|
||||||
|
Serial.println(getSensorTypeId(), HEX);
|
||||||
|
Serial.print("S8 serial number: 0x");
|
||||||
|
Serial.println(getSensorId(), HEX);
|
||||||
|
Serial.print("S8 memory map version: 0x");
|
||||||
|
Serial.println(getMemoryMapVersion(), HEX);
|
||||||
|
}
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ public:
|
|||||||
bool isBaseLineCalibrationDone(void);
|
bool isBaseLineCalibrationDone(void);
|
||||||
bool setAbcPeriod(int hours);
|
bool setAbcPeriod(int hours);
|
||||||
int getAbcPeriod(void);
|
int getAbcPeriod(void);
|
||||||
|
void printInformation(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Variables */
|
/** Variables */
|
||||||
|
|||||||
@@ -131,6 +131,22 @@ void Sgp41::handle(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
void Sgp41::pause() {
|
||||||
|
onPause = true;
|
||||||
|
Serial.println("Pausing SGP41 handler task");
|
||||||
|
// Set latest value to invalid
|
||||||
|
tvocRaw = utils::getInvalidVOC();
|
||||||
|
tvoc = utils::getInvalidVOC();
|
||||||
|
noxRaw = utils::getInvalidNOx();
|
||||||
|
nox = utils::getInvalidNOx();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sgp41::resume() {
|
||||||
|
onPause = false;
|
||||||
|
Serial.println("Resuming SGP41 handler task");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Handle the sensor conditioning and run time udpate value, This method
|
* @brief Handle the sensor conditioning and run time udpate value, This method
|
||||||
* must not call, it's called on private task
|
* must not call, it's called on private task
|
||||||
@@ -152,6 +168,11 @@ void Sgp41::_handle(void) {
|
|||||||
uint16_t srawVoc, srawNox;
|
uint16_t srawVoc, srawNox;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
|
||||||
|
if (onPause) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (getRawSignal(srawVoc, srawNox)) {
|
if (getRawSignal(srawVoc, srawNox)) {
|
||||||
tvocRaw = srawVoc;
|
tvocRaw = srawVoc;
|
||||||
noxRaw = srawNox;
|
noxRaw = srawNox;
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ public:
|
|||||||
bool begin(TwoWire &wire, Stream &stream);
|
bool begin(TwoWire &wire, Stream &stream);
|
||||||
void handle(void);
|
void handle(void);
|
||||||
#else
|
#else
|
||||||
|
/* pause _handle task to read sensor */
|
||||||
|
void pause();
|
||||||
|
/* resume _handle task to read sensor */
|
||||||
|
void resume();
|
||||||
void _handle(void);
|
void _handle(void);
|
||||||
#endif
|
#endif
|
||||||
void end(void);
|
void end(void);
|
||||||
@@ -32,6 +36,7 @@ public:
|
|||||||
int getTvocLearningOffset(void);
|
int getTvocLearningOffset(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool onPause = false;
|
||||||
bool onConditioning = true;
|
bool onConditioning = true;
|
||||||
bool ready = false;
|
bool ready = false;
|
||||||
bool _isBegin = false;
|
bool _isBegin = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user