Skip to content

Commit

Permalink
Merge branch 'develop' into feature/send-pms-sensor-fw-version-to-ag-…
Browse files Browse the repository at this point in the history
…cloud
  • Loading branch information
pnt325 committed Aug 25, 2024
2 parents 81b1313 + 6c3259b commit db21648
Show file tree
Hide file tree
Showing 21 changed files with 280 additions and 117 deletions.
155 changes: 90 additions & 65 deletions docs/local-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,62 +17,77 @@ With the path "/measures/current" you can get the current air quality data.

http://airgradient_ecda3b1eaaaf.local/measures/current

“ecda3b1eaaaf” being the serial number of your monitor
“ecda3b1eaaaf” being the serial number of your monitor.

You get the following response:
~~~
{"wifi":-46,
"serialno":"ecda3b1eaaaf",
"rco2":447,
"pm01":3,
"pm02":7,
"pm10":8,
"pm003Count":442,
"atmp":25.87,
"rhum":43,
"tvocIndex":100,
"tvoc_raw":33051,
"noxIndex":1,
"nox_raw":16307,
"boot":6,
"ledMode":"pm",
"firmwareVersion":"3.0.10beta",
"fwMode":"I-9PSL"}
~~~

|Properties|Type|Explanation|
|-|-|-|
|serialno|String| Serial Number of the monitor|
|wifi|Number| WiFi signal strength|
|pm01, pm02, pm10|Number| PM1, PM2.5 and PM10 in ug/m3|
|rco2|Number| CO2 in ppm|
|pm003Count|Number| Particle count per dL|
|atmp|Number| Temperature in Degrees Celcius|
|rhum|Number| Relative Humidity|
|tvocIndex|Number| Senisiron VOC Index|
|tvoc_raw|Number| VOC raw value|
|noxIndex|Number| Senisirion NOx Index|
|nox_raw|Number| NOx raw value|
|boot|Number| Counts every measurement cycle. Low boot counts indicate restarts.|
|ledMode|String| Current configuration of the LED mode|
|firmwareVersion|String| Current firmware version|
|fwMode|String| Current model name|
```json
{
"wifi": -46,
"serialno": "ecda3b1eaaaf",
"rco2": 447,
"pm01": 3,
"pm02": 7,
"pm10": 8,
"pm003Count": 442,
"atmp": 25.87,
"atmpCompensated": 24.47,
"rhum": 43,
"rhumCompensated": 49,
"tvocIndex": 100,
"tvocRaw": 33051,
"noxIndex": 1,
"noxRaw": 16307,
"boot": 6,
"bootCount": 6,
"ledMode": "pm",
"firmware": "3.1.3",
"model": "I-9PSL"
}
```

| Properties | Type | Explanation |
|------------------|--------|--------------------------------------------------------------------|
| `serialno` | String | Serial Number of the monitor |
| `wifi` | Number | WiFi signal strength |
| `pm01` | Number | PM1 in ug/m3 |
| `pm02` | Number | PM2.5 in ug/m3 |
| `pm10` | Number | PM10 in ug/m3 |
| `pm02Compensated` | Number | PM2.5 in ug/m3 with correction applied (from fw version 3.1.4 onwards) |
| `rco2` | Number | CO2 in ppm |
| `pm003Count` | Number | Particle count per dL |
| `atmp` | Number | Temperature in Degrees Celsius |
| `atmpCompensated` | Number | Temperature in Degrees Celsius with correction applied |
| `rhum` | Number | Relative Humidity |
| `rhumCompensated` | Number | Relative Humidity with correction applied |
| `tvocIndex` | Number | Senisiron VOC Index |
| `tvocRaw` | Number | VOC raw value |
| `noxIndex` | Number | Senisirion NOx Index |
| `noxRaw` | Number | NOx raw value |
| `boot` | Number | Counts every measurement cycle. Low boot counts indicate restarts. |
| `bootCount` | Number | Same as boot property. Required for Home Assistant compatability. Will be depreciated. |
| `ledMode` | String | Current configuration of the LED mode |
| `firmware` | String | Current firmware version |
| `model` | String | Current model name |

Compensated values apply correction algorithms to make the sensor values more accurate. Temperature and relative humidity correction is only applied on the outdoor monitor Open Air but the properties _compensated will still be send also for the indoor monitor AirGradient ONE.

