Skip to content

Commit b1edc5b

Browse files
authored
Merge pull request #838 from opendata-stuttgart/beta
Beta to master
2 parents b78aa0a + f515848 commit b1edc5b

File tree

7 files changed

+174
-98
lines changed

7 files changed

+174
-98
lines changed

.github/workflows/airohr-firmware.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Airrohr Firmware CI workflow
2+
3+
name: Airrohr Firmware CI
4+
on:
5+
push:
6+
branches: [ master, beta, feature/** ]
7+
pull_request:
8+
branches: [ master, beta ]
9+
create:
10+
branches: [ master, beta ]
11+
12+
jobs:
13+
build:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v2
19+
- name: Set up Python
20+
uses: actions/setup-python@v2
21+
with:
22+
python-version: '3.7'
23+
- name: Load dependencies from cache
24+
uses: actions/cache@v2
25+
with:
26+
path: ~/.cache/pip
27+
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
28+
restore-keys: |
29+
${{ runner.os }}-pip-
30+
- name: Install dependencies
31+
run: |
32+
python3 -m pip install --upgrade pip
33+
pip3 install platformio
34+
platformio --version
35+
- name: Run builds
36+
run: |
37+
cd airrohr-firmware && pwd && ls -l && platformio run && ls -l builds/
38+
cd ../airrohr-update-loader && pwd && ls -l && platformio run && ls -l builds/
39+
- name: Tar Results
40+
run: |
41+
tar czvf airrohr-firmware-builds.tar.gz -C airrohr-firmware/builds/ .
42+
- name: Store airrohr-firmware
43+
uses: actions/upload-artifact@v2
44+
with:
45+
if-no-files-found: error
46+
name: airrohr-firmware-builds
47+
path: airrohr-firmware-builds.tar.gz
48+
retention-days: 30
49+
- name: Store update-loader
50+
uses: actions/upload-artifact@v2
51+
with:
52+
if-no-files-found: error
53+
name: airrohr-update-loader
54+
path: airrohr-update-loader/builds/latest_loader.bin
55+
retention-days: 30

.travis.yml

Lines changed: 0 additions & 21 deletions
This file was deleted.

airrohr-firmware/Versions.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
NRZ-2020-133
2+
online since 2020-11-29
3+
* same as NRZ-2020-132-B3
4+
5+
NRZ-2020-132-B3
6+
* Move favicon.ico fetching last
7+
8+
NRZ-2020-132-B2
9+
* Adjust /metrics to be OpenMetrics protocol compatible
10+
* Track last Wifi disconnect reason in Device Status page
11+
* Rework SDS011 response handling to remain non-blocking
12+
13+
NRZ-2020-132-B1
14+
* next beta version
15+
116
NRZ-2020-131
217
online since 2020-10-17
318
* same as NRZ-2020-130-B11
@@ -24,7 +39,6 @@ NRZ-2020-130-B7
2439
* Disabled IPv6 for stable release
2540
* Force I2C clock to 100k for better compatibility with sensors (Fixes #735)
2641
* Update ArduinoJson to 6.16.1
27-
* Force I2C clock to 100k for better compatibility with sensors
2842

2943
NRZ-2020-130-B6
3044
* show new ID

airrohr-firmware/airrohr-firmware.ino

Lines changed: 89 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
#include <pgmspace.h>
6161

6262
// increment on change
63-
#define SOFTWARE_VERSION_STR "NRZ-2020-131"
63+
#define SOFTWARE_VERSION_STR "NRZ-2020-133"
6464
String SOFTWARE_VERSION(SOFTWARE_VERSION_STR);
6565

6666
/*****************************************************************
@@ -347,6 +347,10 @@ unsigned long min_micro = 1000000000;
347347
unsigned long max_micro = 0;
348348

349349
bool is_SDS_running = true;
350+
enum {
351+
SDS_REPLY_HDR = 10,
352+
SDS_REPLY_BODY = 8
353+
} SDS_waiting_for;
350354
bool is_PMS_running = true;
351355
bool is_HPM_running = true;
352356
bool is_NPM_running = true;
@@ -466,6 +470,7 @@ double last_value_GPS_lon = -200.0;
466470
String last_value_GPS_timestamp;
467471
String last_data_string;
468472
int last_signal_strength;
473+
int last_disconnect_reason;
469474

470475
String esp_chipid;
471476
String esp_mac_id;
@@ -634,18 +639,18 @@ static void readConfig(bool oldconfig = false) {
634639
continue;
635640
}
636641
switch (c.cfg_type) {
637-
case Config_Type_Bool:
638-
*(c.cfg_val.as_bool) = boolFromJSON(json, c.cfg_key());
639-
break;
640-
case Config_Type_UInt:
641-
case Config_Type_Time:
642-
*(c.cfg_val.as_uint) = json[c.cfg_key()].as<unsigned int>();
643-
break;
644-
case Config_Type_String:
645-
case Config_Type_Password:
646-
strncpy(c.cfg_val.as_str, json[c.cfg_key()].as<char*>(), c.cfg_len);
647-
c.cfg_val.as_str[c.cfg_len] = '\0';
648-
break;
642+
case Config_Type_Bool:
643+
*(c.cfg_val.as_bool) = boolFromJSON(json, c.cfg_key());
644+
break;
645+
case Config_Type_UInt:
646+
case Config_Type_Time:
647+
*(c.cfg_val.as_uint) = json[c.cfg_key()].as<unsigned int>();
648+
break;
649+
case Config_Type_String:
650+
case Config_Type_Password:
651+
strncpy(c.cfg_val.as_str, json[c.cfg_key()].as<char*>(), c.cfg_len);
652+
c.cfg_val.as_str[c.cfg_len] = '\0';
653+
break;
649654
};
650655
}
651656
String writtenVersion(json["SOFTWARE_VERSION"].as<char*>());
@@ -1588,7 +1593,13 @@ static void webserver_status() {
15881593

15891594
page_content += FPSTR(EMPTY_ROW);
15901595
page_content += F("<tr><td colspan='2'><b>" INTL_ERROR "</b></td></tr>");
1591-
add_table_row_from_value(page_content, F("WiFi"), String(WiFi_error_count));
1596+
String wifiStatus(WiFi_error_count);
1597+
wifiStatus += '/';
1598+
wifiStatus += String(last_signal_strength);
1599+
wifiStatus += '/';
1600+
wifiStatus += String(last_disconnect_reason);
1601+
add_table_row_from_value(page_content, F("WiFi"), wifiStatus);
1602+
15921603
if (last_update_returncode != 0) {
15931604
add_table_row_from_value(page_content, F("OTA Return"),
15941605
last_update_returncode > 0 ? String(last_update_returncode) : HTTPClient::errorToString(last_update_returncode));
@@ -1783,7 +1794,6 @@ static void webserver_data_json() {
17831794
age = 0 - age;
17841795
} else {
17851796
s1 = last_data_string;
1786-
debug_outln(last_data_string, DEBUG_MED_INFO);
17871797
age = msSince(starttime);
17881798
if (age > cfg::sending_intervall_ms) {
17891799
age = 0;
@@ -1797,19 +1807,19 @@ static void webserver_data_json() {
17971807
}
17981808

17991809
/*****************************************************************
1800-
* Webserver prometheus metrics endpoint *
1810+
* Webserver metrics endpoint *
18011811
*****************************************************************/
1802-
static void webserver_prometheus_endpoint() {
1803-
debug_outln_info(F("ws: prometheus endpoint..."));
1804-
String page_content = F("software_version{version=\"" SOFTWARE_VERSION_STR "\",{id}} 1\nuptime_ms{{id}} {up}\nsending_intervall_ms{{id}} {si}\nnumber_of_measurements{{id}} {cs}\n");
1805-
debug_outln_info(F("Parse JSON for Prometheus"));
1812+
static void webserver_metrics_endpoint() {
1813+
debug_outln_info(F("ws: /metrics"));
1814+
RESERVE_STRING(page_content, XLARGE_STR);
1815+
page_content = F("software_version{version=\"" SOFTWARE_VERSION_STR "\",$i} 1\nuptime_ms{$i} $u\nsending_intervall_ms{$i} $s\nnumber_of_measurements{$i} $c\n");
18061816
String id(F("node=\"" SENSOR_BASENAME));
18071817
id += esp_chipid;
18081818
id += '\"';
1809-
page_content.replace("{id}", id);
1810-
page_content.replace("{up}", String(msSince(time_point_device_start_ms)));
1811-
page_content.replace("{si}", String(cfg::sending_intervall_ms));
1812-
page_content.replace("{cs}", String(count_sends));
1819+
page_content.replace("$i", id);
1820+
page_content.replace("$u", String(msSince(time_point_device_start_ms)));
1821+
page_content.replace("$s", String(cfg::sending_intervall_ms));
1822+
page_content.replace("$c", String(count_sends));
18131823
DynamicJsonDocument json2data(JSON_BUFFER_SIZE);
18141824
DeserializationError err = deserializeJson(json2data, last_data_string);
18151825
if (!err) {
@@ -1829,13 +1839,22 @@ static void webserver_prometheus_endpoint() {
18291839
} else {
18301840
debug_outln_error(FPSTR(DBG_TXT_DATA_READ_FAILED));
18311841
}
1842+
page_content += F("# EOF\n");
18321843
debug_outln(page_content, DEBUG_MED_INFO);
18331844
server.send(200, FPSTR(TXT_CONTENT_TYPE_TEXT_PLAIN), page_content);
18341845
}
18351846

18361847
/*****************************************************************
18371848
* Webserver Images *
18381849
*****************************************************************/
1850+
1851+
static void webserver_favicon() {
1852+
server.sendHeader(F("Cache-Control"), F("max-age=2592000, public"));
1853+
1854+
server.send_P(200, TXT_CONTENT_TYPE_IMAGE_PNG,
1855+
LUFTDATEN_INFO_LOGO_PNG, LUFTDATEN_INFO_LOGO_PNG_SIZE);
1856+
}
1857+
18391858
static void webserver_static() {
18401859
server.sendHeader(F("Cache-Control"), F("max-age=2592000, public"));
18411860

@@ -1884,7 +1903,8 @@ static void setup_webserver() {
18841903
server.on(F("/removeConfig"), webserver_removeConfig);
18851904
server.on(F("/reset"), webserver_reset);
18861905
server.on(F("/data.json"), webserver_data_json);
1887-
server.on(F("/metrics"), webserver_prometheus_endpoint);
1906+
server.on(F("/metrics"), webserver_metrics_endpoint);
1907+
server.on(F("/favicon.ico"), webserver_favicon);
18881908
server.on(F(STATIC_PREFIX), webserver_static);
18891909
server.onNotFound(webserver_not_found);
18901910

@@ -2025,6 +2045,9 @@ static void waitForWifiToConnect(int maxRetries) {
20252045
/*****************************************************************
20262046
* WiFi auto connecting script *
20272047
*****************************************************************/
2048+
2049+
static WiFiEventHandler disconnectEventHandler;
2050+
20282051
static void connectWifi() {
20292052
display_debug(F("Connecting to"), String(cfg::wlanssid));
20302053
#if defined(ESP8266)
@@ -2035,6 +2058,10 @@ static void connectWifi() {
20352058
WiFi.setSleepMode(WIFI_NONE_SLEEP);
20362059
WiFi.setPhyMode(WIFI_PHY_MODE_11N);
20372060
delay(100);
2061+
2062+
disconnectEventHandler = WiFi.onStationModeDisconnected([](const WiFiEventStationModeDisconnected& evt) {
2063+
last_disconnect_reason = evt.reason;
2064+
});
20382065
#endif
20392066
if (WiFi.getAutoConnect()) {
20402067
WiFi.setAutoConnect(false);
@@ -2055,7 +2082,7 @@ static void connectWifi() {
20552082
#endif
20562083

20572084
WiFi.mode(WIFI_STA);
2058-
2085+
20592086
#if defined(ESP8266)
20602087
WiFi.hostname(cfg::fs_ssid);
20612088
#endif
@@ -2453,9 +2480,6 @@ static void fetchSensorDS18B20(String& s) {
24532480
* read SDS011 sensor values *
24542481
*****************************************************************/
24552482
static void fetchSensorSDS(String& s) {
2456-
2457-
debug_outln_verbose(FPSTR(DBG_TXT_START_READING), FPSTR(SENSORS_SDS011));
2458-
24592483
if (cfg::sending_intervall_ms > (WARMUPTIME_SDS_MS + READINGTIME_SDS_MS) &&
24602484
msSince(starttime) < (cfg::sending_intervall_ms - (WARMUPTIME_SDS_MS + READINGTIME_SDS_MS))) {
24612485
if (is_SDS_running) {
@@ -2464,27 +2488,37 @@ static void fetchSensorSDS(String& s) {
24642488
} else {
24652489
if (! is_SDS_running) {
24662490
is_SDS_running = SDS_cmd(PmSensorCmd::Start);
2491+
SDS_waiting_for = SDS_REPLY_HDR;
24672492
}
24682493

2469-
const uint8_t constexpr header_measurement[2] = { 0xAA, 0xC0 };
2470-
2471-
while (serialSDS.available() >= 10 &&
2472-
serialSDS.find(header_measurement, sizeof(header_measurement))) {
2494+
while (serialSDS.available() >= SDS_waiting_for) {
2495+
const uint8_t constexpr hdr_measurement[2] = { 0xAA, 0xC0 };
24732496
uint8_t data[8];
2474-
unsigned r = serialSDS.readBytes(data, sizeof(data));
2475-
if (r == sizeof(data) && SDS_checksum_valid(data)) {
2476-
uint32_t pm25_serial = data[0] | (data[1] << 8);
2477-
uint32_t pm10_serial = data[2] | (data[3] << 8);
2478-
2479-
if (msSince(starttime) > (cfg::sending_intervall_ms - READINGTIME_SDS_MS)) {
2480-
sds_pm10_sum += pm10_serial;
2481-
sds_pm25_sum += pm25_serial;
2482-
UPDATE_MIN_MAX(sds_pm10_min, sds_pm10_max, pm10_serial);
2483-
UPDATE_MIN_MAX(sds_pm25_min, sds_pm25_max, pm25_serial);
2484-
debug_outln_verbose(F("PM10 (sec.) : "), String(pm10_serial / 10.0f));
2485-
debug_outln_verbose(F("PM2.5 (sec.): "), String(pm25_serial / 10.0f));
2486-
sds_val_count++;
2497+
2498+
switch (SDS_waiting_for) {
2499+
case SDS_REPLY_HDR:
2500+
if (serialSDS.find(hdr_measurement, sizeof(hdr_measurement)))
2501+
SDS_waiting_for = SDS_REPLY_BODY;
2502+
break;
2503+
case SDS_REPLY_BODY:
2504+
debug_outln_verbose(FPSTR(DBG_TXT_START_READING), FPSTR(SENSORS_SDS011));
2505+
if (serialSDS.readBytes(data, sizeof(data)) == sizeof(data) && SDS_checksum_valid(data)) {
2506+
uint32_t pm25_serial = data[0] | (data[1] << 8);
2507+
uint32_t pm10_serial = data[2] | (data[3] << 8);
2508+
2509+
if (msSince(starttime) > (cfg::sending_intervall_ms - READINGTIME_SDS_MS)) {
2510+
sds_pm10_sum += pm10_serial;
2511+
sds_pm25_sum += pm25_serial;
2512+
UPDATE_MIN_MAX(sds_pm10_min, sds_pm10_max, pm10_serial);
2513+
UPDATE_MIN_MAX(sds_pm25_min, sds_pm25_max, pm25_serial);
2514+
debug_outln_verbose(F("PM10 (sec.) : "), String(pm10_serial / 10.0f));
2515+
debug_outln_verbose(F("PM2.5 (sec.): "), String(pm25_serial / 10.0f));
2516+
sds_val_count++;
2517+
}
24872518
}
2519+
debug_outln_verbose(FPSTR(DBG_TXT_END_READING), FPSTR(SENSORS_SDS011));
2520+
SDS_waiting_for = SDS_REPLY_HDR;
2521+
break;
24882522
}
24892523
}
24902524
}
@@ -2522,8 +2556,6 @@ static void fetchSensorSDS(String& s) {
25222556
}
25232557
}
25242558
}
2525-
2526-
debug_outln_verbose(FPSTR(DBG_TXT_END_READING), FPSTR(SENSORS_SDS011));
25272559
}
25282560

25292561
/*****************************************************************
@@ -4195,14 +4227,17 @@ static unsigned long sendDataToOptionalApis(const String &data) {
41954227

41964228
void setup(void) {
41974229
Debug.begin(9600); // Output to Serial at 9600 baud
4198-
#if defined(ESP8266) && !NPM_READ
4199-
serialSDS.begin(9600, SWSERIAL_8N1, PM_SERIAL_RX, PM_SERIAL_TX);
4200-
serialSDS.enableIntTx(true);
4230+
#if defined (ESP8266)
4231+
serialSDS.begin(
4232+
#if !NPM_READ
4233+
9600, SWSERIAL_8N1,
4234+
#else
4235+
115200, SWSERIAL_8E1,
42014236
#endif
4202-
#if defined(ESP8266) && NPM_READ
4203-
serialSDS.begin(115200, SWSERIAL_8E1, PM_SERIAL_RX, PM_SERIAL_TX);
4237+
PM_SERIAL_RX, PM_SERIAL_TX);
42044238
serialSDS.enableIntTx(true);
42054239
#endif
4240+
42064241
#if defined(ESP32) && !NPM_READ
42074242
serialSDS.begin(9600, SERIAL_8N1, PM_SERIAL_RX, PM_SERIAL_TX);
42084243
#endif
@@ -4212,7 +4247,7 @@ void setup(void) {
42124247
pinMode(PIN_CS, OUTPUT);
42134248
digitalWrite(PIN_CS, LOW);
42144249
#endif
4215-
serialSDS.setTimeout((12 * 9 * 1000) / 9600);
4250+
serialSDS.setTimeout((4 * 12 * 1000) / 9600);
42164251

42174252
#if defined(WIFI_LoRa_32_V2)
42184253
// reset the OLED display, e.g. of the heltec_wifi_lora_32 board

0 commit comments

Comments
 (0)