mirror of
https://github.com/airgradienthq/arduino.git
synced 2025-12-16 10:38:56 +01:00
Compare commits
44 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 | ||
|
|
21b9ddb2ed | ||
|
|
1ee05da5d1 | ||
|
|
626a2240fa |
31
.github/workflows/check.yml
vendored
31
.github/workflows/check.yml
vendored
@@ -1,5 +1,36 @@
|
||||
on: [push, pull_request]
|
||||
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:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
25
README.md
25
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.
|
||||
|
||||
### 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
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -16,15 +16,15 @@ Arduino IDE version 2.x ([download](https://www.arduino.cc/en/software))
|
||||
|
||||
#### Version < 3.2.0
|
||||
|
||||
Using library manager install the latest version (Tools ➝ Manage Libraries... ➝ search for `"airgradient"`)
|
||||
Using library manager install the latest version (Tools ➝ Manage Libraries... ➝ search for `"airgradient"`)
|
||||
|
||||

|
||||
|
||||
#### Version >= 3.3.0
|
||||
|
||||
- From your terminal, go to Arduino libraries folder (windows and mac: `Documents/Arduino/libraries` or linux: `~/Arduino/Libraries`).
|
||||
- With **git** cli, execute this command `git clone --recursive https://github.com/airgradienthq/arduino.git AirGradient_Air_Quality_Sensor`
|
||||
- Restart Arduino IDE
|
||||
- With **git** cli, execute this command `git clone --recursive https://github.com/airgradienthq/arduino.git AirGradient_Air_Quality_Sensor`
|
||||
- Restart Arduino IDE
|
||||
|
||||
3. On tools tab, follow settings below
|
||||
|
||||
@@ -57,7 +57,7 @@ Upload Speed ➝ 921600
|
||||
|
||||

|
||||
|
||||
3. Install AirGradient library on library manager using the latest version (Tools ➝ Manage Libraries... ➝ search for `"airgradient"`)
|
||||
3. Install AirGradient library on library manager using the latest version (Tools ➝ Manage Libraries... ➝ search for `"airgradient"`)
|
||||
|
||||

|
||||
|
||||
@@ -65,7 +65,7 @@ Upload Speed ➝ 921600
|
||||
|
||||

|
||||
|
||||
5. Open sketch to compile (File ➝ Examples ➝ AirGradient Air Quality Sensor ➝ `<Model Option>`). Depends on the DIY model, either `BASIC`, `DiyProIndoorV3_3` and `DiyProIndoorV4_2`
|
||||
5. Open sketch to compile (File ➝ Examples ➝ AirGradient Air Quality Sensor ➝ `<Model Option>`). Depends on the DIY model, either `BASIC`, `DiyProIndoorV3_3` and `DiyProIndoorV4_2`
|
||||
6. Compile
|
||||
|
||||

|
||||
@@ -78,19 +78,19 @@ ModuleNotFoundError: No module named ‘serial’
|
||||
|
||||

|
||||
|
||||
Make sure python pyserial module installed globally in the environment by executing:
|
||||
Make sure python pyserial module installed globally in the environment by executing:
|
||||
|
||||
`$ sudo apt install -y python3-pyserial`
|
||||
|
||||
or
|
||||
or
|
||||
|
||||
`$ pip install pyserial`
|
||||
`$ pip install pyserial`
|
||||
|
||||
Choose based on how python installed on your machine. But most user, using `apt` is better.
|
||||
|
||||
## 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`
|
||||
|
||||
@@ -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 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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Local Server API
|
||||
|
||||
From [firmware version 3.0.10](firmwares) onwards, the AirGradient ONE and Open Air monitors have below API available.
|
||||
From [firmware version 3.0.10](firmwares) onwards, the AirGradient ONE and Open Air monitors have below API available.
|
||||
|
||||
### Discovery
|
||||
|
||||
@@ -20,7 +20,7 @@ http://airgradient_ecda3b1eaaaf.local/measures/current
|
||||
“ecda3b1eaaaf” being the serial number of your monitor.
|
||||
|
||||
You get the following response:
|
||||
```json
|
||||
```json
|
||||
{
|
||||
"wifi": -46,
|
||||
"serialno": "ecda3b1eaaaf",
|
||||
@@ -84,7 +84,7 @@ Compensated values apply correction algorithms to make the sensor values more ac
|
||||
|
||||
"/config" path returns the current configuration of the monitor.
|
||||
|
||||
```json
|
||||
```json
|
||||
{
|
||||
"country": "TH",
|
||||
"pmStandard": "ugm3",
|
||||
@@ -106,7 +106,6 @@ Compensated values apply correction algorithms to make the sensor values more ac
|
||||
"pm02": {
|
||||
"correctionAlgorithm": "epa_2021",
|
||||
"slr": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,22 +118,22 @@ Configuration parameters can be changed with a PUT request to the monitor, e.g.
|
||||
Example to force CO2 calibration
|
||||
|
||||
```bash
|
||||
curl -X PUT -H "Content-Type: application/json" -d '{"co2CalibrationRequested":true}' http://airgradient_84fce612eff4.local/config
|
||||
curl -X PUT -H "Content-Type: application/json" -d '{"co2CalibrationRequested":true}' http://airgradient_84fce612eff4.local/config
|
||||
```
|
||||
|
||||
Example to set monitor to Celsius
|
||||
|
||||
```bash
|
||||
curl -X PUT -H "Content-Type: application/json" -d '{"temperatureUnit":"c"}' http://airgradient_84fce612eff4.local/config
|
||||
curl -X PUT -H "Content-Type: application/json" -d '{"temperatureUnit":"c"}' http://airgradient_84fce612eff4.local/config
|
||||
```
|
||||
|
||||
If you use command prompt on Windows, you need to escape the quotes:
|
||||
|
||||
|
||||
``` -d "{\"param\":\"value\"}" ```
|
||||
|
||||
### Avoiding Conflicts with Configuration on AirGradient Server
|
||||
|
||||
If the monitor is set up on the AirGradient dashboard, it will also receive 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.
|
||||
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)
|
||||
|
||||
@@ -205,34 +204,34 @@ Example correction configuration:
|
||||
Field Name: `pm02`
|
||||
|
||||
| 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_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**:
|
||||
**NOTES**:
|
||||
|
||||
- Set `useEpa2021` to `true` if want to apply EPA 2021 correction factors on top of SLR correction value, otherwise `false`
|
||||
- `intercept` and `scalingFactor` values can be obtained from [this article](https://www.airgradient.com/blog/low-readings-from-pms5003/)
|
||||
- If `configurationControl` is set to `local` (eg. when using Home Assistant), correction need to be set manually, see examples below
|
||||
- If `configurationControl` is set to `local` (eg. when using Home Assistant), correction need to be set manually, see examples below
|
||||
|
||||
**Examples**:
|
||||
|
||||
- PMS5003_20231030
|
||||
- PMS5003_20231030
|
||||
|
||||
```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":true}}}}'
|
||||
```
|
||||
|
||||
- PMS5003_20231218
|
||||
- PMS5003_20231218
|
||||
|
||||
```bash
|
||||
curl --location -X PUT 'http://airgradient_84fce612eff4.local/config' --header 'Content-Type: application/json' --data '{"corrections":{"pm02":{"correctionAlgorithm":"slr_PMS5003_20231218","slr":{"intercept":0,"scalingFactor":0.03525,"useEpa2021":true}}}}'
|
||||
```
|
||||
|
||||
- PMS5003_20240104
|
||||
- PMS5003_20240104
|
||||
|
||||
```bash
|
||||
curl --location -X PUT 'http://airgradient_84fce612eff4.local/config' --header 'Content-Type: application/json' --data '{"corrections":{"pm02":{"correctionAlgorithm":"slr_PMS5003_20240104","slr":{"intercept":0,"scalingFactor":0.02896,"useEpa2021":true}}}}'
|
||||
@@ -245,10 +244,10 @@ Field Name:
|
||||
- Humidity: `rhum`
|
||||
|
||||
| Algorithm | Value | Description | SLR required |
|
||||
|------------|-------------|------|---------|
|
||||
|------------|-------------|------|---------|
|
||||
| Raw | `"none"` | No correction (default) | No |
|
||||
| AirGradient Standard Correction | `"ag_pms5003t_2024"` | Using standard airgradient correction (for outdoor monitor)| No |
|
||||
| Custom | `"custom"` | custom corrections constant, set `intercept` and `scalingFactor` manually | Yes |
|
||||
| Custom | `"custom"` | custom corrections constant, set `intercept` and `scalingFactor` manually | Yes |
|
||||
|
||||
*Table above apply for both Temperature and Humidity*
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## OTA Updates
|
||||
|
||||
From [firmware version 3.1.1](https://github.com/airgradienthq/arduino/tree/3.1.1) onwards, the AirGradient ONE and Open Air monitors support over the air (OTA) updates.
|
||||
From [firmware version 3.1.1](https://github.com/airgradienthq/arduino/tree/3.1.1) onwards, the AirGradient ONE and Open Air monitors support over the air (OTA) updates.
|
||||
|
||||
#### Mechanism
|
||||
|
||||
@@ -10,7 +10,7 @@ The device attempts to update to the latest version on startup and in regular in
|
||||
|
||||
http://hw.airgradient.com/sensors/{deviceId}/generic/os/firmware.bin?current_firmware={GIT_VERSION}
|
||||
|
||||
If does pass the version it is currently running on along to the server through URL parameter 'current_firmware'.
|
||||
If does pass the version it is currently running on along to the server through URL parameter 'current_firmware'.
|
||||
This allows the server to identify if the device is already running on the latest version or should update.
|
||||
|
||||
The following scenarios are possible
|
||||
|
||||
@@ -294,7 +294,7 @@ static bool sgp41Init(void) {
|
||||
configuration.hasSensorSGP = true;
|
||||
return true;
|
||||
} else {
|
||||
Serial.println("Init SGP41 failuire");
|
||||
Serial.println("Init SGP41 failure");
|
||||
configuration.hasSensorSGP = false;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -351,7 +351,7 @@ static bool sgp41Init(void) {
|
||||
configuration.hasSensorSGP = true;
|
||||
return true;
|
||||
} else {
|
||||
Serial.println("Init SGP41 failuire");
|
||||
Serial.println("Init SGP41 failure");
|
||||
configuration.hasSensorSGP = false;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -249,7 +249,7 @@ void loop() {
|
||||
configUpdateHandle();
|
||||
|
||||
localServer._handle();
|
||||
|
||||
|
||||
if (configuration.hasSensorSGP) {
|
||||
ag.sgp41.handle();
|
||||
}
|
||||
@@ -374,7 +374,7 @@ static bool sgp41Init(void) {
|
||||
configuration.hasSensorSGP = true;
|
||||
return true;
|
||||
} else {
|
||||
Serial.println("Init SGP41 failuire");
|
||||
Serial.println("Init SGP41 failure");
|
||||
configuration.hasSensorSGP = false;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -75,8 +75,8 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||
#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 6000 /** ms */
|
||||
#define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** 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 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
|
||||
@@ -123,7 +123,7 @@ static uint32_t factoryBtnPressTime = 0;
|
||||
static AgFirmwareMode fwMode = FW_MODE_I_9PSL;
|
||||
static bool ledBarButtonTest = false;
|
||||
static String fwNewVersion;
|
||||
static int lastCellSignalQuality = 99; // CSQ
|
||||
static int lastCellSignalQuality = 99; // CSQ
|
||||
|
||||
// Default value is 0, indicate its not started yet
|
||||
// In minutes
|
||||
@@ -136,7 +136,7 @@ static void boardInit(void);
|
||||
static void initializeNetwork();
|
||||
static void failedHandler(String msg);
|
||||
static void configurationUpdateSchedule(void);
|
||||
static void configUpdateHandle(void);
|
||||
static void configUpdateHandle(void);
|
||||
static void updateDisplayAndLedBar(void);
|
||||
static void updateTvoc(void);
|
||||
static void updatePm(void);
|
||||
@@ -157,7 +157,7 @@ static void displayExecuteOta(AirgradientOTA::OtaResult result, String msg, int
|
||||
static int calculateMaxPeriod(int updateInterval);
|
||||
static void setMeasurementMaxPeriod();
|
||||
static void newMeasurementCycle();
|
||||
static void restartIfCeClientIssueOverTwoHours();
|
||||
static void restartIfCeClientIssueOverTwoHours();
|
||||
static void networkSignalCheck();
|
||||
static void networkingTask(void *args);
|
||||
|
||||
@@ -180,7 +180,7 @@ void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(100); /** For bester show log */
|
||||
|
||||
// Enable cullular module power board
|
||||
// Enable cullular module power board
|
||||
pinMode(GPIO_EXPANSION_CARD_POWER, OUTPUT);
|
||||
digitalWrite(GPIO_EXPANSION_CARD_POWER, HIGH);
|
||||
|
||||
@@ -193,6 +193,7 @@ void setup() {
|
||||
|
||||
/** Initialize local configure */
|
||||
configuration.begin();
|
||||
configuration.setConfigurationUpdatedCallback(configUpdateHandle);
|
||||
|
||||
/** Init I2C */
|
||||
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
|
||||
@@ -270,6 +271,7 @@ void setup() {
|
||||
|
||||
Serial.println("Display brightness: " + String(configuration.getDisplayBrightness()));
|
||||
oledDisplay.setBrightness(configuration.getDisplayBrightness());
|
||||
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
||||
}
|
||||
|
||||
|
||||
@@ -282,7 +284,7 @@ void setup() {
|
||||
// Queue now only applied for cellular
|
||||
// Allocate queue memory to avoid always reallocation
|
||||
measurementCycleQueue.reserve(RESERVED_MEASUREMENT_CYCLE_CAPACITY);
|
||||
// Initialize mutex to access mesurementCycleQueue
|
||||
// Initialize mutex to access mesurementCycleQueue
|
||||
mutexMeasurementCycleQueue = xSemaphoreCreateMutex();
|
||||
}
|
||||
|
||||
@@ -310,7 +312,7 @@ void setup() {
|
||||
void loop() {
|
||||
if (networkOption == UseCellular) {
|
||||
// Check if cellular client not ready until certain time
|
||||
// Redundant check in both task to make sure its executed
|
||||
// Redundant check in both task to make sure its executed
|
||||
restartIfCeClientIssueOverTwoHours();
|
||||
}
|
||||
|
||||
@@ -369,8 +371,11 @@ void loop() {
|
||||
/** factory reset handle */
|
||||
factoryConfigReset();
|
||||
|
||||
/** check that local configuration changed then do some action */
|
||||
configUpdateHandle();
|
||||
if (configuration.isCommandRequested()) {
|
||||
// Each state machine already has an independent request command check
|
||||
stateMachine.executeCo2Calibration();
|
||||
stateMachine.executeLedBarTest();
|
||||
}
|
||||
}
|
||||
|
||||
static void co2Update(void) {
|
||||
@@ -554,7 +559,7 @@ static bool sgp41Init(void) {
|
||||
configuration.hasSensorSGP = true;
|
||||
return true;
|
||||
} else {
|
||||
Serial.println("Init SGP41 failuire");
|
||||
Serial.println("Init SGP41 failure");
|
||||
configuration.hasSensorSGP = false;
|
||||
}
|
||||
return false;
|
||||
@@ -570,7 +575,7 @@ void checkForFirmwareUpdate(void) {
|
||||
if (networkOption == UseWifi) {
|
||||
agOta = new AirgradientOTAWifi;
|
||||
} else {
|
||||
agOta = new AirgradientOTACellular(cellularCard);
|
||||
agOta = new AirgradientOTACellular(cellularCard, agClient->getICCID());
|
||||
}
|
||||
|
||||
// Indicate main task that firmware update is in progress
|
||||
@@ -720,7 +725,7 @@ static void sendDataToAg() {
|
||||
"task_led", 2048, NULL, 5, NULL);
|
||||
|
||||
delay(1500);
|
||||
|
||||
|
||||
// Build payload to check connection to airgradient server
|
||||
JSONVar root;
|
||||
root["wifi"] = wifiConnector.RSSI();
|
||||
@@ -947,6 +952,8 @@ static void boardInit(void) {
|
||||
} else {
|
||||
Serial.println("Set S8 AbcDays failure");
|
||||
}
|
||||
|
||||
ag->s8.printInformation();
|
||||
}
|
||||
|
||||
localServer.setFwMode(fwMode);
|
||||
@@ -965,7 +972,7 @@ void initializeNetwork() {
|
||||
agSerial->init(GPIO_IIC_RESET);
|
||||
if (agSerial->open()) {
|
||||
Serial.println("Cellular module found");
|
||||
// Initialize cellular module and use cellular as agClient
|
||||
// Initialize cellular module and use cellular as agClient
|
||||
cellularCard = new CellularModuleA7672XX(agSerial, GPIO_POWER_MODULE_PIN);
|
||||
agClient = new AirgradientCellularClient(cellularCard);
|
||||
networkOption = UseCellular;
|
||||
@@ -979,7 +986,7 @@ void initializeNetwork() {
|
||||
}
|
||||
|
||||
if (networkOption == UseCellular) {
|
||||
// Enable serial stream debugging to check the AT command when doing registration
|
||||
// Enable serial stream debugging to check the AT command when doing registration
|
||||
agSerial->setDebug(true);
|
||||
}
|
||||
|
||||
@@ -1000,7 +1007,7 @@ void initializeNetwork() {
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
// Provide openmetrics to have access to last transmission result
|
||||
// Provide openmetrics to have access to last transmission result
|
||||
openMetrics.setAirgradientClient(agClient);
|
||||
|
||||
if (networkOption == UseCellular) {
|
||||
@@ -1013,7 +1020,7 @@ void initializeNetwork() {
|
||||
Serial.println("Cannot initiate wifi connection");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!wifiConnector.isConnected()) {
|
||||
Serial.println("Failed connect to WiFi");
|
||||
if (wifiConnector.isConfigurePorttalTimeout()) {
|
||||
@@ -1022,26 +1029,33 @@ void initializeNetwork() {
|
||||
oledDisplay.setText("", "", "");
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
|
||||
// Directly return because the rest of the function applied if wifi is connect only
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Initiate local network configuration
|
||||
mdnsInit();
|
||||
localServer.begin();
|
||||
// Apply mqtt connection if configured
|
||||
initMqtt();
|
||||
|
||||
|
||||
// Ignore the rest if cloud connection to AirGradient is disabled
|
||||
if (configuration.isCloudConnectionDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send data for the first time to AG server at boot
|
||||
sendDataToAg();
|
||||
// Send data for the first time to AG server at boot only if postDataToAirgradient is enabled
|
||||
if (configuration.isPostDataToAirGradient()) {
|
||||
sendDataToAg();
|
||||
}
|
||||
}
|
||||
|
||||
// Skip fetch configuration if configuration control is set to "local" only
|
||||
if (configuration.getConfigurationControl() == ConfigurationControl::ConfigurationControlLocal) {
|
||||
ledBarEnabledUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string config = agClient->httpFetchConfig();
|
||||
configSchedule.update();
|
||||
@@ -1073,8 +1087,8 @@ static void configurationUpdateSchedule(void) {
|
||||
}
|
||||
|
||||
std::string config = agClient->httpFetchConfig();
|
||||
if (agClient->isLastFetchConfigSucceed() && configuration.parse(config.c_str(), false)) {
|
||||
configUpdateHandle();
|
||||
if (agClient->isLastFetchConfigSucceed()) {
|
||||
configuration.parse(config.c_str(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1083,8 +1097,6 @@ static void configUpdateHandle() {
|
||||
return;
|
||||
}
|
||||
|
||||
stateMachine.executeCo2Calibration();
|
||||
|
||||
String mqttUri = configuration.getMqttBrokerUri();
|
||||
if (mqttClient.isCurrentUri(mqttUri) == false) {
|
||||
mqttClient.end();
|
||||
@@ -1156,11 +1168,6 @@ static void configUpdateHandle() {
|
||||
if (configuration.isDisplayBrightnessChanged()) {
|
||||
oledDisplay.setBrightness(configuration.getDisplayBrightness());
|
||||
}
|
||||
|
||||
stateMachine.executeLedBarTest();
|
||||
}
|
||||
else if(ag->isOpenAir()) {
|
||||
stateMachine.executeLedBarTest();
|
||||
}
|
||||
|
||||
// Update display and led bar notification based on updated configuration
|
||||
@@ -1197,7 +1204,7 @@ static void updateDisplayAndLedBar(void) {
|
||||
}
|
||||
|
||||
if (configuration.isCloudConnectionDisabled()) {
|
||||
// Ignore API related check since cloud is disabled
|
||||
// Ignore API related check since cloud is disabled
|
||||
stateMachine.displayHandle(AgStateMachineNormal);
|
||||
stateMachine.handleLeds(AgStateMachineNormal);
|
||||
return;
|
||||
@@ -1390,7 +1397,7 @@ void postUsingWifi() {
|
||||
void postUsingCellular(bool forcePost) {
|
||||
// Aquire queue mutex to get queue size
|
||||
xSemaphoreTake(mutexMeasurementCycleQueue, portMAX_DELAY);
|
||||
|
||||
|
||||
// Make sure measurement cycle available
|
||||
int queueSize = measurementCycleQueue.size();
|
||||
if (queueSize == 0) {
|
||||
@@ -1400,7 +1407,7 @@ void postUsingCellular(bool forcePost) {
|
||||
}
|
||||
|
||||
// Check queue size if its ready to transmit
|
||||
// It is ready if size is divisible by 3
|
||||
// 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);
|
||||
@@ -1421,20 +1428,24 @@ void postUsingCellular(bool forcePost) {
|
||||
|
||||
// Attempt to send
|
||||
if (agClient->httpPostMeasures(payload) == false) {
|
||||
// Consider network has a problem, retry in next schedule
|
||||
// Consider network has a problem, retry in next schedule
|
||||
Serial.println("Post measures failed, retry in next schedule");
|
||||
return;
|
||||
}
|
||||
|
||||
// Post success, remove the data that previously sent from queue
|
||||
xSemaphoreTake(mutexMeasurementCycleQueue, portMAX_DELAY);
|
||||
measurementCycleQueue.erase(measurementCycleQueue.begin(),
|
||||
measurementCycleQueue.begin() + queueSize);
|
||||
|
||||
if (measurementCycleQueue.capacity() > RESERVED_MEASUREMENT_CYCLE_CAPACITY) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1518,7 +1529,7 @@ void setMeasurementMaxPeriod() {
|
||||
|
||||
int calculateMaxPeriod(int updateInterval) {
|
||||
// 0.8 is 80% reduced interval for max period
|
||||
// NOTE: Both network option use the same measurement interval
|
||||
// NOTE: Both network option use the same measurement interval
|
||||
return (WIFI_MEASUREMENT_INTERVAL - (WIFI_MEASUREMENT_INTERVAL * 0.8)) / updateInterval;
|
||||
}
|
||||
|
||||
@@ -1535,7 +1546,7 @@ void networkSignalCheck() {
|
||||
}
|
||||
|
||||
// Save last signal quality
|
||||
lastCellSignalQuality = result.data;
|
||||
lastCellSignalQuality = result.data;
|
||||
|
||||
if (result.data == 99) {
|
||||
// 99 indicate cellular not attached to network
|
||||
@@ -1548,7 +1559,7 @@ void networkSignalCheck() {
|
||||
}
|
||||
|
||||
/**
|
||||
* If in 2 hours cellular client still not ready, then restart system
|
||||
* If in 2 hours cellular client still not ready, then restart system
|
||||
*/
|
||||
void restartIfCeClientIssueOverTwoHours() {
|
||||
if (agCeClientProblemDetectedTime > 0 &&
|
||||
@@ -1613,10 +1624,10 @@ void networkingTask(void *args) {
|
||||
}
|
||||
|
||||
// Enable at command debug
|
||||
agSerial->setDebug(true);
|
||||
agSerial->setDebug(true);
|
||||
|
||||
// Check if cellular client not ready until certain time
|
||||
// Redundant check in both task to make sure its executed
|
||||
// Redundant check in both task to make sure its executed
|
||||
restartIfCeClientIssueOverTwoHours();
|
||||
|
||||
// Power cycling cellular module due to network issues for more than 1 hour
|
||||
@@ -1655,11 +1666,11 @@ void networkingTask(void *args) {
|
||||
|
||||
// Run scheduler
|
||||
networkSignalCheckSchedule.run();
|
||||
configSchedule.run();
|
||||
transmissionSchedule.run();
|
||||
configSchedule.run();
|
||||
checkForUpdateSchedule.run();
|
||||
|
||||
delay(1000);
|
||||
delay(50);
|
||||
}
|
||||
|
||||
vTaskDelete(handleNetworkTask);
|
||||
@@ -1667,14 +1678,14 @@ void networkingTask(void *args) {
|
||||
|
||||
void newMeasurementCycle() {
|
||||
if (xSemaphoreTake(mutexMeasurementCycleQueue, portMAX_DELAY) == pdTRUE) {
|
||||
// Make sure queue not overflow
|
||||
// Make sure queue not overflow
|
||||
if (measurementCycleQueue.size() >= MAXIMUM_MEASUREMENT_CYCLE_QUEUE) {
|
||||
// Remove the oldest data from queue if queue reach max
|
||||
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);
|
||||
|
||||
@@ -6,8 +6,8 @@ OpenMetrics::OpenMetrics(Measurements &measure, Configuration &config,
|
||||
|
||||
OpenMetrics::~OpenMetrics() {}
|
||||
|
||||
void OpenMetrics::setAirGradient(AirGradient *ag) {
|
||||
this->ag = ag;
|
||||
void OpenMetrics::setAirGradient(AirGradient *ag) {
|
||||
this->ag = ag;
|
||||
}
|
||||
|
||||
void OpenMetrics::setAirgradientClient(AirgradientClient *client) {
|
||||
@@ -202,14 +202,14 @@ String OpenMetrics::getPayload(void) {
|
||||
}
|
||||
if (utils::isValidNOx(nox)) {
|
||||
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",
|
||||
"gauge");
|
||||
add_metric_point("", String(nox));
|
||||
}
|
||||
if (utils::isValidNOx(noxRaw)) {
|
||||
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",
|
||||
"gauge");
|
||||
add_metric_point("", String(noxRaw));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name=AirGradient Air Quality Sensor
|
||||
version=3.3.7
|
||||
version=3.3.9
|
||||
author=AirGradient <support@airgradient.com>
|
||||
maintainer=AirGradient <support@airgradient.com>
|
||||
sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display.
|
||||
|
||||
@@ -15,7 +15,7 @@ framework = arduino
|
||||
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
|
||||
monitor_speed = 115200
|
||||
lib_deps =
|
||||
lib_deps =
|
||||
aglib=symlink://../arduino
|
||||
EEPROM
|
||||
WebServer
|
||||
@@ -32,7 +32,7 @@ platform = espressif8266
|
||||
board = d1_mini
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
lib_deps =
|
||||
lib_deps =
|
||||
aglib=symlink://../arduino
|
||||
EEPROM
|
||||
ESP8266HTTPClient
|
||||
|
||||
@@ -47,7 +47,7 @@ bool AgApiClient::fetchServerConfiguration(void) {
|
||||
}
|
||||
#else
|
||||
HTTPClient client;
|
||||
client.setConnectTimeout(timeoutMs); // Set timeout when establishing connection to server
|
||||
client.setConnectTimeout(timeoutMs); // Set timeout when establishing connection to server
|
||||
client.setTimeout(timeoutMs); // Timeout when waiting for response from AG server
|
||||
if (apiRootChanged) {
|
||||
// If apiRoot is changed, assume not using https
|
||||
@@ -114,7 +114,7 @@ bool AgApiClient::postToServer(String data) {
|
||||
}
|
||||
#else
|
||||
HTTPClient client;
|
||||
client.setConnectTimeout(timeoutMs); // Set timeout when establishing connection to server
|
||||
client.setConnectTimeout(timeoutMs); // Set timeout when establishing connection to server
|
||||
client.setTimeout(timeoutMs); // Timeout when waiting for response from AG server
|
||||
if (apiRootChanged) {
|
||||
// If apiRoot is changed, assume not using https
|
||||
@@ -185,13 +185,13 @@ void AgApiClient::setAirGradient(AirGradient *ag) { this->ag = ag; }
|
||||
|
||||
/**
|
||||
* @brief Send the package to check the connection with cloud
|
||||
*
|
||||
*
|
||||
* @param rssi WiFi RSSI
|
||||
* @param bootCount Boot count
|
||||
* @return true Success
|
||||
* @return false Failure
|
||||
*/
|
||||
bool AgApiClient::sendPing(int rssi, int bootCount) {
|
||||
bool AgApiClient::sendPing(int rssi, int bootCount) {
|
||||
JSONVar root;
|
||||
root["wifi"] = rssi;
|
||||
root["boot"] = bootCount;
|
||||
|
||||
@@ -114,7 +114,7 @@ PMCorrectionAlgorithm Configuration::matchPmAlgorithm(String algorithm) {
|
||||
|
||||
const size_t enumSize = COR_ALGO_PM_SLR_CUSTOM + 1; // Get the actual size of the enum
|
||||
PMCorrectionAlgorithm result = COR_ALGO_PM_UNKNOWN;;
|
||||
|
||||
|
||||
// Loop through enum values
|
||||
for (size_t enumVal = 0; enumVal < enumSize; enumVal++) {
|
||||
if (algorithm == PM_CORRECTION_ALGORITHM_NAMES[enumVal]) {
|
||||
@@ -122,7 +122,7 @@ PMCorrectionAlgorithm Configuration::matchPmAlgorithm(String algorithm) {
|
||||
}
|
||||
}
|
||||
|
||||
// If string not match from enum, check if correctionAlgorithm is one of the PM batch corrections
|
||||
// If string not match from enum, check if correctionAlgorithm is one of the PM batch corrections
|
||||
if (result == COR_ALGO_PM_UNKNOWN) {
|
||||
// Check the substring "slr_PMS5003_xxxxxxxx"
|
||||
if (algorithm.substring(0, 11) == "slr_PMS5003") {
|
||||
@@ -456,6 +456,10 @@ bool Configuration::begin(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Configuration::setConfigurationUpdatedCallback(ConfigurationUpdatedCallback_t callback) {
|
||||
_callback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse JSON configura string to local configure
|
||||
*
|
||||
@@ -745,7 +749,7 @@ bool Configuration::parse(String data, bool isLocal) {
|
||||
}
|
||||
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)
|
||||
// then set its value to default (empty)
|
||||
jconfig[jprop_mqttBrokerUrl] = jprop_mqttBrokerUrl_default;
|
||||
}
|
||||
else {
|
||||
@@ -951,15 +955,18 @@ bool Configuration::parse(String data, bool isLocal) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (ledBarTestRequested || co2CalibrationRequested) {
|
||||
commandRequested = true;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
updated = true;
|
||||
saveConfig();
|
||||
printConfig();
|
||||
} else {
|
||||
if (ledBarTestRequested || co2CalibrationRequested) {
|
||||
updated = true;
|
||||
}
|
||||
_callback();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1066,7 +1073,7 @@ String Configuration::getMqttBrokerUri(void) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get HTTP domain for post measures and get configuration
|
||||
* @brief Get HTTP domain for post measures and get configuration
|
||||
*
|
||||
* @return String http domain, might be empty string
|
||||
*/
|
||||
@@ -1159,6 +1166,12 @@ bool Configuration::isUpdated(void) {
|
||||
return updated;
|
||||
}
|
||||
|
||||
bool Configuration::isCommandRequested(void) {
|
||||
bool oldState = this->commandRequested;
|
||||
this->commandRequested = false;
|
||||
return oldState;
|
||||
}
|
||||
|
||||
String Configuration::jsonTypeInvalidMessage(String name, String type) {
|
||||
return "'" + name + "' type is invalid, expecting '" + type + "'";
|
||||
}
|
||||
@@ -1549,13 +1562,13 @@ void Configuration::setDisableCloudConnection(bool disable) {
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
bool Configuration::isLedBarModeChanged(void) {
|
||||
bool Configuration::isLedBarModeChanged(void) {
|
||||
bool changed = _ledBarModeChanged;
|
||||
_ledBarModeChanged = false;
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool Configuration::isMonitorDisplayCompensatedValues(void) {
|
||||
bool Configuration::isMonitorDisplayCompensatedValues(void) {
|
||||
return jconfig[jprop_monitorDisplayCompensatedValues];
|
||||
}
|
||||
|
||||
@@ -1578,8 +1591,8 @@ bool Configuration::isPMCorrectionChanged(void) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if PM correction is enabled
|
||||
*
|
||||
* @brief Check if PM correction is enabled
|
||||
*
|
||||
* @return true if PM correction algorithm is not None, otherwise false
|
||||
*/
|
||||
bool Configuration::isPMCorrectionEnabled(void) {
|
||||
|
||||
@@ -28,6 +28,7 @@ private:
|
||||
bool co2CalibrationRequested;
|
||||
bool ledBarTestRequested;
|
||||
bool updated;
|
||||
bool commandRequested = false;
|
||||
String failedMessage;
|
||||
bool _noxLearnOffsetChanged;
|
||||
bool _tvocLearningOffsetChanged;
|
||||
@@ -70,6 +71,9 @@ public:
|
||||
bool hasSensorSGP = true;
|
||||
bool hasSensorSHT = true;
|
||||
|
||||
typedef void (*ConfigurationUpdatedCallback_t)();
|
||||
void setConfigurationUpdatedCallback(ConfigurationUpdatedCallback_t callback);
|
||||
|
||||
bool begin(void);
|
||||
bool parse(String data, bool isLocal);
|
||||
String toString(void);
|
||||
@@ -90,6 +94,7 @@ public:
|
||||
void reset(void);
|
||||
String getModel(void);
|
||||
bool isUpdated(void);
|
||||
bool isCommandRequested(void);
|
||||
String getFailedMesage(void);
|
||||
void setPostToAirGradient(bool enable);
|
||||
bool noxLearnOffsetChanged(void);
|
||||
@@ -116,6 +121,8 @@ public:
|
||||
PMCorrection getPMCorrection(void);
|
||||
TempHumCorrection getTempCorrection(void);
|
||||
TempHumCorrection getHumCorrection(void);
|
||||
private:
|
||||
ConfigurationUpdatedCallback_t _callback;
|
||||
};
|
||||
|
||||
#endif /** _AG_CONFIG_H_ */
|
||||
|
||||
@@ -8,8 +8,8 @@ AgSchedule::~AgSchedule() {}
|
||||
void AgSchedule::run(void) {
|
||||
uint32_t ms = (uint32_t)(millis() - count);
|
||||
if (ms >= period) {
|
||||
handler();
|
||||
count = millis();
|
||||
handler();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
#define RGB_COLOR_R 255, 0, 0 /** Red */
|
||||
#define RGB_COLOR_G 0, 255, 0 /** Green */
|
||||
#define RGB_COLOR_Y 255, 150, 0 /** Yellow */
|
||||
#define RGB_COLOR_O 255, 40, 0 /** Orange */
|
||||
#define RGB_COLOR_Y 255, 255, 0 /** Yellow */
|
||||
#define RGB_COLOR_O 255, 128, 0 /** Orange */
|
||||
#define RGB_COLOR_P 180, 0, 255 /** Purple */
|
||||
#define RGB_COLOR_CLEAR 0, 0, 0 /** No color */
|
||||
|
||||
|
||||
@@ -568,7 +568,7 @@ float Measurements::getAverage(MeasurementType type, int ch) {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return measurementAverage;
|
||||
return measurementAverage;
|
||||
}
|
||||
|
||||
String Measurements::pms5003FirmwareVersion(int fwCode) {
|
||||
@@ -656,59 +656,59 @@ void Measurements::printCurrentPMAverage(int ch) {
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
Serial.printf("[%d] Particle Count 5.0 = -\n", ch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -716,7 +716,7 @@ void Measurements::printCurrentPMAverage(int ch) {
|
||||
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");
|
||||
Serial.printf("[%d] Particle Count 10 = -\n", ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -895,7 +895,7 @@ std::string Measurements::buildMeasuresPayload(Measures &mc) {
|
||||
|
||||
oss << ",";
|
||||
|
||||
// Temperature
|
||||
// Temperature
|
||||
if (utils::isValidTemperature(mc.temperature[0]) && utils::isValidTemperature(mc.temperature[1])) {
|
||||
float temp = (mc.temperature[0] + mc.temperature[1]) / 2.0f;
|
||||
oss << std::round(temp * 10);
|
||||
|
||||
@@ -149,7 +149,7 @@ public:
|
||||
*
|
||||
* @param type measurement type that will be retrieve
|
||||
* @param ch target type value channel
|
||||
* @return moving average value of target measurements type
|
||||
* @return moving average value of target measurements type
|
||||
*/
|
||||
float getAverage(MeasurementType type, int ch = 1);
|
||||
|
||||
|
||||
@@ -358,7 +358,7 @@ bool WifiConnector::isConnected(void) { return WiFi.isConnected(); }
|
||||
* this method
|
||||
*
|
||||
*/
|
||||
void WifiConnector::reset(void) {
|
||||
void WifiConnector::reset(void) {
|
||||
if(this->wifi == NULL) {
|
||||
this->wifi = new WiFiManager();
|
||||
if(this->wifi == NULL){
|
||||
@@ -366,7 +366,7 @@ void WifiConnector::reset(void) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
WIFI()->resetSettings();
|
||||
WIFI()->resetSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -406,7 +406,7 @@ bool WifiConnector::isConfigurePorttalTimeout(void) { return connectorTimeout; }
|
||||
|
||||
/**
|
||||
* @brief Set wifi connect to default WiFi
|
||||
*
|
||||
*
|
||||
*/
|
||||
void WifiConnector::setDefault(void) {
|
||||
WiFi.begin("airgradient", "cleanair");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "AirGradient.h"
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#else
|
||||
#else
|
||||
#include "WiFi.h"
|
||||
#endif
|
||||
|
||||
@@ -57,7 +57,7 @@ String AirGradient::getBoardName(void) {
|
||||
|
||||
/**
|
||||
* @brief Board Type is ONE_INDOOR
|
||||
*
|
||||
*
|
||||
* @return true ONE_INDOOR
|
||||
* @return false Other
|
||||
*/
|
||||
@@ -65,15 +65,15 @@ bool AirGradient::isOne(void) {
|
||||
return boardType == BoardType::ONE_INDOOR;
|
||||
}
|
||||
|
||||
bool AirGradient::isOpenAir(void) {
|
||||
return boardType == BoardType::OPEN_AIR_OUTDOOR;
|
||||
bool AirGradient::isOpenAir(void) {
|
||||
return boardType == BoardType::OPEN_AIR_OUTDOOR;
|
||||
}
|
||||
|
||||
bool AirGradient::isPro4_2(void) {
|
||||
return boardType == BoardType::DIY_PRO_INDOOR_V4_2;
|
||||
}
|
||||
|
||||
bool AirGradient::isPro3_3(void) {
|
||||
bool AirGradient::isPro3_3(void) {
|
||||
return boardType == BoardType::DIY_PRO_INDOOR_V3_3;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "Main/utils.h"
|
||||
|
||||
#ifndef GIT_VERSION
|
||||
#define GIT_VERSION "3.3.7-snap"
|
||||
#define GIT_VERSION "3.3.9-snap"
|
||||
#endif
|
||||
|
||||
|
||||
@@ -177,9 +177,9 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Check that Airgradient object is OPEN_AIR
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool isOpenAir(void);
|
||||
|
||||
|
||||
Submodule src/Libraries/airgradient-client updated: a4ac14936e...c23bb2ceac
Submodule src/Libraries/airgradient-ota updated: c772392427...7b103e9073
@@ -27,7 +27,7 @@ enum BoardType {
|
||||
|
||||
/**
|
||||
* @brief Board definitions
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct BoardDef {
|
||||
/** Board Support CO2 SenseS8 */
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
/**
|
||||
* @brief The class define how to handle the LED
|
||||
*
|
||||
*
|
||||
*/
|
||||
class StatusLed {
|
||||
public:
|
||||
|
||||
@@ -260,13 +260,14 @@ bool MqttClient::connect(String id) {
|
||||
connected = false;
|
||||
if (user.isEmpty()) {
|
||||
logInfo("Connect without auth");
|
||||
if(CLIENT()->connect(id.c_str())) {
|
||||
connected = true;
|
||||
}
|
||||
return connected;
|
||||
connected = CLIENT()->connect(id.c_str());
|
||||
} else {
|
||||
logInfo("Connect with auth");
|
||||
connected = CLIENT()->connect(id.c_str(), user.c_str(), password.c_str());
|
||||
}
|
||||
return CLIENT()->connect(id.c_str(), user.c_str(), password.c_str());
|
||||
return connected;
|
||||
}
|
||||
|
||||
void MqttClient::handle(void) {
|
||||
if (isBegin == false) {
|
||||
return;
|
||||
|
||||
@@ -316,10 +316,10 @@ int PMSBase::pm25ToAQI(int pm02) {
|
||||
|
||||
|
||||
/**
|
||||
* @brief SLR correction for PM2.5
|
||||
*
|
||||
* @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
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
/** For PMS5003T*/
|
||||
int16_t getTemp(void);
|
||||
uint16_t getHum(void);
|
||||
uint8_t getFirmwareVersion(void);
|
||||
uint8_t getFirmwareVersion(void);
|
||||
uint8_t getErrorCode(void);
|
||||
|
||||
int pm25ToAQI(int pm02);
|
||||
|
||||
@@ -189,21 +189,21 @@ float PMS5003::compensate(float pm25, float humidity) { return pms.compensate(pm
|
||||
|
||||
/**
|
||||
* @brief Get sensor firmware version
|
||||
*
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int PMS5003::getFirmwareVersion(void) { return _ver; }
|
||||
|
||||
/**
|
||||
* @brief Get sensor error code
|
||||
*
|
||||
* @return uint8_t
|
||||
*
|
||||
* @return uint8_t
|
||||
*/
|
||||
uint8_t PMS5003::getErrorCode(void) { return pms.getErrorCode(); }
|
||||
|
||||
/**
|
||||
* @brief Is sensor connect with device
|
||||
*
|
||||
*
|
||||
* @return true Connected
|
||||
* @return false Removed
|
||||
*/
|
||||
@@ -255,14 +255,14 @@ void PMS5003::resetFailCount(void) {
|
||||
|
||||
/**
|
||||
* @brief Get number of fail count
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int PMS5003::getFailCount(void) { return pms.getFailCount(); }
|
||||
|
||||
/**
|
||||
* @brief Get number of fail count max
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int PMS5003::getFailCountMax(void) { return pms.getFailCountMax(); }
|
||||
|
||||
@@ -218,21 +218,21 @@ float PMS5003T::compensate(float pm25, float humidity) { return pms.compensate(p
|
||||
|
||||
/**
|
||||
* @brief Get module(s) firmware version
|
||||
*
|
||||
*
|
||||
* @return int Version code
|
||||
*/
|
||||
int PMS5003T::getFirmwareVersion(void) { return _ver; }
|
||||
|
||||
/**
|
||||
* @brief Get sensor error code
|
||||
*
|
||||
* @return uint8_t
|
||||
*
|
||||
* @return uint8_t
|
||||
*/
|
||||
uint8_t PMS5003T::getErrorCode(void) { return pms.getErrorCode(); }
|
||||
|
||||
/**
|
||||
* @brief Is sensor connect to device
|
||||
*
|
||||
*
|
||||
* @return true Connected
|
||||
* @return false Removed
|
||||
*/
|
||||
@@ -281,14 +281,14 @@ void PMS5003T::resetFailCount(void) {
|
||||
|
||||
/**
|
||||
* @brief Get fail count
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int PMS5003T::getFailCount(void) { return pms.getFailCount(); }
|
||||
|
||||
/**
|
||||
* @brief Get fail count max
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int PMS5003T::getFailCountMax(void) { return pms.getFailCountMax(); }
|
||||
|
||||
@@ -6,11 +6,11 @@ PMS5003TBase::~PMS5003TBase() {}
|
||||
|
||||
/**
|
||||
* @brief Compensate the temperature
|
||||
*
|
||||
*
|
||||
* Reference formula: https://www.airgradient.com/documentation/correction-algorithms/
|
||||
*
|
||||
* @param temp
|
||||
* @return * float
|
||||
*
|
||||
* @param temp
|
||||
* @return * float
|
||||
*/
|
||||
float PMS5003TBase::compensateTemp(float temp) {
|
||||
if (temp < 10.0f) {
|
||||
@@ -21,11 +21,11 @@ float PMS5003TBase::compensateTemp(float temp) {
|
||||
|
||||
/**
|
||||
* @brief Compensate the humidity
|
||||
*
|
||||
*
|
||||
* Reference formula: https://www.airgradient.com/documentation/correction-algorithms/
|
||||
*
|
||||
* @param temp
|
||||
* @return * float
|
||||
*
|
||||
* @param temp
|
||||
* @return * float
|
||||
*/
|
||||
float PMS5003TBase::compensateHum(float hum) {
|
||||
hum = hum * 1.259f + 7.34f;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
class PMS5003TBase
|
||||
{
|
||||
private:
|
||||
|
||||
|
||||
public:
|
||||
PMS5003TBase();
|
||||
~PMS5003TBase();
|
||||
|
||||
@@ -835,3 +835,13 @@ bool S8::setAbcPeriod(int hours) {
|
||||
* @return int Hour
|
||||
*/
|
||||
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 setAbcPeriod(int hours);
|
||||
int getAbcPeriod(void);
|
||||
void printInformation(void);
|
||||
|
||||
private:
|
||||
/** Variables */
|
||||
|
||||
@@ -140,7 +140,7 @@ void Sgp41::pause() {
|
||||
tvoc = utils::getInvalidVOC();
|
||||
noxRaw = utils::getInvalidNOx();
|
||||
nox = utils::getInvalidNOx();
|
||||
}
|
||||
}
|
||||
|
||||
void Sgp41::resume() {
|
||||
onPause = false;
|
||||
|
||||
Reference in New Issue
Block a user