mirror of
https://github.com/airgradienthq/arduino.git
synced 2025-12-18 21:12:49 +01:00
Compare commits
7 Commits
feat/provi
...
feat/ce-co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
844674d8ee | ||
|
|
72bf812235 | ||
|
|
2c37ab9895 | ||
|
|
565a7fa9fd | ||
|
|
9e07b67951 | ||
|
|
23f8c383fd | ||
|
|
c0ad1dbfad |
@@ -59,24 +59,24 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "freertos/projdefs.h"
|
#include "freertos/projdefs.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 WIFI_SERVER_CONFIG_SYNC_INTERVAL 1 * 60000 /** ms */
|
#define WIFI_SERVER_CONFIG_SYNC_INTERVAL 1 * 60000 /** ms */
|
||||||
#define WIFI_MEASUREMENT_INTERVAL 1 * 60000 /** ms */
|
#define WIFI_MEASUREMENT_INTERVAL 1 * 60000 /** ms */
|
||||||
#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 3 * 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 */
|
||||||
#define SENSOR_CO2_UPDATE_INTERVAL 4000 /** ms */
|
#define SENSOR_CO2_UPDATE_INTERVAL 4000 /** ms */
|
||||||
#define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */
|
#define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */
|
||||||
#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 TIME_TO_START_POWER_CYCLE_CELLULAR_MODULE (1 * 60) /** minutes */
|
||||||
#define TIMEOUT_WAIT_FOR_CELLULAR_MODULE_READY (2 * 60) /** minutes */
|
#define TIMEOUT_WAIT_FOR_CELLULAR_MODULE_READY (2 * 60) /** minutes */
|
||||||
|
|
||||||
#define MEASUREMENT_TRANSMIT_CYCLE 3
|
#define MEASUREMENT_TRANSMIT_CYCLE 3
|
||||||
#define MAXIMUM_MEASUREMENT_CYCLE_QUEUE 80
|
#define MAXIMUM_MEASUREMENT_CYCLE_QUEUE 80
|
||||||
@@ -88,7 +88,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|||||||
#define OLED_I2C_ADDR 0x3C
|
#define OLED_I2C_ADDR 0x3C
|
||||||
|
|
||||||
/** Power pin */
|
/** Power pin */
|
||||||
#define GPIO_POWER_MODULE_PIN 5
|
#define GPIO_POWER_MODULE_PIN 5
|
||||||
#define GPIO_EXPANSION_CARD_POWER 4
|
#define GPIO_EXPANSION_CARD_POWER 4
|
||||||
#define GPIO_IIC_RESET 3
|
#define GPIO_IIC_RESET 3
|
||||||
|
|
||||||
@@ -100,15 +100,21 @@ static Configuration configuration(Serial);
|
|||||||
static Measurements measurements(configuration);
|
static Measurements measurements(configuration);
|
||||||
static AirGradient *ag;
|
static AirGradient *ag;
|
||||||
static OledDisplay oledDisplay(configuration, measurements, Serial);
|
static OledDisplay oledDisplay(configuration, measurements, Serial);
|
||||||
static StateMachine stateMachine(oledDisplay, Serial, measurements, configuration);
|
static StateMachine stateMachine(oledDisplay, Serial, measurements,
|
||||||
static WifiConnector wifiConnector(oledDisplay, Serial, stateMachine, configuration);
|
configuration);
|
||||||
|
static WifiConnector wifiConnector(oledDisplay, Serial, stateMachine,
|
||||||
|
configuration);
|
||||||
static OpenMetrics openMetrics(measurements, configuration, wifiConnector);
|
static OpenMetrics openMetrics(measurements, configuration, wifiConnector);
|
||||||
static LocalServer localServer(Serial, openMetrics, measurements, configuration, wifiConnector);
|
static LocalServer localServer(Serial, openMetrics, measurements, configuration,
|
||||||
|
wifiConnector);
|
||||||
static AgSerial *agSerial;
|
static AgSerial *agSerial;
|
||||||
static CellularModule *cellularCard;
|
static CellularModule *cellularCard;
|
||||||
static AirgradientClient *agClient;
|
static AirgradientClient *agClient;
|
||||||
|
|
||||||
enum NetworkOption { UseWifi, UseCellular };
|
enum NetworkOption {
|
||||||
|
UseWifi,
|
||||||
|
UseCellular
|
||||||
|
};
|
||||||
NetworkOption networkOption;
|
NetworkOption networkOption;
|
||||||
TaskHandle_t handleNetworkTask = NULL;
|
TaskHandle_t handleNetworkTask = NULL;
|
||||||
static bool firmwareUpdateInProgress = false;
|
static bool firmwareUpdateInProgress = false;
|
||||||
@@ -156,7 +162,8 @@ static void networkSignalCheck();
|
|||||||
static void networkingTask(void *args);
|
static void networkingTask(void *args);
|
||||||
|
|
||||||
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, updateDisplayAndLedBar);
|
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, updateDisplayAndLedBar);
|
||||||
AgSchedule configSchedule(WIFI_SERVER_CONFIG_SYNC_INTERVAL, configurationUpdateSchedule);
|
AgSchedule configSchedule(WIFI_SERVER_CONFIG_SYNC_INTERVAL,
|
||||||
|
configurationUpdateSchedule);
|
||||||
AgSchedule transmissionSchedule(WIFI_TRANSMISSION_INTERVAL, sendDataToServer);
|
AgSchedule transmissionSchedule(WIFI_TRANSMISSION_INTERVAL, sendDataToServer);
|
||||||
AgSchedule measurementSchedule(WIFI_MEASUREMENT_INTERVAL, newMeasurementCycle);
|
AgSchedule measurementSchedule(WIFI_MEASUREMENT_INTERVAL, newMeasurementCycle);
|
||||||
AgSchedule co2Schedule(SENSOR_CO2_UPDATE_INTERVAL, co2Update);
|
AgSchedule co2Schedule(SENSOR_CO2_UPDATE_INTERVAL, co2Update);
|
||||||
@@ -219,15 +226,17 @@ void setup() {
|
|||||||
/** 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) {
|
||||||
oledDisplay.setText("Press now for",
|
oledDisplay.setText(
|
||||||
configuration.isOfflineMode() ? "online mode" : "offline mode", "");
|
"Press now for",
|
||||||
|
configuration.isOfflineMode() ? "online mode" : "offline mode", "");
|
||||||
uint32_t startTime = millis();
|
uint32_t startTime = millis();
|
||||||
while (true) {
|
while (true) {
|
||||||
if (ag->button.getState() == ag->button.BUTTON_PRESSED) {
|
if (ag->button.getState() == ag->button.BUTTON_PRESSED) {
|
||||||
configuration.setOfflineMode(!configuration.isOfflineMode());
|
configuration.setOfflineMode(!configuration.isOfflineMode());
|
||||||
|
|
||||||
oledDisplay.setText("Offline Mode",
|
oledDisplay.setText(
|
||||||
configuration.isOfflineMode() ? " = True" : " = False", "");
|
"Offline Mode",
|
||||||
|
configuration.isOfflineMode() ? " = True" : " = False", "");
|
||||||
delay(1000);
|
delay(1000);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -247,7 +256,12 @@ void setup() {
|
|||||||
if (connectToNetwork) {
|
if (connectToNetwork) {
|
||||||
oledDisplay.setText("Initialize", "network...", "");
|
oledDisplay.setText("Initialize", "network...", "");
|
||||||
initializeNetwork();
|
initializeNetwork();
|
||||||
wifiConnector.stopBLE();
|
}
|
||||||
|
|
||||||
|
/** Set offline mode without saving, cause wifi is not configured */
|
||||||
|
if (wifiConnector.hasConfigurated() == false && networkOption == UseWifi) {
|
||||||
|
Serial.println("Set offline mode cause wifi is not configurated");
|
||||||
|
configuration.setOfflineModeWithoutSave(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Show display Warning up */
|
/** Show display Warning up */
|
||||||
@@ -260,6 +274,7 @@ void setup() {
|
|||||||
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (networkOption == UseCellular) {
|
if (networkOption == UseCellular) {
|
||||||
// If using cellular re-set scheduler interval
|
// If using cellular re-set scheduler interval
|
||||||
configSchedule.setPeriod(CELLULAR_SERVER_CONFIG_SYNC_INTERVAL);
|
configSchedule.setPeriod(CELLULAR_SERVER_CONFIG_SYNC_INTERVAL);
|
||||||
@@ -276,7 +291,7 @@ void setup() {
|
|||||||
// Only run network task if monitor is not in offline mode
|
// Only run network task if monitor is not in offline mode
|
||||||
if (configuration.isOfflineMode() == false) {
|
if (configuration.isOfflineMode() == false) {
|
||||||
BaseType_t xReturned =
|
BaseType_t xReturned =
|
||||||
xTaskCreate(networkingTask, "NetworkingTask", 4096, null, 5, &handleNetworkTask);
|
xTaskCreate(networkingTask, "NetworkingTask", 4096, null, 5, &handleNetworkTask);
|
||||||
if (xReturned == pdPASS) {
|
if (xReturned == pdPASS) {
|
||||||
Serial.println("Success create networking task");
|
Serial.println("Success create networking task");
|
||||||
} else {
|
} else {
|
||||||
@@ -287,9 +302,11 @@ void setup() {
|
|||||||
// Log monitor mode for debugging purpose
|
// Log monitor mode for debugging purpose
|
||||||
if (configuration.isOfflineMode()) {
|
if (configuration.isOfflineMode()) {
|
||||||
Serial.println("Running monitor in offline mode");
|
Serial.println("Running monitor in offline mode");
|
||||||
} else if (configuration.isCloudConnectionDisabled()) {
|
}
|
||||||
|
else if (configuration.isCloudConnectionDisabled()) {
|
||||||
Serial.println("Running monitor without connection to AirGradient server");
|
Serial.println("Running monitor without connection to AirGradient server");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
@@ -336,7 +353,7 @@ void loop() {
|
|||||||
static bool pmsConnected = false;
|
static bool pmsConnected = false;
|
||||||
if (pmsConnected != ag->pms5003.connected()) {
|
if (pmsConnected != ag->pms5003.connected()) {
|
||||||
pmsConnected = ag->pms5003.connected();
|
pmsConnected = ag->pms5003.connected();
|
||||||
Serial.printf("PMS sensor %s \n", pmsConnected ? "connected" : "removed");
|
Serial.printf("PMS sensor %s \n", pmsConnected?"connected":"removed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -375,7 +392,9 @@ static void co2Update(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void printMeasurements() { measurements.printCurrentAverage(); }
|
void printMeasurements() {
|
||||||
|
measurements.printCurrentAverage();
|
||||||
|
}
|
||||||
|
|
||||||
static void mdnsInit(void) {
|
static void mdnsInit(void) {
|
||||||
if (!MDNS.begin(localServer.getHostname().c_str())) {
|
if (!MDNS.begin(localServer.getHostname().c_str())) {
|
||||||
@@ -384,7 +403,8 @@ static void mdnsInit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MDNS.addService("_airgradient", "_tcp", 80);
|
MDNS.addService("_airgradient", "_tcp", 80);
|
||||||
MDNS.addServiceTxt("_airgradient", "_tcp", "model", AgFirmwareModeName(fwMode));
|
MDNS.addServiceTxt("_airgradient", "_tcp", "model",
|
||||||
|
AgFirmwareModeName(fwMode));
|
||||||
MDNS.addServiceTxt("_airgradient", "_tcp", "serialno", ag->deviceId());
|
MDNS.addServiceTxt("_airgradient", "_tcp", "serialno", ag->deviceId());
|
||||||
MDNS.addServiceTxt("_airgradient", "_tcp", "fw_ver", ag->getVersion());
|
MDNS.addServiceTxt("_airgradient", "_tcp", "fw_ver", ag->getVersion());
|
||||||
MDNS.addServiceTxt("_airgradient", "_tcp", "vendor", "AirGradient");
|
MDNS.addServiceTxt("_airgradient", "_tcp", "vendor", "AirGradient");
|
||||||
@@ -408,7 +428,8 @@ static void createMqttTask(void) {
|
|||||||
String payload = measurements.toString(true, fwMode, wifiConnector.RSSI());
|
String payload = measurements.toString(true, fwMode, wifiConnector.RSSI());
|
||||||
String topic = "airgradient/readings/" + ag->deviceId();
|
String topic = "airgradient/readings/" + ag->deviceId();
|
||||||
|
|
||||||
if (mqttClient.publish(topic.c_str(), payload.c_str(), payload.length())) {
|
if (mqttClient.publish(topic.c_str(), payload.c_str(),
|
||||||
|
payload.length())) {
|
||||||
Serial.println("MQTT sync success");
|
Serial.println("MQTT sync success");
|
||||||
} else {
|
} else {
|
||||||
Serial.println("MQTT sync failure");
|
Serial.println("MQTT sync failure");
|
||||||
@@ -426,7 +447,8 @@ static void createMqttTask(void) {
|
|||||||
static void initMqtt(void) {
|
static void initMqtt(void) {
|
||||||
String mqttUri = configuration.getMqttBrokerUri();
|
String mqttUri = configuration.getMqttBrokerUri();
|
||||||
if (mqttUri.isEmpty()) {
|
if (mqttUri.isEmpty()) {
|
||||||
Serial.println("MQTT is not configured, skipping initialization of MQTT client");
|
Serial.println(
|
||||||
|
"MQTT is not configured, skipping initialization of MQTT client");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,7 +509,7 @@ static void factoryConfigReset(void) {
|
|||||||
Serial.println("Factory reset successful");
|
Serial.println("Factory reset successful");
|
||||||
}
|
}
|
||||||
delay(3000);
|
delay(3000);
|
||||||
oledDisplay.setText("", "", "");
|
oledDisplay.setText("","","");
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -525,7 +547,7 @@ static void ledBarEnabledUpdate(void) {
|
|||||||
ag->ledBar.setBrightness(brightness);
|
ag->ledBar.setBrightness(brightness);
|
||||||
ag->ledBar.setEnable(configuration.getLedBarMode() != LedBarModeOff);
|
ag->ledBar.setEnable(configuration.getLedBarMode() != LedBarModeOff);
|
||||||
}
|
}
|
||||||
ag->ledBar.show();
|
ag->ledBar.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,11 +618,11 @@ void otaHandlerCallback(AirgradientOTA::OtaResult result, const char *msg) {
|
|||||||
displayExecuteOta(result, "", std::stoi(msg));
|
displayExecuteOta(result, "", std::stoi(msg));
|
||||||
break;
|
break;
|
||||||
case AirgradientOTA::Failed:
|
case AirgradientOTA::Failed:
|
||||||
displayExecuteOta(result, "", 0);
|
displayExecuteOta(result, "", 0);
|
||||||
if (configuration.hasSensorSGP && networkOption == UseCellular) {
|
if (configuration.hasSensorSGP && networkOption == UseCellular) {
|
||||||
ag->sgp41.resume();
|
ag->sgp41.resume();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AirgradientOTA::Skipped:
|
case AirgradientOTA::Skipped:
|
||||||
case AirgradientOTA::AlreadyUpToDate:
|
case AirgradientOTA::AlreadyUpToDate:
|
||||||
displayExecuteOta(result, "", 0);
|
displayExecuteOta(result, "", 0);
|
||||||
@@ -616,7 +638,7 @@ void otaHandlerCallback(AirgradientOTA::OtaResult result, const char *msg) {
|
|||||||
|
|
||||||
static void displayExecuteOta(AirgradientOTA::OtaResult result, String msg, int processing) {
|
static void displayExecuteOta(AirgradientOTA::OtaResult result, String msg, int processing) {
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case AirgradientOTA::Starting:
|
case AirgradientOTA::Starting:
|
||||||
if (ag->isOne()) {
|
if (ag->isOne()) {
|
||||||
oledDisplay.showFirmwareUpdateVersion(msg);
|
oledDisplay.showFirmwareUpdateVersion(msg);
|
||||||
} else {
|
} else {
|
||||||
@@ -685,7 +707,6 @@ static void sendDataToAg() {
|
|||||||
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnecting);
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnecting);
|
||||||
}
|
}
|
||||||
stateMachine.handleLeds(AgStateMachineWiFiOkServerConnecting);
|
stateMachine.handleLeds(AgStateMachineWiFiOkServerConnecting);
|
||||||
wifiConnector.bleNotifyStatus(PROV_CONNECTING_TO_SERVER);
|
|
||||||
|
|
||||||
/** Task handle led connecting animation */
|
/** Task handle led connecting animation */
|
||||||
xTaskCreate(
|
xTaskCreate(
|
||||||
@@ -693,7 +714,8 @@ static void sendDataToAg() {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
// ledSmHandler();
|
// ledSmHandler();
|
||||||
stateMachine.handleLeds();
|
stateMachine.handleLeds();
|
||||||
if (stateMachine.getLedState() != AgStateMachineWiFiOkServerConnecting) {
|
if (stateMachine.getLedState() !=
|
||||||
|
AgStateMachineWiFiOkServerConnecting) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
delay(LED_BAR_ANIMATION_PERIOD);
|
delay(LED_BAR_ANIMATION_PERIOD);
|
||||||
@@ -714,13 +736,11 @@ static void sendDataToAg() {
|
|||||||
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnected);
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnected);
|
||||||
}
|
}
|
||||||
stateMachine.handleLeds(AgStateMachineWiFiOkServerConnected);
|
stateMachine.handleLeds(AgStateMachineWiFiOkServerConnected);
|
||||||
wifiConnector.bleNotifyStatus(PROV_SERVER_REACHABLE);
|
|
||||||
} else {
|
} else {
|
||||||
if (ag->isOne()) {
|
if (ag->isOne()) {
|
||||||
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnectFailed);
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnectFailed);
|
||||||
}
|
}
|
||||||
stateMachine.handleLeds(AgStateMachineWiFiOkServerConnectFailed);
|
stateMachine.handleLeds(AgStateMachineWiFiOkServerConnectFailed);
|
||||||
wifiConnector.bleNotifyStatus(PROV_ERR_SERVER_UNREACHABLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stateMachine.handleLeds(AgStateMachineNormal);
|
stateMachine.handleLeds(AgStateMachineNormal);
|
||||||
@@ -741,7 +761,8 @@ static void oneIndoorInit(void) {
|
|||||||
/** Show boot display */
|
/** Show boot display */
|
||||||
Serial.println("Firmware Version: " + ag->getVersion());
|
Serial.println("Firmware Version: " + ag->getVersion());
|
||||||
|
|
||||||
oledDisplay.setText("AirGradient ONE", "FW Version: ", ag->getVersion().c_str());
|
oledDisplay.setText("AirGradient ONE",
|
||||||
|
"FW Version: ", ag->getVersion().c_str());
|
||||||
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
||||||
|
|
||||||
ag->ledBar.begin();
|
ag->ledBar.begin();
|
||||||
@@ -772,9 +793,9 @@ static void oneIndoorInit(void) {
|
|||||||
WiFi.begin("airgradient", "cleanair");
|
WiFi.begin("airgradient", "cleanair");
|
||||||
oledDisplay.setText("Configure WiFi", "connect to", "\'airgradient\'");
|
oledDisplay.setText("Configure WiFi", "connect to", "\'airgradient\'");
|
||||||
delay(2500);
|
delay(2500);
|
||||||
oledDisplay.setText("Rebooting...", "", "");
|
oledDisplay.setText("Rebooting...", "","");
|
||||||
delay(2500);
|
delay(2500);
|
||||||
oledDisplay.setText("", "", "");
|
oledDisplay.setText("","","");
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -900,7 +921,8 @@ static void openAirInit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fwMode == FW_MODE_O_1PP) {
|
if (fwMode == FW_MODE_O_1PP) {
|
||||||
int count = (configuration.hasSensorPMS1 ? 1 : 0) + (configuration.hasSensorPMS2 ? 1 : 0);
|
int count = (configuration.hasSensorPMS1 ? 1 : 0) +
|
||||||
|
(configuration.hasSensorPMS2 ? 1 : 0);
|
||||||
if (count == 1) {
|
if (count == 1) {
|
||||||
fwMode = FW_MODE_O_1P;
|
fwMode = FW_MODE_O_1P;
|
||||||
}
|
}
|
||||||
@@ -930,6 +952,8 @@ static void boardInit(void) {
|
|||||||
} else {
|
} else {
|
||||||
Serial.println("Set S8 AbcDays failure");
|
Serial.println("Set S8 AbcDays failure");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ag->s8.printInformation();
|
||||||
}
|
}
|
||||||
|
|
||||||
localServer.setFwMode(fwMode);
|
localServer.setFwMode(fwMode);
|
||||||
@@ -974,7 +998,7 @@ void initializeNetwork() {
|
|||||||
delay(2500);
|
delay(2500);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!agClient->begin(ag->deviceId().c_str())) {
|
if (!agClient->begin(ag->deviceId().c_str(), AirgradientClient::ONE_OPENAIR)) {
|
||||||
oledDisplay.setText("Client", "initialization", "failed");
|
oledDisplay.setText("Client", "initialization", "failed");
|
||||||
delay(5000);
|
delay(5000);
|
||||||
oledDisplay.showRebooting();
|
oledDisplay.showRebooting();
|
||||||
@@ -992,18 +1016,22 @@ void initializeNetwork() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (networkOption == UseWifi) {
|
if (networkOption == UseWifi) {
|
||||||
String modelName = AgFirmwareModeName(fwMode);
|
if (!wifiConnector.connect()) {
|
||||||
if (!wifiConnector.connect(modelName)) {
|
|
||||||
Serial.println("Cannot initiate wifi connection");
|
Serial.println("Cannot initiate wifi connection");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wifiConnector.isConnected()) {
|
if (!wifiConnector.isConnected()) {
|
||||||
Serial.println("Failed connect to WiFi");
|
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
|
// Initiate local network configuration
|
||||||
@@ -1029,7 +1057,7 @@ void initializeNetwork() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string config = agClient->httpFetchConfig();
|
std::string config = agClient->coapFetchConfig();
|
||||||
configSchedule.update();
|
configSchedule.update();
|
||||||
// Check if fetch configuration failed or fetch succes but parsing failed
|
// Check if fetch configuration failed or fetch succes but parsing failed
|
||||||
if (agClient->isLastFetchConfigSucceed() == false ||
|
if (agClient->isLastFetchConfigSucceed() == false ||
|
||||||
@@ -1038,28 +1066,27 @@ void initializeNetwork() {
|
|||||||
if (agClient->isRegisteredOnAgServer() == false) {
|
if (agClient->isRegisteredOnAgServer() == false) {
|
||||||
stateMachine.displaySetAddToDashBoard();
|
stateMachine.displaySetAddToDashBoard();
|
||||||
stateMachine.displayHandle(AgStateMachineWiFiOkServerOkSensorConfigFailed);
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerOkSensorConfigFailed);
|
||||||
wifiConnector.bleNotifyStatus(PROV_ERR_MONITOR_NOT_REGISTERED);
|
|
||||||
} else {
|
} else {
|
||||||
stateMachine.displayClearAddToDashBoard();
|
stateMachine.displayClearAddToDashBoard();
|
||||||
wifiConnector.bleNotifyStatus(PROV_ERR_GET_MONITOR_CONFIG_FAILED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stateMachine.handleLeds(AgStateMachineWiFiOkServerOkSensorConfigFailed);
|
stateMachine.handleLeds(AgStateMachineWiFiOkServerOkSensorConfigFailed);
|
||||||
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
ledBarEnabledUpdate();
|
ledBarEnabledUpdate();
|
||||||
wifiConnector.bleNotifyStatus(PROV_MONITOR_CONFIGURED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void configurationUpdateSchedule(void) {
|
static void configurationUpdateSchedule(void) {
|
||||||
if (configuration.getConfigurationControl() == ConfigurationControl::ConfigurationControlLocal) {
|
if (configuration.getConfigurationControl() ==
|
||||||
|
ConfigurationControl::ConfigurationControlLocal) {
|
||||||
Serial.println("Ignore fetch server configuration, configurationControl set to local");
|
Serial.println("Ignore fetch server configuration, configurationControl set to local");
|
||||||
agClient->resetFetchConfigurationStatus();
|
agClient->resetFetchConfigurationStatus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string config = agClient->httpFetchConfig();
|
std::string config = agClient->coapFetchConfig();
|
||||||
if (agClient->isLastFetchConfigSucceed()) {
|
if (agClient->isLastFetchConfigSucceed()) {
|
||||||
configuration.parse(config.c_str(), false);
|
configuration.parse(config.c_str(), false);
|
||||||
}
|
}
|
||||||
@@ -1087,7 +1114,8 @@ static void configUpdateHandle() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (configuration.hasSensorSGP) {
|
if (configuration.hasSensorSGP) {
|
||||||
if (configuration.noxLearnOffsetChanged() || configuration.tvocLearnOffsetChanged()) {
|
if (configuration.noxLearnOffsetChanged() ||
|
||||||
|
configuration.tvocLearnOffsetChanged()) {
|
||||||
ag->sgp41.end();
|
ag->sgp41.end();
|
||||||
|
|
||||||
int oldTvocOffset = ag->sgp41.getTvocLearningOffset();
|
int oldTvocOffset = ag->sgp41.getTvocLearningOffset();
|
||||||
@@ -1098,12 +1126,14 @@ static void configUpdateHandle() {
|
|||||||
resultStr = "failure";
|
resultStr = "failure";
|
||||||
}
|
}
|
||||||
if (oldTvocOffset != configuration.getTvocLearningOffset()) {
|
if (oldTvocOffset != configuration.getTvocLearningOffset()) {
|
||||||
Serial.printf("Setting tvocLearningOffset from %d to %d hours %s\r\n", oldTvocOffset,
|
Serial.printf("Setting tvocLearningOffset from %d to %d hours %s\r\n",
|
||||||
configuration.getTvocLearningOffset(), resultStr);
|
oldTvocOffset, configuration.getTvocLearningOffset(),
|
||||||
|
resultStr);
|
||||||
}
|
}
|
||||||
if (oldNoxOffset != configuration.getNoxLearningOffset()) {
|
if (oldNoxOffset != configuration.getNoxLearningOffset()) {
|
||||||
Serial.printf("Setting noxLearningOffset from %d to %d hours %s\r\n", oldNoxOffset,
|
Serial.printf("Setting noxLearningOffset from %d to %d hours %s\r\n",
|
||||||
configuration.getNoxLearningOffset(), resultStr);
|
oldNoxOffset, configuration.getNoxLearningOffset(),
|
||||||
|
resultStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1125,7 +1155,7 @@ static void configUpdateHandle() {
|
|||||||
if (configuration.getLedBarBrightness() == 0) {
|
if (configuration.getLedBarBrightness() == 0) {
|
||||||
ag->ledBar.setEnable(false);
|
ag->ledBar.setEnable(false);
|
||||||
} else {
|
} else {
|
||||||
if (configuration.getLedBarMode() == LedBarMode::LedBarModeOff) {
|
if(configuration.getLedBarMode() == LedBarMode::LedBarModeOff) {
|
||||||
ag->ledBar.setEnable(false);
|
ag->ledBar.setEnable(false);
|
||||||
} else {
|
} else {
|
||||||
ag->ledBar.setEnable(true);
|
ag->ledBar.setEnable(true);
|
||||||
@@ -1163,8 +1193,9 @@ static void updateDisplayAndLedBar(void) {
|
|||||||
stateMachine.handleLeds(AgStateMachineWiFiLost);
|
stateMachine.handleLeds(AgStateMachineWiFiLost);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (networkOption == UseCellular) {
|
}
|
||||||
if (agClient->isClientReady() == false) {
|
else if (networkOption == UseCellular) {
|
||||||
|
if (agClient->isClientReady() == false) {
|
||||||
// Same action as wifi
|
// Same action as wifi
|
||||||
stateMachine.displayHandle(AgStateMachineWiFiLost);
|
stateMachine.displayHandle(AgStateMachineWiFiLost);
|
||||||
stateMachine.handleLeds(AgStateMachineWiFiLost);
|
stateMachine.handleLeds(AgStateMachineWiFiLost);
|
||||||
@@ -1361,8 +1392,8 @@ void postUsingWifi() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* forcePost to force post without checking transmit cycle
|
* forcePost to force post without checking transmit cycle
|
||||||
*/
|
*/
|
||||||
void postUsingCellular(bool forcePost) {
|
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);
|
||||||
@@ -1385,18 +1416,19 @@ void postUsingCellular(bool forcePost) {
|
|||||||
|
|
||||||
// Build payload include all measurements from queue
|
// Build payload include all measurements from queue
|
||||||
std::string payload;
|
std::string payload;
|
||||||
|
bool extendPmMeasures = configuration.isExtendedPmMeasuresEnabled();
|
||||||
payload += std::to_string(CELLULAR_MEASUREMENT_INTERVAL / 1000); // Convert to seconds
|
payload += std::to_string(CELLULAR_MEASUREMENT_INTERVAL / 1000); // Convert to seconds
|
||||||
for (int i = 0; i < queueSize; i++) {
|
for (int i = 0; i < queueSize; i++) {
|
||||||
auto mc = measurementCycleQueue.at(i);
|
auto mc = measurementCycleQueue.at(i);
|
||||||
payload += ",";
|
payload += ",";
|
||||||
payload += measurements.buildMeasuresPayload(mc);
|
payload += measurements.buildMeasuresPayload(mc, extendPmMeasures);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release before actually post measures that might takes too long
|
// Release before actually post measures that might takes too long
|
||||||
xSemaphoreGive(mutexMeasurementCycleQueue);
|
xSemaphoreGive(mutexMeasurementCycleQueue);
|
||||||
|
|
||||||
// Attempt to send
|
// Attempt to send
|
||||||
if (agClient->httpPostMeasures(payload) == false) {
|
if (agClient->coapPostMeasures(payload) == false) {
|
||||||
// Consider network has a problem, retry in next schedule
|
// Consider network has a problem, retry in next schedule
|
||||||
Serial.println("Post measures failed, retry in next schedule");
|
Serial.println("Post measures failed, retry in next schedule");
|
||||||
return;
|
return;
|
||||||
@@ -1502,6 +1534,7 @@ int calculateMaxPeriod(int updateInterval) {
|
|||||||
return (WIFI_MEASUREMENT_INTERVAL - (WIFI_MEASUREMENT_INTERVAL * 0.8)) / updateInterval;
|
return (WIFI_MEASUREMENT_INTERVAL - (WIFI_MEASUREMENT_INTERVAL * 0.8)) / updateInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void networkSignalCheck() {
|
void networkSignalCheck() {
|
||||||
if (networkOption == UseWifi) {
|
if (networkOption == UseWifi) {
|
||||||
Serial.printf("WiFi RSSI %d\n", wifiConnector.RSSI());
|
Serial.printf("WiFi RSSI %d\n", wifiConnector.RSSI());
|
||||||
@@ -1527,11 +1560,12 @@ void networkSignalCheck() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If in 2 hours cellular client still not ready, then restart system
|
* If in 2 hours cellular client still not ready, then restart system
|
||||||
*/
|
*/
|
||||||
void restartIfCeClientIssueOverTwoHours() {
|
void restartIfCeClientIssueOverTwoHours() {
|
||||||
if (agCeClientProblemDetectedTime > 0 &&
|
if (agCeClientProblemDetectedTime > 0 &&
|
||||||
(MINUTES() - agCeClientProblemDetectedTime) > TIMEOUT_WAIT_FOR_CELLULAR_MODULE_READY) {
|
(MINUTES() - agCeClientProblemDetectedTime) >
|
||||||
|
TIMEOUT_WAIT_FOR_CELLULAR_MODULE_READY) {
|
||||||
// Give up wait
|
// Give up wait
|
||||||
Serial.println("Rebooting because CE client issues for 2 hours detected");
|
Serial.println("Rebooting because CE client issues for 2 hours detected");
|
||||||
int i = 3;
|
int i = 3;
|
||||||
@@ -1582,7 +1616,8 @@ void networkingTask(void *args) {
|
|||||||
delay(1000);
|
delay(1000);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if (networkOption == UseCellular) {
|
}
|
||||||
|
else if (networkOption == UseCellular) {
|
||||||
if (agClient->isClientReady() == false) {
|
if (agClient->isClientReady() == false) {
|
||||||
// Start time if value still default
|
// Start time if value still default
|
||||||
if (agCeClientProblemDetectedTime == 0) {
|
if (agCeClientProblemDetectedTime == 0) {
|
||||||
@@ -1620,7 +1655,7 @@ void networkingTask(void *args) {
|
|||||||
|
|
||||||
// Client is ready
|
// Client is ready
|
||||||
agCeClientProblemDetectedTime = 0; // reset to default
|
agCeClientProblemDetectedTime = 0; // reset to default
|
||||||
agSerial->setDebug(false); // disable at command debug
|
agSerial->setDebug(false); // disable at command debug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1662,3 +1697,4 @@ void newMeasurementCycle() {
|
|||||||
Serial.printf("Free heap: %u\n", ESP.getFreeHeap());
|
Serial.printf("Free heap: %u\n", ESP.getFreeHeap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ lib_deps =
|
|||||||
WiFiClientSecure
|
WiFiClientSecure
|
||||||
Update
|
Update
|
||||||
DNSServer
|
DNSServer
|
||||||
h2zero/NimBLE-Arduino@^2.1.0
|
|
||||||
|
|
||||||
[env:esp8266]
|
[env:esp8266]
|
||||||
platform = espressif8266
|
platform = espressif8266
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ JSON_PROP_DEF(monitorDisplayCompensatedValues);
|
|||||||
JSON_PROP_DEF(corrections);
|
JSON_PROP_DEF(corrections);
|
||||||
JSON_PROP_DEF(atmp);
|
JSON_PROP_DEF(atmp);
|
||||||
JSON_PROP_DEF(rhum);
|
JSON_PROP_DEF(rhum);
|
||||||
|
JSON_PROP_DEF(extendedPmMeasures);
|
||||||
|
|
||||||
#define jprop_model_default ""
|
#define jprop_model_default ""
|
||||||
#define jprop_country_default "TH"
|
#define jprop_country_default "TH"
|
||||||
@@ -78,6 +79,7 @@ JSON_PROP_DEF(rhum);
|
|||||||
#define jprop_displayBrightness_default 100
|
#define jprop_displayBrightness_default 100
|
||||||
#define jprop_offlineMode_default false
|
#define jprop_offlineMode_default false
|
||||||
#define jprop_monitorDisplayCompensatedValues_default false
|
#define jprop_monitorDisplayCompensatedValues_default false
|
||||||
|
#define jprop_extendedPmMeasures_default false
|
||||||
|
|
||||||
JSONVar jconfig;
|
JSONVar jconfig;
|
||||||
|
|
||||||
@@ -400,6 +402,7 @@ void Configuration::defaultConfig(void) {
|
|||||||
jconfig[jprop_model] = jprop_model_default;
|
jconfig[jprop_model] = jprop_model_default;
|
||||||
jconfig[jprop_offlineMode] = jprop_offlineMode_default;
|
jconfig[jprop_offlineMode] = jprop_offlineMode_default;
|
||||||
jconfig[jprop_monitorDisplayCompensatedValues] = jprop_monitorDisplayCompensatedValues_default;
|
jconfig[jprop_monitorDisplayCompensatedValues] = jprop_monitorDisplayCompensatedValues_default;
|
||||||
|
jconfig[jprop_extendedPmMeasures] = jprop_extendedPmMeasures_default;
|
||||||
|
|
||||||
// PM2.5 default correction
|
// PM2.5 default correction
|
||||||
pmCorrection.algorithm = COR_ALGO_PM_NONE;
|
pmCorrection.algorithm = COR_ALGO_PM_NONE;
|
||||||
@@ -940,6 +943,26 @@ bool Configuration::parse(String data, bool isLocal) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (JSON.typeof_(root[jprop_extendedPmMeasures]) == "boolean") {
|
||||||
|
bool value = root[jprop_extendedPmMeasures];
|
||||||
|
bool oldValue = jconfig[jprop_extendedPmMeasures];
|
||||||
|
if (value != oldValue) {
|
||||||
|
changed = true;
|
||||||
|
configLogInfo(String(jprop_extendedPmMeasures),
|
||||||
|
String(oldValue ? "true" : "false"),
|
||||||
|
String(value ? "true" : "false"));
|
||||||
|
jconfig[jprop_extendedPmMeasures] = value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (jsonTypeInvalid(root[jprop_extendedPmMeasures], "boolean")) {
|
||||||
|
failedMessage = jsonTypeInvalidMessage(
|
||||||
|
String(jprop_extendedPmMeasures), "boolean");
|
||||||
|
jsonInvalid();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// PM2.5 Corrections
|
// PM2.5 Corrections
|
||||||
if (updatePmCorrection(root)) {
|
if (updatePmCorrection(root)) {
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -1002,6 +1025,11 @@ bool Configuration::isTemperatureUnitInF(void) {
|
|||||||
return (unit == "f");
|
return (unit == "f");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Configuration::isExtendedPmMeasuresEnabled(void) {
|
||||||
|
return jconfig[jprop_extendedPmMeasures];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Country name, it's short name ex: TH = Thailand
|
* @brief Country name, it's short name ex: TH = Thailand
|
||||||
*
|
*
|
||||||
@@ -1368,6 +1396,18 @@ void Configuration::toConfig(const char *buf) {
|
|||||||
logInfo("toConfig: disableCloudConnection changed");
|
logInfo("toConfig: disableCloudConnection changed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** validate extendedPmMeasures configuration */
|
||||||
|
if (JSON.typeof_(jconfig[jprop_extendedPmMeasures]) != "boolean") {
|
||||||
|
isConfigFieldInvalid = true;
|
||||||
|
} else {
|
||||||
|
isConfigFieldInvalid = false;
|
||||||
|
}
|
||||||
|
if (isConfigFieldInvalid) {
|
||||||
|
jconfig[jprop_extendedPmMeasures] = jprop_extendedPmMeasures_default;
|
||||||
|
changed = true;
|
||||||
|
logInfo("toConfig: extendedPmMeasures changed");
|
||||||
|
}
|
||||||
|
|
||||||
/** validate configuration control */
|
/** validate configuration control */
|
||||||
if (JSON.typeof_(jprop_configurationControl) != "string") {
|
if (JSON.typeof_(jprop_configurationControl) != "string") {
|
||||||
isConfigFieldInvalid = true;
|
isConfigFieldInvalid = true;
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ public:
|
|||||||
String toString(void);
|
String toString(void);
|
||||||
String toString(AgFirmwareMode fwMode);
|
String toString(AgFirmwareMode fwMode);
|
||||||
bool isTemperatureUnitInF(void);
|
bool isTemperatureUnitInF(void);
|
||||||
|
bool isExtendedPmMeasuresEnabled(void);
|
||||||
String getCountry(void);
|
String getCountry(void);
|
||||||
bool isPmStandardInUSAQI(void);
|
bool isPmStandardInUSAQI(void);
|
||||||
int getCO2CalibrationAbcDays(void);
|
int getCO2CalibrationAbcDays(void);
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ static unsigned char OFFLINE_BITS[] = {
|
|||||||
0xE6, 0x00, 0xFE, 0x1F, 0xFE, 0x1F, 0xE6, 0x00, 0x62, 0x00,
|
0xE6, 0x00, 0xFE, 0x1F, 0xFE, 0x1F, 0xE6, 0x00, 0x62, 0x00,
|
||||||
0x30, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x30, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
};
|
};
|
||||||
|
// {
|
||||||
|
// 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x60, 0x00, 0x62, 0x00, 0xE2, 0x00,
|
||||||
|
// 0xFE, 0x1F, 0xFE, 0x1F, 0xE2, 0x00, 0x62, 0x00, 0x60, 0x00, 0x30, 0x00,
|
||||||
|
// 0x00, 0x00, 0x00, 0x00, };
|
||||||
/**
|
/**
|
||||||
* @brief Show dashboard temperature and humdity
|
* @brief Show dashboard temperature and humdity
|
||||||
*
|
*
|
||||||
@@ -267,37 +270,6 @@ void OledDisplay::setText(const char *line1, const char *line2,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OledDisplay::showWiFiProvisioning(bool firstRun, int countdown) {
|
|
||||||
if (firstRun) {
|
|
||||||
DISP()->clearBuffer();
|
|
||||||
DISP()->setFont(u8g2_font_t0_16_tf);
|
|
||||||
DISP()->drawStr(1, 25, "to WiFi hotspot:");
|
|
||||||
DISP()->drawStr(1, 40, "\"airgradient-");
|
|
||||||
DISP()->drawStr(1, 55, (ag->deviceId() + "\"").c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now just update countdown area
|
|
||||||
char buf[16];
|
|
||||||
snprintf(buf, sizeof(buf), "%ds to connect", countdown);
|
|
||||||
DISP()->setDrawColor(0); // erase previous text
|
|
||||||
DISP()->drawBox(0, 0, 128, 14); // clear top region
|
|
||||||
DISP()->setDrawColor(1); // draw new text in white
|
|
||||||
DISP()->setFont(u8g2_font_t0_16_tf);
|
|
||||||
DISP()->drawStr(1, 10, buf);
|
|
||||||
|
|
||||||
// Blink the BLE mark section
|
|
||||||
if (countdown % 2 == 0) {
|
|
||||||
DISP()->setFont(u8g2_font_t0_12b_tf);
|
|
||||||
DISP()->drawStr(108, 60, "BLE");
|
|
||||||
} else {
|
|
||||||
DISP()->setDrawColor(0);
|
|
||||||
DISP()->drawBox(108, 48, 20, 16);
|
|
||||||
DISP()->setDrawColor(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
DISP()->sendBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Update dashboard content
|
* @brief Update dashboard content
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ public:
|
|||||||
void setText(String &line1, String &line2, String &line3, String &line4);
|
void setText(String &line1, String &line2, String &line3, String &line4);
|
||||||
void setText(const char *line1, const char *line2, const char *line3,
|
void setText(const char *line1, const char *line2, const char *line3,
|
||||||
const char *line4);
|
const char *line4);
|
||||||
void showWiFiProvisioning(bool firstRun, int countdown);
|
|
||||||
void showDashboard(void);
|
void showDashboard(void);
|
||||||
void showDashboard(DashboardStatus status);
|
void showDashboard(DashboardStatus status);
|
||||||
void setBrightness(int percent);
|
void setBrightness(int percent);
|
||||||
|
|||||||
@@ -494,10 +494,13 @@ void StateMachine::displayHandle(AgStateMachineState state) {
|
|||||||
if (ag->isBasic()) {
|
if (ag->isBasic()) {
|
||||||
String ssid = "\"airgradient-" + ag->deviceId() + "\" " +
|
String ssid = "\"airgradient-" + ag->deviceId() + "\" " +
|
||||||
String(wifiConnectCountDown) + String("s");
|
String(wifiConnectCountDown) + String("s");
|
||||||
disp.setText("Connect to hotspot:", ssid.c_str(), "");
|
disp.setText("Connect tohotspot:", ssid.c_str(), "");
|
||||||
} else {
|
} else {
|
||||||
// NOTE: This bool is hardcoded!
|
String line1 = String(wifiConnectCountDown) + "s to connect";
|
||||||
disp.showWiFiProvisioning((wifiConnectCountDown == 180), wifiConnectCountDown);
|
String line2 = "to WiFi hotspot:";
|
||||||
|
String line3 = "\"airgradient-";
|
||||||
|
String line4 = ag->deviceId() + "\"";
|
||||||
|
disp.setText(line1, line2, line3, line4);
|
||||||
}
|
}
|
||||||
wifiConnectCountDown--;
|
wifiConnectCountDown--;
|
||||||
}
|
}
|
||||||
@@ -645,7 +648,7 @@ void StateMachine::handleLeds(AgStateMachineState state) {
|
|||||||
ag->ledBar.clear();
|
ag->ledBar.clear();
|
||||||
ag->ledBar.setColor(0, 0, 255, ag->ledBar.getNumberOfLeds() / 2);
|
ag->ledBar.setColor(0, 0, 255, ag->ledBar.getNumberOfLeds() / 2);
|
||||||
} else {
|
} else {
|
||||||
ag->statusLed.setStep();
|
ag->statusLed.setToggle();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -885,7 +885,7 @@ Measurements::Measures Measurements::getMeasures() {
|
|||||||
return mc;
|
return mc;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Measurements::buildMeasuresPayload(Measures &mc) {
|
std::string Measurements::buildMeasuresPayload(Measures &mc, bool extendedPmMeasures) {
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
|
|
||||||
// CO2
|
// CO2
|
||||||
@@ -984,6 +984,76 @@ std::string Measurements::buildMeasuresPayload(Measures &mc) {
|
|||||||
oss << mc.signal;
|
oss << mc.signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (extendedPmMeasures) {
|
||||||
|
oss << ",,,,,,,,"; // Add placeholder for MAX payload (BMS & O3/NO2)
|
||||||
|
|
||||||
|
/// PM 0.5 particle count
|
||||||
|
if (utils::isValidPm03Count(mc.pm_05_pc[0]) && utils::isValidPm03Count(mc.pm_05_pc[1])) {
|
||||||
|
oss << std::round((mc.pm_05_pc[0] + mc.pm_05_pc[1]) / 2.0f);
|
||||||
|
} else if (utils::isValidPm03Count(mc.pm_05_pc[0])) {
|
||||||
|
oss << std::round(mc.pm_05_pc[0]);
|
||||||
|
} else if (utils::isValidPm03Count(mc.pm_05_pc[1])) {
|
||||||
|
oss << std::round(mc.pm_05_pc[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
/// PM 1.0 particle count
|
||||||
|
if (utils::isValidPm03Count(mc.pm_01_pc[0]) && utils::isValidPm03Count(mc.pm_01_pc[1])) {
|
||||||
|
oss << std::round((mc.pm_01_pc[0] + mc.pm_01_pc[1]) / 2.0f);
|
||||||
|
} else if (utils::isValidPm03Count(mc.pm_01_pc[0])) {
|
||||||
|
oss << std::round(mc.pm_01_pc[0]);
|
||||||
|
} else if (utils::isValidPm03Count(mc.pm_01_pc[1])) {
|
||||||
|
oss << std::round(mc.pm_01_pc[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
/// PM 2.5 particle count
|
||||||
|
if (utils::isValidPm03Count(mc.pm_25_pc[0]) && utils::isValidPm03Count(mc.pm_25_pc[1])) {
|
||||||
|
oss << std::round((mc.pm_25_pc[0] + mc.pm_25_pc[1]) / 2.0f);
|
||||||
|
} else if (utils::isValidPm03Count(mc.pm_25_pc[0])) {
|
||||||
|
oss << std::round(mc.pm_25_pc[0]);
|
||||||
|
} else if (utils::isValidPm03Count(mc.pm_25_pc[1])) {
|
||||||
|
oss << std::round(mc.pm_25_pc[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
/// PM 5.0 particle count
|
||||||
|
if (utils::isValidPm03Count(mc.pm_5_pc[0]) && utils::isValidPm03Count(mc.pm_5_pc[1])) {
|
||||||
|
oss << std::round((mc.pm_5_pc[0] + mc.pm_5_pc[1]) / 2.0f);
|
||||||
|
} else if (utils::isValidPm03Count(mc.pm_5_pc[0])) {
|
||||||
|
oss << std::round(mc.pm_5_pc[0]);
|
||||||
|
} else if (utils::isValidPm03Count(mc.pm_5_pc[1])) {
|
||||||
|
oss << std::round(mc.pm_5_pc[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
/// PM 10 particle count
|
||||||
|
if (utils::isValidPm03Count(mc.pm_10_pc[0]) && utils::isValidPm03Count(mc.pm_10_pc[1])) {
|
||||||
|
oss << std::round((mc.pm_10_pc[0] + mc.pm_10_pc[1]) / 2.0f);
|
||||||
|
} else if (utils::isValidPm03Count(mc.pm_10_pc[0])) {
|
||||||
|
oss << std::round(mc.pm_10_pc[0]);
|
||||||
|
} else if (utils::isValidPm03Count(mc.pm_10_pc[1])) {
|
||||||
|
oss << std::round(mc.pm_10_pc[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
/// PM2.5 standard particle
|
||||||
|
if (utils::isValidPm(mc.pm_25_sp[0]) && utils::isValidPm(mc.pm_25_sp[1])) {
|
||||||
|
float pm10 = (mc.pm_25_sp[0] + mc.pm_25_sp[1]) / 2.0f;
|
||||||
|
oss << std::round(pm10 * 10);
|
||||||
|
} else if (utils::isValidPm(mc.pm_25_sp[0])) {
|
||||||
|
oss << std::round(mc.pm_25_sp[0] * 10);
|
||||||
|
} else if (utils::isValidPm(mc.pm_25_sp[1])) {
|
||||||
|
oss << std::round(mc.pm_25_sp[1] * 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ public:
|
|||||||
|
|
||||||
Measures getMeasures();
|
Measures getMeasures();
|
||||||
|
|
||||||
std::string buildMeasuresPayload(Measures &measures);
|
std::string buildMeasuresPayload(Measures &mc, bool extendedPmMeasures);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to true if want to debug every update value
|
* Set to true if want to debug every update value
|
||||||
|
|||||||
@@ -1,21 +1,9 @@
|
|||||||
#include "AgWiFiConnector.h"
|
#include "AgWiFiConnector.h"
|
||||||
#include "Arduino.h"
|
|
||||||
#include "Libraries/WiFiManager/WiFiManager.h"
|
#include "Libraries/WiFiManager/WiFiManager.h"
|
||||||
#include "Libraries/Arduino_JSON/src/Arduino_JSON.h"
|
|
||||||
#include "WiFiType.h"
|
|
||||||
#include "esp32-hal.h"
|
|
||||||
|
|
||||||
#define WIFI_CONNECT_COUNTDOWN_MAX 180
|
#define WIFI_CONNECT_COUNTDOWN_MAX 180
|
||||||
#define WIFI_HOTSPOT_PASSWORD_DEFAULT "cleanair"
|
#define WIFI_HOTSPOT_PASSWORD_DEFAULT "cleanair"
|
||||||
|
|
||||||
|
|
||||||
#define BLE_SERVICE_UUID "acbcfea8-e541-4c40-9bfd-17820f16c95c"
|
|
||||||
#define BLE_CRED_CHAR_UUID "703fa252-3d2a-4da9-a05c-83b0d9cacb8e"
|
|
||||||
#define BLE_SCAN_CHAR_UUID "467a080f-e50f-42c9-b9b2-a2ab14d82725"
|
|
||||||
|
|
||||||
#define BLE_CRED_BIT (1 << 0)
|
|
||||||
#define BLE_SCAN_BIT (1 << 1)
|
|
||||||
|
|
||||||
#define WIFI() ((WiFiManager *)(this->wifi))
|
#define WIFI() ((WiFiManager *)(this->wifi))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,7 +32,7 @@ WifiConnector::~WifiConnector() {}
|
|||||||
* @return true Success
|
* @return true Success
|
||||||
* @return false Failure
|
* @return false Failure
|
||||||
*/
|
*/
|
||||||
bool WifiConnector::connect(String modelName) {
|
bool WifiConnector::connect(void) {
|
||||||
if (wifi == NULL) {
|
if (wifi == NULL) {
|
||||||
wifi = new WiFiManager();
|
wifi = new WiFiManager();
|
||||||
if (wifi == NULL) {
|
if (wifi == NULL) {
|
||||||
@@ -73,89 +61,61 @@ bool WifiConnector::connect(String modelName) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!WiFi.isConnected()) {
|
WIFI()->setConfigPortalBlocking(false);
|
||||||
// Erase already saved default credentials
|
WIFI()->setConnectTimeout(15);
|
||||||
WiFi.disconnect(false, true);
|
WIFI()->setTimeout(WIFI_CONNECT_COUNTDOWN_MAX);
|
||||||
}
|
|
||||||
|
WIFI()->setAPCallback([this](WiFiManager *obj) { _wifiApCallback(); });
|
||||||
|
WIFI()->setSaveConfigCallback([this]() { _wifiSaveConfig(); });
|
||||||
|
WIFI()->setSaveParamsCallback([this]() { _wifiSaveParamCallback(); });
|
||||||
|
WIFI()->setConfigPortalTimeoutCallback([this]() {_wifiTimeoutCallback();});
|
||||||
|
if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_3() || ag->isBasic()) {
|
||||||
|
disp.setText("Connecting to", "WiFi", "...");
|
||||||
} else {
|
} else {
|
||||||
Serial.printf("Attempt connect to configured ssid: %d\n", wifiSSID.c_str());
|
logInfo("Connecting to WiFi...");
|
||||||
// WiFi.begin() already called before, it will attempt connect when wifi creds already persist
|
|
||||||
|
|
||||||
sm.ledAnimationInit();
|
|
||||||
sm.handleLeds(AgStateMachineWiFiManagerStaConnecting);
|
|
||||||
sm.displayHandle(AgStateMachineWiFiManagerStaConnecting);
|
|
||||||
|
|
||||||
uint32_t ledPeriod = millis();
|
|
||||||
uint32_t startTime = millis();
|
|
||||||
while (WiFi.status() != WL_CONNECTED && (millis() - startTime) < 15000) {
|
|
||||||
/** LED animations */
|
|
||||||
if ((millis() - ledPeriod) >= 100) {
|
|
||||||
ledPeriod = millis();
|
|
||||||
sm.handleLeds();
|
|
||||||
}
|
|
||||||
delay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!WiFi.isConnected()) {
|
|
||||||
// WiFi not connect, show indicator.
|
|
||||||
sm.ledAnimationInit();
|
|
||||||
sm.handleLeds(AgStateMachineWiFiManagerConnectFailed);
|
|
||||||
sm.displayHandle(AgStateMachineWiFiManagerConnectFailed);
|
|
||||||
delay(3000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
ssid = "airgradient-" + ag->deviceId();
|
||||||
|
|
||||||
if (WiFi.isConnected()) {
|
// ssid = "AG-" + String(ESP.getChipId(), HEX);
|
||||||
sm.handleLeds(AgStateMachineWiFiManagerStaConnected);
|
WIFI()->setConfigPortalTimeout(WIFI_CONNECT_COUNTDOWN_MAX);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable provision by both BLE and WiFi portal
|
|
||||||
WiFiManagerParameter disableCloud("chbPostToAg", "Prevent Connection to AirGradient Server", "T",
|
WiFiManagerParameter disableCloud("chbPostToAg", "Prevent Connection to AirGradient Server", "T",
|
||||||
2, "type=\"checkbox\" ", WFM_LABEL_AFTER);
|
2, "type=\"checkbox\" ", WFM_LABEL_AFTER);
|
||||||
|
WIFI()->addParameter(&disableCloud);
|
||||||
WiFiManagerParameter disableCloudInfo(
|
WiFiManagerParameter disableCloudInfo(
|
||||||
"<p>Prevent connection to the AirGradient Server. Important: Only enable "
|
"<p>Prevent connection to the AirGradient Server. Important: Only enable "
|
||||||
"it if you are sure you don't want to use any AirGradient cloud "
|
"it if you are sure you don't want to use any AirGradient cloud "
|
||||||
"features. As a result you will not receive automatic firmware updates, "
|
"features. As a result you will not receive automatic firmware updates, "
|
||||||
"configuration settings from cloud and the measure data will not reach the AirGradient dashboard.</p>");
|
"configuration settings from cloud and the measure data will not reach the AirGradient dashboard.</p>");
|
||||||
setupProvisionByPortal(&disableCloud, &disableCloudInfo);
|
WIFI()->addParameter(&disableCloudInfo);
|
||||||
|
|
||||||
|
WIFI()->autoConnect(ssid.c_str(), WIFI_HOTSPOT_PASSWORD_DEFAULT);
|
||||||
|
|
||||||
|
logInfo("Wait for configure portal");
|
||||||
|
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
// Provision by BLE only for ESP32
|
// Task handle WiFi connection.
|
||||||
setupProvisionByBLE(modelName.c_str());
|
|
||||||
|
|
||||||
// Task handling WiFi portal
|
|
||||||
xTaskCreate(
|
xTaskCreate(
|
||||||
[](void *obj) {
|
[](void *obj) {
|
||||||
WifiConnector *connector = (WifiConnector *)obj;
|
WifiConnector *connector = (WifiConnector *)obj;
|
||||||
while (connector->_wifiConfigPortalActive()) {
|
while (connector->_wifiConfigPortalActive()) {
|
||||||
if (connector->isBleClientConnected()) {
|
connector->_wifiProcess();
|
||||||
Serial.println("Stopping portal because BLE connected");
|
vTaskDelay(1);
|
||||||
connector->_wifiStop();
|
|
||||||
connector->provisionMethod = ProvisionMethod::BLE;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
connector->_wifiProcess();
|
vTaskDelete(NULL);
|
||||||
vTaskDelay(1);
|
},
|
||||||
}
|
"wifi_cfg", 4096, this, 10, NULL);
|
||||||
vTaskDelete(NULL);
|
|
||||||
},
|
|
||||||
"wifi_cfg", 4096, this, 10, NULL);
|
|
||||||
|
|
||||||
|
/** Wait for WiFi connect and show LED, display status */
|
||||||
// Wait for WiFi connect and show LED, display status
|
|
||||||
uint32_t dispPeriod = millis();
|
uint32_t dispPeriod = millis();
|
||||||
uint32_t ledPeriod = millis();
|
uint32_t ledPeriod = millis();
|
||||||
bool clientConnectChanged = false;
|
bool clientConnectChanged = false;
|
||||||
|
|
||||||
// By default wifi portal loops run first
|
|
||||||
// Provision method defined when either wifi or ble client connected first
|
|
||||||
// If wifi client connect, then ble server will be stopped
|
|
||||||
// If ble client connect, then wifi portal will be stopped (see wifi_cfg task)
|
|
||||||
AgStateMachineState stateOld = sm.getDisplayState();
|
AgStateMachineState stateOld = sm.getDisplayState();
|
||||||
while (WIFI()->getConfigPortalActive()) {
|
while (WIFI()->getConfigPortalActive()) {
|
||||||
/** LED animation and display update content */
|
/** LED animatoin and display update content */
|
||||||
if (WiFi.isConnected() == false) {
|
if (WiFi.isConnected() == false) {
|
||||||
/** Display countdown */
|
/** Display countdown */
|
||||||
uint32_t ms;
|
uint32_t ms;
|
||||||
@@ -185,11 +145,6 @@ bool WifiConnector::connect(String modelName) {
|
|||||||
clientConnectChanged = clientConnected;
|
clientConnectChanged = clientConnected;
|
||||||
if (clientConnectChanged) {
|
if (clientConnectChanged) {
|
||||||
sm.handleLeds(AgStateMachineWiFiManagerPortalActive);
|
sm.handleLeds(AgStateMachineWiFiManagerPortalActive);
|
||||||
if (bleServerRunning) {
|
|
||||||
Serial.println("Stopping BLE since wifi is connected");
|
|
||||||
stopBLE();
|
|
||||||
provisionMethod = ProvisionMethod::WiFi;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
sm.ledAnimationInit();
|
sm.ledAnimationInit();
|
||||||
sm.handleLeds(AgStateMachineWiFiManagerMode);
|
sm.handleLeds(AgStateMachineWiFiManagerMode);
|
||||||
@@ -202,74 +157,6 @@ bool WifiConnector::connect(String modelName) {
|
|||||||
|
|
||||||
delay(1); // avoid watchdog timer reset.
|
delay(1); // avoid watchdog timer reset.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (provisionMethod == ProvisionMethod::BLE) {
|
|
||||||
disp.setText("Provision by", "BLE", "");
|
|
||||||
sm.ledAnimationInit();
|
|
||||||
sm.handleLeds(AgStateMachineWiFiManagerPortalActive);
|
|
||||||
|
|
||||||
uint32_t wdMillis = 0;
|
|
||||||
|
|
||||||
// Loop until the BLE client disconnected or WiFi connected
|
|
||||||
while (isBleClientConnected() && !WiFi.isConnected()) {
|
|
||||||
EventBits_t bits = xEventGroupWaitBits(
|
|
||||||
bleEventGroup,
|
|
||||||
BLE_SCAN_BIT | BLE_CRED_BIT,
|
|
||||||
pdTRUE,
|
|
||||||
pdFALSE,
|
|
||||||
10 / portTICK_PERIOD_MS
|
|
||||||
);
|
|
||||||
|
|
||||||
if (bits & BLE_CRED_BIT) {
|
|
||||||
Serial.printf("Connecting to %s...\n", ssid.c_str());
|
|
||||||
wifiConnecting = true;
|
|
||||||
|
|
||||||
sm.ledAnimationInit();
|
|
||||||
sm.handleLeds(AgStateMachineWiFiManagerStaConnecting);
|
|
||||||
sm.displayHandle(AgStateMachineWiFiManagerStaConnecting);
|
|
||||||
|
|
||||||
uint32_t startTime = millis();
|
|
||||||
while (WiFi.status() != WL_CONNECTED && (millis() - startTime) < 15000) {
|
|
||||||
// Led animations
|
|
||||||
if ((millis() - ledPeriod) >= 100) {
|
|
||||||
ledPeriod = millis();
|
|
||||||
sm.handleLeds();
|
|
||||||
}
|
|
||||||
delay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WiFi.status() != WL_CONNECTED) {
|
|
||||||
Serial.println("Failed connect to WiFi");
|
|
||||||
// If not connect send status through BLE while also turn led and display indicator
|
|
||||||
WiFi.disconnect();
|
|
||||||
wifiConnecting = false;
|
|
||||||
bleNotifyStatus(PROV_ERR_WIFI_CONNECT_FAILED);
|
|
||||||
|
|
||||||
// Show failed inficator then revert back to provision mode
|
|
||||||
sm.ledAnimationInit();
|
|
||||||
sm.handleLeds(AgStateMachineWiFiManagerConnectFailed);
|
|
||||||
sm.displayHandle(AgStateMachineWiFiManagerConnectFailed);
|
|
||||||
delay(3000);
|
|
||||||
sm.ledAnimationInit();
|
|
||||||
disp.setText("Provision by", "BLE", "");
|
|
||||||
sm.handleLeds(AgStateMachineWiFiManagerPortalActive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (bits & BLE_SCAN_BIT) {
|
|
||||||
handleBleScanRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure watchdog fed every minute
|
|
||||||
if ((millis() - wdMillis) >= 60000) {
|
|
||||||
wdMillis = millis();
|
|
||||||
ag->watchdog.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
delay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.println("Exit provision by BLE");
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
_wifiProcess();
|
_wifiProcess();
|
||||||
#endif
|
#endif
|
||||||
@@ -293,7 +180,6 @@ bool WifiConnector::connect(String modelName) {
|
|||||||
config.setDisableCloudConnection(result == "T");
|
config.setDisableCloudConnection(result == "T");
|
||||||
}
|
}
|
||||||
hasPortalConfig = false;
|
hasPortalConfig = false;
|
||||||
bleNotifyStatus(PROV_WIFI_CONNECT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -320,11 +206,6 @@ bool WifiConnector::wifiClientConnected(void) {
|
|||||||
return WiFi.softAPgetStationNum() ? true : false;
|
return WiFi.softAPgetStationNum() ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool WifiConnector::isBleClientConnected() {
|
|
||||||
return bleClientConnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Handle WiFiManage softAP setup completed callback
|
* @brief Handle WiFiManage softAP setup completed callback
|
||||||
*
|
*
|
||||||
@@ -367,10 +248,6 @@ bool WifiConnector::_wifiConfigPortalActive(void) {
|
|||||||
}
|
}
|
||||||
void WifiConnector::_wifiTimeoutCallback(void) { connectorTimeout = true; }
|
void WifiConnector::_wifiTimeoutCallback(void) { connectorTimeout = true; }
|
||||||
|
|
||||||
void WifiConnector::_wifiStop() {
|
|
||||||
WIFI()->stopConfigPortal();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Process WiFiManager connection
|
* @brief Process WiFiManager connection
|
||||||
*
|
*
|
||||||
@@ -527,28 +404,6 @@ bool WifiConnector::hasConfigurated(void) {
|
|||||||
*/
|
*/
|
||||||
bool WifiConnector::isConfigurePorttalTimeout(void) { return connectorTimeout; }
|
bool WifiConnector::isConfigurePorttalTimeout(void) { return connectorTimeout; }
|
||||||
|
|
||||||
|
|
||||||
void WifiConnector::bleNotifyStatus(int status) {
|
|
||||||
if (!bleServerRunning) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pServer->getConnectedCount()) {
|
|
||||||
NimBLEService* pSvc = pServer->getServiceByUUID(BLE_SERVICE_UUID);
|
|
||||||
if (pSvc) {
|
|
||||||
NimBLECharacteristic* pChr = pSvc->getCharacteristic(BLE_CRED_CHAR_UUID);
|
|
||||||
if (pChr) {
|
|
||||||
char tosend[50];
|
|
||||||
memset(tosend, 0, 50);
|
|
||||||
sprintf(tosend, "{\"status\":%d}", status);
|
|
||||||
Serial.printf("BLE Notify >> %s \n", tosend);
|
|
||||||
pChr->setValue(String(tosend));
|
|
||||||
pChr->notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set wifi connect to default WiFi
|
* @brief Set wifi connect to default WiFi
|
||||||
*
|
*
|
||||||
@@ -556,307 +411,3 @@ void WifiConnector::bleNotifyStatus(int status) {
|
|||||||
void WifiConnector::setDefault(void) {
|
void WifiConnector::setDefault(void) {
|
||||||
WiFi.begin("airgradient", "cleanair");
|
WiFi.begin("airgradient", "cleanair");
|
||||||
}
|
}
|
||||||
|
|
||||||
int WifiConnector::scanAndFilterWiFi(WiFiNetwork networks[], int maxResults) {
|
|
||||||
Serial.println("Scanning for Wi-Fi networks...");
|
|
||||||
int n = WiFi.scanNetworks(false, true); // async=false, show_hidden=true
|
|
||||||
Serial.printf("Found %d networks\n", n);
|
|
||||||
|
|
||||||
const int MAX_NETWORKS = 50;
|
|
||||||
|
|
||||||
if (n <= 0) {
|
|
||||||
Serial.println("No networks found");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
WiFiNetwork allNetworks[MAX_NETWORKS];
|
|
||||||
int allCount = 0;
|
|
||||||
|
|
||||||
// Collect valid networks (filter weak or empty SSID)
|
|
||||||
for (int i = 0; i < n && allCount < MAX_NETWORKS; ++i) {
|
|
||||||
String ssid = WiFi.SSID(i);
|
|
||||||
int32_t rssi = WiFi.RSSI(i);
|
|
||||||
bool open = (WiFi.encryptionType(i) == WIFI_AUTH_OPEN);
|
|
||||||
|
|
||||||
if (ssid.length() == 0 || rssi < -75) continue;
|
|
||||||
|
|
||||||
allNetworks[allCount++] = {ssid, rssi, open};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove duplicates (keep the strongest)
|
|
||||||
WiFiNetwork uniqueNetworks[MAX_NETWORKS];
|
|
||||||
int uniqueCount = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < allCount; i++) {
|
|
||||||
bool exists = false;
|
|
||||||
for (int j = 0; j < uniqueCount; j++) {
|
|
||||||
if (uniqueNetworks[j].ssid == allNetworks[i].ssid) {
|
|
||||||
exists = true;
|
|
||||||
if (allNetworks[i].rssi > uniqueNetworks[j].rssi)
|
|
||||||
uniqueNetworks[j] = allNetworks[i]; // keep stronger one
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!exists && uniqueCount < MAX_NETWORKS) {
|
|
||||||
uniqueNetworks[uniqueCount++] = allNetworks[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort by RSSI descending (simple bubble sort for small lists)
|
|
||||||
for (int i = 0; i < uniqueCount - 1; i++) {
|
|
||||||
for (int j = i + 1; j < uniqueCount; j++) {
|
|
||||||
if (uniqueNetworks[j].rssi > uniqueNetworks[i].rssi) {
|
|
||||||
WiFiNetwork temp = uniqueNetworks[i];
|
|
||||||
uniqueNetworks[i] = uniqueNetworks[j];
|
|
||||||
uniqueNetworks[j] = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy to output array
|
|
||||||
int resultCount = (uniqueCount > maxResults) ? maxResults : uniqueCount;
|
|
||||||
for (int i = 0; i < resultCount; i++) {
|
|
||||||
networks[i] = uniqueNetworks[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.printf("Returning %d filtered networks\n", resultCount);
|
|
||||||
return resultCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
String WifiConnector::buildPaginatedWiFiJSON(WiFiNetwork networks[], int totalFound,
|
|
||||||
int page, int batchSize, int totalPages) {
|
|
||||||
// Calculate start and end indices for this page
|
|
||||||
int startIdx = (page - 1) * batchSize;
|
|
||||||
int endIdx = startIdx + batchSize;
|
|
||||||
if (endIdx > totalFound) {
|
|
||||||
endIdx = totalFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build JSON object with pagination
|
|
||||||
JSONVar jsonRoot;
|
|
||||||
JSONVar jsonArray;
|
|
||||||
|
|
||||||
for (int i = startIdx; i < endIdx; i++) {
|
|
||||||
JSONVar obj;
|
|
||||||
obj["s"] = networks[i].ssid;
|
|
||||||
obj["r"] = networks[i].rssi;
|
|
||||||
obj["o"] = networks[i].open ? 1 : 0;
|
|
||||||
jsonArray[i - startIdx] = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonRoot["wifi"] = jsonArray;
|
|
||||||
jsonRoot["page"] = page;
|
|
||||||
jsonRoot["tpage"] = totalPages;
|
|
||||||
jsonRoot["found"] = totalFound;
|
|
||||||
|
|
||||||
String jsonString = JSON.stringify(jsonRoot);
|
|
||||||
|
|
||||||
Serial.printf("Page %d/%d JSON: %s\n", page, totalPages, jsonString.c_str());
|
|
||||||
|
|
||||||
return jsonString;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WifiConnector::handleBleScanRequest() {
|
|
||||||
const int BATCH_SIZE = 3;
|
|
||||||
const int MAX_RESULTS = 30;
|
|
||||||
WiFiNetwork networks[MAX_RESULTS];
|
|
||||||
|
|
||||||
// Scan and filter networks once
|
|
||||||
int networkCount = scanAndFilterWiFi(networks, MAX_RESULTS);
|
|
||||||
|
|
||||||
// Calculate total pages
|
|
||||||
int totalFound = (networkCount + BATCH_SIZE - 1) / BATCH_SIZE;
|
|
||||||
|
|
||||||
NimBLEService* pSvc = pServer->getServiceByUUID(BLE_SERVICE_UUID);
|
|
||||||
if (!pSvc) {
|
|
||||||
Serial.println("BLE service not found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NimBLECharacteristic* pChr = pSvc->getCharacteristic(BLE_SCAN_CHAR_UUID);
|
|
||||||
if (!pChr) {
|
|
||||||
Serial.println("BLE scan characteristic not found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (networkCount == 0) {
|
|
||||||
Serial.println("No networks found to send");
|
|
||||||
String tosend = "{\"found\":0}";
|
|
||||||
pChr->setValue(tosend);
|
|
||||||
pChr->notify();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send results in batches
|
|
||||||
for (int page = 1; page <= totalFound; page++) {
|
|
||||||
String batchJson = buildPaginatedWiFiJSON(networks, networkCount,
|
|
||||||
page, BATCH_SIZE, totalFound);
|
|
||||||
pChr->setValue(batchJson);
|
|
||||||
pChr->notify();
|
|
||||||
|
|
||||||
Serial.printf("Sent WiFi scan page %d/%d through BLE notify\n", page, totalFound);
|
|
||||||
|
|
||||||
// Delay between batches (except last one)
|
|
||||||
if (page < totalFound) {
|
|
||||||
delay(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.println("All WiFi scan pages sent successfully");
|
|
||||||
}
|
|
||||||
|
|
||||||
void WifiConnector::setupProvisionByPortal(WiFiManagerParameter *disableCloudParam, WiFiManagerParameter *disableCloudInfo) {
|
|
||||||
WIFI()->setConfigPortalBlocking(false);
|
|
||||||
WIFI()->setConnectTimeout(15);
|
|
||||||
WIFI()->setTimeout(WIFI_CONNECT_COUNTDOWN_MAX);
|
|
||||||
WIFI()->setBreakAfterConfig(true);
|
|
||||||
|
|
||||||
WIFI()->setAPCallback([this](WiFiManager *obj) { _wifiApCallback(); });
|
|
||||||
WIFI()->setSaveConfigCallback([this]() { _wifiSaveConfig(); });
|
|
||||||
WIFI()->setSaveParamsCallback([this]() { _wifiSaveParamCallback(); });
|
|
||||||
WIFI()->setConfigPortalTimeoutCallback([this]() {_wifiTimeoutCallback();});
|
|
||||||
if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_3() || ag->isBasic()) {
|
|
||||||
disp.setText("Connecting to", "WiFi", "...");
|
|
||||||
} else {
|
|
||||||
logInfo("Connecting to WiFi...");
|
|
||||||
}
|
|
||||||
ssid = "airgradient-" + ag->deviceId();
|
|
||||||
|
|
||||||
// ssid = "AG-" + String(ESP.getChipId(), HEX);
|
|
||||||
WIFI()->setConfigPortalTimeout(WIFI_CONNECT_COUNTDOWN_MAX);
|
|
||||||
|
|
||||||
WIFI()->addParameter(disableCloudParam);
|
|
||||||
WIFI()->addParameter(disableCloudInfo);
|
|
||||||
|
|
||||||
WIFI()->autoConnect(ssid.c_str(), WIFI_HOTSPOT_PASSWORD_DEFAULT);
|
|
||||||
|
|
||||||
logInfo("Wait for configure portal");
|
|
||||||
}
|
|
||||||
|
|
||||||
void WifiConnector::setupProvisionByBLE(const char *modelName) {
|
|
||||||
NimBLEDevice::init("AirGradient");
|
|
||||||
NimBLEDevice::setPower(3); /** +3db */
|
|
||||||
|
|
||||||
/** bonding, MITM, don't need BLE secure connections as we are using passkey pairing */
|
|
||||||
NimBLEDevice::setSecurityAuth(false, false, true);
|
|
||||||
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_NO_INPUT_OUTPUT);
|
|
||||||
|
|
||||||
pServer = NimBLEDevice::createServer();
|
|
||||||
pServer->setCallbacks(new ServerCallbacks(this));
|
|
||||||
|
|
||||||
// Service and characteristics for device information
|
|
||||||
NimBLEService *pServDeviceInfo = pServer->createService("180A");
|
|
||||||
NimBLECharacteristic *pModelCharacteristic = pServDeviceInfo->createCharacteristic("2A24", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC);
|
|
||||||
pModelCharacteristic->setValue(modelName);
|
|
||||||
NimBLECharacteristic *pSerialCharacteristic = pServDeviceInfo->createCharacteristic("2A25", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC);
|
|
||||||
pSerialCharacteristic->setValue(ag->deviceId().c_str());
|
|
||||||
NimBLECharacteristic *pFwCharacteristic = pServDeviceInfo->createCharacteristic("2A26", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC);
|
|
||||||
pFwCharacteristic->setValue(ag->getVersion().c_str());
|
|
||||||
NimBLECharacteristic *pManufCharacteristic = pServDeviceInfo->createCharacteristic("2A29", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC);
|
|
||||||
pManufCharacteristic->setValue("AirGradient");
|
|
||||||
|
|
||||||
// Service and characteristics for wifi provisioning
|
|
||||||
NimBLEService *pServProvisioning = pServer->createService(BLE_SERVICE_UUID);
|
|
||||||
auto characteristicCallback = new CharacteristicCallbacks(this);
|
|
||||||
NimBLECharacteristic *pCredentialCharacteristic =
|
|
||||||
pServProvisioning->createCharacteristic(BLE_CRED_CHAR_UUID,
|
|
||||||
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC |
|
|
||||||
NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_ENC | NIMBLE_PROPERTY::NOTIFY);
|
|
||||||
pCredentialCharacteristic->setCallbacks(characteristicCallback);
|
|
||||||
NimBLECharacteristic *pScanCharacteristic =
|
|
||||||
pServProvisioning->createCharacteristic(BLE_SCAN_CHAR_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_ENC | NIMBLE_PROPERTY::NOTIFY);
|
|
||||||
pScanCharacteristic->setCallbacks(characteristicCallback);
|
|
||||||
|
|
||||||
// Start services
|
|
||||||
pServProvisioning->start();
|
|
||||||
pServDeviceInfo->start();
|
|
||||||
|
|
||||||
// Advertise
|
|
||||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
|
||||||
// Format advertising data
|
|
||||||
String mdata;
|
|
||||||
mdata += (char)0xFF;
|
|
||||||
mdata += (char)0xFF;
|
|
||||||
mdata += modelName;
|
|
||||||
mdata += '#';
|
|
||||||
mdata += ag->deviceId();
|
|
||||||
pAdvertising->setManufacturerData(mdata.c_str());
|
|
||||||
// Start advertise
|
|
||||||
pAdvertising->start();
|
|
||||||
bleServerRunning = true;
|
|
||||||
|
|
||||||
// Create event group
|
|
||||||
bleEventGroup = xEventGroupCreate();
|
|
||||||
if (bleEventGroup == NULL) {
|
|
||||||
Serial.println("Failed to create BLE event group!");
|
|
||||||
// This case is very unlikely
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.println("Provision by BLE ready");
|
|
||||||
}
|
|
||||||
|
|
||||||
void WifiConnector::stopBLE() {
|
|
||||||
if (bleServerRunning) {
|
|
||||||
Serial.println("Stopping BLE");
|
|
||||||
NimBLEDevice::deinit();
|
|
||||||
}
|
|
||||||
bleServerRunning = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// BLE innerclass implementation
|
|
||||||
//
|
|
||||||
|
|
||||||
WifiConnector::ServerCallbacks::ServerCallbacks(WifiConnector* parent)
|
|
||||||
: parent(parent) {}
|
|
||||||
|
|
||||||
void WifiConnector::ServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
|
|
||||||
Serial.printf("Client address: %s\n", connInfo.getAddress().toString().c_str());
|
|
||||||
parent->bleClientConnected = true;
|
|
||||||
NimBLEDevice::stopAdvertising();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WifiConnector::ServerCallbacks::onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) {
|
|
||||||
Serial.printf("Client disconnected - start advertising\n");
|
|
||||||
NimBLEDevice::startAdvertising();
|
|
||||||
parent->bleClientConnected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WifiConnector::ServerCallbacks::onAuthenticationComplete(NimBLEConnInfo& connInfo) {
|
|
||||||
Serial.println("\n========== PAIRING COMPLETE ==========");
|
|
||||||
Serial.printf("Peer Address: %s\n", connInfo.getAddress().toString().c_str());
|
|
||||||
Serial.printf("Encrypted: %s\n", connInfo.isEncrypted() ? "YES" : "NO");
|
|
||||||
Serial.printf("Authenticated: %s\n", connInfo.isAuthenticated() ? "YES" : "NO");
|
|
||||||
Serial.printf("Key Size: %d bits\n", connInfo.getSecKeySize() * 8);
|
|
||||||
Serial.println("======================================\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
WifiConnector::CharacteristicCallbacks::CharacteristicCallbacks(WifiConnector* parent)
|
|
||||||
: parent(parent) {}
|
|
||||||
|
|
||||||
void WifiConnector::CharacteristicCallbacks::onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo) {
|
|
||||||
Serial.printf("%s : onRead(), value: %s\n", pCharacteristic->getUUID().toString().c_str(),
|
|
||||||
pCharacteristic->getValue().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void WifiConnector::CharacteristicCallbacks::onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo) {
|
|
||||||
Serial.printf("%s : onWrite(), value: %s\n", pCharacteristic->getUUID().toString().c_str(),
|
|
||||||
pCharacteristic->getValue().c_str());
|
|
||||||
|
|
||||||
auto bleCred = NimBLEUUID(BLE_CRED_CHAR_UUID);
|
|
||||||
if (pCharacteristic->getUUID().equals(bleCred)) {
|
|
||||||
if (!parent->wifiConnecting) {
|
|
||||||
JSONVar root = JSON.parse(pCharacteristic->getValue().c_str());
|
|
||||||
|
|
||||||
String ssid = root["ssid"];
|
|
||||||
String pass = root["password"];
|
|
||||||
|
|
||||||
WiFi.begin(ssid.c_str(), pass.c_str());
|
|
||||||
xEventGroupSetBits(parent->bleEventGroup, BLE_CRED_BIT);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
xEventGroupSetBits(parent->bleEventGroup, BLE_SCAN_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,49 +5,16 @@
|
|||||||
#include "AgStateMachine.h"
|
#include "AgStateMachine.h"
|
||||||
#include "AirGradient.h"
|
#include "AirGradient.h"
|
||||||
#include "AgConfigure.h"
|
#include "AgConfigure.h"
|
||||||
#include "Libraries/WiFiManager/WiFiManager.h"
|
|
||||||
#include "Main/PrintLog.h"
|
#include "Main/PrintLog.h"
|
||||||
#include "NimBLECharacteristic.h"
|
|
||||||
#include "NimBLEService.h"
|
|
||||||
#include "esp32-hal.h"
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <NimBLEDevice.h>
|
|
||||||
|
|
||||||
// Provisioning Status Codes
|
|
||||||
#define PROV_WIFI_CONNECT 0 // WiFi Connect
|
|
||||||
#define PROV_CONNECTING_TO_SERVER 1 // Connecting to server
|
|
||||||
#define PROV_SERVER_REACHABLE 2 // Server reachable
|
|
||||||
#define PROV_MONITOR_CONFIGURED 3 // Monitor configured properly on dashboard
|
|
||||||
|
|
||||||
// Provisioning Error Codes
|
|
||||||
#define PROV_ERR_WIFI_CONNECT_FAILED 10 // Failed to connect to WiFi
|
|
||||||
#define PROV_ERR_SERVER_UNREACHABLE 11 // Server unreachable
|
|
||||||
#define PROV_ERR_GET_MONITOR_CONFIG_FAILED 12 // Failed to get monitor configuration from dashboard
|
|
||||||
#define PROV_ERR_MONITOR_NOT_REGISTERED 13 // Monitor is not registered on dashboard
|
|
||||||
|
|
||||||
class WifiConnector : public PrintLog {
|
class WifiConnector : public PrintLog {
|
||||||
public:
|
|
||||||
enum class ProvisionMethod {
|
|
||||||
Unknown = 0,
|
|
||||||
WiFi,
|
|
||||||
BLE
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WiFiNetwork {
|
|
||||||
String ssid;
|
|
||||||
int32_t rssi;
|
|
||||||
bool open;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AirGradient *ag;
|
AirGradient *ag;
|
||||||
OledDisplay &disp;
|
OledDisplay &disp;
|
||||||
StateMachine &sm;
|
StateMachine &sm;
|
||||||
Configuration &config;
|
Configuration &config;
|
||||||
NimBLEServer *pServer;
|
|
||||||
|
|
||||||
EventGroupHandle_t bleEventGroup;
|
|
||||||
|
|
||||||
String ssid;
|
String ssid;
|
||||||
void *wifi = NULL;
|
void *wifi = NULL;
|
||||||
@@ -55,51 +22,16 @@ private:
|
|||||||
uint32_t lastRetry;
|
uint32_t lastRetry;
|
||||||
bool hasPortalConfig = false;
|
bool hasPortalConfig = false;
|
||||||
bool connectorTimeout = false;
|
bool connectorTimeout = false;
|
||||||
bool bleServerRunning = false;
|
|
||||||
bool bleClientConnected = false;
|
|
||||||
bool wifiConnecting = false;
|
|
||||||
ProvisionMethod provisionMethod = ProvisionMethod::Unknown;
|
|
||||||
|
|
||||||
bool wifiClientConnected(void);
|
bool wifiClientConnected(void);
|
||||||
bool isBleClientConnected();
|
|
||||||
int scanAndFilterWiFi(WiFiNetwork networks[], int maxResults);
|
|
||||||
String buildPaginatedWiFiJSON(WiFiNetwork networks[], int totalCount,
|
|
||||||
int page, int batchSize, int totalPages);
|
|
||||||
void handleBleScanRequest();
|
|
||||||
|
|
||||||
// BLE server handler
|
|
||||||
class ServerCallbacks : public NimBLEServerCallbacks {
|
|
||||||
public:
|
|
||||||
explicit ServerCallbacks(WifiConnector *parent);
|
|
||||||
void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) override;
|
|
||||||
void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason) override;
|
|
||||||
void onAuthenticationComplete(NimBLEConnInfo &connInfo) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
WifiConnector *parent;
|
|
||||||
};
|
|
||||||
|
|
||||||
// BLE Characteristics handler
|
|
||||||
class CharacteristicCallbacks : public NimBLECharacteristicCallbacks {
|
|
||||||
public:
|
|
||||||
explicit CharacteristicCallbacks(WifiConnector *parent);
|
|
||||||
void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo) override;
|
|
||||||
void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo) override;
|
|
||||||
private:
|
|
||||||
WifiConnector *parent;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void setAirGradient(AirGradient *ag);
|
void setAirGradient(AirGradient *ag);
|
||||||
|
|
||||||
WifiConnector(OledDisplay &disp, Stream &log, StateMachine &sm, Configuration &config);
|
WifiConnector(OledDisplay &disp, Stream &log, StateMachine &sm, Configuration& config);
|
||||||
~WifiConnector();
|
~WifiConnector();
|
||||||
|
|
||||||
void setupProvisionByPortal(WiFiManagerParameter *disableCloudParam, WiFiManagerParameter *disableCloudInfo);
|
bool connect(void);
|
||||||
void setupProvisionByBLE(const char *modelName);
|
|
||||||
void stopBLE();
|
|
||||||
bool connect(String modelName = "");
|
|
||||||
void disconnect(void);
|
void disconnect(void);
|
||||||
void handle(void);
|
void handle(void);
|
||||||
void _wifiApCallback(void);
|
void _wifiApCallback(void);
|
||||||
@@ -107,7 +39,6 @@ public:
|
|||||||
void _wifiSaveParamCallback(void);
|
void _wifiSaveParamCallback(void);
|
||||||
bool _wifiConfigPortalActive(void);
|
bool _wifiConfigPortalActive(void);
|
||||||
void _wifiTimeoutCallback(void);
|
void _wifiTimeoutCallback(void);
|
||||||
void _wifiStop();
|
|
||||||
void _wifiProcess();
|
void _wifiProcess();
|
||||||
bool isConnected(void);
|
bool isConnected(void);
|
||||||
void reset(void);
|
void reset(void);
|
||||||
@@ -116,11 +47,8 @@ public:
|
|||||||
bool hasConfigurated(void);
|
bool hasConfigurated(void);
|
||||||
bool isConfigurePorttalTimeout(void);
|
bool isConfigurePorttalTimeout(void);
|
||||||
|
|
||||||
|
const char* defaultSsid = "airgradient";
|
||||||
void bleNotifyStatus(int status);
|
const char* defaultPassword = "cleanair";
|
||||||
|
|
||||||
const char *defaultSsid = "airgradient";
|
|
||||||
const char *defaultPassword = "cleanair";
|
|
||||||
void setDefault(void);
|
void setDefault(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Submodule src/Libraries/airgradient-client updated: c23bb2ceac...3a37795565
@@ -72,36 +72,6 @@ void StatusLed::setToggle(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void StatusLed::setStep(void) {
|
|
||||||
static uint8_t step = 0;
|
|
||||||
|
|
||||||
// Pattern definition
|
|
||||||
const bool pattern[] = {
|
|
||||||
true, // 0: ON
|
|
||||||
false, // 1: OFF
|
|
||||||
true, // 2: ON
|
|
||||||
false, // 3: OFF
|
|
||||||
false, // 4: OFF
|
|
||||||
false, // 5: OFF
|
|
||||||
false, // 6: OFF
|
|
||||||
false, // 7: OFF
|
|
||||||
false, // 8: OFF
|
|
||||||
false // 9: OFF
|
|
||||||
};
|
|
||||||
|
|
||||||
if (pattern[step]) {
|
|
||||||
this->setOn();
|
|
||||||
} else {
|
|
||||||
this->setOff();
|
|
||||||
}
|
|
||||||
|
|
||||||
step++;
|
|
||||||
if (step >= sizeof(pattern)) {
|
|
||||||
step = 0; // restart pattern
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get current LED state
|
* @brief Get current LED state
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ public:
|
|||||||
void setOn(void);
|
void setOn(void);
|
||||||
void setOff(void);
|
void setOff(void);
|
||||||
void setToggle(void);
|
void setToggle(void);
|
||||||
void setStep(void);
|
|
||||||
State getState(void);
|
State getState(void);
|
||||||
String toString(StatusLed::State state);
|
String toString(StatusLed::State state);
|
||||||
|
|
||||||
|
|||||||
@@ -835,3 +835,13 @@ bool S8::setAbcPeriod(int hours) {
|
|||||||
* @return int Hour
|
* @return int Hour
|
||||||
*/
|
*/
|
||||||
int S8::getAbcPeriod(void) { return getCalibPeriodABC(); }
|
int S8::getAbcPeriod(void) { return getCalibPeriodABC(); }
|
||||||
|
|
||||||
|
|
||||||
|
void S8::printInformation(void) {
|
||||||
|
Serial.print("S8 type ID: 0x");
|
||||||
|
Serial.println(getSensorTypeId(), HEX);
|
||||||
|
Serial.print("S8 serial number: 0x");
|
||||||
|
Serial.println(getSensorId(), HEX);
|
||||||
|
Serial.print("S8 memory map version: 0x");
|
||||||
|
Serial.println(getMemoryMapVersion(), HEX);
|
||||||
|
}
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ public:
|
|||||||
bool isBaseLineCalibrationDone(void);
|
bool isBaseLineCalibrationDone(void);
|
||||||
bool setAbcPeriod(int hours);
|
bool setAbcPeriod(int hours);
|
||||||
int getAbcPeriod(void);
|
int getAbcPeriod(void);
|
||||||
|
void printInformation(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Variables */
|
/** Variables */
|
||||||
|
|||||||
Reference in New Issue
Block a user