Merge branch 'develop' into feature/send-pms-sensor-fw-version-to-ag-cloud

This commit is contained in:
Phat Nguyen
2024-08-25 20:46:28 +07:00
21 changed files with 279 additions and 116 deletions

View File

@ -22,6 +22,7 @@ AgApiClient::~AgApiClient() {}
void AgApiClient::begin(void) {
getConfigFailed = false;
postToServerFailed = false;
logInfo("Init apiRoot: " + apiRoot);
logInfo("begin");
}
@ -44,9 +45,8 @@ bool AgApiClient::fetchServerConfiguration(void) {
return false;
}
String uri =
"http://hw.airgradient.com/sensors/airgradient:" + ag->deviceId() +
"/one/config";
String uri = apiRoot + "/sensors/airgradient:" +
ag->deviceId() + "/one/config";
/** Init http client */
#ifdef ESP8266
@ -66,6 +66,10 @@ bool AgApiClient::fetchServerConfiguration(void) {
/** Get data */
int retCode = client.GET();
logInfo(String("GET: ") + uri);
logInfo(String("Return code: ") + String(retCode));
if (retCode != 200) {
client.end();
getConfigFailed = true;
@ -112,18 +116,23 @@ bool AgApiClient::postToServer(String data) {
String uri =
"http://hw.airgradient.com/sensors/airgradient:" + ag->deviceId() +
"/measures";
logInfo("Post uri: " + uri);
logInfo("Post data: " + data);
// logInfo("Post uri: " + uri);
// logInfo("Post data: " + data);
WiFiClient wifiClient;
HTTPClient client;
if (client.begin(wifiClient, uri.c_str()) == false) {
logError("Init client failed");
return false;
}
client.addHeader("content-type", "application/json");
int retCode = client.POST(data);
client.end();
logInfo(String("POST: ") + uri);
logInfo(String("DATA: ") + data);
logInfo(String("Return code: ") + String(retCode));
if ((retCode == 200) || (retCode == 429)) {
postToServerFailed = false;
return true;
@ -177,3 +186,7 @@ bool AgApiClient::sendPing(int rssi, int bootCount) {
root["boot"] = bootCount;
return postToServer(JSON.stringify(root));
}
String AgApiClient::getApiRoot() const { return apiRoot; }
void AgApiClient::setApiRoot(const String &apiRoot) { this->apiRoot = apiRoot; }

View File

@ -20,6 +20,7 @@ class AgApiClient : public PrintLog {
private:
Configuration &config;
AirGradient *ag;
String apiRoot = "http://hw.airgradient.com";
bool getConfigFailed;
bool postToServerFailed;
@ -37,6 +38,8 @@ public:
bool isNotAvailableOnDashboard(void);
void setAirGradient(AirGradient *ag);
bool sendPing(int rssi, int bootCount);
String getApiRoot() const;
void setApiRoot(const String &apiRoot);
};
#endif /** _AG_API_CLIENT_H_ */

View File

@ -264,12 +264,14 @@ bool Configuration::parse(String data, bool isLocal) {
jconfig[jprop_configurationControl]);
}
/** Check to return result if configurationControl is 'cloud' */
if (ctrl ==
String(CONFIGURATION_CONTROL_NAME
[ConfigurationControl::ConfigurationControlCloud])) {
failedMessage = String(msg);
return true;
/** Return failed if new "configurationControl" new and old is "cloud" */
if (ctrl == String(CONFIGURATION_CONTROL_NAME [ConfigurationControl::ConfigurationControlCloud])) {
if(ctrl != lastCtrl) {
return true;
} else {
failedMessage = String(msg);
return false;
}
}
} else {
failedMessage =

View File

@ -304,6 +304,10 @@ void OledDisplay::showDashboard(const char *status) {
DISP()->drawStr(55, 27, "PM2.5");
/** Draw PM2.5 value */
int pm25 = value.pm25_1;
if (config.hasSensorSHT) {
pm25 = ag->pms5003.compensated(pm25, value.Humidity);
}
DISP()->setFont(u8g2_font_t0_22b_tf);
if (config.isPmStandardInUSAQI()) {
if (utils::isValidPMS(value.pm25_1)) {
@ -338,7 +342,7 @@ void OledDisplay::showDashboard(const char *status) {
DISP()->drawStr(100, 39, strBuf);
/** Draw NOx label */
DISP()->drawStr(85, 53, "NOx:");
DISP()->drawStr(100, 53, "NOx:");
if (utils::isValidNOx(value.NOx)) {
sprintf(strBuf, "%d", value.NOx);
} else {
@ -360,6 +364,10 @@ void OledDisplay::showDashboard(const char *status) {
ag->display.setText(strBuf);
/** Set PM */
int pm25 = value.pm25_1;
if(config.hasSensorSHT) {
pm25 = (int)ag->pms5003.compensated(pm25, value.Humidity);
}
ag->display.setCursor(0, 12);
if (utils::isValidPMS(value.pm25_1)) {
snprintf(strBuf, sizeof(strBuf), "PM2.5:%d", value.pm25_1);

View File

@ -88,6 +88,13 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
}
}
if (config->hasSensorSHT && config->hasSensorPMS1) {
int pm25 = ag->pms5003.compensated(this->pm25_1, this->Humidity);
if (pm25 >= 0) {
root["pm02Compensated"] = pm25;
}
}
} else {
if (config->hasSensorPMS1 && config->hasSensorPMS2) {
if (utils::isValidPMS(this->pm01_1) && utils::isValidPMS(this->pm01_2)) {
@ -122,6 +129,11 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
}
}
}
int pm25 = (ag->pms5003t_1.compensated(this->pm25_1, this->temp_1) +
ag->pms5003t_2.compensated(this->pm25_2, this->temp_2)) /
2;
root["pm02Compensated"] = pm25;
}
if (fwMode == FW_MODE_O_1PS || fwMode == FW_MODE_O_1PST) {
@ -159,6 +171,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
}
}
}
root["pm02Compensated"] = ag->pms5003t_1.compensated(this->pm25_1, this->temp_1);
if (!localServer) {
root[json_prop_pmFirmware] =
pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion());
@ -199,6 +212,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
}
}
}
root["pm02Compensated"] = ag->pms5003t_2.compensated(this->pm25_2, this->temp_2);
if(!localServer) {
root[json_prop_pmFirmware] =
pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion());
@ -239,6 +253,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
}
}
}
root["pm02Compensated"] = ag->pms5003t_1.compensated(this->pm25_1, this->temp_1);
if(!localServer) {
root[json_prop_pmFirmware] =
pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion());
@ -276,6 +291,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
}
}
}
root["pm02Compensated"] = ag->pms5003t_1.compensated(this->pm25_1, this->temp_1);
if(!localServer) {
root[json_prop_pmFirmware] =
pms5003TFirmwareVersion(ag->pms5003t_2.getFirmwareVersion());
@ -316,6 +332,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
}
}
}
root["channels"]["1"]["pm02Compensated"] = ag->pms5003t_1.compensated(this->pm25_1, this->temp_1);
// PMS5003T version
if(!localServer) {
@ -357,6 +374,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
}
}
}
root["channels"]["2"]["pm02Compensated"] = ag->pms5003t_2.compensated(this->pm25_2, this->temp_2);
// PMS5003T version
if(!localServer) {
root["channels"]["2"][json_prop_pmFirmware] =

View File

@ -41,7 +41,14 @@ String AirGradient::getVersion(void) { return GIT_VERSION; }
BoardType AirGradient::getBoardType(void) { return boardType; }
double AirGradient::round2(double value) {
return (int)(value * 100 + 0.5) / 100.0;
double ret;
if (value >= 0) {
ret = (int)(value * 100 + 0.5f);
} else {
ret = (int)(value * 100 - 0.5f);
}
return ret / 100;
}
String AirGradient::getBoardName(void) {

View File

@ -15,7 +15,7 @@
#include "Main/utils.h"
#ifndef GIT_VERSION
#define GIT_VERSION "snapshot"
#define GIT_VERSION "3.1.5-snap"
#endif
/**

View File

@ -12,10 +12,14 @@
#define VALID_PMS_MIN (0)
#define INVALID_PMS (-1)
#define VALID_PMS03COUNT_MIN (0)
#define VALID_CO2_MAX (10000)
#define VALID_CO2_MIN (0)
#define INVALID_CO2 (-1)
#define VALID_NOX_MIN (0)
#define VALID_VOC_MIN (0)
#define INVALID_NOX (-1)
#define INVALID_VOC (-1)
@ -24,49 +28,49 @@ utils::utils(/* args */) {}
utils::~utils() {}
bool utils::isValidTemperature(float value) {
if (value >= VALID_TEMPERATURE_MIN && value <= VALID_TEMPERATURE_MAX) {
if ((value >= VALID_TEMPERATURE_MIN) && (value <= VALID_TEMPERATURE_MAX)) {
return true;
}
return false;
}
bool utils::isValidHumidity(float value) {
if (value >= VALID_HUMIDITY_MIN && value <= VALID_HUMIDITY_MAX) {
if ((value >= VALID_HUMIDITY_MIN) && (value <= VALID_HUMIDITY_MAX)) {
return true;
}
return false;
}
bool utils::isValidCO2(int16_t value) {
if (value >= VALID_CO2_MIN && value <= VALID_CO2_MAX) {
if ((value >= VALID_CO2_MIN) && (value <= VALID_CO2_MAX)) {
return true;
}
return false;
}
bool utils::isValidPMS(int value) {
if (value >= VALID_PMS_MIN && value <= VALID_PMS_MAX) {
if ((value >= VALID_PMS_MIN) && (value <= VALID_PMS_MAX)) {
return true;
}
return false;
}
bool utils::isValidPMS03Count(int value) {
if (value >= 0) {
if (value >= VALID_PMS03COUNT_MIN) {
return true;
}
return false;
}
bool utils::isValidNOx(int value) {
if (value > INVALID_NOX) {
if (value >= VALID_NOX_MIN) {
return true;
}
return false;
}
bool utils::isValidVOC(int value) {
if (value > INVALID_VOC) {
if (value >= VALID_VOC_MIN) {
return true;
}
return false;

View File

@ -3,7 +3,7 @@
/**
* @brief Init and check that sensor has connected
*
*
* @param stream UART stream
* @return true Sucecss
* @return false Failure
@ -86,7 +86,7 @@ void PMSBase::handle() {
case 2: {
buf[bufIndex++] = value;
if (bufIndex >= 4) {
len = toValue(&buf[2]);
len = toI16(&buf[2]);
if (len != 28) {
// Serial.printf("Got good bad len %d\r\n", len);
len += 4;
@ -152,98 +152,98 @@ bool PMSBase::isFailed(void) { return failed; }
*
* @return uint16_t
*/
uint16_t PMSBase::getRaw0_1(void) { return toValue(&package[4]); }
uint16_t PMSBase::getRaw0_1(void) { return toU16(&package[4]); }
/**
* @brief Read PMS 2.5 ug/m3 with CF = 1 PM estimates
*
* @return uint16_t
*/
uint16_t PMSBase::getRaw2_5(void) { return toValue(&package[6]); }
uint16_t PMSBase::getRaw2_5(void) { return toU16(&package[6]); }
/**
* @brief Read PMS 10 ug/m3 with CF = 1 PM estimates
*
* @return uint16_t
*/
uint16_t PMSBase::getRaw10(void) { return toValue(&package[8]); }
uint16_t PMSBase::getRaw10(void) { return toU16(&package[8]); }
/**
* @brief Read PMS 0.1 ug/m3
*
* @return uint16_t
*/
uint16_t PMSBase::getPM0_1(void) { return toValue(&package[10]); }
uint16_t PMSBase::getPM0_1(void) { return toU16(&package[10]); }
/**
* @brief Read PMS 2.5 ug/m3
*
* @return uint16_t
*/
uint16_t PMSBase::getPM2_5(void) { return toValue(&package[12]); }
uint16_t PMSBase::getPM2_5(void) { return toU16(&package[12]); }
/**
* @brief Read PMS 10 ug/m3
*
* @return uint16_t
*/
uint16_t PMSBase::getPM10(void) { return toValue(&package[14]); }
uint16_t PMSBase::getPM10(void) { return toU16(&package[14]); }
/**
* @brief Get numnber concentrations over 0.3 um/0.1L
*
* @return uint16_t
*/
uint16_t PMSBase::getCount0_3(void) { return toValue(&package[16]); }
uint16_t PMSBase::getCount0_3(void) { return toU16(&package[16]); }
/**
* @brief Get numnber concentrations over 0.5 um/0.1L
*
* @return uint16_t
*/
uint16_t PMSBase::getCount0_5(void) { return toValue(&package[18]); }
uint16_t PMSBase::getCount0_5(void) { return toU16(&package[18]); }
/**
* @brief Get numnber concentrations over 1.0 um/0.1L
*
* @return uint16_t
*/
uint16_t PMSBase::getCount1_0(void) { return toValue(&package[20]); }
uint16_t PMSBase::getCount1_0(void) { return toU16(&package[20]); }
/**
* @brief Get numnber concentrations over 2.5 um/0.1L
*
* @return uint16_t
*/
uint16_t PMSBase::getCount2_5(void) { return toValue(&package[22]); }
uint16_t PMSBase::getCount2_5(void) { return toU16(&package[22]); }
/**
* @brief Get numnber concentrations over 5.0 um/0.1L (only PMS5003)
*
* @return uint16_t
*/
uint16_t PMSBase::getCount5_0(void) { return toValue(&package[24]); }
uint16_t PMSBase::getCount5_0(void) { return toU16(&package[24]); }
/**
* @brief Get numnber concentrations over 10.0 um/0.1L (only PMS5003)
*
* @return uint16_t
*/
uint16_t PMSBase::getCount10(void) { return toValue(&package[26]); }
uint16_t PMSBase::getCount10(void) { return toU16(&package[26]); }
/**
* @brief Get temperature (only PMS5003T)
*
* @return uint16_t
*/
uint16_t PMSBase::getTemp(void) { return toValue(&package[24]); }
int16_t PMSBase::getTemp(void) { return toI16(&package[24]); }
/**
* @brief Get humidity (only PMS5003T)
*
* @return uint16_t
*/
uint16_t PMSBase::getHum(void) { return toValue(&package[26]); }
uint16_t PMSBase::getHum(void) { return toU16(&package[26]); }
/**
* @brief Get firmware version code
@ -284,13 +284,58 @@ int PMSBase::pm25ToAQI(int pm02) {
return 500;
}
/**
* @brief Correction PM2.5
*
* @param pm25 Raw PM2.5 value
* @param humidity Humidity value (%)
* @return int
*/
int PMSBase::compensated(int pm25, float humidity) {
float value;
if (humidity < 0) {
humidity = 0;
}
if (humidity > 100) {
humidity = 100;
}
if(pm25 < 30) {
value = (pm25 * 0.524f) - (humidity * 0.0862f) + 5.75f;
} else if(pm25 < 50) {
value = (0.786f * (pm25 / 20 - 3 / 2) + 0.524f * (1 - (pm25 / 20 - 3 / 2))) * pm25 - (0.0862f * humidity) + 5.75f;
} else if(pm25 < 210) {
value = (0.786f * pm25) - (0.0862f * humidity) + 5.75f;
} else if(pm25 < 260) {
value = (0.69f * (pm25/50 - 21/5) + 0.786f * (1 - (pm25/50 - 21/5))) * pm25 - (0.0862f * humidity * (1 - (pm25/50 - 21/5))) + (2.966f * (pm25/50 -21/5)) + (5.75f * (1 - (pm25/50 - 21/5))) + (8.84f * (1.e-4) * pm25* (pm25/50 - 21/5));
} else {
value = 2.966f + (0.69f * pm25) + (8.84f * (1.e-4) * pm25);
}
if(value < 0) {
value = 0;
}
return (int)value;
}
/**
* @brief Convert two byte value to uint16_t value
*
* @param buf bytes array (must be >= 2)
* @return uint16_t
* @return int16_t
*/
uint16_t PMSBase::toValue(char *buf) { return (buf[0] << 8) | buf[1]; }
int16_t PMSBase::toI16(char *buf) {
int16_t value = buf[0];
value = (value << 8) | buf[1];
return value;
}
uint16_t PMSBase::toU16(char *buf) {
uint16_t value = buf[0];
value = (value << 8) | buf[1];
return value;
}
/**
* @brief Validate package data
@ -304,7 +349,7 @@ bool PMSBase::validate(char *buf) {
for (int i = 0; i < 30; i++) {
sum += buf[i];
}
if (sum == toValue(&buf[30])) {
if (sum == toU16(&buf[30])) {
for (int i = 0; i < 32; i++) {
package[i] = buf[i];
}

View File

@ -24,12 +24,13 @@ public:
uint16_t getCount10(void);
/** For PMS5003T*/
uint16_t getTemp(void);
int16_t getTemp(void);
uint16_t getHum(void);
uint8_t getFirmwareVersion(void);
uint8_t getErrorCode(void);
int pm25ToAQI(int pm02);
int compensated(int pm25, float humidity);
private:
Stream *stream;
@ -38,7 +39,8 @@ private:
bool failed = false;
uint32_t lastRead;
uint16_t toValue(char *buf);
int16_t toI16(char *buf);
uint16_t toU16(char* buf);
bool validate(char *buf);
};

View File

@ -121,6 +121,17 @@ int PMS5003::getPm03ParticleCount(void) {
*/
int PMS5003::convertPm25ToUsAqi(int pm25) { return pms.pm25ToAQI(pm25); }
/**
* @brief Correct PM2.5
*
* @param pm25 PM2.5 raw value
* @param humidity Humidity value
* @return float
*/
int PMS5003::compensated(int pm25, float humidity) {
return pms.compensated(pm25, humidity);
}
/**
* @brief Get sensor firmware version
*

View File

@ -24,6 +24,7 @@ public:
int getPm10Ae(void);
int getPm03ParticleCount(void);
int convertPm25ToUsAqi(int pm25);
int compensated(int pm25, float humidity);
int getFirmwareVersion(void);
uint8_t getErrorCode(void);

View File

@ -164,6 +164,17 @@ float PMS5003T::getRelativeHumidity(void) {
return pms.getHum() / 10.0f;
}
/**
* @brief Correct PM2.5
*
* @param pm25 PM2.5 raw value
* @param humidity Humidity value
* @return float
*/
float PMS5003T::compensated(int pm25, float humidity) {
return pms.compensated(pm25, humidity);
}
/**
* @brief Get module(s) firmware version
*

View File

@ -29,6 +29,7 @@ public:
int convertPm25ToUsAqi(int pm25);
float getTemperature(void);
float getRelativeHumidity(void);
float compensated(int pm25, float humidity);
int getFirmwareVersion(void);
uint8_t getErrorCode(void);