Skip to content

Commit b70ee75

Browse files
authored
Merge pull request #302 from airgradienthq/improve-ce-reconnection
Improve cellular client reconnection
2 parents 9fb01d4 + c6846c8 commit b70ee75

File tree

2 files changed

+99
-17
lines changed

2 files changed

+99
-17
lines changed

examples/OneOpenAir/OneOpenAir.ino

Lines changed: 98 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ https://forum.airgradient.com/
2626
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
2727
2828
*/
29-
3029
#include "AgConfigure.h"
3130
#include "AgSchedule.h"
3231
#include "AgStateMachine.h"
@@ -37,6 +36,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
3736
#include "Arduino.h"
3837
#include "EEPROM.h"
3938
#include "ESPmDNS.h"
39+
#include "Libraries/airgradient-client/src/common.h"
4040
#include "LocalServer.h"
4141
#include "MqttClient.h"
4242
#include "OpenMetrics.h"
@@ -45,6 +45,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
4545
#include <HardwareSerial.h>
4646
#include <WebServer.h>
4747
#include <WiFi.h>
48+
#include <cstdint>
4849
#include <string>
4950

5051
#include "Libraries/airgradient-client/src/agSerial.h"
@@ -65,7 +66,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
6566
#define WIFI_TRANSMISSION_INTERVAL 1 * 60000 /** ms */
6667
#define CELLULAR_SERVER_CONFIG_SYNC_INTERVAL 30 * 60000 /** ms */
6768
#define CELLULAR_MEASUREMENT_INTERVAL 3 * 60000 /** ms */
68-
#define CELLULAR_TRANSMISSION_INTERVAL 9 * 60000 /** ms */
69+
#define CELLULAR_TRANSMISSION_INTERVAL 3 * 60000 /** ms */
6970
#define MQTT_SYNC_INTERVAL 60000 /** ms */
7071
#define SENSOR_CO2_CALIB_COUNTDOWN_MAX 5 /** sec */
7172
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */
@@ -74,7 +75,10 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
7475
#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 6000 /** ms */
7576
#define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */
7677
#define FIRMWARE_CHECK_FOR_UPDATE_MS (60 * 60 * 1000) /** ms */
78+
#define TIME_TO_START_POWER_CYCLE_CELLULAR_MODULE (1 * 60) /** minutes */
79+
#define TIMEOUT_WAIT_FOR_CELLULAR_MODULE_READY (2 * 60) /** minutes */
7780

81+
#define MEASUREMENT_TRANSMIT_CYCLE 3
7882
#define MAXIMUM_MEASUREMENT_CYCLE_QUEUE 80
7983
#define RESERVED_MEASUREMENT_CYCLE_CAPACITY 10
8084

@@ -88,6 +92,8 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
8892
#define GPIO_EXPANSION_CARD_POWER 4
8993
#define GPIO_IIC_RESET 3
9094

95+
#define MINUTES() ((uint32_t)(esp_timer_get_time() / 1000 / 1000 / 60))
96+
9197
static MqttClient mqttClient(Serial);
9298
static TaskHandle_t mqttTask = NULL;
9399
static Configuration configuration(Serial);
@@ -102,7 +108,7 @@ static OpenMetrics openMetrics(measurements, configuration, wifiConnector);
102108
static LocalServer localServer(Serial, openMetrics, measurements, configuration,
103109
wifiConnector);
104110
static AgSerial *agSerial;
105-
static CellularModule *cell;
111+
static CellularModule *cellularCard;
106112
static AirgradientClient *agClient;
107113

