mirror of
https://github.com/airgradienthq/arduino.git
synced 2025-07-04 11:46:32 +02:00
[temporary commit]
This commit is contained in:
129
src/AgApiClient.cpp
Normal file
129
src/AgApiClient.cpp
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
#include "AgApiClient.h"
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
|
||||||
|
void AgApiClient::printLog(String log) {
|
||||||
|
debugLog.printf("[AgApiClient] %s\r\n", log.c_str());
|
||||||
|
// Serial.printf("[AgApiClient] %s\r\n", log.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
AgApiClient::AgApiClient(Stream &debug, AgConfigure &config)
|
||||||
|
: debugLog(debug), config(config) {}
|
||||||
|
|
||||||
|
AgApiClient::~AgApiClient() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the API client
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void AgApiClient::begin(void) {
|
||||||
|
getConfigFailed = false;
|
||||||
|
postToServerFailed = false;
|
||||||
|
printLog("Begin");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get configuration from AirGradient cloud
|
||||||
|
*
|
||||||
|
* @param deviceId Device ID
|
||||||
|
* @return true Success
|
||||||
|
* @return false Failure
|
||||||
|
*/
|
||||||
|
bool AgApiClient::fetchServerConfiguration(String deviceId) {
|
||||||
|
if (config.getConfigurationControl() ==
|
||||||
|
ConfigurationControl::ConfigurationControlLocal) {
|
||||||
|
printLog("Ignore fetch server configuratoin");
|
||||||
|
|
||||||
|
// Clear server configuration failed flag, cause it's ignore but not
|
||||||
|
// really failed
|
||||||
|
getConfigFailed = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String uri = "http://hw.airgradient.com/sensors/airgradient:" + deviceId +
|
||||||
|
"/one/config";
|
||||||
|
|
||||||
|
/** Init http client */
|
||||||
|
HTTPClient client;
|
||||||
|
if (client.begin(uri) == false) {
|
||||||
|
getConfigFailed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get data */
|
||||||
|
int retCode = client.GET();
|
||||||
|
if (retCode != 200) {
|
||||||
|
client.end();
|
||||||
|
getConfigFailed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** clear failed */
|
||||||
|
getConfigFailed = false;
|
||||||
|
|
||||||
|
/** Get response string */
|
||||||
|
String respContent = client.getString();
|
||||||
|
client.end();
|
||||||
|
|
||||||
|
printLog("Server configuration: " + respContent);
|
||||||
|
|
||||||
|
/** Parse configuration and return result */
|
||||||
|
return config.parse(respContent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Post data to AirGradient cloud
|
||||||
|
*
|
||||||
|
* @param deviceId Device Id
|
||||||
|
* @param data String JSON
|
||||||
|
* @return true Success
|
||||||
|
* @return false Failure
|
||||||
|
*/
|
||||||
|
bool AgApiClient::postToServer(String deviceId, String data) {
|
||||||
|
if (config.isPostDataToAirGradient() == false) {
|
||||||
|
printLog("Ignore post data to server");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WiFi.isConnected() == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String uri =
|
||||||
|
"http://hw.airgradient.com/sensors/airgradient:" + deviceId + "/measures";
|
||||||
|
printLog("Post uri: " + uri);
|
||||||
|
printLog("Post data: " + data);
|
||||||
|
|
||||||
|
WiFiClient wifiClient;
|
||||||
|
HTTPClient client;
|
||||||
|
if (client.begin(wifiClient, uri.c_str()) == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
client.addHeader("content-type", "application/json");
|
||||||
|
int retCode = client.POST(data);
|
||||||
|
client.end();
|
||||||
|
|
||||||
|
if ((retCode == 200) || (retCode == 429)) {
|
||||||
|
postToServerFailed = false;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
printLog("Post response failed code: " + String(retCode));
|
||||||
|
}
|
||||||
|
postToServerFailed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get failed status when get configuration from AirGradient cloud
|
||||||
|
*
|
||||||
|
* @return true Success
|
||||||
|
* @return false Failure
|
||||||
|
*/
|
||||||
|
bool AgApiClient::isFetchConfigureFailed(void) { return getConfigFailed; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get failed status when post data to AirGradient cloud
|
||||||
|
*
|
||||||
|
* @return true Success
|
||||||
|
* @return false Failure
|
||||||
|
*/
|
||||||
|
bool AgApiClient::isPostToServerFailed(void) { return postToServerFailed; }
|
38
src/AgApiClient.h
Normal file
38
src/AgApiClient.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* @file AgApiClient.h
|
||||||
|
* @brief HTTP client connect post data to Aigradient cloud.
|
||||||
|
* @version 0.1
|
||||||
|
* @date 2024-Apr-02
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _AG_API_CLIENT_H_
|
||||||
|
#define _AG_API_CLIENT_H_
|
||||||
|
|
||||||
|
#include "AgConfigure.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
class AgApiClient {
|
||||||
|
private:
|
||||||
|
Stream &debugLog;
|
||||||
|
AgConfigure &config;
|
||||||
|
|
||||||
|
bool getConfigFailed;;
|
||||||
|
bool postToServerFailed;
|
||||||
|
|
||||||
|
void printLog(String log);
|
||||||
|
|
||||||
|
public:
|
||||||
|
AgApiClient(Stream &stream, AgConfigure &config);
|
||||||
|
~AgApiClient();
|
||||||
|
|
||||||
|
void begin(void);
|
||||||
|
bool fetchServerConfiguration(String deviceId);
|
||||||
|
bool postToServer(String deviceId, String data);
|
||||||
|
bool isFetchConfigureFailed(void);
|
||||||
|
bool isPostToServerFailed(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /** _AG_API_CLIENT_H_ */
|
452
src/AgConfigure.cpp
Normal file
452
src/AgConfigure.cpp
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
#include "AgConfigure.h"
|
||||||
|
#include "EEPROM.h"
|
||||||
|
|
||||||
|
const char *CONFIGURATION_CONTROL_NAME[] = {
|
||||||
|
[ConfigurationControlLocal] = "local",
|
||||||
|
[ConfigurationControlCloud] = "cloud",
|
||||||
|
[ConfigurationControlBoth] = "both"};
|
||||||
|
|
||||||
|
void AgConfigure::printLog(String log) {
|
||||||
|
debugLog.printf("[AgConfigure] %s\r\n", log.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
String AgConfigure::getLedBarModeName(LedBarMode mode) {
|
||||||
|
LedBarMode ledBarMode = mode;
|
||||||
|
if (ledBarMode == LedBarModeOff) {
|
||||||
|
return String("off");
|
||||||
|
} else if (ledBarMode == LedBarModePm) {
|
||||||
|
return String("pm");
|
||||||
|
} else if (ledBarMode == LedBarModeCO2) {
|
||||||
|
return String("co2");
|
||||||
|
} else {
|
||||||
|
return String("off");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AgConfigure::saveConfig(void) {
|
||||||
|
config._check = 0;
|
||||||
|
int len = sizeof(config) - sizeof(config._check);
|
||||||
|
uint8_t *data = (uint8_t *)&config;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
config._check += data[i];
|
||||||
|
}
|
||||||
|
EEPROM.writeBytes(0, &config, sizeof(config));
|
||||||
|
EEPROM.commit();
|
||||||
|
printLog("Save Config");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AgConfigure::loadConfig(void) {
|
||||||
|
if (EEPROM.readBytes(0, &config, sizeof(config)) != sizeof(config)) {
|
||||||
|
printLog("Load configure failed");
|
||||||
|
defaultConfig();
|
||||||
|
} else {
|
||||||
|
uint32_t sum = 0;
|
||||||
|
uint8_t *data = (uint8_t *)&config;
|
||||||
|
int len = sizeof(config) - sizeof(config._check);
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
sum += data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sum != config._check) {
|
||||||
|
printLog("Configure validate invalid");
|
||||||
|
defaultConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void AgConfigure::defaultConfig(void) {
|
||||||
|
// Default country is null
|
||||||
|
memset(config.country, 0, sizeof(config.country));
|
||||||
|
// Default MQTT broker is null.
|
||||||
|
memset(config.mqttBroker, 0, sizeof(config.mqttBroker));
|
||||||
|
|
||||||
|
config.configurationControl = ConfigurationControl::ConfigurationControlBoth;
|
||||||
|
config.inUSAQI = false; // pmStandard = ugm3
|
||||||
|
config.inF = false;
|
||||||
|
config.postDataToAirGradient = true;
|
||||||
|
config.displayMode = true;
|
||||||
|
config.useRGBLedBar = LedBarMode::LedBarModeCO2;
|
||||||
|
config.abcDays = 7;
|
||||||
|
config.tvocLearningOffset = 12;
|
||||||
|
config.noxLearningOffset = 12;
|
||||||
|
config.temperatureUnit = 'c';
|
||||||
|
|
||||||
|
saveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AgConfigure::printConfig(void) { printLog(toString()); }
|
||||||
|
|
||||||
|
AgConfigure::AgConfigure(Stream &debugLog) : debugLog(debugLog) {}
|
||||||
|
|
||||||
|
AgConfigure::~AgConfigure() {}
|
||||||
|
|
||||||
|
bool AgConfigure::begin(void) {
|
||||||
|
EEPROM.begin(512);
|
||||||
|
loadConfig();
|
||||||
|
printConfig();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse JSON configura string to local configure
|
||||||
|
*
|
||||||
|
* @param data JSON string data
|
||||||
|
* @param isLocal true of data got from local, otherwise get from Aigradient
|
||||||
|
* server
|
||||||
|
* @return true Success
|
||||||
|
* @return false Failure
|
||||||
|
*/
|
||||||
|
bool AgConfigure::parse(String data, bool isLocal) {
|
||||||
|
JSONVar root = JSON.parse(data);
|
||||||
|
if (JSON.typeof_(root) == "undefined") {
|
||||||
|
printLog("Configuration JSON invalid");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
printLog("Parse configure success");
|
||||||
|
|
||||||
|
/** Is configuration changed */
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
/** Get ConfigurationControl */
|
||||||
|
if (JSON.typeof_(root["configurationControl"]) == "string") {
|
||||||
|
String configurationControl = root["configurationControl"];
|
||||||
|
if (configurationControl ==
|
||||||
|
String(CONFIGURATION_CONTROL_NAME
|
||||||
|
[ConfigurationControl::ConfigurationControlLocal])) {
|
||||||
|
config.configurationControl =
|
||||||
|
(uint8_t)ConfigurationControl::ConfigurationControlLocal;
|
||||||
|
changed = true;
|
||||||
|
} else if (configurationControl ==
|
||||||
|
String(CONFIGURATION_CONTROL_NAME
|
||||||
|
[ConfigurationControl::ConfigurationControlCloud])) {
|
||||||
|
config.configurationControl =
|
||||||
|
(uint8_t)ConfigurationControl::ConfigurationControlCloud;
|
||||||
|
changed = true;
|
||||||
|
} else if (configurationControl ==
|
||||||
|
String(CONFIGURATION_CONTROL_NAME
|
||||||
|
[ConfigurationControl::ConfigurationControlBoth])) {
|
||||||
|
config.configurationControl =
|
||||||
|
(uint8_t)ConfigurationControl::ConfigurationControlBoth;
|
||||||
|
changed = true;
|
||||||
|
} else {
|
||||||
|
printLog("'configurationControl' value '" + configurationControl +
|
||||||
|
"' invalid");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((config.configurationControl ==
|
||||||
|
(byte)ConfigurationControl::ConfigurationControlCloud)) {
|
||||||
|
printLog("Ignore, cause ConfigurationControl is " +
|
||||||
|
String(CONFIGURATION_CONTROL_NAME[config.configurationControl]));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON.typeof_(root["country"]) == "string") {
|
||||||
|
String country = root["country"];
|
||||||
|
if (country.length() == 2) {
|
||||||
|
if (country != String(config.country)) {
|
||||||
|
changed = true;
|
||||||
|
snprintf(config.country, sizeof(config.country), country.c_str());
|
||||||
|
printLog("Set country: " + country);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update temperature unit if get configuration from server
|
||||||
|
if (isLocal == false) {
|
||||||
|
if (country == "US") {
|
||||||
|
if (config.temperatureUnit == 'c') {
|
||||||
|
changed = true;
|
||||||
|
config.temperatureUnit = 'f';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (config.temperatureUnit == 'f') {
|
||||||
|
changed = true;
|
||||||
|
config.temperatureUnit = 'c';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printLog("Country name " + country +
|
||||||
|
" invalid. Find details here (ALPHA-2): "
|
||||||
|
"https://www.iban.com/country-codes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON.typeof_(root["pmStandard"]) == "string") {
|
||||||
|
String pmStandard = root["pmStandard"];
|
||||||
|
bool inUSAQI = true;
|
||||||
|
if (pmStandard == "ugm3") {
|
||||||
|
inUSAQI = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inUSAQI != config.inUSAQI) {
|
||||||
|
config.inUSAQI = inUSAQI;
|
||||||
|
changed = true;
|
||||||
|
printLog("Set PM standard: " + pmStandard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON.typeof_(root["co2CalibrationRequested"]) == "boolean") {
|
||||||
|
co2CalibrationRequested = root["co2CalibrationRequested"];
|
||||||
|
printLog("Set co2CalibrationRequested: " + String(co2CalibrationRequested));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON.typeof_(root["ledBarTestRequested"]) == "boolean") {
|
||||||
|
ledBarTestRequested = root["ledBarTestRequested"];
|
||||||
|
printLog("Set ledBarTestRequested: " + String(ledBarTestRequested));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON.typeof_(root["ledBarMode"]) == "string") {
|
||||||
|
String mode = root["ledBarMode"];
|
||||||
|
uint8_t ledBarMode = config.useRGBLedBar;
|
||||||
|
if (mode == "co2") {
|
||||||
|
ledBarMode = LedBarModeCO2;
|
||||||
|
} else if (mode == "pm") {
|
||||||
|
ledBarMode = LedBarModePm;
|
||||||
|
} else if (mode == "off") {
|
||||||
|
ledBarMode = LedBarModeOff;
|
||||||
|
} else {
|
||||||
|
ledBarMode = config.useRGBLedBar;
|
||||||
|
printLog("ledBarMode value '" + mode + "' invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ledBarMode != config.useRGBLedBar) {
|
||||||
|
config.useRGBLedBar = ledBarMode;
|
||||||
|
changed = true;
|
||||||
|
printLog("Set ledBarMode: " + mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON.typeof_(root["displayMode"]) == "string") {
|
||||||
|
String mode = root["displayMode"];
|
||||||
|
bool displayMode = false;
|
||||||
|
if (mode == "on") {
|
||||||
|
displayMode = true;
|
||||||
|
} else if (mode == "off") {
|
||||||
|
displayMode = false;
|
||||||
|
} else {
|
||||||
|
displayMode = config.displayMode;
|
||||||
|
printLog("displayMode '" + mode + "' invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayMode != config.displayMode) {
|
||||||
|
changed = true;
|
||||||
|
config.displayMode = displayMode;
|
||||||
|
printLog("Set displayMode: " + mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON.typeof_(root["abcDays"]) == "number") {
|
||||||
|
int abcDays = root["abcDays"];
|
||||||
|
if (abcDays != config.abcDays) {
|
||||||
|
config.abcDays = abcDays;
|
||||||
|
changed = true;
|
||||||
|
printLog("Set abcDays: " + String(abcDays));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON.typeof_(root["tvocLearningOffset"]) == "number") {
|
||||||
|
int tvocLearningOffset = root["tvocLearningOffset"];
|
||||||
|
if (tvocLearningOffset != config.tvocLearningOffset) {
|
||||||
|
changed = true;
|
||||||
|
config.tvocLearningOffset = tvocLearningOffset;
|
||||||
|
printLog("Set tvocLearningOffset: " + String(tvocLearningOffset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON.typeof_(root["noxLearningOffset"]) == "number") {
|
||||||
|
int noxLearningOffset = root["noxLearningOffset"];
|
||||||
|
if (noxLearningOffset != config.noxLearningOffset) {
|
||||||
|
changed = true;
|
||||||
|
config.noxLearningOffset = noxLearningOffset;
|
||||||
|
printLog("Set noxLearningOffset: " + String(noxLearningOffset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON.typeof_(root["mqttBrokerUrl"]) == "string") {
|
||||||
|
String broker = root["mqttBrokerUrl"];
|
||||||
|
if (broker.length() < sizeof(config.mqttBroker)) {
|
||||||
|
if (broker != String(config.mqttBroker)) {
|
||||||
|
changed = true;
|
||||||
|
snprintf(config.mqttBroker, sizeof(config.mqttBroker), broker.c_str());
|
||||||
|
printLog("Set mqttBrokerUrl: " + broker);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printLog("Error: mqttBroker length invalid: " + String(broker.length()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char temperatureUnit = 0;
|
||||||
|
if (JSON.typeof_(root["temperatureUnit"]) == "string") {
|
||||||
|
String unit = root["temperatureUnit"];
|
||||||
|
if (unit == "c" || unit == "C") {
|
||||||
|
temperatureUnit = 'c';
|
||||||
|
} else if (unit == "f" || unit == "F") {
|
||||||
|
temperatureUnit = 'f';
|
||||||
|
} else {
|
||||||
|
temperatureUnit = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (temperatureUnit != config.temperatureUnit) {
|
||||||
|
changed = true;
|
||||||
|
config.temperatureUnit = temperatureUnit;
|
||||||
|
if (temperatureUnit == 0) {
|
||||||
|
printLog("set temperatureUnit: null");
|
||||||
|
} else {
|
||||||
|
printLog("set temperatureUnit: " + String(temperatureUnit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON.typeof_(root["postDataToAirGradient"]) == "boolean") {
|
||||||
|
bool post = root["postDataToAirGradient"];
|
||||||
|
if (post != config.postDataToAirGradient) {
|
||||||
|
changed = true;
|
||||||
|
config.postDataToAirGradient = post;
|
||||||
|
printLog("Set postDataToAirGradient: " + String(post));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse data only got from AirGradient server */
|
||||||
|
if (isLocal == false) {
|
||||||
|
if (JSON.typeof_(root["model"]) == "string") {
|
||||||
|
String model = root["model"];
|
||||||
|
if (model.length() < sizeof(config.model)) {
|
||||||
|
if (model != String(config.model)) {
|
||||||
|
changed = true;
|
||||||
|
snprintf(config.model, sizeof(config.model), model.c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printLog("Error: modal name length invalid: " + String(model.length()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
saveConfig();
|
||||||
|
}
|
||||||
|
printConfig();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String AgConfigure::toString(void) {
|
||||||
|
JSONVar root;
|
||||||
|
|
||||||
|
/** "country" */
|
||||||
|
root["Country"] = String(config.country);
|
||||||
|
|
||||||
|
/** "pmStandard" */
|
||||||
|
if (config.inUSAQI) {
|
||||||
|
root["pmStandard"] = "USAQI";
|
||||||
|
} else {
|
||||||
|
root["pmStandard"] = "ugm3";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** co2CalibrationRequested */
|
||||||
|
/** ledBarTestRequested */
|
||||||
|
|
||||||
|
/** "ledBarMode" */
|
||||||
|
root["ledBarMode"] = getLedBarModeName();
|
||||||
|
|
||||||
|
/** "displayMode" */
|
||||||
|
root["displayMode"] = config.displayMode;
|
||||||
|
|
||||||
|
/** "abcDays" */
|
||||||
|
root["abcDays"] = config.abcDays;
|
||||||
|
|
||||||
|
/** "tvocLearningOffset" */
|
||||||
|
root["tvocLearningOffset"] = config.tvocLearningOffset;
|
||||||
|
|
||||||
|
/** "noxLearningOffset" */
|
||||||
|
root["noxLearningOffset"] = config.noxLearningOffset;
|
||||||
|
|
||||||
|
/** "mqttBrokerUrl" */
|
||||||
|
root["mqttBrokerUrl"] = String(config.mqttBroker);
|
||||||
|
|
||||||
|
/** "temperatureUnit" */
|
||||||
|
root["temperatureUnit"] = String(config.temperatureUnit);
|
||||||
|
|
||||||
|
/** configurationControl */
|
||||||
|
root["configurationControl"] =
|
||||||
|
String(CONFIGURATION_CONTROL_NAME[config.configurationControl]);
|
||||||
|
|
||||||
|
/** "postDataToAirGradient" */
|
||||||
|
root["postDataToAirGradient"] = config.postDataToAirGradient;
|
||||||
|
|
||||||
|
return JSON.stringify(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AgConfigure::isTemperatureUnitInF(void) {
|
||||||
|
return (config.temperatureUnit == 'f');
|
||||||
|
}
|
||||||
|
|
||||||
|
String AgConfigure::getCountry(void) { return String(config.country); }
|
||||||
|
|
||||||
|
bool AgConfigure::isPmStandardInUSAQI(void) { return config.inUSAQI; }
|
||||||
|
|
||||||
|
int AgConfigure::getCO2CalirationAbcDays(void) { return config.abcDays; }
|
||||||
|
|
||||||
|
LedBarMode AgConfigure::getLedBarMode(void) {
|
||||||
|
return (LedBarMode)config.useRGBLedBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
String AgConfigure::getLedBarModeName(void) {
|
||||||
|
return getLedBarModeName((LedBarMode)config.useRGBLedBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AgConfigure::getDisplayMode(void) { return config.displayMode; }
|
||||||
|
|
||||||
|
String AgConfigure::getMqttBrokerUri(void) { return String(config.mqttBroker); }
|
||||||
|
|
||||||
|
bool AgConfigure::isPostDataToAirGradient(void) {
|
||||||
|
return config.postDataToAirGradient;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigurationControl AgConfigure::getConfigurationControl(void) {
|
||||||
|
return (ConfigurationControl)config.configurationControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CO2 manual calib request, the request flag will clear after get. Must
|
||||||
|
* call this after parse success
|
||||||
|
*
|
||||||
|
* @return true Requested
|
||||||
|
* @return false Not requested
|
||||||
|
*/
|
||||||
|
bool AgConfigure::isCo2CalibrationRequested(void) {
|
||||||
|
bool requested = co2CalibrationRequested;
|
||||||
|
co2CalibrationRequested = false; // clear requested
|
||||||
|
return requested;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED bar test request, the request flag will clear after get. Must call
|
||||||
|
* this function after parse success
|
||||||
|
*
|
||||||
|
* @return true Requested
|
||||||
|
* @return false Not requested
|
||||||
|
*/
|
||||||
|
bool AgConfigure::isLedBarTestRequested(void) {
|
||||||
|
bool requested = ledBarTestRequested;
|
||||||
|
ledBarTestRequested = false;
|
||||||
|
return requested;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset default configure
|
||||||
|
*/
|
||||||
|
void AgConfigure::reset(void) {
|
||||||
|
defaultConfig();
|
||||||
|
printLog("Reset to default configure");
|
||||||
|
printConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get model name, it's usage for offline mode
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
String AgConfigure::getModel(void) { return String(config.model); }
|
66
src/AgConfigure.h
Normal file
66
src/AgConfigure.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#ifndef _AG_CONFIG_H_
|
||||||
|
#define _AG_CONFIG_H_
|
||||||
|
|
||||||
|
#include <AirGradient.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Arduino_JSON.h>
|
||||||
|
|
||||||
|
class AgConfigure {
|
||||||
|
private:
|
||||||
|
struct Config {
|
||||||
|
char model[20];
|
||||||
|
char country[3]; /** Country name has only 2 character, ex: TH = Thailand */
|
||||||
|
char mqttBroker[256]; /** MQTT broker URI */
|
||||||
|
bool inUSAQI; /** If PM standard "ugm3" inUSAQI = false, otherwise is true
|
||||||
|
*/
|
||||||
|
bool inF; /** Temperature unit F */
|
||||||
|
bool postDataToAirGradient; /** If true, monitor will not POST data to
|
||||||
|
airgradient server. Make sure no error
|
||||||
|
message shown on monitor */
|
||||||
|
uint8_t configurationControl; /** If true, configuration from airgradient server
|
||||||
|
will be ignored */
|
||||||
|
bool displayMode; /** true if enable display */
|
||||||
|
uint8_t useRGBLedBar;
|
||||||
|
uint8_t abcDays;
|
||||||
|
int tvocLearningOffset;
|
||||||
|
int noxLearningOffset;
|
||||||
|
char temperatureUnit; // 'f' or 'c'
|
||||||
|
|
||||||
|
uint32_t _check;
|
||||||
|
};
|
||||||
|
struct Config config;
|
||||||
|
bool co2CalibrationRequested;
|
||||||
|
bool ledBarTestRequested;
|
||||||
|
Stream &debugLog;
|
||||||
|
|
||||||
|
void printLog(String log);
|
||||||
|
String getLedBarModeName(LedBarMode mode);
|
||||||
|
void saveConfig(void);
|
||||||
|
void loadConfig(void);
|
||||||
|
void defaultConfig(void);
|
||||||
|
void printConfig(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
AgConfigure(Stream &debugLog);
|
||||||
|
~AgConfigure();
|
||||||
|
|
||||||
|
bool begin(void);
|
||||||
|
bool parse(String data, bool isLocal);
|
||||||
|
String toString(void);
|
||||||
|
bool isTemperatureUnitInF(void);
|
||||||
|
String getCountry(void);
|
||||||
|
bool isPmStandardInUSAQI(void);
|
||||||
|
int getCO2CalirationAbcDays(void);
|
||||||
|
LedBarMode getLedBarMode(void);
|
||||||
|
String getLedBarModeName(void);
|
||||||
|
bool getDisplayMode(void);
|
||||||
|
String getMqttBrokerUri(void);
|
||||||
|
bool isPostDataToAirGradient(void);
|
||||||
|
ConfigurationControl getConfigurationControl(void);
|
||||||
|
bool isCo2CalibrationRequested(void);
|
||||||
|
bool isLedBarTestRequested(void);
|
||||||
|
void reset(void);
|
||||||
|
String getModel(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /** _AG_CONFIG_H_ */
|
21
src/AgSchedule.cpp
Normal file
21
src/AgSchedule.cpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#include "AgSchedule.h"
|
||||||
|
|
||||||
|
AgSchedule::AgSchedule(int period, void (*handler)(void))
|
||||||
|
: period(period), handler(handler) {}
|
||||||
|
|
||||||
|
AgSchedule::~AgSchedule() {}
|
||||||
|
|
||||||
|
void AgSchedule::run(void) {
|
||||||
|
uint32_t ms = (uint32_t)(millis() - count);
|
||||||
|
if (ms >= period) {
|
||||||
|
handler();
|
||||||
|
count = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set schedule period
|
||||||
|
*
|
||||||
|
* @param period Period in ms
|
||||||
|
*/
|
||||||
|
void AgSchedule::setPeriod(int period) { this->period = period; }
|
19
src/AgSchedule.h
Normal file
19
src/AgSchedule.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#ifndef _AG_SCHEDULE_H_
|
||||||
|
#define _AG_SCHEDULE_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
class AgSchedule {
|
||||||
|
private:
|
||||||
|
int period;
|
||||||
|
void (*handler)(void);
|
||||||
|
uint32_t count;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AgSchedule(int period, void (*handler)(void));
|
||||||
|
~AgSchedule();
|
||||||
|
void run(void);
|
||||||
|
void setPeriod(int period);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /** _AG_SCHEDULE_H_ */
|
60
src/AgStateMachine.h
Normal file
60
src/AgStateMachine.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#ifndef _AG_STATE_MACHINE_H_
|
||||||
|
#define _AG_STATE_MACHINE_H_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Application state machine state
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
enum AgStateMachine {
|
||||||
|
/** In WiFi Manger Mode */
|
||||||
|
AgStateMachineWiFiManagerMode,
|
||||||
|
|
||||||
|
/** WiFi Manager has connected to mobile phone */
|
||||||
|
AgStateMachineWiFiManagerPortalActive,
|
||||||
|
|
||||||
|
/** After SSID and PW entered and OK clicked, connection to WiFI network is
|
||||||
|
attempted*/
|
||||||
|
AgStateMachineWiFiManagerStaConnecting,
|
||||||
|
|
||||||
|
/** Connecting to WiFi worked */
|
||||||
|
AgStateMachineWiFiManagerStaConnected,
|
||||||
|
|
||||||
|
/** Once connected to WiFi an attempt to reach the server is performed */
|
||||||
|
AgStateMachineWiFiOkServerConnecting,
|
||||||
|
|
||||||
|
/** Server is reachable, all fine */
|
||||||
|
AgStateMachineWiFiOkServerConnected,
|
||||||
|
|
||||||
|
/** =================================== *
|
||||||
|
* Exceptions during WIFi Setup *
|
||||||
|
* =================================== **/
|
||||||
|
/** Cannot connect to WiFi (e.g. wrong password, WPA Enterprise etc.) */
|
||||||
|
AgStateMachineWiFiManagerConnectFailed,
|
||||||
|
|
||||||
|
/** Connected to WiFi but server not reachable, e.g. firewall
|
||||||
|
block/whitelisting needed etc. */
|
||||||
|
AgStateMachineWiFiOkServerConnectFailed,
|
||||||
|
|
||||||
|
/** Server reachable but sensor not configured correctly*/
|
||||||
|
AgStateMachineWiFiOkServerOkSensorConfigFailed,
|
||||||
|
|
||||||
|
/** =================================== *
|
||||||
|
* During Normal Operation *
|
||||||
|
* =================================== **/
|
||||||
|
|
||||||
|
/** Connection to WiFi network failed credentials incorrect encryption not
|
||||||
|
supported etc. */
|
||||||
|
AgStateMachineWiFiLost,
|
||||||
|
|
||||||
|
/** Connected to WiFi network but the server cannot be reached through the
|
||||||
|
internet, e.g. blocked by firewall */
|
||||||
|
AgStateMachineServerLost,
|
||||||
|
|
||||||
|
/** Server is reachable but there is some configuration issue to be fixed on
|
||||||
|
the server side */
|
||||||
|
AgStateMachineSensorConfigFailed,
|
||||||
|
|
||||||
|
AgStateMachineNormal,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /** _AG_STATE_MACHINE_H_ */
|
@ -2,6 +2,24 @@
|
|||||||
|
|
||||||
#define AG_LIB_VER "3.0.9"
|
#define AG_LIB_VER "3.0.9"
|
||||||
|
|
||||||
|
const char *AgFirmwareModeName(AgFirmwareMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case FW_MODE_I_9PSL:
|
||||||
|
return "I-9PSL";
|
||||||
|
case FW_MODE_O_1PP:
|
||||||
|
return "O-1PP";
|
||||||
|
case FW_MODE_O_1PPT:
|
||||||
|
return "O-1PPT";
|
||||||
|
case FW_MODE_O_1PST:
|
||||||
|
return "O-1PST";
|
||||||
|
case FW_MDOE_O_1PS:
|
||||||
|
return "0-1PS";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
|
||||||
AirGradient::AirGradient(BoardType type)
|
AirGradient::AirGradient(BoardType type)
|
||||||
: pms5003(type), pms5003t_1(type), pms5003t_2(type), s8(type), sgp41(type),
|
: pms5003(type), pms5003t_1(type), pms5003t_2(type), s8(type), sgp41(type),
|
||||||
display(type), boardType(type), button(type), statusLed(type),
|
display(type), boardType(type), button(type), statusLed(type),
|
||||||
|
@ -15,20 +15,38 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief RGB LED bar mode for ONE_INDOOR board
|
* @brief RGB LED bar mode for ONE_INDOOR board
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
enum UseLedBar {
|
enum LedBarMode {
|
||||||
UseLedBarOff, /** Don't use LED bar */
|
/** Don't use LED bar */
|
||||||
UseLedBarPM, /** Use LED bar for PMS */
|
LedBarModeOff,
|
||||||
UseLedBarCO2, /** Use LED bar for CO2 */
|
|
||||||
|
/** Use LED bar for show PM2.5 value level */
|
||||||
|
LedBarModePm,
|
||||||
|
|
||||||
|
/** Use LED bar for show CO2 value level */
|
||||||
|
LedBarModeCO2,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ConfigurationControl {
|
enum ConfigurationControl {
|
||||||
Local, /** Allow set configuration from local over HTTP server */
|
/** Allow set configuration from local over device HTTP server */
|
||||||
Cloud, /** Allow set configuration from Airgradient webserver */
|
ConfigurationControlLocal,
|
||||||
Both /** Allow set configuration from Local and Cloud */
|
|
||||||
|
/** Allow set configuration from Airgradient cloud */
|
||||||
|
ConfigurationControlCloud,
|
||||||
|
|
||||||
|
/** Allow set configuration from Local and Cloud */
|
||||||
|
ConfigurationControlBoth
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum AgFirmwareMode {
|
||||||
|
FW_MODE_I_9PSL, /** ONE_INDOOR */
|
||||||
|
FW_MODE_O_1PST, /** PMS5003T, S8 and SGP41 */
|
||||||
|
FW_MODE_O_1PPT, /** PMS5003T_1, PMS5003T_2, SGP41 */
|
||||||
|
FW_MODE_O_1PP, /** PMS5003T_1, PMS5003T_2 */
|
||||||
|
FW_MDOE_O_1PS /** PMS5003T, S8 */
|
||||||
|
};
|
||||||
|
const char* AgFirmwareModeName(AgFirmwareMode mode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Class with define all the sensor has supported by Airgradient. Each
|
* @brief Class with define all the sensor has supported by Airgradient. Each
|
||||||
* sensor usage must be init before use.
|
* sensor usage must be init before use.
|
||||||
|
170
src/MqttClient.cpp
Normal file
170
src/MqttClient.cpp
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
#include "MqttClient.h"
|
||||||
|
|
||||||
|
static void __mqtt_event_handler(void *handler_args, esp_event_base_t base,
|
||||||
|
int32_t event_id, void *event_data);
|
||||||
|
|
||||||
|
MqttClient::MqttClient(Stream &debugLog) : debugLog(debugLog) {}
|
||||||
|
|
||||||
|
MqttClient::~MqttClient() {}
|
||||||
|
|
||||||
|
bool MqttClient::begin(String uri) {
|
||||||
|
if (isBegin) {
|
||||||
|
_printLog("Already begin, calll 'end' and try again");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (uri.isEmpty()) {
|
||||||
|
Serial.println("Mqtt uri is empty");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->uri = uri;
|
||||||
|
_printLog("Init uri: " + uri);
|
||||||
|
|
||||||
|
/** config esp_mqtt client */
|
||||||
|
esp_mqtt_client_config_t config = {
|
||||||
|
.uri = this->uri.c_str(),
|
||||||
|
};
|
||||||
|
|
||||||
|
/** init client */
|
||||||
|
client = esp_mqtt_client_init(&config);
|
||||||
|
if (client == NULL) {
|
||||||
|
_printLog("Init client failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Register event */
|
||||||
|
if (esp_mqtt_client_register_event(client, MQTT_EVENT_ANY, __mqtt_event_handler,
|
||||||
|
this) != ESP_OK) {
|
||||||
|
_printLog("Register event failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_mqtt_client_start(client) != ESP_OK) {
|
||||||
|
_printLog("Client start failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isBegin = true;
|
||||||
|
connectionFailedCount = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttClient::end(void) {
|
||||||
|
if (!isBegin) {
|
||||||
|
_printLog("Already end, call 'begin' and try again");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_mqtt_client_disconnect(client);
|
||||||
|
esp_mqtt_client_stop(client);
|
||||||
|
esp_mqtt_client_destroy(client);
|
||||||
|
client = NULL;
|
||||||
|
isBegin = false;
|
||||||
|
|
||||||
|
Serial.println("De-init");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttClient::_printLog(String log) {
|
||||||
|
debugLog.println("[MqttClient]" + log);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttClient::_updateConnected(bool connected) {
|
||||||
|
this->connected = connected;
|
||||||
|
if (connected) {
|
||||||
|
connectionFailedCount = 0;
|
||||||
|
} else {
|
||||||
|
connectionFailedCount++;
|
||||||
|
_printLog("Connection failed count " + String(connectionFailedCount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MqttClient::publish(String &topic, String &payload) {
|
||||||
|
if (!isBegin) {
|
||||||
|
_printLog("Error: No-initialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!connected) {
|
||||||
|
_printLog("Error: Client disconnected");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_mqtt_client_publish(client, topic.c_str(), payload.c_str(),
|
||||||
|
payload.length(), 0, 0) == ESP_OK) {
|
||||||
|
_printLog("Publish topic: " + topic);
|
||||||
|
_printLog("Publish payload: " + payload);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
_printLog("Error: publish");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check that URI is same as current initialized URI
|
||||||
|
*
|
||||||
|
* @param uri Target URI
|
||||||
|
* @return true Same
|
||||||
|
* @return false Difference
|
||||||
|
*/
|
||||||
|
bool MqttClient::isCurrentUri(String &uri) {
|
||||||
|
if (this->uri == uri) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get MQTT client connected status
|
||||||
|
*
|
||||||
|
* @return true Connected
|
||||||
|
* @return false Disconnected
|
||||||
|
*/
|
||||||
|
bool MqttClient::isConnected(void) { return connected; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get number of connection failed
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
int MqttClient::getConnectionFailedCount(void) { return connectionFailedCount; }
|
||||||
|
|
||||||
|
static void __mqtt_event_handler(void *handler_args, esp_event_base_t base,
|
||||||
|
int32_t event_id, void *event_data) {
|
||||||
|
MqttClient *mqtt = (MqttClient *)handler_args;
|
||||||
|
|
||||||
|
esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)event_data;
|
||||||
|
esp_mqtt_client_handle_t client = event->client;
|
||||||
|
|
||||||
|
int msg_id;
|
||||||
|
switch ((esp_mqtt_event_id_t)event_id) {
|
||||||
|
case MQTT_EVENT_CONNECTED:
|
||||||
|
mqtt->_printLog("MQTT_EVENT_CONNECTED");
|
||||||
|
mqtt->_updateConnected(true);
|
||||||
|
break;
|
||||||
|
case MQTT_EVENT_DISCONNECTED:
|
||||||
|
mqtt->_printLog("MQTT_EVENT_DISCONNECTED");
|
||||||
|
mqtt->_updateConnected(false);
|
||||||
|
break;
|
||||||
|
case MQTT_EVENT_SUBSCRIBED:
|
||||||
|
break;
|
||||||
|
case MQTT_EVENT_UNSUBSCRIBED:
|
||||||
|
break;
|
||||||
|
case MQTT_EVENT_PUBLISHED:
|
||||||
|
break;
|
||||||
|
case MQTT_EVENT_DATA:
|
||||||
|
break;
|
||||||
|
case MQTT_EVENT_ERROR:
|
||||||
|
Serial.println("MQTT_EVENT_ERROR");
|
||||||
|
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
|
||||||
|
mqtt->_printLog("Reported from esp-tls: " +
|
||||||
|
String(event->error_handle->esp_tls_last_esp_err));
|
||||||
|
mqtt->_printLog("Reported from tls stack: " +
|
||||||
|
String(event->error_handle->esp_tls_stack_err));
|
||||||
|
mqtt->_printLog("Captured as transport's socket errno: " +
|
||||||
|
String(event->error_handle->esp_transport_sock_errno));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Serial.printf("Other event id:%d\r\n", event->event_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
30
src/MqttClient.h
Normal file
30
src/MqttClient.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef _AG_MQTT_CLIENT_H_
|
||||||
|
#define _AG_MQTT_CLIENT_H_
|
||||||
|
|
||||||
|
#include "mqtt_client.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
class MqttClient {
|
||||||
|
private:
|
||||||
|
Stream &debugLog;
|
||||||
|
bool isBegin = false;
|
||||||
|
String uri;
|
||||||
|
esp_mqtt_client_handle_t client;
|
||||||
|
bool connected = false;
|
||||||
|
int connectionFailedCount = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MqttClient(Stream &debugLog);
|
||||||
|
~MqttClient();
|
||||||
|
|
||||||
|
bool begin(String uri);
|
||||||
|
void end(void);
|
||||||
|
void _printLog(String log);
|
||||||
|
void _updateConnected(bool connected);
|
||||||
|
bool publish(String &topic, String &payload);
|
||||||
|
bool isCurrentUri(String &uri);
|
||||||
|
bool isConnected(void);
|
||||||
|
int getConnectionFailedCount(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /** _AG_MQTT_CLIENT_H_ */
|
Reference in New Issue
Block a user