First working integration using airgradientClient

This commit is contained in:
samuelbles07
2025-03-14 01:41:23 +07:00
parent c94b886360
commit 4b356920c2
4 changed files with 140 additions and 77 deletions

3
.gitignore vendored
View File

@ -3,3 +3,6 @@ build
.vscode .vscode
/.idea/ /.idea/
.pio .pio
.cache
logs

View File

@ -45,6 +45,11 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
#include <WebServer.h> #include <WebServer.h>
#include <WiFi.h> #include <WiFi.h>
#include "Libraries/airgradient-client/src/agSerial.h"
#include "Libraries/airgradient-client/src/cellularModuleA7672xx.h"
#include "Libraries/airgradient-client/src/airgradientCellularClient.h"
#include "Libraries/airgradient-client/src/airgradientWifiClient.h"
#define LED_BAR_ANIMATION_PERIOD 100 /** ms */ #define LED_BAR_ANIMATION_PERIOD 100 /** ms */
#define DISP_UPDATE_INTERVAL 2500 /** ms */ #define DISP_UPDATE_INTERVAL 2500 /** ms */
#define SERVER_CONFIG_SYNC_INTERVAL 60000 /** ms */ #define SERVER_CONFIG_SYNC_INTERVAL 60000 /** ms */
@ -63,6 +68,11 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
#define I2C_SCL_PIN 6 #define I2C_SCL_PIN 6
#define OLED_I2C_ADDR 0x3C #define OLED_I2C_ADDR 0x3C
/** Power pin */
#define GPIO_POWER_MODULE_PIN 5
#define GPIO_EXPANSION_CARD_POWER 4
#define GPIO_IIC_RESET 3
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);
@ -80,6 +90,10 @@ static OtaHandler otaHandler;
static LocalServer localServer(Serial, openMetrics, measurements, configuration, static LocalServer localServer(Serial, openMetrics, measurements, configuration,
wifiConnector); wifiConnector);
static AgSerial *agSerial;
static CellularModule *cell;
static AirgradientClient *agClient;
static uint32_t factoryBtnPressTime = 0; static uint32_t factoryBtnPressTime = 0;
static AgFirmwareMode fwMode = FW_MODE_I_9PSL; static AgFirmwareMode fwMode = FW_MODE_I_9PSL;
@ -87,7 +101,7 @@ static bool ledBarButtonTest = false;
static String fwNewVersion; static String fwNewVersion;
static void boardInit(void); static void boardInit(void);
static void initializeNetwork(void); static void initializeNetwork(bool useWifi);
static void failedHandler(String msg); static void failedHandler(String msg);
static void configurationUpdateSchedule(void); static void configurationUpdateSchedule(void);
static void updateDisplayAndLedBar(void); static void updateDisplayAndLedBar(void);
@ -153,24 +167,51 @@ void setup() {
oledDisplay.setAirGradient(ag); oledDisplay.setAirGradient(ag);
stateMachine.setAirGradient(ag); stateMachine.setAirGradient(ag);
wifiConnector.setAirGradient(ag); wifiConnector.setAirGradient(ag);
apiClient.setAirGradient(ag);
openMetrics.setAirGradient(ag); openMetrics.setAirGradient(ag);
localServer.setAirGraident(ag); localServer.setAirGraident(ag);
measurements.setAirGradient(ag); measurements.setAirGradient(ag);
/** Example set custom API root URL */
// apiClient.setApiRoot("https://example.custom.api");
/** Init sensor */ /** Init sensor */
boardInit(); boardInit();
setMeasurementMaxPeriod(); setMeasurementMaxPeriod();
// Comment below line to disable debug measurement readings // Comment below line to disable debug measurement readings
measurements.setDebug(true); measurements.setDebug(false);
/** Connecting wifi */ // Check if cellular module available
bool connectToWifi = false; bool cellularFound = false;
if (ag->isOne()) { agSerial = new AgSerial(Wire);
agSerial->init(GPIO_IIC_RESET);
if (agSerial->open()) {
Serial.println("Cellular module found");
// Enable cellular load switch
pinMode(GPIO_EXPANSION_CARD_POWER, OUTPUT);
digitalWrite(GPIO_EXPANSION_CARD_POWER, HIGH);
// Initialize cellular module and use cellular as agClient
cell = new CellularModuleA7672XX(agSerial, GPIO_POWER_MODULE_PIN);
agClient = new AirgradientCellularClient(cell);
cellularFound = true;
//! NOTE: TMP
configuration.setOfflineModeWithoutSave(false);
} else {
Serial.println("Cellular module not available, using wifi");
delete agSerial;
agSerial = nullptr;
// Use wifi as agClient
agClient = new AirgradientWifiClient;
cellularFound = false;
}
if (!agClient->begin()) {
// TODO: Need to do retry when agclient already in other task
Serial.println("Failed start Airgradient Client FAILED");
assert(0);
}
// bool connectToWifi = false;
bool connectToNetwork = true;
if (ag->isOne()) { // Offline mode only available for indoor monitor
/** Show message confirm offline mode, should me perform if LED bar button /** Show message confirm offline mode, should me perform if LED bar button
* test pressed */ * test pressed */
if (ledBarButtonTest == false) { if (ledBarButtonTest == false) {
@ -193,21 +234,23 @@ void setup() {
break; break;
} }
} }
connectToWifi = !configuration.isOfflineMode(); connectToNetwork = !configuration.isOfflineMode();
} else { } else {
configuration.setOfflineModeWithoutSave(true); configuration.setOfflineModeWithoutSave(true);
} }
} else {
connectToWifi = true;
} }
// else {
// connectToWifi = true;
// }
// Initialize networking configuration // Initialize networking configuration
if (connectToWifi) { if (connectToNetwork) {
initializeNetwork(); bool useWifi = !cellularFound;
initializeNetwork(useWifi);
} }
/** Set offline mode without saving, cause wifi is not configured */ /** Set offline mode without saving, cause wifi is not configured */
if (wifiConnector.hasConfigurated() == false) { if (wifiConnector.hasConfigurated() == false && !cellularFound) {
Serial.println("Set offline mode cause wifi is not configurated"); Serial.println("Set offline mode cause wifi is not configurated");
configuration.setOfflineModeWithoutSave(true); configuration.setOfflineModeWithoutSave(true);
} }
@ -274,7 +317,7 @@ void loop() {
configUpdateHandle(); configUpdateHandle();
/** Firmware check for update handle */ /** Firmware check for update handle */
checkForUpdateSchedule.run(); // checkForUpdateSchedule.run(); //! Temporary until ota cellular
} }
static void co2Update(void) { static void co2Update(void) {
@ -464,10 +507,10 @@ static void checkForFirmwareUpdate(void) {
return; return;
} }
if (!wifiConnector.isConnected()) { // if (!wifiConnector.isConnected()) {
Serial.println("wifi not connected, ignored"); // Serial.println("wifi not connected, ignored");
return; // return;
} // }
Serial.println("perform"); Serial.println("perform");
otaHandler.setHandlerCallback(otaHandlerCallback); otaHandler.setHandlerCallback(otaHandlerCallback);
@ -593,7 +636,13 @@ static void sendDataToAg() {
"task_led", 2048, NULL, 5, NULL); "task_led", 2048, NULL, 5, NULL);
delay(1500); delay(1500);
if (apiClient.sendPing(wifiConnector.RSSI(), measurements.bootCount())) {
// Build payload to check connection to airgradient server
JSONVar root;
root["wifi"] = wifiConnector.RSSI();
root["boot"] = measurements.bootCount();
std::string payload = JSON.stringify(root).c_str();
if (agClient->httpPostMeasures(ag->getDeviceId(), payload)) {
if (ag->isOne()) { if (ag->isOne()) {
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnected); stateMachine.displayHandle(AgStateMachineWiFiOkServerConnected);
} }
@ -828,39 +877,38 @@ static void failedHandler(String msg) {
} }
} }
void initializeNetwork() { void initializeNetwork(bool useWifi) {
if (!wifiConnector.connect()) { if (useWifi) {
Serial.println("Cannot initiate wifi connection"); if (!wifiConnector.connect()) {
return; Serial.println("Cannot initiate wifi connection");
} return;
}
if (!wifiConnector.isConnected()) {
Serial.println("Failed connect to WiFi"); if (!wifiConnector.isConnected()) {
if (wifiConnector.isConfigurePorttalTimeout()) { Serial.println("Failed connect to WiFi");
oledDisplay.showRebooting(); if (wifiConnector.isConfigurePorttalTimeout()) {
delay(2500); oledDisplay.showRebooting();
oledDisplay.setText("", "", ""); delay(2500);
ESP.restart(); oledDisplay.setText("", "", "");
ESP.restart();
}
// Directly return because the rest of the function applied if wifi is connect only
return;
}
// Initiate local network configuration
mdnsInit();
localServer.begin();
// Apply mqtt connection if configured
initMqtt();
// Ignore the rest if cloud connection to AirGradient is disabled
if (configuration.isCloudConnectionDisabled()) {
return;
} }
// Directly return because the rest of the function applied if wifi is connect only
return;
} }
// Initiate local network configuration
mdnsInit();
localServer.begin();
// Apply mqtt connection if configured
initMqtt();
// Ignore the rest if cloud connection to AirGradient is disabled
if (configuration.isCloudConnectionDisabled()) {
return;
}
// Initialize api client
apiClient.begin();
// Send data for the first time to AG server at boot // Send data for the first time to AG server at boot
sendDataToAg(); sendDataToAg();
@ -868,15 +916,17 @@ void initializeNetwork() {
#ifdef ESP8266 #ifdef ESP8266
// ota not supported // ota not supported
#else #else
checkForFirmwareUpdate(); // checkForFirmwareUpdate(); //! Temporary until ota cellular
checkForUpdateSchedule.update(); // checkForUpdateSchedule.update();
#endif #endif
apiClient.fetchServerConfiguration(); std::string config = agClient->httpFetchConfig(ag->getDeviceId());
configSchedule.update(); configSchedule.update();
if (apiClient.isFetchConfigurationFailed()) { // Check if fetch configuration failed or fetch succes but parsing failed
if (agClient->isLastFetchConfigSucceed() == false ||
configuration.parse(config.c_str(), false) == false) {
if (ag->isOne()) { if (ag->isOne()) {
if (apiClient.isNotAvailableOnDashboard()) { if (agClient->isRegisteredOnAgServer() == false) {
stateMachine.displaySetAddToDashBoard(); stateMachine.displaySetAddToDashBoard();
stateMachine.displayHandle(AgStateMachineWiFiOkServerOkSensorConfigFailed); stateMachine.displayHandle(AgStateMachineWiFiOkServerOkSensorConfigFailed);
} else { } else {
@ -885,7 +935,8 @@ void initializeNetwork() {
} }
stateMachine.handleLeds(AgStateMachineWiFiOkServerOkSensorConfigFailed); stateMachine.handleLeds(AgStateMachineWiFiOkServerOkSensorConfigFailed);
delay(DISPLAY_DELAY_SHOW_CONTENT_MS); delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
} else { }
else {
ledBarEnabledUpdate(); ledBarEnabledUpdate();
} }
} }
@ -895,16 +946,17 @@ static void configurationUpdateSchedule(void) {
configuration.getConfigurationControl() == ConfigurationControl::ConfigurationControlLocal) { configuration.getConfigurationControl() == ConfigurationControl::ConfigurationControlLocal) {
Serial.println("Ignore fetch server configuration. Either mode is offline or cloud connection " Serial.println("Ignore fetch server configuration. Either mode is offline or cloud connection "
"disabled or configurationControl set to local"); "disabled or configurationControl set to local");
apiClient.resetFetchConfigurationStatus(); agClient->resetFetchConfigurationStatus();
return; return;
} }
if (wifiConnector.isConnected() == false) { // if (wifiConnector.isConnected() == false) {
Serial.println(" WiFi not connected, skipping fetch configuration from AG server"); // Serial.println(" WiFi not connected, skipping fetch configuration from AG server");
return; // return;
} // }
if (apiClient.fetchServerConfiguration()) { std::string config = agClient->httpFetchConfig(ag->getDeviceId());
if (agClient->isLastFetchConfigSucceed() && configuration.parse(config.c_str(), false)) {
configUpdateHandle(); configUpdateHandle();
} }
} }
@ -1001,11 +1053,11 @@ static void updateDisplayAndLedBar(void) {
return; return;
} }
if (wifiConnector.isConnected() == false) { // if (wifiConnector.isConnected() == false) {
stateMachine.displayHandle(AgStateMachineWiFiLost); // stateMachine.displayHandle(AgStateMachineWiFiLost);
stateMachine.handleLeds(AgStateMachineWiFiLost); // stateMachine.handleLeds(AgStateMachineWiFiLost);
return; // return;
} // }
if (configuration.isCloudConnectionDisabled()) { if (configuration.isCloudConnectionDisabled()) {
// Ignore API related check since cloud is disabled // Ignore API related check since cloud is disabled
@ -1015,14 +1067,15 @@ static void updateDisplayAndLedBar(void) {
} }
AgStateMachineState state = AgStateMachineNormal; AgStateMachineState state = AgStateMachineNormal;
if (apiClient.isFetchConfigurationFailed()) { if (agClient->isLastFetchConfigSucceed() == false) {
state = AgStateMachineSensorConfigFailed; state = AgStateMachineSensorConfigFailed;
if (apiClient.isNotAvailableOnDashboard()) { if (agClient->isRegisteredOnAgServer() == false) {
stateMachine.displaySetAddToDashBoard(); stateMachine.displaySetAddToDashBoard();
} else { } else {
stateMachine.displayClearAddToDashBoard(); stateMachine.displayClearAddToDashBoard();
} }
} else if (apiClient.isPostToServerFailed() && configuration.isPostDataToAirGradient()) { } else if (agClient->isLastPostMeasureSucceed() == false &&
configuration.isPostDataToAirGradient()) {
state = AgStateMachineServerLost; state = AgStateMachineServerLost;
} }
@ -1190,13 +1243,14 @@ static void sendDataToServer(void) {
return; return;
} }
if (wifiConnector.isConnected() == false) { // if (wifiConnector.isConnected() == false) {
Serial.println("WiFi not connected, skipping data transmission to AG server"); // Serial.println("WiFi not connected, skipping data transmission to AG server");
return; // return;
} // }
String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI()); String measures = measurements.toString(false, fwMode, wifiConnector.RSSI());
if (apiClient.postToServer(syncData)) { std::string payload = std::string(measures.c_str());
if (agClient->httpPostMeasures(ag->getDeviceId(), payload)) {
Serial.println(); Serial.println();
Serial.println("Online mode and isPostToAirGradient = true"); Serial.println("Online mode and isPostToAirGradient = true");
Serial.println(); Serial.println();

View File

@ -85,3 +85,7 @@ String AirGradient::deviceId(void) {
mac.toLowerCase(); mac.toLowerCase();
return mac; return mac;
} }
std::string AirGradient::getDeviceId(void) {
return std::string(deviceId().c_str());
}

View File

@ -212,6 +212,8 @@ public:
*/ */
String deviceId(void); String deviceId(void);
std::string getDeviceId(void);
private: private:
BoardType boardType; BoardType boardType;
}; };