Skip to content

Commit 447671d

Browse files
committed
support for SCD30 added
+ limit channels to 11 only in AP mode (station mode should only use frequencies of available routers)
1 parent 883197e commit 447671d

36 files changed

+185
-25
lines changed

airrohr-firmware/Versions.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
NRZ-2020-134-B3
2+
* support for SCD30 added
3+
14
NRZ-2020-134-B2
25
* dew point added
36
* pressure at sea level added

airrohr-firmware/airrohr-cfg.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ enum ConfigShapeId {
4444
Config_bmp_read,
4545
Config_bmx280_read,
4646
Config_sht3x_read,
47+
Config_scd30_read,
4748
Config_ds18b20_read,
4849
Config_dnms_read,
4950
Config_dnms_correction,
@@ -112,6 +113,7 @@ static constexpr char CFG_KEY_SPS30_READ[] PROGMEM = "sps30_read";
112113
static constexpr char CFG_KEY_BMP_READ[] PROGMEM = "bmp_read";
113114
static constexpr char CFG_KEY_BMX280_READ[] PROGMEM = "bmx280_read";
114115
static constexpr char CFG_KEY_SHT3X_READ[] PROGMEM = "sht3x_read";
116+
static constexpr char CFG_KEY_SCD30_READ[] PROGMEM = "scd30_read";
115117
static constexpr char CFG_KEY_DS18B20_READ[] PROGMEM = "ds18b20_read";
116118
static constexpr char CFG_KEY_DNMS_READ[] PROGMEM = "dnms_read";
117119
static constexpr char CFG_KEY_DNMS_CORRECTION[] PROGMEM = "dnms_correction";
@@ -180,6 +182,7 @@ static constexpr ConfigShapeEntry configShape[] PROGMEM = {
180182
{ Config_Type_Bool, 0, CFG_KEY_BMP_READ, &cfg::bmp_read },
181183
{ Config_Type_Bool, 0, CFG_KEY_BMX280_READ, &cfg::bmx280_read },
182184
{ Config_Type_Bool, 0, CFG_KEY_SHT3X_READ, &cfg::sht3x_read },
185+
{ Config_Type_Bool, 0, CFG_KEY_SCD30_READ, &cfg::scd30_read },
183186
{ Config_Type_Bool, 0, CFG_KEY_DS18B20_READ, &cfg::ds18b20_read },
184187
{ Config_Type_Bool, 0, CFG_KEY_DNMS_READ, &cfg::dnms_read },
185188
{ Config_Type_String, sizeof(cfg::dnms_correction)-1, CFG_KEY_DNMS_CORRECTION, cfg::dnms_correction },

airrohr-firmware/airrohr-cfg.h.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
Bool bmp_read
2121
Bool bmx280_read
2222
Bool sht3x_read
23+
Bool scd30_read
2324
Bool ds18b20_read
2425
Bool dnms_read
2526
String dnms_correction

airrohr-firmware/airrohr-firmware.ino

Lines changed: 112 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ String SOFTWARE_VERSION(SOFTWARE_VERSION_STR);
110110
#include <Adafruit_SHT31.h>
111111
#include <StreamString.h>
112112
#include <DallasTemperature.h>
113+
#include <SparkFun_SCD30_Arduino_Library.h>
113114
#include <TinyGPS++.h>
114115
#include "./bmx280_i2c.h"
115116
#include "./sps30_i2c.h"
@@ -171,6 +172,7 @@ namespace cfg {
171172
bool bmx280_read = BMX280_READ;
172173
char height_above_sealevel[8] = "0";
173174
bool sht3x_read = SHT3X_READ;
175+
bool scd30_read = SCD30_READ;
174176
bool ds18b20_read = DS18B20_READ;
175177
bool dnms_read = DNMS_READ;
176178
char dnms_correction[LEN_DNMS_CORRECTION] = DNMS_CORRECTION;
@@ -250,6 +252,7 @@ bool htu21d_init_failed = false;
250252
bool bmp_init_failed = false;
251253
bool bmx280_init_failed = false;
252254
bool sht3x_init_failed = false;
255+
bool scd30_init_failed = false;
253256
bool dnms_init_failed = false;
254257
bool gps_init_failed = false;
255258
bool airrohr_selftest_failed = false;
@@ -321,6 +324,11 @@ Adafruit_SHT31 sht3x;
321324
OneWire oneWire;
322325
DallasTemperature ds18b20(&oneWire);
323326

327+
/*****************************************************************
328+
* SCD30 declaration *
329+
*****************************************************************/
330+
SCD30 scd30;
331+
324332
/*****************************************************************
325333
* GPS declaration *
326334
*****************************************************************/
@@ -378,6 +386,9 @@ float last_value_HTU21D_T = -128.0;
378386
float last_value_HTU21D_H = -1.0;
379387
float last_value_SHT3X_T = -128.0;
380388
float last_value_SHT3X_H = -1.0;
389+
float last_value_SCD30_T = -128.0;
390+
float last_value_SCD30_H = -1.0;
391+
uint16_t last_value_SCD30_CO2 = 0;
381392

382393
uint32_t sds_pm10_sum = 0;
383394
uint32_t sds_pm25_sum = 0;
@@ -601,14 +612,12 @@ static void disable_unneeded_nmea() {
601612
serialGPS->println(F("$PUBX,40,VTG,0,0,0,0*5E")); // Track made good and ground speed
602613
}
603614

604-
605615
/*****************************************************************
606616
* read config from spiffs *
607617
*****************************************************************/
608618

609619
/* backward compatibility for the times when we stored booleans as strings */
610-
static bool boolFromJSON(const DynamicJsonDocument& json, const __FlashStringHelper* key)
611-
{
620+
static bool boolFromJSON(const DynamicJsonDocument& json, const __FlashStringHelper* key) {
612621
if (json[key].is<char*>()) {
613622
return !strcmp_P(json[key].as<char*>(), PSTR("true"));
614623
}
@@ -838,11 +847,9 @@ static float pressure_at_sealevel(const float temperature, const float pressure)
838847
return pressure_at_sealevel;
839848
}
840849

841-
842850
/*****************************************************************
843851
* html helper functions *
844852
*****************************************************************/
845-
846853
static void start_html_page(String& page_content, const String& title) {
847854
last_page_load = millis();
848855

@@ -1051,7 +1058,6 @@ static void webserver_root() {
10511058
/*****************************************************************
10521059
* Webserver config: show config page *
10531060
*****************************************************************/
1054-
10551061
static void webserver_config_send_body_get(String& page_content) {
10561062
auto add_form_checkbox = [&page_content](const ConfigShapeId cfgid, const String& info) {
10571063
page_content += form_checkbox(cfgid, info, true);
@@ -1182,6 +1188,7 @@ static void webserver_config_send_body_get(String& page_content) {
11821188
add_form_checkbox_sensor(Config_htu21d_read, FPSTR(INTL_HTU21D));
11831189
add_form_checkbox_sensor(Config_bmx280_read, FPSTR(INTL_BMX280));
11841190
add_form_checkbox_sensor(Config_sht3x_read, FPSTR(INTL_SHT3X));
1191+
add_form_checkbox_sensor(Config_scd30_read, FPSTR(INTL_SCD30));
11851192

11861193
// Paginate page after ~ 1500 Bytes
11871194
server.sendContent(page_content);
@@ -1445,6 +1452,7 @@ static void webserver_values() {
14451452
const String unit_Deg("°");
14461453
const String unit_P("hPa");
14471454
const String unit_T("°C");
1455+
const String unit_CO2("ppm");
14481456
const String unit_NC();
14491457
const String unit_LA(F("dB(A)"));
14501458
float dew_point_temp;
@@ -1564,6 +1572,14 @@ static void webserver_values() {
15641572
add_table_value(FPSTR(SENSORS_SHT3X), FPSTR(INTL_DEW_POINT), isnan(dew_point_temp) ? "-" : String(dew_point_temp,1), unit_T);
15651573
page_content += FPSTR(EMPTY_ROW);
15661574
}
1575+
if (cfg::scd30_read) {
1576+
add_table_t_value(FPSTR(SENSORS_SCD30), FPSTR(INTL_TEMPERATURE), last_value_SCD30_T);
1577+
add_table_h_value(FPSTR(SENSORS_SCD30), FPSTR(INTL_HUMIDITY), last_value_SCD30_H);
1578+
add_table_value(FPSTR(SENSORS_SCD30), FPSTR(INTL_CO2_PPM), check_display_value(last_value_SCD30_CO2,0,0,0),unit_CO2);
1579+
dew_point_temp = dew_point(last_value_SCD30_T, last_value_SCD30_H);
1580+
add_table_value(FPSTR(SENSORS_SCD30), FPSTR(INTL_DEW_POINT), isnan(dew_point_temp) ? "-" : String(dew_point_temp,1), unit_T);
1581+
page_content += FPSTR(EMPTY_ROW);
1582+
}
15671583
if (cfg::ds18b20_read) {
15681584
add_table_t_value(FPSTR(SENSORS_DS18B20), FPSTR(INTL_TEMPERATURE), last_value_DS18B20_T);
15691585
page_content += FPSTR(EMPTY_ROW);
@@ -2032,6 +2048,17 @@ static void wifiConfig() {
20322048
SSID.toCharArray(wifiInfo[i].ssid, sizeof(wifiInfo[0].ssid));
20332049
}
20342050

2051+
// Use 13 channels if locale is not "EN"
2052+
wifi_country_t wifi;
2053+
wifi.policy = WIFI_COUNTRY_POLICY_MANUAL;
2054+
strcpy(wifi.cc, INTL_LANG);
2055+
wifi.nchan = (INTL_LANG[0] == 'E' && INTL_LANG[1] == 'N') ? 11 : 13;
2056+
wifi.schan = 1;
2057+
2058+
#if defined(ESP8266)
2059+
wifi_set_country(&wifi);
2060+
#endif
2061+
20352062
WiFi.mode(WIFI_AP);
20362063
const IPAddress apIP(192, 168, 4, 1);
20372064
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
@@ -2060,6 +2087,16 @@ static void wifiConfig() {
20602087
}
20612088

20622089
WiFi.softAPdisconnect(true);
2090+
2091+
wifi.policy = WIFI_COUNTRY_POLICY_MANUAL;
2092+
strcpy(wifi.cc, INTL_LANG);
2093+
wifi.nchan = 13;
2094+
wifi.schan = 1;
2095+
2096+
#if defined(ESP8266)
2097+
wifi_set_country(&wifi);
2098+
#endif
2099+
20632100
WiFi.mode(WIFI_STA);
20642101

20652102
dnsServer.stop();
@@ -2131,11 +2168,11 @@ static void connectWifi() {
21312168
WiFi.setAutoReconnect(true);
21322169
}
21332170

2134-
// Use 13 channels if locale is not "EN"
2171+
// Use 13 channels for connect to known AP
21352172
wifi_country_t wifi;
21362173
wifi.policy = WIFI_COUNTRY_POLICY_MANUAL;
21372174
strcpy(wifi.cc, INTL_LANG);
2138-
wifi.nchan = (INTL_LANG[0] == 'E' && INTL_LANG[1] == 'N') ? 11 : 13;
2175+
wifi.nchan = 13;
21392176
wifi.schan = 1;
21402177

21412178
#if defined(ESP8266)
@@ -2476,6 +2513,33 @@ static void fetchSensorSHT3x(String& s) {
24762513
debug_outln_verbose(FPSTR(DBG_TXT_END_READING), FPSTR(SENSORS_SHT3X));
24772514
}
24782515

2516+
/*****************************************************************
2517+
* read SHT3x sensor values *
2518+
*****************************************************************/
2519+
static void fetchSensorSCD30(String& s) {
2520+
debug_outln_verbose(FPSTR(DBG_TXT_START_READING), FPSTR(SENSORS_SCD30));
2521+
2522+
const auto t = scd30.getTemperature();
2523+
const auto h = scd30.getHumidity();
2524+
const auto c = scd30.getCO2();
2525+
2526+
if (isnan(h) || isnan(t) || isnan(c)) {
2527+
last_value_SCD30_T = -128.0;
2528+
last_value_SCD30_H = -1.0;
2529+
last_value_SCD30_CO2 = 0;
2530+
debug_outln_error(F("SCD30 read failed"));
2531+
} else {
2532+
last_value_SCD30_T = t;
2533+
last_value_SCD30_H = h;
2534+
last_value_SCD30_CO2 = c;
2535+
add_Value2Json(s, F("SCD30_temperature"), FPSTR(DBG_TXT_TEMPERATURE), last_value_SCD30_T);
2536+
add_Value2Json(s, F("SCD30_humidity"), FPSTR(DBG_TXT_HUMIDITY), last_value_SCD30_H);
2537+
add_Value2Json(s, F("SCD30_co2_ppm"), FPSTR(DBG_TXT_CO2PPM), last_value_SCD30_CO2);
2538+
}
2539+
debug_outln_info(FPSTR(DBG_TXT_SEP));
2540+
debug_outln_verbose(FPSTR(DBG_TXT_END_READING), FPSTR(SENSORS_SCD30));
2541+
}
2542+
24792543
/*****************************************************************
24802544
* read BMP280/BME280 sensor values *
24812545
*****************************************************************/
@@ -3725,17 +3789,20 @@ static void display_values() {
37253789
if (cfg::dht_read || cfg::ds18b20_read || cfg::htu21d_read || cfg::bmp_read || cfg::bmx280_read || cfg::sht3x_read) {
37263790
screens[screen_count++] = 3;
37273791
}
3728-
if (cfg::gps_read) {
3792+
if (cfg::scd30_read) {
37293793
screens[screen_count++] = 4;
37303794
}
3731-
if (cfg::dnms_read) {
3795+
if (cfg::gps_read) {
37323796
screens[screen_count++] = 5;
37333797
}
3798+
if (cfg::dnms_read) {
3799+
screens[screen_count++] = 6;
3800+
}
37343801
if (cfg::display_wifi_info) {
3735-
screens[screen_count++] = 6; // Wifi info
3802+
screens[screen_count++] = 7; // Wifi info
37363803
}
37373804
if (cfg::display_device_info) {
3738-
screens[screen_count++] = 7; // chipID, firmware and count of measurements
3805+
screens[screen_count++] = 8; // chipID, firmware and count of measurements
37393806
}
37403807
// update size of "screens" when adding more screens!
37413808
if (cfg::has_display || cfg::has_sh1106 || lcd_2004) {
@@ -3769,27 +3836,29 @@ static void display_values() {
37693836
while (line_count < 3) { display_lines[line_count++] = emptyString; }
37703837
break;
37713838
case 4:
3839+
display_header = "SCD30";
3840+
display_lines[0] = "Temp.: "; display_lines[0] += check_display_value(last_value_SCD30_H, -128, 1, 6); display_lines[0] += " °C";
3841+
display_lines[1] = "Hum.: "; display_lines[1] += check_display_value(last_value_SCD30_T, -1, 1, 6); display_lines[1] += " %";
3842+
display_lines[2] = "CO2: "; display_lines[2] += check_display_value(last_value_SCD30_CO2, 0, 0, 6); display_lines[2] += " ppm";
3843+
case 5:
37723844
display_header = "NEO6M";
3773-
display_lines[0] = "Lat: ";
3774-
display_lines[0] += check_display_value(lat_value, -200.0, 6, 10);
3775-
display_lines[1] = "Lon: ";
3776-
display_lines[1] += check_display_value(lon_value, -200.0, 6, 10);
3777-
display_lines[2] = "Alt: ";
3778-
display_lines[2] += check_display_value(alt_value, -1000.0, 2, 10);
3845+
display_lines[0] = "Lat: "; display_lines[0] += check_display_value(lat_value, -200.0, 6, 10);
3846+
display_lines[1] = "Lon: "; display_lines[1] += check_display_value(lon_value, -200.0, 6, 10);
3847+
display_lines[2] = "Alt: "; display_lines[2] += check_display_value(alt_value, -1000.0, 2, 10);
37793848
break;
3780-
case 5:
3849+
case 6:
37813850
display_header = FPSTR(SENSORS_DNMS);
37823851
display_lines[0] = std::move(tmpl(F("LAeq: {v} db(A)"), check_display_value(la_eq_value, -1, 1, 6)));
37833852
display_lines[1] = std::move(tmpl(F("LA_max: {v} db(A)"), check_display_value(la_max_value, -1, 1, 6)));
37843853
display_lines[2] = std::move(tmpl(F("LA_min: {v} db(A)"), check_display_value(la_min_value, -1, 1, 6)));
37853854
break;
3786-
case 6:
3855+
case 7:
37873856
display_header = F("Wifi info");
37883857
display_lines[0] = "IP: "; display_lines[0] += WiFi.localIP().toString();
37893858
display_lines[1] = "SSID: "; display_lines[1] += WiFi.SSID();
37903859
display_lines[2] = std::move(tmpl(F("Signal: {v} %"), String(calcWiFiSignalQuality(last_signal_strength))));
37913860
break;
3792-
case 7:
3861+
case 8:
37933862
display_header = F("Device Info");
37943863
display_lines[0] = "ID: "; display_lines[0] += esp_chipid;
37953864
display_lines[1] = "FW: "; display_lines[1] += SOFTWARE_VERSION;
@@ -3864,20 +3933,24 @@ static void display_values() {
38643933
display_lines[1] = std::move(tmpl(F("H: {v} %"), check_display_value(h_value, -1, 1, 6)));
38653934
break;
38663935
case 4:
3936+
display_lines[0] = std::move(tmpl(F("T/H: {v}"), check_display_value(last_value_SCD30_T, -128, 1, 5) + " / " + check_display_value(last_value_SCD30_H,-1,0,3)));
3937+
display_lines[1] = std::move(tmpl(F("CO2: {v} ppm"), check_display_value(last_value_SCD30_CO2, 0, 0, 6)));
3938+
break;
3939+
case 5:
38673940
display_lines[0] = "Lat: ";
38683941
display_lines[0] += check_display_value(lat_value, -200.0, 6, 11);
38693942
display_lines[1] = "Lon: ";
38703943
display_lines[1] += check_display_value(lon_value, -200.0, 6, 11);
38713944
break;
3872-
case 5:
3945+
case 6:
38733946
display_lines[0] = std::move(tmpl(F("LAeq: {v} db(A)"), check_display_value(la_eq_value, -1, 1, 6)));
38743947
display_lines[1] = std::move(tmpl(F("LA_max: {v} db(A)"), check_display_value(la_max_value, -1, 1, 6)));
38753948
break;
3876-
case 6:
3949+
case 7:
38773950
display_lines[0] = WiFi.localIP().toString();
38783951
display_lines[1] = WiFi.SSID();
38793952
break;
3880-
case 7:
3953+
case 8:
38813954
display_lines[0] = "ID: ";
38823955
display_lines[0] += esp_chipid;
38833956
display_lines[1] = "FW: ";
@@ -4143,6 +4216,14 @@ static void powerOnTestSensors() {
41434216
}
41444217
}
41454218

4219+
if (cfg::scd30_read) {
4220+
debug_outln_info(F("Read SCD30..."));
4221+
if (!scd30.begin()) {
4222+
debug_outln_error(F("Check SCD30 wiring"));
4223+
scd30_init_failed = true;
4224+
}
4225+
}
4226+
41464227
if (cfg::ds18b20_read) {
41474228
oneWire.begin(ONEWIRE_PIN);
41484229
ds18b20.begin(); // Start DS18B20
@@ -4544,6 +4625,13 @@ void loop(void) {
45444625
sum_send_time += sendSensorCommunity(result, SHT3X_API_PIN, FPSTR(SENSORS_SHT3X), "SHT3X_");
45454626
result = emptyString;
45464627
}
4628+
if (cfg::scd30_read && (! scd30_init_failed )) {
4629+
// getting temperature and humidity (optional)
4630+
fetchSensorSCD30(result);
4631+
data += result;
4632+
sum_send_time += sendSensorCommunity(result, SCD30_API_PIN, FPSTR(SENSORS_SCD30), "SCD30_");
4633+
result = emptyString;
4634+
}
45474635
if (cfg::ds18b20_read) {
45484636
// getting temperature (optional)
45494637
fetchSensorDS18B20(result);

airrohr-firmware/ext_def.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const char WWW_PASSWORD[] PROGMEM = "";
1212

1313
// Sensor Wifi config (config mode)
1414
#define FS_SSID ""
15-
#define FS_PWD ""
15+
#define FS_PWD "airrohrcfg"
1616

1717
// Where to send the data?
1818
#define SEND2SENSORCOMMUNITY 1
@@ -263,6 +263,10 @@ static const char MEASUREMENT_NAME_INFLUX[] PROGMEM = "feinstaub";
263263
#define SHT3X_READ 0
264264
#define SHT3X_API_PIN 7
265265

266+
// SHT3x, temperature, pressure, CO2
267+
#define SCD30_READ 0
268+
#define SCD30_API_PIN 17
269+
266270
// DS18B20, temperature
267271
#define DS18B20_READ 0
268272
#define DS18B20_API_PIN 13

airrohr-firmware/html-content.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const char TXT_CONTENT_TYPE_IMAGE_PNG[] PROGMEM = "image/png";
88
const char DBG_TXT_TEMPERATURE[] PROGMEM = "Temperature (°C): ";
99
const char DBG_TXT_HUMIDITY[] PROGMEM = "Humidity (%): ";
1010
const char DBG_TXT_PRESSURE[] PROGMEM = "Pressure (hPa): ";
11+
const char DBG_TXT_CO2PPM[] PROGMEM = "CO₂ (ppm): ";
1112
const char DBG_TXT_START_READING[] PROGMEM = "R/ ";
1213
const char DBG_TXT_END_READING[] PROGMEM = "/R ";
1314
const char DBG_TXT_CHECKSUM_IS[] PROGMEM = "Checksum is: ";
@@ -33,6 +34,7 @@ const char SENSORS_DHT22[] PROGMEM = "DHT22";
3334
const char SENSORS_DS18B20[] PROGMEM = "DS18B20";
3435
const char SENSORS_HTU21D[] PROGMEM = "HTU21D";
3536
const char SENSORS_SHT3X[] PROGMEM = "SHT3x";
37+
const char SENSORS_SCD30[] PROGMEM = "SCD30";
3638
const char SENSORS_BMP180[] PROGMEM = "BMP180";
3739
const char SENSORS_BME280[] PROGMEM = "BME280";
3840
const char SENSORS_BMP280[] PROGMEM = "BMP280";

0 commit comments

Comments
 (0)