108114
enum NetworkOption {
@@ -119,6 +125,10 @@ static bool ledBarButtonTest = false;
119125
static String fwNewVersion;
120126
static int lastCellSignalQuality = 99; // CSQ
121127

128+
// Default value is 0, indicate its not started yet
129+
// In minutes
130+
uint32_t agCeClientProblemDetectedTime = 0;
131+
122132
SemaphoreHandle_t mutexMeasurementCycleQueue;
123133
static std::vector<Measurements::Measures> measurementCycleQueue;
124134

@@ -146,6 +156,7 @@ static void displayExecuteOta(AirgradientOTA::OtaResult result, String msg, int
146156
static int calculateMaxPeriod(int updateInterval);
147157
static void setMeasurementMaxPeriod();
148158
static void newMeasurementCycle();
159+
static void restartIfCeClientIssueOverTwoHours();
149160
static void networkSignalCheck();
150161
static void networkingTask(void *args);
151162

@@ -298,6 +309,12 @@ void setup() {
298309
}
299310

300311
void loop() {
312+
if (networkOption == UseCellular) {
313+
// Check if cellular client not ready until certain time
314+
// Redundant check in both task to make sure its executed
315+
restartIfCeClientIssueOverTwoHours();
316+
}
317+
301318
// Schedule to feed external watchdog
302319
watchdogFeedSchedule.run();
303320

@@ -542,7 +559,7 @@ void checkForFirmwareUpdate(void) {
542559
if (networkOption == UseWifi) {
543560
agOta = new AirgradientOTAWifi;
544561
} else {
545-
agOta = new AirgradientOTACellular(cell);
562+
agOta = new AirgradientOTACellular(cellularCard);
546563
}
547564

548565
// Indicate main task that ota is performing
@@ -926,8 +943,8 @@ void initializeNetwork() {
926943
if (agSerial->open()) {
927944
Serial.println("Cellular module found");
928945
// Initialize cellular module and use cellular as agClient
929-
cell = new CellularModuleA7672XX(agSerial, GPIO_POWER_MODULE_PIN);
930-
agClient = new AirgradientCellularClient(cell);
946+
cellularCard = new CellularModuleA7672XX(agSerial, GPIO_POWER_MODULE_PIN);
947+
agClient = new AirgradientCellularClient(cellularCard);
931948
networkOption = UseCellular;
932949
} else {
933950
Serial.println("Cellular module not available, using wifi");
@@ -1341,7 +1358,10 @@ void postUsingWifi() {
13411358
Serial.printf("Free heap: %u\n", ESP.getFreeHeap());
13421359
}
13431360

1344-
void postUsingCellular() {
1361+
/**
1362+
* forcePost to force post without checking transmit cycle
1363+
*/
1364+
void postUsingCellular(bool forcePost) {
13451365
// Aquire queue mutex to get queue size
13461366
xSemaphoreTake(mutexMeasurementCycleQueue, portMAX_DELAY);
13471367

@@ -1353,6 +1373,14 @@ void postUsingCellular() {
13531373
return;
13541374
}
13551375

1376+
// Check queue size if its ready to transmit
1377+
// It is ready if size is divisible by 3
1378+
if (!forcePost && (queueSize % MEASUREMENT_TRANSMIT_CYCLE) > 0) {
1379+
Serial.printf("Not ready to transmit, queue size are %d\n", queueSize);
1380+
xSemaphoreGive(mutexMeasurementCycleQueue);
1381+
return;
1382+
}
1383+
13561384
// Build payload include all measurements from queue
13571385
std::string payload;
13581386
payload += std::to_string(CELLULAR_MEASUREMENT_INTERVAL / 1000); // Convert to seconds
@@ -1394,7 +1422,7 @@ void sendDataToServer(void) {
13941422
if (networkOption == UseWifi) {
13951423
postUsingWifi();
13961424
} else if (networkOption == UseCellular) {
1397-
postUsingCellular();
1425+
postUsingCellular(false);
13981426
}
13991427
}
14001428

@@ -1473,7 +1501,7 @@ void networkSignalCheck() {
14731501
if (networkOption == UseWifi) {
14741502
Serial.printf("WiFi RSSI %d\n", wifiConnector.RSSI());
14751503
} else if (networkOption == UseCellular) {
1476-
auto result = cell->retrieveSignal();
1504+
auto result = cellularCard->retrieveSignal();
14771505
if (result.status != CellReturnStatus::Ok) {
14781506
agClient->setClientReady(false);
14791507
lastCellSignalQuality = 99;
@@ -1493,6 +1521,31 @@ void networkSignalCheck() {
14931521
}
14941522
}
14951523

1524+
/**
1525+
* If in 2 hours cellular client still not ready, then restart system
1526+
*/
1527+
void restartIfCeClientIssueOverTwoHours() {
1528+
if (agCeClientProblemDetectedTime > 0 &&
1529+
(MINUTES() - agCeClientProblemDetectedTime) >
1530+
TIMEOUT_WAIT_FOR_CELLULAR_MODULE_READY) {
1531+
// Give up wait
1532+
Serial.println("Rebooting because CE client issues for 2 hours detected");
1533+
int i = 3;
1534+
while (i != 0) {
1535+
if (ag->isOne()) {
1536+
String tmp = "Rebooting in " + String(i);
1537+
oledDisplay.setText("CE error", "since 2h", tmp.c_str());
1538+
} else {
1539+
Serial.println("Rebooting... " + String(i));
1540+
}
1541+
i = i - 1;
1542+
delay(1000);
1543+
}
1544+
oledDisplay.setBrightness(0);
1545+
esp_restart();
1546+
}
1547+
}
1548+
14961549
void networkingTask(void *args) {
14971550
// OTA check on boot
14981551
#ifdef ESP8266
@@ -1510,7 +1563,7 @@ void networkingTask(void *args) {
15101563
delay(20000);
15111564
networkSignalCheck();
15121565
newMeasurementCycle();
1513-
sendDataToServer();
1566+
postUsingCellular(true);
15141567
measurementSchedule.update();
15151568
}
15161569

@@ -1529,14 +1582,43 @@ void networkingTask(void *args) {
15291582
}
15301583
else if (networkOption == UseCellular) {
15311584
if (agClient->isClientReady() == false) {
1532-
Serial.println("Cellular client not ready, ensuring connection...");
1585+
// Start time if value still default
1586+
if (agCeClientProblemDetectedTime == 0) {
1587+
agCeClientProblemDetectedTime = MINUTES();
1588+
}
1589+
1590+
// Enable at command debug
15331591
agSerial->setDebug(true);
1534-
if (agClient->ensureClientConnection() == false) {
1535-
Serial.println("Cellular client connection not ready, retry in 5s...");
1536-
delay(5000);
1592+
1593+
// Check if cellular client not ready until certain time
1594+
// Redundant check in both task to make sure its executed
1595+
restartIfCeClientIssueOverTwoHours();
1596+
1597+
// Power cycling cellular module due to network issues for more than 1 hour
1598+
bool resetModule = true;
1599+
if ((MINUTES() - agCeClientProblemDetectedTime) >
1600+
TIME_TO_START_POWER_CYCLE_CELLULAR_MODULE) {
1601+
Serial.println("The CE client hasn't recovered in more than 1 hour, "
1602+
"performing a power cycle");
1603+
cellularCard->powerOff();
1604+
delay(2000);
1605+
cellularCard->powerOn();
1606+
delay(10000);
1607+
// no need to reset module when calling ensureClientConnection()
1608+
resetModule = false;
1609+
}
1610+
1611+
// Attempt to reconnect
1612+
Serial.println("Cellular client not ready, ensuring connection...");
1613+
if (agClient->ensureClientConnection(resetModule) == false) {
1614+
Serial.println("Cellular client connection not ready, retry in 30s...");
1615+
delay(30000); // before retry, wait for 30s
15371616
continue;
15381617
}
1539-
agSerial->setDebug(false);
1618+
1619+
// Client is ready
1620+
agCeClientProblemDetectedTime = 0; // reset to default
1621+
agSerial->setDebug(false); // disable at command debug
15401622
}
15411623
}
15421624

@@ -1568,7 +1650,7 @@ void newMeasurementCycle() {
15681650

15691651
// Get current measures
15701652
auto mc = measurements.getMeasures();
1571-
mc.signal = cell->csqToDbm(lastCellSignalQuality); // convert to RSSI
1653+
mc.signal = cellularCard->csqToDbm(lastCellSignalQuality); // convert to RSSI
15721654

15731655
measurementCycleQueue.push_back(mc);
15741656
Serial.println("New measurement cycle added to queue");

0 commit comments

Comments
 (0)