mirror of
https://github.com/airgradienthq/arduino.git
synced 2025-07-29 08:27:17 +02:00
First working integration using airgradientClient
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,3 +3,6 @@ build
|
|||||||
.vscode
|
.vscode
|
||||||
/.idea/
|
/.idea/
|
||||||
.pio
|
.pio
|
||||||
|
.cache
|
||||||
|
logs
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
@ -212,6 +212,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
String deviceId(void);
|
String deviceId(void);
|
||||||
|
|
||||||
|
std::string getDeviceId(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BoardType boardType;
|
BoardType boardType;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user