@@ -26,7 +26,6 @@ https://forum.airgradient.com/
26
26
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
27
27
28
28
*/
29
-
30
29
#include " AgConfigure.h"
31
30
#include " AgSchedule.h"
32
31
#include " AgStateMachine.h"
@@ -37,6 +36,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
37
36
#include " Arduino.h"
38
37
#include " EEPROM.h"
39
38
#include " ESPmDNS.h"
39
+ #include " Libraries/airgradient-client/src/common.h"
40
40
#include " LocalServer.h"
41
41
#include " MqttClient.h"
42
42
#include " OpenMetrics.h"
@@ -45,6 +45,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
45
45
#include < HardwareSerial.h>
46
46
#include < WebServer.h>
47
47
#include < WiFi.h>
48
+ #include < cstdint>
48
49
#include < string>
49
50
50
51
#include " Libraries/airgradient-client/src/agSerial.h"
@@ -65,7 +66,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
65
66
#define WIFI_TRANSMISSION_INTERVAL 1 * 60000 /* * ms */
66
67
#define CELLULAR_SERVER_CONFIG_SYNC_INTERVAL 30 * 60000 /* * ms */
67
68
#define CELLULAR_MEASUREMENT_INTERVAL 3 * 60000 /* * ms */
68
- #define CELLULAR_TRANSMISSION_INTERVAL 9 * 60000 /* * ms */
69
+ #define CELLULAR_TRANSMISSION_INTERVAL 3 * 60000 /* * ms */
69
70
#define MQTT_SYNC_INTERVAL 60000 /* * ms */
70
71
#define SENSOR_CO2_CALIB_COUNTDOWN_MAX 5 /* * sec */
71
72
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /* * ms */
@@ -74,7 +75,10 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
74
75
#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 6000 /* * ms */
75
76
#define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /* * ms */
76
77
#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 */
77
80
81
+ #define MEASUREMENT_TRANSMIT_CYCLE 3
78
82
#define MAXIMUM_MEASUREMENT_CYCLE_QUEUE 80
79
83
#define RESERVED_MEASUREMENT_CYCLE_CAPACITY 10
80
84
@@ -88,6 +92,8 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
88
92
#define GPIO_EXPANSION_CARD_POWER 4
89
93
#define GPIO_IIC_RESET 3
90
94
95
+ #define MINUTES () ((uint32_t )(esp_timer_get_time() / 1000 / 1000 / 60 ))
96
+
91
97
static MqttClient mqttClient (Serial);
92
98
static TaskHandle_t mqttTask = NULL ;
93
99
static Configuration configuration (Serial);
@@ -102,7 +108,7 @@ static OpenMetrics openMetrics(measurements, configuration, wifiConnector);
102
108
static LocalServer localServer (Serial, openMetrics, measurements, configuration,
103
109
wifiConnector);
104
110
static AgSerial *agSerial;
105
- static CellularModule *cell ;
111
+ static CellularModule *cellularCard ;
106
112
static AirgradientClient *agClient;
107
113
108
114
enum NetworkOption {
@@ -119,6 +125,10 @@ static bool ledBarButtonTest = false;
119
125
static String fwNewVersion;
120
126
static int lastCellSignalQuality = 99 ; // CSQ
121
127
128
+ // Default value is 0, indicate its not started yet
129
+ // In minutes
130
+ uint32_t agCeClientProblemDetectedTime = 0 ;
131
+
122
132
SemaphoreHandle_t mutexMeasurementCycleQueue;
123
133
static std::vector<Measurements::Measures> measurementCycleQueue;
124
134
@@ -146,6 +156,7 @@ static void displayExecuteOta(AirgradientOTA::OtaResult result, String msg, int
146
156
static int calculateMaxPeriod (int updateInterval);
147
157
static void setMeasurementMaxPeriod ();
148
158
static void newMeasurementCycle ();
159
+ static void restartIfCeClientIssueOverTwoHours ();
149
160
static void networkSignalCheck ();
150
161
static void networkingTask (void *args);
151
162
@@ -298,6 +309,12 @@ void setup() {
298
309
}
299
310
300
311
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
+
301
318
// Schedule to feed external watchdog
302
319
watchdogFeedSchedule.run ();
303
320
@@ -542,7 +559,7 @@ void checkForFirmwareUpdate(void) {
542
559
if (networkOption == UseWifi) {
543
560
agOta = new AirgradientOTAWifi;
544
561
} else {
545
- agOta = new AirgradientOTACellular (cell );
562
+ agOta = new AirgradientOTACellular (cellularCard );
546
563
}
547
564
548
565
// Indicate main task that ota is performing
@@ -926,8 +943,8 @@ void initializeNetwork() {
926
943
if (agSerial->open ()) {
927
944
Serial.println (" Cellular module found" );
928
945
// 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 );
931
948
networkOption = UseCellular;
932
949
} else {
933
950
Serial.println (" Cellular module not available, using wifi" );
@@ -1341,7 +1358,10 @@ void postUsingWifi() {
1341
1358
Serial.printf (" Free heap: %u\n " , ESP.getFreeHeap ());
1342
1359
}
1343
1360
1344
- void postUsingCellular () {
1361
+ /* *
1362
+ * forcePost to force post without checking transmit cycle
1363
+ */
1364
+ void postUsingCellular (bool forcePost) {
1345
1365
// Aquire queue mutex to get queue size
1346
1366
xSemaphoreTake (mutexMeasurementCycleQueue, portMAX_DELAY);
1347
1367
@@ -1353,6 +1373,14 @@ void postUsingCellular() {
1353
1373
return ;
1354
1374
}
1355
1375
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
+
1356
1384
// Build payload include all measurements from queue
1357
1385
std::string payload;
1358
1386
payload += std::to_string (CELLULAR_MEASUREMENT_INTERVAL / 1000 ); // Convert to seconds
@@ -1394,7 +1422,7 @@ void sendDataToServer(void) {
1394
1422
if (networkOption == UseWifi) {
1395
1423
postUsingWifi ();
1396
1424
} else if (networkOption == UseCellular) {
1397
- postUsingCellular ();
1425
+ postUsingCellular (false );
1398
1426
}
1399
1427
}
1400
1428
@@ -1473,7 +1501,7 @@ void networkSignalCheck() {
1473
1501
if (networkOption == UseWifi) {
1474
1502
Serial.printf (" WiFi RSSI %d\n " , wifiConnector.RSSI ());
1475
1503
} else if (networkOption == UseCellular) {
1476
- auto result = cell ->retrieveSignal ();
1504
+ auto result = cellularCard ->retrieveSignal ();
1477
1505
if (result.status != CellReturnStatus::Ok) {
1478
1506
agClient->setClientReady (false );
1479
1507
lastCellSignalQuality = 99 ;
@@ -1493,6 +1521,31 @@ void networkSignalCheck() {
1493
1521
}
1494
1522
}
1495
1523
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
+
1496
1549
void networkingTask (void *args) {
1497
1550
// OTA check on boot
1498
1551
#ifdef ESP8266
@@ -1510,7 +1563,7 @@ void networkingTask(void *args) {
1510
1563
delay (20000 );
1511
1564
networkSignalCheck ();
1512
1565
newMeasurementCycle ();
1513
- sendDataToServer ( );
1566
+ postUsingCellular ( true );
1514
1567
measurementSchedule.update ();
1515
1568
}
1516
1569
@@ -1529,14 +1582,43 @@ void networkingTask(void *args) {
1529
1582
}
1530
1583
else if (networkOption == UseCellular) {
1531
1584
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
1533
1591
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
1537
1616
continue ;
1538
1617
}
1539
- agSerial->setDebug (false );
1618
+
1619
+ // Client is ready
1620
+ agCeClientProblemDetectedTime = 0 ; // reset to default
1621
+ agSerial->setDebug (false ); // disable at command debug
1540
1622
}
1541
1623
}
1542
1624
@@ -1568,7 +1650,7 @@ void newMeasurementCycle() {
1568
1650
1569
1651
// Get current measures
1570
1652
auto mc = measurements.getMeasures ();
1571
- mc.signal = cell ->csqToDbm (lastCellSignalQuality); // convert to RSSI
1653
+ mc.signal = cellularCard ->csqToDbm (lastCellSignalQuality); // convert to RSSI
1572
1654
1573
1655
measurementCycleQueue.push_back (mc);
1574
1656
Serial.println (" New measurement cycle added to queue" );
0 commit comments