Skip to content

Commit

Permalink
Merge pull request #838 from opendata-stuttgart/beta
Browse files Browse the repository at this point in the history
Beta to master
  • Loading branch information
ricki-z committed Nov 29, 2020
2 parents b78aa0a + f515848 commit b1edc5b
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 98 deletions.
55 changes: 55 additions & 0 deletions .github/workflows/airohr-firmware.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Airrohr Firmware CI workflow

name: Airrohr Firmware CI
on:
push:
branches: [ master, beta, feature/** ]
pull_request:
branches: [ master, beta ]
create:
branches: [ master, beta ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Load dependencies from cache
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
python3 -m pip install --upgrade pip
pip3 install platformio
platformio --version
- name: Run builds
run: |
cd airrohr-firmware && pwd && ls -l && platformio run && ls -l builds/
cd ../airrohr-update-loader && pwd && ls -l && platformio run && ls -l builds/
- name: Tar Results
run: |
tar czvf airrohr-firmware-builds.tar.gz -C airrohr-firmware/builds/ .
- name: Store airrohr-firmware
uses: actions/upload-artifact@v2
with:
if-no-files-found: error
name: airrohr-firmware-builds
path: airrohr-firmware-builds.tar.gz
retention-days: 30
- name: Store update-loader
uses: actions/upload-artifact@v2
with:
if-no-files-found: error
name: airrohr-update-loader
path: airrohr-update-loader/builds/latest_loader.bin
retention-days: 30
21 changes: 0 additions & 21 deletions .travis.yml

This file was deleted.

16 changes: 15 additions & 1 deletion airrohr-firmware/Versions.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
NRZ-2020-133
online since 2020-11-29
* same as NRZ-2020-132-B3

NRZ-2020-132-B3
* Move favicon.ico fetching last

NRZ-2020-132-B2
* Adjust /metrics to be OpenMetrics protocol compatible
* Track last Wifi disconnect reason in Device Status page
* Rework SDS011 response handling to remain non-blocking

NRZ-2020-132-B1
* next beta version

NRZ-2020-131
online since 2020-10-17
* same as NRZ-2020-130-B11
Expand All @@ -24,7 +39,6 @@ NRZ-2020-130-B7
* Disabled IPv6 for stable release
* Force I2C clock to 100k for better compatibility with sensors (Fixes #735)
* Update ArduinoJson to 6.16.1
* Force I2C clock to 100k for better compatibility with sensors

NRZ-2020-130-B6
* show new ID
Expand Down
143 changes: 89 additions & 54 deletions airrohr-firmware/airrohr-firmware.ino
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
#include <pgmspace.h>

// increment on change
#define SOFTWARE_VERSION_STR "NRZ-2020-131"
#define SOFTWARE_VERSION_STR "NRZ-2020-133"
String SOFTWARE_VERSION(SOFTWARE_VERSION_STR);

/*****************************************************************
Expand Down Expand Up @@ -347,6 +347,10 @@ unsigned long min_micro = 1000000000;
unsigned long max_micro = 0;

bool is_SDS_running = true;
enum {
SDS_REPLY_HDR = 10,
SDS_REPLY_BODY = 8
} SDS_waiting_for;
bool is_PMS_running = true;
bool is_HPM_running = true;
bool is_NPM_running = true;
Expand Down Expand Up @@ -466,6 +470,7 @@ double last_value_GPS_lon = -200.0;
String last_value_GPS_timestamp;
String last_data_string;
int last_signal_strength;
int last_disconnect_reason;

String esp_chipid;
String esp_mac_id;
Expand Down Expand Up @@ -634,18 +639,18 @@ static void readConfig(bool oldconfig = false) {
continue;
}
switch (c.cfg_type) {
case Config_Type_Bool:
*(c.cfg_val.as_bool) = boolFromJSON(json, c.cfg_key());
break;
case Config_Type_UInt:
case Config_Type_Time:
*(c.cfg_val.as_uint) = json[c.cfg_key()].as<unsigned int>();
break;
case Config_Type_String:
case Config_Type_Password:
strncpy(c.cfg_val.as_str, json[c.cfg_key()].as<char*>(), c.cfg_len);
c.cfg_val.as_str[c.cfg_len] = '\0';
break;
case Config_Type_Bool:
*(c.cfg_val.as_bool) = boolFromJSON(json, c.cfg_key());
break;
case Config_Type_UInt:
case Config_Type_Time:
*(c.cfg_val.as_uint) = json[c.cfg_key()].as<unsigned int>();
break;
case Config_Type_String:
case Config_Type_Password:
strncpy(c.cfg_val.as_str, json[c.cfg_key()].as<char*>(), c.cfg_len);
c.cfg_val.as_str[c.cfg_len] = '\0';
break;
};
}
String writtenVersion(json["SOFTWARE_VERSION"].as<char*>());
Expand Down Expand Up @@ -1588,7 +1593,13 @@ static void webserver_status() {

page_content += FPSTR(EMPTY_ROW);
page_content += F("<tr><td colspan='2'><b>" INTL_ERROR "</b></td></tr>");
add_table_row_from_value(page_content, F("WiFi"), String(WiFi_error_count));
String wifiStatus(WiFi_error_count);
wifiStatus += '/';
wifiStatus += String(last_signal_strength);
wifiStatus += '/';
wifiStatus += String(last_disconnect_reason);
add_table_row_from_value(page_content, F("WiFi"), wifiStatus);

if (last_update_returncode != 0) {
add_table_row_from_value(page_content, F("OTA Return"),
last_update_returncode > 0 ? String(last_update_returncode) : HTTPClient::errorToString(last_update_returncode));
Expand Down Expand Up @@ -1783,7 +1794,6 @@ static void webserver_data_json() {
age = 0 - age;
} else {
s1 = last_data_string;
debug_outln(last_data_string, DEBUG_MED_INFO);
age = msSince(starttime);
if (age > cfg::sending_intervall_ms) {
age = 0;
Expand All @@ -1797,19 +1807,19 @@ static void webserver_data_json() {
}

/*****************************************************************
* Webserver prometheus metrics endpoint *
* Webserver metrics endpoint *
*****************************************************************/
static void webserver_prometheus_endpoint() {
debug_outln_info(F("ws: prometheus endpoint..."));
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");
debug_outln_info(F("Parse JSON for Prometheus"));
static void webserver_metrics_endpoint() {
debug_outln_info(F("ws: /metrics"));
RESERVE_STRING(page_content, XLARGE_STR);
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");
String id(F("node=\"" SENSOR_BASENAME));
id += esp_chipid;
id += '\"';
page_content.replace("{id}", id);
page_content.replace("{up}", String(msSince(time_point_device_start_ms)));
page_content.replace("{si}", String(cfg::sending_intervall_ms));
page_content.replace("{cs}", String(count_sends));
page_content.replace("$i", id);
page_content.replace("$u", String(msSince(time_point_device_start_ms)));
page_content.replace("$s", String(cfg::sending_intervall_ms));
page_content.replace("$c", String(count_sends));
DynamicJsonDocument json2data(JSON_BUFFER_SIZE);
DeserializationError err = deserializeJson(json2data, last_data_string);
if (!err) {
Expand All @@ -1829,13 +1839,22 @@ static void webserver_prometheus_endpoint() {
} else {
debug_outln_error(FPSTR(DBG_TXT_DATA_READ_FAILED));
}
page_content += F("# EOF\n");
debug_outln(page_content, DEBUG_MED_INFO);
server.send(200, FPSTR(TXT_CONTENT_TYPE_TEXT_PLAIN), page_content);
}

/*****************************************************************
* Webserver Images *
*****************************************************************/

static void webserver_favicon() {
server.sendHeader(F("Cache-Control"), F("max-age=2592000, public"));

server.send_P(200, TXT_CONTENT_TYPE_IMAGE_PNG,
LUFTDATEN_INFO_LOGO_PNG, LUFTDATEN_INFO_LOGO_PNG_SIZE);
}

static void webserver_static() {
server.sendHeader(F("Cache-Control"), F("max-age=2592000, public"));

Expand Down Expand Up @@ -1884,7 +1903,8 @@ static void setup_webserver() {
server.on(F("/removeConfig"), webserver_removeConfig);
server.on(F("/reset"), webserver_reset);
server.on(F("/data.json"), webserver_data_json);
server.on(F("/metrics"), webserver_prometheus_endpoint);
server.on(F("/metrics"), webserver_metrics_endpoint);
server.on(F("/favicon.ico"), webserver_favicon);
server.on(F(STATIC_PREFIX), webserver_static);
server.onNotFound(webserver_not_found);

Expand Down Expand Up @@ -2025,6 +2045,9 @@ static void waitForWifiToConnect(int maxRetries) {
/*****************************************************************
* WiFi auto connecting script *
*****************************************************************/

static WiFiEventHandler disconnectEventHandler;

static void connectWifi() {
display_debug(F("Connecting to"), String(cfg::wlanssid));
#if defined(ESP8266)
Expand All @@ -2035,6 +2058,10 @@ static void connectWifi() {
WiFi.setSleepMode(WIFI_NONE_SLEEP);
WiFi.setPhyMode(WIFI_PHY_MODE_11N);
delay(100);

disconnectEventHandler = WiFi.onStationModeDisconnected([](const WiFiEventStationModeDisconnected& evt) {
last_disconnect_reason = evt.reason;
});
#endif
if (WiFi.getAutoConnect()) {
WiFi.setAutoConnect(false);
Expand All @@ -2055,7 +2082,7 @@ static void connectWifi() {
#endif

WiFi.mode(WIFI_STA);

#if defined(ESP8266)
WiFi.hostname(cfg::fs_ssid);
#endif
Expand Down Expand Up @@ -2453,9 +2480,6 @@ static void fetchSensorDS18B20(String& s) {
* read SDS011 sensor values *
*****************************************************************/
static void fetchSensorSDS(String& s) {

debug_outln_verbose(FPSTR(DBG_TXT_START_READING), FPSTR(SENSORS_SDS011));

if (cfg::sending_intervall_ms > (WARMUPTIME_SDS_MS + READINGTIME_SDS_MS) &&
msSince(starttime) < (cfg::sending_intervall_ms - (WARMUPTIME_SDS_MS + READINGTIME_SDS_MS))) {
if (is_SDS_running) {
Expand All @@ -2464,27 +2488,37 @@ static void fetchSensorSDS(String& s) {
} else {
if (! is_SDS_running) {
is_SDS_running = SDS_cmd(PmSensorCmd::Start);
SDS_waiting_for = SDS_REPLY_HDR;
}

const uint8_t constexpr header_measurement[2] = { 0xAA, 0xC0 };

while (serialSDS.available() >= 10 &&
serialSDS.find(header_measurement, sizeof(header_measurement))) {
while (serialSDS.available() >= SDS_waiting_for) {
const uint8_t constexpr hdr_measurement[2] = { 0xAA, 0xC0 };
uint8_t data[8];
unsigned r = serialSDS.readBytes(data, sizeof(data));
if (r == sizeof(data) && SDS_checksum_valid(data)) {
uint32_t pm25_serial = data[0] | (data[1] << 8);
uint32_t pm10_serial = data[2] | (data[3] << 8);

if (msSince(starttime) > (cfg::sending_intervall_ms - READINGTIME_SDS_MS)) {
sds_pm10_sum += pm10_serial;
sds_pm25_sum += pm25_serial;
UPDATE_MIN_MAX(sds_pm10_min, sds_pm10_max, pm10_serial);
UPDATE_MIN_MAX(sds_pm25_min, sds_pm25_max, pm25_serial);
debug_outln_verbose(F("PM10 (sec.) : "), String(pm10_serial / 10.0f));
debug_outln_verbose(F("PM2.5 (sec.): "), String(pm25_serial / 10.0f));
sds_val_count++;

switch (SDS_waiting_for) {
case SDS_REPLY_HDR:
if (serialSDS.find(hdr_measurement, sizeof(hdr_measurement)))
SDS_waiting_for = SDS_REPLY_BODY;
break;
case SDS_REPLY_BODY:
debug_outln_verbose(FPSTR(DBG_TXT_START_READING), FPSTR(SENSORS_SDS011));
if (serialSDS.readBytes(data, sizeof(data)) == sizeof(data) && SDS_checksum_valid(data)) {
uint32_t pm25_serial = data[0] | (data[1] << 8);
uint32_t pm10_serial = data[2] | (data[3] << 8);

if (msSince(starttime) > (cfg::sending_intervall_ms - READINGTIME_SDS_MS)) {
sds_pm10_sum += pm10_serial;
sds_pm25_sum += pm25_serial;
UPDATE_MIN_MAX(sds_pm10_min, sds_pm10_max, pm10_serial);
UPDATE_MIN_MAX(sds_pm25_min, sds_pm25_max, pm25_serial);
debug_outln_verbose(F("PM10 (sec.) : "), String(pm10_serial / 10.0f));
debug_outln_verbose(F("PM2.5 (sec.): "), String(pm25_serial / 10.0f));
sds_val_count++;
}
}
debug_outln_verbose(FPSTR(DBG_TXT_END_READING), FPSTR(SENSORS_SDS011));
SDS_waiting_for = SDS_REPLY_HDR;
break;
}
}
}
Expand Down Expand Up @@ -2522,8 +2556,6 @@ static void fetchSensorSDS(String& s) {
}
}
}

debug_outln_verbose(FPSTR(DBG_TXT_END_READING), FPSTR(SENSORS_SDS011));
}

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

void setup(void) {
Debug.begin(9600); // Output to Serial at 9600 baud
#if defined(ESP8266) && !NPM_READ
serialSDS.begin(9600, SWSERIAL_8N1, PM_SERIAL_RX, PM_SERIAL_TX);
serialSDS.enableIntTx(true);
#if defined (ESP8266)
serialSDS.begin(
#if !NPM_READ
9600, SWSERIAL_8N1,
#else
115200, SWSERIAL_8E1,
#endif
#if defined(ESP8266) && NPM_READ
serialSDS.begin(115200, SWSERIAL_8E1, PM_SERIAL_RX, PM_SERIAL_TX);
PM_SERIAL_RX, PM_SERIAL_TX);
serialSDS.enableIntTx(true);
#endif

#if defined(ESP32) && !NPM_READ
serialSDS.begin(9600, SERIAL_8N1, PM_SERIAL_RX, PM_SERIAL_TX);
#endif
Expand All @@ -4212,7 +4247,7 @@ void setup(void) {
pinMode(PIN_CS, OUTPUT);
digitalWrite(PIN_CS, LOW);
#endif
serialSDS.setTimeout((12 * 9 * 1000) / 9600);
serialSDS.setTimeout((4 * 12 * 1000) / 9600);

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

0 comments on commit b1edc5b

Please sign in to comment.