|
|
|
@ -26,7 +26,6 @@ https://forum.airgradient.com/
|
|
|
|
|
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "AgConfigure.h"
|
|
|
|
|
#include "AgSchedule.h"
|
|
|
|
|
#include "AgStateMachine.h"
|
|
|
|
@ -37,6 +36,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|
|
|
|
#include "Arduino.h"
|
|
|
|
|
#include "EEPROM.h"
|
|
|
|
|
#include "ESPmDNS.h"
|
|
|
|
|
#include "Libraries/airgradient-client/src/common.h"
|
|
|
|
|
#include "LocalServer.h"
|
|
|
|
|
#include "MqttClient.h"
|
|
|
|
|
#include "OpenMetrics.h"
|
|
|
|
@ -45,6 +45,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|
|
|
|
#include <HardwareSerial.h>
|
|
|
|
|
#include <WebServer.h>
|
|
|
|
|
#include <WiFi.h>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
|
|
#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 CELLULAR_SERVER_CONFIG_SYNC_INTERVAL 30 * 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 SENSOR_CO2_CALIB_COUNTDOWN_MAX 5 /** sec */
|
|
|
|
|
#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 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 MEASUREMENT_TRANSMIT_CYCLE 3
|
|
|
|
|
#define MAXIMUM_MEASUREMENT_CYCLE_QUEUE 80
|
|
|
|
|
#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_IIC_RESET 3
|
|
|
|
|
|
|
|
|
|
#define MINUTES() ((uint32_t)(esp_timer_get_time() / 1000 / 1000 / 60))
|
|
|
|
|
|
|
|
|
|
static MqttClient mqttClient(Serial);
|
|
|
|
|
static TaskHandle_t mqttTask = NULL;
|
|
|
|
|
static Configuration configuration(Serial);
|
|
|
|
@ -102,7 +108,7 @@ static OpenMetrics openMetrics(measurements, configuration, wifiConnector);
|
|
|
|
|
static LocalServer localServer(Serial, openMetrics, measurements, configuration,
|
|
|
|
|
wifiConnector);
|
|
|
|
|
static AgSerial *agSerial;
|
|
|
|
|
static CellularModule *cell;
|
|
|
|
|
static CellularModule *cellularCard;
|
|
|
|
|
static AirgradientClient *agClient;
|
|
|
|
|
|
|
|
|
|
enum NetworkOption {
|
|
|
|
@ -111,12 +117,17 @@ enum NetworkOption {
|
|
|
|
|
};
|
|
|
|
|
NetworkOption networkOption;
|
|
|
|
|
TaskHandle_t handleNetworkTask = NULL;
|
|
|
|
|
static bool otaInProgress = false;
|
|
|
|
|
static bool firmwareUpdateInProgress = false;
|
|
|
|
|
|
|
|
|
|
static uint32_t factoryBtnPressTime = 0;
|
|
|
|
|
static AgFirmwareMode fwMode = FW_MODE_I_9PSL;
|
|
|
|
|
static bool ledBarButtonTest = false;
|
|
|
|
|
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;
|
|
|
|
|
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 void setMeasurementMaxPeriod();
|
|
|
|
|
static void newMeasurementCycle();
|
|
|
|
|
static void restartIfCeClientIssueOverTwoHours();
|
|
|
|
|
static void networkSignalCheck();
|
|
|
|
|
static void networkingTask(void *args);
|
|
|
|
|
|
|
|
|
@ -198,7 +210,7 @@ void setup() {
|
|
|
|
|
oledDisplay.setAirGradient(ag);
|
|
|
|
|
stateMachine.setAirGradient(ag);
|
|
|
|
|
wifiConnector.setAirGradient(ag);
|
|
|
|
|
openMetrics.setAirGradient(ag, agClient);
|
|
|
|
|
openMetrics.setAirGradient(ag);
|
|
|
|
|
localServer.setAirGraident(ag);
|
|
|
|
|
measurements.setAirGradient(ag);
|
|
|
|
|
|
|
|
|
@ -259,6 +271,7 @@ void setup() {
|
|
|
|
|
|
|
|
|
|
Serial.println("Display brightness: " + String(configuration.getDisplayBrightness()));
|
|
|
|
|
oledDisplay.setBrightness(configuration.getDisplayBrightness());
|
|
|
|
|
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -297,11 +310,17 @@ 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
|
|
|
|
|
restartIfCeClientIssueOverTwoHours();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Schedule to feed external watchdog
|
|
|
|
|
watchdogFeedSchedule.run();
|
|
|
|
|
|
|
|
|
|
if (otaInProgress) {
|
|
|
|
|
// OTA currently in progress, temporarily disable running sensor schedules
|
|
|
|
|
if (firmwareUpdateInProgress) {
|
|
|
|
|
// Firmare update currently in progress, temporarily disable running sensor schedules
|
|
|
|
|
delay(10000);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
@ -334,7 +353,7 @@ void loop() {
|
|
|
|
|
static bool pmsConnected = false;
|
|
|
|
|
if (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 {
|
|
|
|
@ -537,35 +556,37 @@ static bool sgp41Init(void) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void checkForFirmwareUpdate(void) {
|
|
|
|
|
if (configuration.isCloudConnectionDisabled()) {
|
|
|
|
|
Serial.println("Cloud connection is disabled, skip firmware update");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AirgradientOTA *agOta;
|
|
|
|
|
if (networkOption == UseWifi) {
|
|
|
|
|
agOta = new AirgradientOTAWifi;
|
|
|
|
|
} else {
|
|
|
|
|
agOta = new AirgradientOTACellular(cell);
|
|
|
|
|
agOta = new AirgradientOTACellular(cellularCard);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Indicate main task that ota is performing
|
|
|
|
|
Serial.println("Check for firmware update, disabling main task");
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
// Indicate main task that firmware update is in progress
|
|
|
|
|
firmwareUpdateInProgress = true;
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
otaInProgress = false;
|
|
|
|
|
if (configuration.hasSensorSGP && networkOption == UseCellular) {
|
|
|
|
|
// Re-start SGP41 task
|
|
|
|
|
if (!sgp41Init()) {
|
|
|
|
|
Serial.println("Failed re-start SGP41 task");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Indicate main task that firmware update finish
|
|
|
|
|
firmwareUpdateInProgress = false;
|
|
|
|
|
|
|
|
|
|
delete agOta;
|
|
|
|
|
Serial.println();
|
|
|
|
@ -573,14 +594,25 @@ void checkForFirmwareUpdate(void) {
|
|
|
|
|
|
|
|
|
|
void otaHandlerCallback(AirgradientOTA::OtaResult result, const char *msg) {
|
|
|
|
|
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);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case AirgradientOTA::InProgress:
|
|
|
|
|
Serial.printf("OTA progress: %s\n", msg);
|
|
|
|
|
displayExecuteOta(result, "", std::stoi(msg));
|
|
|
|
|
break;
|
|
|
|
|
case AirgradientOTA::Failed:
|
|
|
|
|
displayExecuteOta(result, "", 0);
|
|
|
|
|
if (configuration.hasSensorSGP && networkOption == UseCellular) {
|
|
|
|
|
ag->sgp41.resume();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case AirgradientOTA::Skipped:
|
|
|
|
|
case AirgradientOTA::AlreadyUpToDate:
|
|
|
|
|
displayExecuteOta(result, "", 0);
|
|
|
|
@ -647,7 +679,11 @@ static void displayExecuteOta(AirgradientOTA::OtaResult result, String msg, int
|
|
|
|
|
}
|
|
|
|
|
delay(1000);
|
|
|
|
|
}
|
|
|
|
|
oledDisplay.setAirGradient(0);
|
|
|
|
|
|
|
|
|
|
if (ag->isOne()) {
|
|
|
|
|
oledDisplay.setAirGradient(0);
|
|
|
|
|
oledDisplay.setBrightness(0);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
@ -925,8 +961,8 @@ void initializeNetwork() {
|
|
|
|
|
if (agSerial->open()) {
|
|
|
|
|
Serial.println("Cellular module found");
|
|
|
|
|
// Initialize cellular module and use cellular as agClient
|
|
|
|
|
cell = new CellularModuleA7672XX(agSerial, GPIO_POWER_MODULE_PIN);
|
|
|
|
|
agClient = new AirgradientCellularClient(cell);
|
|
|
|
|
cellularCard = new CellularModuleA7672XX(agSerial, GPIO_POWER_MODULE_PIN);
|
|
|
|
|
agClient = new AirgradientCellularClient(cellularCard);
|
|
|
|
|
networkOption = UseCellular;
|
|
|
|
|
} else {
|
|
|
|
|
Serial.println("Cellular module not available, using wifi");
|
|
|
|
@ -959,6 +995,9 @@ void initializeNetwork() {
|
|
|
|
|
ESP.restart();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Provide openmetrics to have access to last transmission result
|
|
|
|
|
openMetrics.setAirgradientClient(agClient);
|
|
|
|
|
|
|
|
|
|
if (networkOption == UseCellular) {
|
|
|
|
|
// Disabling it again
|
|
|
|
|
agSerial->setDebug(false);
|
|
|
|
@ -1340,7 +1379,10 @@ void postUsingWifi() {
|
|
|
|
|
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
|
|
|
|
|
xSemaphoreTake(mutexMeasurementCycleQueue, portMAX_DELAY);
|
|
|
|
|
|
|
|
|
@ -1352,6 +1394,14 @@ void postUsingCellular() {
|
|
|
|
|
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
|
|
|
|
|
std::string payload;
|
|
|
|
|
payload += std::to_string(CELLULAR_MEASUREMENT_INTERVAL / 1000); // Convert to seconds
|
|
|
|
@ -1373,13 +1423,17 @@ void postUsingCellular() {
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1393,7 +1447,7 @@ void sendDataToServer(void) {
|
|
|
|
|
if (networkOption == UseWifi) {
|
|
|
|
|
postUsingWifi();
|
|
|
|
|
} else if (networkOption == UseCellular) {
|
|
|
|
|
postUsingCellular();
|
|
|
|
|
postUsingCellular(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1472,44 +1526,75 @@ void networkSignalCheck() {
|
|
|
|
|
if (networkOption == UseWifi) {
|
|
|
|
|
Serial.printf("WiFi RSSI %d\n", wifiConnector.RSSI());
|
|
|
|
|
} else if (networkOption == UseCellular) {
|
|
|
|
|
auto result = cell->retrieveSignal();
|
|
|
|
|
auto result = cellularCard->retrieveSignal();
|
|
|
|
|
if (result.status != CellReturnStatus::Ok) {
|
|
|
|
|
agClient->setClientReady(false);
|
|
|
|
|
lastCellSignalQuality = 99;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save last signal quality
|
|
|
|
|
lastCellSignalQuality = result.data;
|
|
|
|
|
|
|
|
|
|
if (result.data == 99) {
|
|
|
|
|
// 99 indicate cellular not attached to network
|
|
|
|
|
agClient->setClientReady(false);
|
|
|
|
|
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) {
|
|
|
|
|
// OTA check on boot
|
|
|
|
|
#ifdef ESP8266
|
|
|
|
|
// ota not supported
|
|
|
|
|
#else
|
|
|
|
|
// because cellular it takes too long, watchdog triggered
|
|
|
|
|
checkForFirmwareUpdate();
|
|
|
|
|
checkForUpdateSchedule.update();
|
|
|
|
|
// If cloud connection enabled, run first transmission to server at boot
|
|
|
|
|
if (configuration.isCloudConnectionDisabled() == false) {
|
|
|
|
|
// OTA check on boot
|
|
|
|
|
#ifndef ESP8266
|
|
|
|
|
checkForFirmwareUpdate();
|
|
|
|
|
checkForUpdateSchedule.update();
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Because cellular interval is longer, needs to send first measures cycle on
|
|
|
|
|
// boot to indicate that its online
|
|
|
|
|
if (networkOption == UseCellular) {
|
|
|
|
|
Serial.println("Prepare first measures cycle to send on boot for 20s");
|
|
|
|
|
delay(20000);
|
|
|
|
|
newMeasurementCycle();
|
|
|
|
|
sendDataToServer();
|
|
|
|
|
measurementSchedule.update();
|
|
|
|
|
// Because cellular interval is longer, needs to send first measures cycle on
|
|
|
|
|
// boot to indicate that its online
|
|
|
|
|
if (networkOption == UseCellular) {
|
|
|
|
|
Serial.println("Prepare first measures cycle to send on boot for 20s");
|
|
|
|
|
delay(20000);
|
|
|
|
|
networkSignalCheck();
|
|
|
|
|
newMeasurementCycle();
|
|
|
|
|
postUsingCellular(true);
|
|
|
|
|
measurementSchedule.update();
|
|
|
|
|
}
|
|
|
|
|
// Reset scheduler
|
|
|
|
|
configSchedule.update();
|
|
|
|
|
transmissionSchedule.update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reset scheduler
|
|
|
|
|
configSchedule.update();
|
|
|
|
|
transmissionSchedule.update();
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
// Handle reconnection based on mode
|
|
|
|
|
if (networkOption == UseWifi) {
|
|
|
|
@ -1521,21 +1606,50 @@ void networkingTask(void *args) {
|
|
|
|
|
}
|
|
|
|
|
else if (networkOption == UseCellular) {
|
|
|
|
|
if (agClient->isClientReady() == false) {
|
|
|
|
|
Serial.println("Cellular client not ready, ensuring connection...");
|
|
|
|
|
// Start time if value still default
|
|
|
|
|
if (agCeClientProblemDetectedTime == 0) {
|
|
|
|
|
agCeClientProblemDetectedTime = MINUTES();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Enable at command debug
|
|
|
|
|
agSerial->setDebug(true);
|
|
|
|
|
if (agClient->ensureClientConnection() == false) {
|
|
|
|
|
Serial.println("Cellular client connection not ready, retry in 5s...");
|
|
|
|
|
delay(5000);
|
|
|
|
|
|
|
|
|
|
// 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...");
|
|
|
|
|
if (agClient->ensureClientConnection(resetModule) == false) {
|
|
|
|
|
Serial.println("Cellular client connection not ready, retry in 30s...");
|
|
|
|
|
delay(30000); // before retry, wait for 30s
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
agSerial->setDebug(false);
|
|
|
|
|
|
|
|
|
|
// Client is ready
|
|
|
|
|
agCeClientProblemDetectedTime = 0; // reset to default
|
|
|
|
|
agSerial->setDebug(false); // disable at command debug
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If connection to AirGradient server disable don't run config and transmission schedule
|
|
|
|
|
if (configuration.isCloudConnectionDisabled()) {
|
|
|
|
|
delay(1000);
|
|
|
|
|
return;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run scheduler
|
|
|
|
@ -1558,7 +1672,10 @@ void newMeasurementCycle() {
|
|
|
|
|
measurementCycleQueue.erase(measurementCycleQueue.begin());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get current measures
|
|
|
|
|
auto mc = measurements.getMeasures();
|
|
|
|
|
mc.signal = cellularCard->csqToDbm(lastCellSignalQuality); // convert to RSSI
|
|
|
|
|
|
|
|
|
|
measurementCycleQueue.push_back(mc);
|
|
|
|
|
Serial.println("New measurement cycle added to queue");
|
|
|
|
|
// Release mutex
|
|
|
|
|