mirror of
https://github.com/airgradienthq/arduino.git
synced 2025-06-26 16:21:33 +02:00
Compare commits
56 Commits
3.3.0
...
fix/api-ro
Author | SHA1 | Date | |
---|---|---|---|
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 |
1
.github/workflows/check.yml
vendored
1
.github/workflows/check.yml
vendored
@ -38,6 +38,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
|
||||||
|
@ -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,
|
||||||
@ -146,7 +147,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 +156,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**
|
||||||
|
@ -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;
|
||||||
@ -145,6 +156,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);
|
||||||
|
|
||||||
@ -297,11 +309,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 +352,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 {
|
||||||
@ -423,6 +441,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();
|
||||||
@ -536,31 +559,28 @@ void checkForFirmwareUpdate(void) {
|
|||||||
if (networkOption == UseWifi) {
|
if (networkOption == UseWifi) {
|
||||||
agOta = new AirgradientOTAWifi;
|
agOta = new AirgradientOTAWifi;
|
||||||
} else {
|
} else {
|
||||||
agOta = new AirgradientOTACellular(cell);
|
agOta = new AirgradientOTACellular(cellularCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 +588,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 +673,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 +843,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");
|
||||||
@ -922,8 +955,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 +967,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 +989,11 @@ void initializeNetwork() {
|
|||||||
ESP.restart();
|
ESP.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
@ -1026,6 +1077,16 @@ static void configUpdateHandle() {
|
|||||||
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()) {
|
||||||
@ -1309,7 +1370,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 +1385,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
|
||||||
@ -1362,7 +1434,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 +1504,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 +1513,54 @@ 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) {
|
||||||
// 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,8 +1570,9 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1494,12 +1591,43 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1529,7 +1657,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
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
name=AirGradient Air Quality Sensor
|
name=AirGradient Air Quality Sensor
|
||||||
version=3.3.0
|
version=3.3.6
|
||||||
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;
|
||||||
@ -735,11 +738,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 +757,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];
|
||||||
@ -1030,6 +1065,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
|
||||||
*
|
*
|
||||||
@ -1115,7 +1160,7 @@ bool Configuration::isUpdated(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 +1314,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;
|
||||||
|
@ -82,6 +82,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);
|
||||||
|
@ -804,16 +804,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 +827,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();
|
||||||
}
|
}
|
||||||
|
@ -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.6-snap"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
Submodule src/Libraries/airgradient-client updated: 774f8b70e6...a4ac14936e
Submodule src/Libraries/airgradient-ota updated: 24d2dc537c...c772392427
@ -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