#### Get Configuration Parameters (GET)
With the path "/config" you can get the current configuration.
~~~
{"country":"US",
"pmStandard":"ugm3",
"ledBarMode":"pm",
"displayMode":"on",
"abcDays":30,
"tvocLearningOffset":12,
"noxLearningOffset":12,
"mqttBrokerUrl":"",
"temperatureUnit":"f",
"configurationControl":"both",
"postDataToAirGradient":true}
~~~
```json
{
"country": "US",
"pmStandard": "ugm3",
"ledBarMode": "pm",
"displayMode": "on",
"abcDays": 30,
"tvocLearningOffset": 12,
"noxLearningOffset": 12,
"mqttBrokerUrl": "",
"temperatureUnit": "f",
"configurationControl": "both",
"postDataToAirGradient": true
}
```

#### Set Configuration Parameters (PUT)

Expand All @@ -82,24 +97,34 @@ Example to force CO2 calibration

```curl -X PUT -H "Content-Type: application/json" -d '{"co2CalibrationRequested":true}' http://airgradient_84fce612eff4.local/config ```

Example to set monitor to Celcius
Example to set monitor to Celsius

```curl -X PUT -H "Content-Type: application/json" -d '{"temperatureUnit":"c"}' http://airgradient_84fce612eff4.local/config ```

If you use command prompt on Windows, you need to escape the quotes:

``` -d "{\"param\":\"value\"}" ```

#### Avoiding Conflicts with Configuration on AirGradient Server
If the monitor is setup on the AirGradient dashboard, it will also receive configurations from there. In case you do not want this, please set "configurationControl" to local. In case you set it to cloud and want to change it to local, you need to make a factory reset.
If the monitor is set up on the AirGradient dashboard, it will also receive configurations from there. In case you do not want this, please set `configurationControl` to `local`. In case you set it to `cloud` and want to change it to `local`, you need to make a factory reset.

#### Configuration Parameters (GET/PUT)

