mirror of
https://github.com/airgradienthq/arduino.git
synced 2025-06-29 17:50:58 +02:00
Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
3d26a54d69 | |||
b70ee75d50 | |||
c6846c818a | |||
0b1c901a76 | |||
83504c8628 | |||
4487992748 | |||
3c8a65a329 | |||
673d564ddb | |||
423eb4808f | |||
18a710ffc2 | |||
040cb79a4d | |||
52d3dc03f1 | |||
1c6bc3ec55 | |||
34d7c93e14 | |||
fee1dc25d6 |
@ -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 {
|
||||||
@ -119,6 +125,10 @@ static bool ledBarButtonTest = false;
|
|||||||
static String fwNewVersion;
|
static String fwNewVersion;
|
||||||
static int lastCellSignalQuality = 99; // CSQ
|
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;
|
||||||
|
|
||||||
@ -146,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);
|
||||||
|
|
||||||
@ -298,6 +309,12 @@ 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();
|
||||||
|
|
||||||
@ -542,7 +559,7 @@ 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 ota is performing
|
||||||
@ -926,8 +943,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");
|
||||||
@ -1341,7 +1358,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);
|
||||||
|
|
||||||
@ -1353,6 +1373,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
|
||||||
@ -1394,7 +1422,7 @@ void sendDataToServer(void) {
|
|||||||
if (networkOption == UseWifi) {
|
if (networkOption == UseWifi) {
|
||||||
postUsingWifi();
|
postUsingWifi();
|
||||||
} else if (networkOption == UseCellular) {
|
} else if (networkOption == UseCellular) {
|
||||||
postUsingCellular();
|
postUsingCellular(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1473,7 +1501,7 @@ 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;
|
lastCellSignalQuality = 99;
|
||||||
@ -1493,6 +1521,31 @@ void networkSignalCheck() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
#ifdef ESP8266
|
||||||
@ -1510,7 +1563,7 @@ void networkingTask(void *args) {
|
|||||||
delay(20000);
|
delay(20000);
|
||||||
networkSignalCheck();
|
networkSignalCheck();
|
||||||
newMeasurementCycle();
|
newMeasurementCycle();
|
||||||
sendDataToServer();
|
postUsingCellular(true);
|
||||||
measurementSchedule.update();
|
measurementSchedule.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1529,14 +1582,43 @@ void networkingTask(void *args) {
|
|||||||
}
|
}
|
||||||
else if (networkOption == UseCellular) {
|
else if (networkOption == UseCellular) {
|
||||||
if (agClient->isClientReady() == false) {
|
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);
|
agSerial->setDebug(true);
|
||||||
if (agClient->ensureClientConnection() == false) {
|
|
||||||
Serial.println("Cellular client connection not ready, retry in 5s...");
|
// Check if cellular client not ready until certain time
|
||||||
delay(5000);
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
agSerial->setDebug(false);
|
|
||||||
|
// Client is ready
|
||||||
|
agCeClientProblemDetectedTime = 0; // reset to default
|
||||||
|
agSerial->setDebug(false); // disable at command debug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1568,7 +1650,7 @@ void newMeasurementCycle() {
|
|||||||
|
|
||||||
// Get current measures
|
// Get current measures
|
||||||
auto mc = measurements.getMeasures();
|
auto mc = measurements.getMeasures();
|
||||||
mc.signal = cell->csqToDbm(lastCellSignalQuality); // convert to RSSI
|
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");
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
name=AirGradient Air Quality Sensor
|
name=AirGradient Air Quality Sensor
|
||||||
version=3.3.4
|
version=3.3.5
|
||||||
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.
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
#include "Main/utils.h"
|
#include "Main/utils.h"
|
||||||
|
|
||||||
#ifndef GIT_VERSION
|
#ifndef GIT_VERSION
|
||||||
#define GIT_VERSION "3.3.4-snap"
|
#define GIT_VERSION "3.3.5-snap"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
Submodule src/Libraries/airgradient-client updated: d24b369604...a4ac14936e
Reference in New Issue
Block a user