|Properties|Type|Accepted Values|Example|
|-|-|-|-|
|country|String| Country code as [ALPHA-2 notation](https://www.iban.com/country-codes) | {"country": "TH"}|
|pmStandard|String|ugm3 : ug/m3 <br> usaqi: USAQI | {"pmStandard": "ugm3"}|
|ledBarMode|String|co2: LED bar displays CO2 <br> pm: LED bar displays PM <br> off: Turn off LED bar | {"ledBarMode": "off"}|
|abcDays|Number|Number of days for CO2 automatic baseline balibration. Maximum 200 days. Default 8 days. | {"abcDays": 8}|
|mqttBrokerUrl|String|MQTT broker URL. | {"mqttBrokerUrl":"mqtt://192.168.0.18:1883"} |
|temperatureUnit|String|c or C: Degree Celsius °C <br>f or F: Degree Fahrenheit °F | {"temperatureUnit": "c"}|
|configurationControl|String|both : Accept local and cloud configuration <br>local : Accept only local configuration <br>cloud : Accept only cloud configuration | {"configurationControl": "both"}|
|postDataToAirGradient|Boolean|Send data to AirGradient cloud: <br>true : Enabled <br>false: Disabled | {"postDataToAirGradient": true}|
|co2CalibrationRequested|Boolean|Trigger CO2 calibration (400ppm) on monitor:<br>true : Calibration will be triggered | {"co2CalibrationRequested": true}|
|ledBarTestRequested|Boolean|Test LED bar:<br> true : LEDs will run test sequence | {"ledBarTestRequested": true}|
| Properties | Description | Type | Accepted Values | Example |
|-------------------------|:-------------------------------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------|
| `country` | Country where the device is. | String | Country code as [ALPHA-2 notation](https://www.iban.com/country-codes) | {"country": "TH"} |
| `model` | Hardware identifier (only GET). | String | I-9PSL-DE | {"model": "I-9PSL-DE"} |
| `pmStandard` | Particle matter standard used on the display. | String | `ugm3`: ug/m3 <br> `us-aqi`: USAQI | {"pmStandard": "ugm3"} |
| `ledBarMode` | Mode in which the led bar can be set. | String | `co2`: LED bar displays CO2 <br>`pm`: LED bar displays PM <br>`off`: Turn off LED bar | {"ledBarMode": "off"} |
| `displayBrightness` | Brightness of the Display. | Number | 0-100 | {"displayBrightness": 50} |
| `ledBarBrightness` | Brightness of the LEDBar. | Number | 0-100 | {"ledBarBrightness": 40} |
| `abcDays` | Number of days for CO2 automatic baseline calibration. | Number | Maximum 200 days. Default 8 days. | {"abcDays": 8} |
| `mqttBrokerUrl` | MQTT broker URL. | String | | {"mqttBrokerUrl": "mqtt://192.168.0.18:1883"} |
| `temperatureUnit` | Temperature unit shown on the display. | String | `c` or `C`: Degree Celsius °C <br>`f` or `F`: Degree Fahrenheit °F | {"temperatureUnit": "c"} |
| `configurationControl` | The configuration source of the device. | String | `both`: Accept local and cloud configuration <br>`local`: Accept only local configuration <br>`cloud`: Accept only cloud configuration | {"configurationControl": "both"} |
| `postDataToAirGradient` | Send data to AirGradient cloud. | Boolean | `true`: Enabled <br>`false`: Disabled | {"postDataToAirGradient": true} |
| `co2CalibrationRequested` | Can be set to trigger a calibration. | Boolean | `true`: CO2 calibration (400ppm) will be triggered | {"co2CalibrationRequested": true} |
| `ledBarTestRequested` | Can be set to trigger a test. | Boolean | `true` : LEDs will run test sequence | {"ledBarTestRequested": true} |
| `noxLearningOffset` | Set NOx learning gain offset. | Number | 0-720 (default 12) | {"noxLearningOffset": 12} |
| `tvocLearningOffset` | Set VOC learning gain offset. | Number | 0-720 (default 12) | {"tvocLearningOffset": 12} |
| `offlineMode` | Set monitor to run without WiFi. | Boolean | `false`: Disabled (default) <br> `true`: Enabled | {"offlineMode": true} |
3 changes: 3 additions & 0 deletions examples/BASIC/BASIC.ino
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ void setup() {
openMetrics.setAirGradient(&ag);
localServer.setAirGraident(&ag);

/** Example set custom API root URL */
// apiClient.setApiRoot("https://example.custom.api");

/** Init sensor */
boardInit();

Expand Down
3 changes: 3 additions & 0 deletions examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ void setup() {
openMetrics.setAirGradient(&ag);
localServer.setAirGraident(&ag);

/** Example set custom API root URL */
// apiClient.setApiRoot("https://example.custom.api");

/** Init sensor */
boardInit();

Expand Down
3 changes: 3 additions & 0 deletions examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ void setup() {
openMetrics.setAirGradient(&ag);
localServer.setAirGraident(&ag);

/** Example set custom API root URL */
// apiClient.setApiRoot("https://example.custom.api");

/** Init sensor */
boardInit();

Expand Down
7 changes: 5 additions & 2 deletions examples/OneOpenAir/OneOpenAir.ino
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ void setup() {
openMetrics.setAirGradient(ag);
localServer.setAirGraident(ag);

/** Example set custom API root URL */
// apiClient.setApiRoot("https://example.custom.api");

/** Init sensor */
boardInit();

Expand Down Expand Up @@ -421,8 +424,8 @@ static void factoryConfigReset(void) {
}

/** Reset WIFI */
WiFi.enableSTA(true); // Incase offline mode
WiFi.disconnect(true, true);
Serial.println("Set wifi connect to 'airgradient' as default");
WiFi.begin("airgradient", "cleanair");

/** Reset local config */
configuration.reset();
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=AirGradient Air Quality Sensor
version=3.1.4
version=3.1.5
author=AirGradient <support@airgradient.com>
maintainer=AirGradient <support@airgradient.com>
sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display.
Expand Down
14 changes: 7 additions & 7 deletions partitions.csv
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x1E0000,
app1, app, ota_1, 0x1F0000,0x1E0000,
spiffs, data, spiffs, 0x3D0000,0x20000,
coredump, data, coredump,0x3F0000,0x10000,
# Name ,Type ,SubType ,Offset ,Size ,Flags
nvs ,data ,nvs ,0x9000 ,0x5000 ,
otadata ,data ,ota ,0xe000 ,0x2000 ,
app0 ,app ,ota_0 ,0x10000 ,0x1E0000 ,
app1 ,app ,ota_1 ,0x1F0000 ,0x1E0000 ,
spiffs ,data ,spiffs ,0x3D0000 ,0x20000 ,
coredump ,data ,coredump ,0x3F0000 ,0x10000 ,
23 changes: 18 additions & 5 deletions src/AgApiClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ AgApiClient::~AgApiClient() {}
void AgApiClient::begin(void) {
getConfigFailed = false;
postToServerFailed = false;
logInfo("Init apiRoot: " + apiRoot);
logInfo("begin");
}

Expand All @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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; }
3 changes: 3 additions & 0 deletions src/AgApiClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class AgApiClient : public PrintLog {
private:
Configuration &config;
AirGradient *ag;
String apiRoot = "http://hw.airgradient.com";

bool getConfigFailed;
bool postToServerFailed;
Expand All @@ -37,6 +38,8 @@ class AgApiClient : public PrintLog {
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_ */
14 changes: 8 additions & 6 deletions src/AgConfigure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
10 changes: 9 additions & 1 deletion src/AgOledDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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 {
Expand All @@ -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);
Expand Down
Loading

0 comments on commit db21648

Please sign in to comment.