Browse Source

sns: Add BME680 (#2295)

Add support for BME680 using libalgobsec proprietary algorithms for precise Indoor Air Quality (IAQ) measurement. Unlike traditional CO2 sensors - and good ones are expensive - it measures nearly all VOCs compounds in the air (plus other gases) and compensates those measurements with its built-in temperature and humidity sensors to determine indoor air quality.

Co-authored-by: Max Prokhorov <prokhorov.max@outlook.com>
mcspr-patch-1
Rui Marinho 4 years ago
committed by GitHub
parent
commit
6266930a4d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 524 additions and 17 deletions
  1. +1
    -0
      README.md
  2. +3
    -0
      code/espurna/board.cpp
  3. +1
    -0
      code/espurna/config/arduino.h
  4. +9
    -0
      code/espurna/config/dependencies.h
  5. +2
    -2
      code/espurna/config/general.h
  6. +34
    -9
      code/espurna/config/sensors.h
  7. +6
    -1
      code/espurna/config/types.h
  8. +1
    -1
      code/espurna/debug.cpp
  9. +43
    -1
      code/espurna/sensor.cpp
  10. +344
    -0
      code/espurna/sensors/BME680Sensor.h
  11. +2
    -1
      code/espurna/sensors/BaseSensor.h
  12. +2
    -1
      code/platformio.ini
  13. +2
    -0
      code/scripts/espurna_utils/__init__.py
  14. +0
    -1
      code/scripts/espurna_utils/ldscripts.py
  15. +69
    -0
      code/scripts/espurna_utils/libalgobsec.py
  16. +4
    -0
      code/scripts/pio_main.py
  17. +1
    -0
      code/test/build/sensor.h

+ 1
- 0
README.md View File

@ -93,6 +93,7 @@ Since November 2018, Max Prokhorov (**@mcspr**) is also actively working as a co
* Environment * Environment
* **DHT11 / DHT12 / DHT22 / DHT21 / AM2301 / Itead's SI7021** * **DHT11 / DHT12 / DHT22 / DHT21 / AM2301 / Itead's SI7021**
* **BMP180**, **BMP280** and **BME280** pressure, humidity (BME280) and temperature (BMP280 & BME280) sensor by Bosch * **BMP180**, **BMP280** and **BME280** pressure, humidity (BME280) and temperature (BMP280 & BME280) sensor by Bosch
* **BME680** pressure, humidity, temperature and gas sensor by Bosch with support for Bosch's proprietary library BSEC for accurate Indoor Air Quality (IAQ) monitoring (⚠️ [learn more](https://github.com/xoseperez/espurna/wiki/Sensors#bme680-environmental-sensor))
* **TMP35** and **TMP36** analog temperature sensors * **TMP35** and **TMP36** analog temperature sensors
* **MAX6675** temperature sensor * **MAX6675** temperature sensor
* **NTC** temperature sensors * **NTC** temperature sensors


+ 3
- 0
code/espurna/board.cpp View File

@ -203,6 +203,9 @@ PROGMEM const char espurna_sensors[] =
#if BMX280_SUPPORT #if BMX280_SUPPORT
"BMX280 " "BMX280 "
#endif #endif
#if BME680_SUPPORT
"BME680 "
#endif
#if CSE7766_SUPPORT #if CSE7766_SUPPORT
"CSE7766 " "CSE7766 "
#endif #endif


+ 1
- 0
code/espurna/config/arduino.h View File

@ -229,6 +229,7 @@
//#define BH1750_SUPPORT 1 //#define BH1750_SUPPORT 1
//#define BMP180_SUPPORT 1 //#define BMP180_SUPPORT 1
//#define BMX280_SUPPORT 1 //#define BMX280_SUPPORT 1
//#define BME680_SUPPORT 1
//#define CSE7766_SUPPORT 1 //#define CSE7766_SUPPORT 1
//#define DALLAS_SUPPORT 1 //#define DALLAS_SUPPORT 1
//#define DHT_SUPPORT 1 //#define DHT_SUPPORT 1


+ 9
- 0
code/espurna/config/dependencies.h View File

@ -221,3 +221,12 @@
#warning "MQTT_MAX_PACKET_SIZE should be set in `build_flags = ...` of the environment! Default value is used instead." #warning "MQTT_MAX_PACKET_SIZE should be set in `build_flags = ...` of the environment! Default value is used instead."
#endif #endif
#endif #endif
//------------------------------------------------------------------------------
// Disable BME680 support if using Core version 2.3.0 due to memory constraints.
#if BME680_SUPPORT && defined(ARDUINO_ESP8266_RELEASE_2_3_0)
#warning "BME680_SUPPORT is not available when using Arduino Core 2.3.0 due to memory constraints. Please use Arduino Core 2.6.3+ instead (or set `platform = ${common.platform_latest}` for the latest version)."
#undef BME680_SUPPORT
#define BME680_SUPPORT 0
#endif

+ 2
- 2
code/espurna/config/general.h View File

@ -667,7 +667,7 @@
#endif #endif
// ref: https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html#config-lwip-esp-gratuitous-arp // ref: https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html#config-lwip-esp-gratuitous-arp
// ref: https://github.com/xoseperez/espurna/pull/1877#issuecomment-525612546
// ref: https://github.com/xoseperez/espurna/pull/1877#issuecomment-525612546
// //
// Broadcast gratuitous ARP periodically to update ARP tables on the AP and all devices on the same network. // Broadcast gratuitous ARP periodically to update ARP tables on the AP and all devices on the same network.
// Helps to solve compatibility issues when ESP fails to timely reply to ARP requests, causing the device's ARP table entry to expire. // Helps to solve compatibility issues when ESP fails to timely reply to ARP requests, causing the device's ARP table entry to expire.
@ -1028,7 +1028,7 @@
#endif #endif
#ifndef MQTT_SECURE_CLIENT_MFLN #ifndef MQTT_SECURE_CLIENT_MFLN
#define MQTT_SECURE_CLIENT_MFLN SECURE_CLIENT_MFLN // Use global MFLN setting by default
#define MQTT_SECURE_CLIENT_MFLN SECURE_CLIENT_MFLN // Use global MFLN setting by default
#endif #endif
#ifndef MQTT_SECURE_CLIENT_INCLUDE_CA #ifndef MQTT_SECURE_CLIENT_INCLUDE_CA


+ 34
- 9
code/espurna/config/sensors.h View File

@ -782,39 +782,39 @@
// LDR sensor // LDR sensor
// Enable support by passing LDR_SUPPORT=1 build flag // Enable support by passing LDR_SUPPORT=1 build flag
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#ifndef LDR_SUPPORT #ifndef LDR_SUPPORT
#define LDR_SUPPORT 0 #define LDR_SUPPORT 0
#endif #endif
#ifndef LDR_SAMPLES #ifndef LDR_SAMPLES
#define LDR_SAMPLES 10 // Number of samples #define LDR_SAMPLES 10 // Number of samples
#endif #endif
#ifndef LDR_DELAY #ifndef LDR_DELAY
#define LDR_DELAY 0 // Delay between samples in micros #define LDR_DELAY 0 // Delay between samples in micros
#endif #endif
#ifndef LDR_TYPE #ifndef LDR_TYPE
#define LDR_TYPE LDR_GL5528 #define LDR_TYPE LDR_GL5528
#endif #endif
#ifndef LDR_ON_GROUND #ifndef LDR_ON_GROUND
#define LDR_ON_GROUND true #define LDR_ON_GROUND true
#endif #endif
#ifndef LDR_RESISTOR #ifndef LDR_RESISTOR
#define LDR_RESISTOR 10000 // Resistance #define LDR_RESISTOR 10000 // Resistance
#endif #endif
#ifndef LDR_MULTIPLICATION #ifndef LDR_MULTIPLICATION
#define LDR_MULTIPLICATION 32017200 #define LDR_MULTIPLICATION 32017200
#endif #endif
#ifndef LDR_POWER #ifndef LDR_POWER
#define LDR_POWER 1.5832 #define LDR_POWER 1.5832
#endif #endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// MHZ19 CO2 sensor // MHZ19 CO2 sensor
// Enable support by passing MHZ19_SUPPORT=1 build flag // Enable support by passing MHZ19_SUPPORT=1 build flag
@ -1303,6 +1303,29 @@
#define SI1145_ADDRESS 0x60 #define SI1145_ADDRESS 0x60
#endif #endif
//------------------------------------------------------------------------------
// BME680
// Enable support by passing BME680_SUPPORT=1 build flag
//------------------------------------------------------------------------------
#ifndef BME680_SUPPORT
#define BME680_SUPPORT 0
#endif
#ifndef BME680_I2C_ADDRESS
#define BME680_I2C_ADDRESS 0x00 // 0x00 means auto
#endif
#ifndef BME680_BSEC_CONFIG
#define BME680_BSEC_CONFIG BME680_BSEC_CONFIG_GENERIC_33V_3S_4D // BSEC config config value. By default, 3.3V as supply voltage,
#endif // 3 seconds as maximum time between bsec_sensor_control` calls
// and 4 days as the time considered for background calibration.
#ifndef BME680_STATE_SAVE_INTERVAL
#define BME680_STATE_SAVE_INTERVAL 0 // How frequently (in milliseconds) should state be stored in
#endif // non-volatile memory. A common value would be every 6h or
// 360 * 60 * 1000 milliseconds. By default, this is disabled.
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// ADC // ADC
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -1356,6 +1379,7 @@
BH1750_SUPPORT || \ BH1750_SUPPORT || \
BMP180_SUPPORT || \ BMP180_SUPPORT || \
BMX280_SUPPORT || \ BMX280_SUPPORT || \
BME680_SUPPORT || \
EMON_ADC121_SUPPORT || \ EMON_ADC121_SUPPORT || \
EMON_ADS1X15_SUPPORT || \ EMON_ADS1X15_SUPPORT || \
SHT3X_I2C_SUPPORT || \ SHT3X_I2C_SUPPORT || \
@ -1391,6 +1415,7 @@
ANALOG_SUPPORT || \ ANALOG_SUPPORT || \
BH1750_SUPPORT || \ BH1750_SUPPORT || \
BMP180_SUPPORT || \ BMP180_SUPPORT || \
BME680_SUPPORT || \
BMX280_SUPPORT || \ BMX280_SUPPORT || \
CSE7766_SUPPORT || \ CSE7766_SUPPORT || \
DALLAS_SUPPORT || \ DALLAS_SUPPORT || \


+ 6
- 1
code/espurna/config/types.h View File

@ -333,6 +333,7 @@
#define SENSOR_SI1145_ID 39 #define SENSOR_SI1145_ID 39
#define SENSOR_HDC1080_ID 40 #define SENSOR_HDC1080_ID 40
#define SENSOR_PZEM004TV30_ID 41 #define SENSOR_PZEM004TV30_ID 41
#define SENSOR_BME680_ID 42
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// Magnitudes // Magnitudes
@ -372,8 +373,12 @@
#define MAGNITUDE_RESISTANCE 30 #define MAGNITUDE_RESISTANCE 30
#define MAGNITUDE_PH 31 #define MAGNITUDE_PH 31
#define MAGNITUDE_FREQUENCY 32 #define MAGNITUDE_FREQUENCY 32
#define MAGNITUDE_IAQ 33
#define MAGNITUDE_IAQ_ACCURACY 34
#define MAGNITUDE_IAQ_STATIC 35
#define MAGNITUDE_VOC 36
#define MAGNITUDE_MAX 33
#define MAGNITUDE_MAX 38
#define SENSOR_ERROR_OK 0 // No error #define SENSOR_ERROR_OK 0 // No error
#define SENSOR_ERROR_OUT_OF_RANGE 1 // Result out of sensor range #define SENSOR_ERROR_OUT_OF_RANGE 1 // Result out of sensor range


+ 1
- 1
code/espurna/debug.cpp View File

@ -328,7 +328,7 @@ DebugLogMode convert(const String& value) {
void debugConfigureBoot() { void debugConfigureBoot() {
static_assert( static_assert(
std::is_same<int, std::underlying_type<DebugLogMode>::type>::value,
std::is_same<int, std::underlying_type<DebugLogMode>::type>::value,
"should be able to match DebugLogMode with int" "should be able to match DebugLogMode with int"
); );


+ 43
- 1
code/espurna/sensor.cpp View File

@ -59,6 +59,10 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include "sensors/BMX280Sensor.h" #include "sensors/BMX280Sensor.h"
#endif #endif
#if BME680_SUPPORT
#include "sensors/BME680Sensor.h"
#endif
#if CSE7766_SUPPORT #if CSE7766_SUPPORT
#include "sensors/CSE7766Sensor.h" #include "sensors/CSE7766Sensor.h"
#endif #endif
@ -515,6 +519,8 @@ sensor_magnitude_t::sensor_magnitude_t(unsigned char slot, unsigned char index_l
++_counts[type]; ++_counts[type];
switch (type) { switch (type) {
case MAGNITUDE_IAQ:
case MAGNITUDE_IAQ_STATIC:
case MAGNITUDE_ENERGY: case MAGNITUDE_ENERGY:
filter = new LastFilter(); filter = new LastFilter();
break; break;
@ -641,6 +647,18 @@ String magnitudeTopic(unsigned char type) {
case MAGNITUDE_CO2: case MAGNITUDE_CO2:
result = F("co2"); result = F("co2");
break; break;
case MAGNITUDE_VOC:
result = F("voc");
break;
case MAGNITUDE_IAQ:
result = F("iaq");
break;
case MAGNITUDE_IAQ_ACCURACY:
result = F("iaq_accuracy");
break;
case MAGNITUDE_IAQ_STATIC:
result = F("iaq_static");
break;
case MAGNITUDE_LUX: case MAGNITUDE_LUX:
result = F("lux"); result = F("lux");
break; break;
@ -927,6 +945,10 @@ const char * const _magnitudeSettingsPrefix(unsigned char type) {
case MAGNITUDE_PM2dot5: return "pm1dot5"; case MAGNITUDE_PM2dot5: return "pm1dot5";
case MAGNITUDE_PM10: return "pm10"; case MAGNITUDE_PM10: return "pm10";
case MAGNITUDE_CO2: return "co2"; case MAGNITUDE_CO2: return "co2";
case MAGNITUDE_VOC: return "voc";
case MAGNITUDE_IAQ: return "iaq";
case MAGNITUDE_IAQ_ACCURACY: return "iaqAccuracy";
case MAGNITUDE_IAQ_STATIC: return "iaqStatic";
case MAGNITUDE_LUX: return "lux"; case MAGNITUDE_LUX: return "lux";
case MAGNITUDE_UVA: return "uva"; case MAGNITUDE_UVA: return "uva";
case MAGNITUDE_UVB: return "uvb"; case MAGNITUDE_UVB: return "uvb";
@ -1155,6 +1177,18 @@ String magnitudeName(unsigned char type) {
case MAGNITUDE_CO2: case MAGNITUDE_CO2:
result = F("CO2"); result = F("CO2");
break; break;
case MAGNITUDE_VOC:
result = F("VOC");
break;
case MAGNITUDE_IAQ_STATIC:
result = F("IAQ (Static)");
break;
case MAGNITUDE_IAQ:
result = F("IAQ");
break;
case MAGNITUDE_IAQ_ACCURACY:
result = F("IAQ Accuracy");
break;
case MAGNITUDE_LUX: case MAGNITUDE_LUX:
result = F("Lux"); result = F("Lux");
break; break;
@ -1570,6 +1604,14 @@ void _sensorLoad() {
} }
#endif #endif
#if BME680_SUPPORT
{
BME680Sensor * sensor = new BME680Sensor();
sensor->setAddress(BME680_I2C_ADDRESS);
_sensors.push_back(sensor);
}
#endif
#if CSE7766_SUPPORT #if CSE7766_SUPPORT
{ {
CSE7766Sensor * sensor = new CSE7766Sensor(); CSE7766Sensor * sensor = new CSE7766Sensor();
@ -1986,7 +2028,7 @@ void _sensorLoad() {
_sensors.push_back(sensor); _sensors.push_back(sensor);
} }
#endif #endif
#if T6613_SUPPORT #if T6613_SUPPORT
{ {
T6613Sensor * sensor = new T6613Sensor(); T6613Sensor * sensor = new T6613Sensor();


+ 344
- 0
code/espurna/sensors/BME680Sensor.h View File

@ -0,0 +1,344 @@
// -----------------------------------------------------------------------------
// BME680 Sensor over I2C
// Copyright (C) 2020 by Rui Marinho <ruipmarinho at gmail dot com>
//
// The BSEC software binaries and includes are only available for use after accepting its software
// license agreement. By enabling this sensor integration, you are agreeing to the terms of the license
// agreement available at the following URL:
//
// https://ae-bst.resource.bosch.com/media/_tech/media/bsec/2017-07-17_ClickThrough_License_Terms_Environmentalib_SW_CLEAN.pdf
//
// The Arduino wrapper and BME680 Sensor API used for this integration are licensed under the following terms:
//
// Copyright (c) 2020 Bosch Sensortec GmbH. All rights reserved.
//
// BSD-3-Clause
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// For more details, please refer to https://github.com/BoschSensortec/BSEC-Arduino-library.
// -----------------------------------------------------------------------------
#if SENSOR_SUPPORT && BME680_SUPPORT
#pragma once
#include <Arduino.h>
#include <bsec.h>
#include "I2CSensor.h"
// Available configuration modes based on parameters:
// voltage / maximum time between sensor calls / time considered
// for background calibration.
#define BME680_BSEC_CONFIG_GENERIC_18V_3S_4D 0
#define BME680_BSEC_CONFIG_GENERIC_18V_3S_28D 1
#define BME680_BSEC_CONFIG_GENERIC_18V_300S_4D 2
#define BME680_BSEC_CONFIG_GENERIC_18V_300S_28D 3
#define BME680_BSEC_CONFIG_GENERIC_33V_3S_4D 4
#define BME680_BSEC_CONFIG_GENERIC_33V_3S_28D 5
#define BME680_BSEC_CONFIG_GENERIC_33V_300S_4D 6
#define BME680_BSEC_CONFIG_GENERIC_33V_300S_28D 7
const uint8_t bsec_config_iaq[] = {
#if BME680_BSEC_CONFIG == BME680_BSEC_CONFIG_GENERIC_18V_3S_4D
#include <config/generic_18v_3s_28d/bsec_iaq.txt>
#elif BME680_BSEC_CONFIG == BME680_BSEC_CONFIG_GENERIC_18V_3S_28D
#include <config/generic_18v_300s_4d/bsec_iaq.txt>
#elif BME680_BSEC_CONFIG == BME680_BSEC_CONFIG_GENERIC_18V_300S_4D
#include <config/generic_18v_300s_28d/bsec_iaq.txt>
#elif BME680_BSEC_CONFIG == BME680_BSEC_CONFIG_GENERIC_18V_300S_28D
#include <config/generic_33v_3s_4d/bsec_iaq.txt>
#elif BME680_BSEC_CONFIG == BME680_BSEC_CONFIG_GENERIC_33V_3S_4D
#include <config/generic_33v_3s_28d/bsec_iaq.txt>
#elif BME680_BSEC_CONFIG == BME680_BSEC_CONFIG_GENERIC_33V_3S_28D
#include <config/generic_33v_300s_4d/bsec_iaq.txt>
#elif BME680_BSEC_CONFIG == BME680_BSEC_CONFIG_GENERIC_33V_300S_4D
#include <config/generic_33v_300s_28d/bsec_iaq.txt>
#elif BME680_BSEC_CONFIG == BME680_BSEC_CONFIG_GENERIC_33V_300S_28D
#include <config/generic_33v_3s_4d/bsec_iaq.txt>
#endif
};
class BME680Sensor : public I2CSensor<> {
public:
// ---------------------------------------------------------------------
// Public
// ---------------------------------------------------------------------
BME680Sensor() {
_sensor_id = SENSOR_BME680_ID;
_count = 9;
}
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
void begin() {
if (!_dirty) {
return;
}
// I2C auto-discover
unsigned char addresses[] = {BME680_I2C_ADDR_PRIMARY, BME680_I2C_ADDR_SECONDARY};
_address = _begin_i2c(_address, sizeof(addresses), addresses);
if (_address == 0) return;
iaqSensor.begin(_address, Wire);
#if SENSOR_DEBUG
DEBUG_MSG("[BME680] BSEC library version v%u.%u.%u.%u\n",
iaqSensor.version.major,
iaqSensor.version.minor,
iaqSensor.version.major_bugfix,
iaqSensor.version.minor_bugfix
);
#endif
if (!_isSensorOk()) {
_error = SENSOR_ERROR_OTHER;
return;
}
iaqSensor.setConfig(bsec_config_iaq);
_loadState();
float sampleRate;
// BSEC configuration with 300s allows for the sensor to sleep for 300s
// on the ULP mode in order to minimize power consumption.
if (BME680_BSEC_CONFIG == BME680_BSEC_CONFIG_GENERIC_18V_300S_4D ||
BME680_BSEC_CONFIG == BME680_BSEC_CONFIG_GENERIC_18V_300S_4D ||
BME680_BSEC_CONFIG == BME680_BSEC_CONFIG_GENERIC_18V_300S_4D) {
sampleRate = BSEC_SAMPLE_RATE_ULP;
} else {
sampleRate = BSEC_SAMPLE_RATE_LP;
}
iaqSensor.updateSubscription(sensorList, 12, sampleRate);
if (!_isSensorOk()) {
_error = SENSOR_ERROR_OTHER;
return;
}
_ready = true;
_dirty = false;
}
// Descriptive name of the sensor
String description() {
char buffer[21];
snprintf(buffer, sizeof(buffer), "BME680 @ I2C (0x%02X)", _address);
return String(buffer);
}
// Type for slot # index
unsigned char type(unsigned char index) {
if (index == 0) return MAGNITUDE_TEMPERATURE;
if (index == 1) return MAGNITUDE_HUMIDITY;
if (index == 2) return MAGNITUDE_PRESSURE;
if (index == 3) return MAGNITUDE_RESISTANCE;
if (index == 4) return MAGNITUDE_IAQ_ACCURACY;
if (index == 5) return MAGNITUDE_IAQ;
if (index == 6) return MAGNITUDE_IAQ_STATIC;
if (index == 7) return MAGNITUDE_CO2;
if (index == 8) return MAGNITUDE_VOC;
return MAGNITUDE_NONE;
}
void _loadState() {
String storedState = getSetting("bsecState");
if (storedState.length() == 0) {
#if SENSOR_DEBUG
DEBUG_MSG("[BME680] Previous state not found\n");
#endif
return;
}
hexDecode(storedState.c_str(), storedState.length(), _bsecState, sizeof(_bsecState));
iaqSensor.setState(_bsecState);
#if SENSOR_DEBUG
DEBUG_MSG("[BME680] Loaded previous state %s\n", storedState.c_str());
#endif
}
void _saveState() {
if (BME680_STATE_SAVE_INTERVAL == 0) {
return;
}
static unsigned long last_millis = 0;
if (_iaqAccuracy < 3 || (millis() - last_millis < BME680_STATE_SAVE_INTERVAL)) {
return;
}
iaqSensor.getState(_bsecState);
char storedState[BSEC_MAX_STATE_BLOB_SIZE * 2 + 1] = {0};
hexEncode(_bsecState, BSEC_MAX_STATE_BLOB_SIZE, storedState, sizeof(storedState));
setSetting("bsecState", storedState);
last_millis = millis();
}
// The maximum allowed time between two `bsec_sensor_control` calls depends on
// configuration profile `bsec_config_iaq` below.
void tick() {
_error = SENSOR_ERROR_OK;
if (!_isSensorOk()) {
_error = SENSOR_ERROR_OTHER;
return;
}
if (iaqSensor.run()) {
_rawTemperature = iaqSensor.rawTemperature;
_rawHumidity = iaqSensor.rawHumidity;
_temperature = iaqSensor.temperature;
_humidity = iaqSensor.humidity;
_pressure = iaqSensor.pressure / 100;
_gasResistance = iaqSensor.gasResistance;
_iaqAccuracy = iaqSensor.iaqAccuracy;
_iaq = iaqSensor.iaq;
_iaqStatic = iaqSensor.staticIaq;
_co2Equivalent = iaqSensor.co2Equivalent;
_breathVocEquivalent = iaqSensor.breathVocEquivalent;
_saveState();
}
}
// Current value for slot # index
double value(unsigned char index) {
if (index == 0) return _temperature;
if (index == 1) return _humidity;
if (index == 2) return _pressure;
if (index == 3) return _gasResistance;
if (index == 4) return _iaqAccuracy;
if (index == 5) return _iaq;
if (index == 6) return _iaqStatic;
if (index == 7) return _co2Equivalent;
if (index == 8) return _breathVocEquivalent;
return 0;
}
protected:
bool _isSensorOk() {
if (iaqSensor.status == BSEC_OK && iaqSensor.bme680Status == BME680_OK) {
return true;
}
#if SENSOR_DEBUG
if (iaqSensor.status != BSEC_OK) {
if (iaqSensor.status < BSEC_OK) {
DEBUG_MSG("[BME680] BSEC error code %d\n", iaqSensor.status);
} else {
DEBUG_MSG("[BME680] BSEC warning code %d\n", iaqSensor.status);
}
}
#endif
#if SENSOR_DEBUG
if (iaqSensor.bme680Status != BME680_OK) {
if (iaqSensor.bme680Status < BME680_OK) {
DEBUG_MSG("[BME680] Error code %d\n", iaqSensor.bme680Status);
} else {
DEBUG_MSG("[BME680] Warning code %d\n", iaqSensor.bme680Status);
}
}
#endif
return false;
}
bsec_virtual_sensor_t sensorList[12] = {
BSEC_OUTPUT_RAW_TEMPERATURE, // Unscaled (raw) temperature (ºC).
BSEC_OUTPUT_RAW_PRESSURE, // Unscaled (raw) pressure (Pa).
BSEC_OUTPUT_RAW_HUMIDITY, // Unscaled (raw) relative humidity (%).
BSEC_OUTPUT_RAW_GAS, // Gas resistance (Ohm). The resistance value changes according to the
// VOC concentration (the higher the concentration of reducing VOCs,
// the lower the resistance and vice versa).
BSEC_OUTPUT_IAQ, // Scaled Indoor Air Quality based on the recent sensor history, ideal
// for mobile applications (e.g. carry-on devices). The scale ranges from
// 0 (clean air) to 500 (heavily polluted air). The automatic background
// calibration process ensures that consistent IAQ performance is achieved
// after certain of days (depending on BSEC configuration - 4d or 28d).
BSEC_OUTPUT_STATIC_IAQ, // Unscaled Indoor Air Quality, optimized for stationary applications
// (e.g. fixed indoor devices).
BSEC_OUTPUT_CO2_EQUIVALENT, // Estimate of CO2 measured in the air.
BSEC_OUTPUT_BREATH_VOC_EQUIVALENT, // Breath VOC represents the most important compounds in an exhaled
// breath of healthy humans.
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, // Temperature compensated for the influence of sensor heater (ºC).
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, // Relative humidity compensated for the influence of sensor heater (%).
BSEC_OUTPUT_STABILIZATION_STATUS, // Indicates initial stabilization status of the gas sensor element:
// ongoing (0) or finished (1).
BSEC_OUTPUT_RUN_IN_STATUS, // Indicates power-on stabilization status of the gas sensor element:
// ongoing (0) or finished (1).
};
float _breathVocEquivalent = 0.0f;
float _co2Equivalent = 0.0f;
float _gasResistance = 0.0f;
float _humidity = 0.0f;
float _iaq = 0.0f;
float _pressure = 0.0f;
float _rawHumidity = 0.0f;
float _rawTemperature = 0.0f;
float _temperature = 0.0f;
uint8_t _bsecState[BSEC_MAX_STATE_BLOB_SIZE] = {0};
uint8_t _iaqAccuracy = 0;
float _iaqStatic = 0;
Bsec iaqSensor;
};
#endif // SENSOR_SUPPORT && BME680_SUPPORT

+ 2
- 1
code/espurna/sensors/BaseSensor.h View File

@ -117,9 +117,10 @@ class BaseSensor {
case MAGNITUDE_PM1dot0: case MAGNITUDE_PM1dot0:
case MAGNITUDE_PM2dot5: case MAGNITUDE_PM2dot5:
return sensor::Unit::MicrogrammPerCubicMeter; return sensor::Unit::MicrogrammPerCubicMeter;
case MAGNITUDE_CO:
case MAGNITUDE_CO2: case MAGNITUDE_CO2:
case MAGNITUDE_NO2: case MAGNITUDE_NO2:
case MAGNITUDE_CO:
case MAGNITUDE_VOC:
return sensor::Unit::PartsPerMillion; return sensor::Unit::PartsPerMillion;
case MAGNITUDE_LUX: case MAGNITUDE_LUX:
return sensor::Unit::Lux; return sensor::Unit::Lux;


+ 2
- 1
code/platformio.ini View File

@ -155,6 +155,7 @@ lib_deps =
https://github.com/mcleng/MAX6675-Library#2.0.1 https://github.com/mcleng/MAX6675-Library#2.0.1
https://github.com/ThingPulse/esp8266-oled-ssd1306#3398c97 https://github.com/ThingPulse/esp8266-oled-ssd1306#3398c97
Adafruit SI1145 Library@~1.1.1 Adafruit SI1145 Library@~1.1.1
https://github.com/BoschSensortec/BSEC-Arduino-library.git#c5503e0
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# COMMON ENVIRONMENT SETTINGS: # COMMON ENVIRONMENT SETTINGS:
@ -252,7 +253,7 @@ board_build.ldscript = ${common.ldscript_1m}
platform = ${common.platform_latest} platform = ${common.platform_latest}
board = ${common.board_2m} board = ${common.board_2m}
platform_packages = platform_packages =
${common.git_platform_packages}
${common.git_platform_packages}
board_build.ldscript = ${common.ldscript_2m} board_build.ldscript = ${common.ldscript_2m}
[env:esp8266-4m-git-base] [env:esp8266-4m-git-base]


+ 2
- 0
code/scripts/espurna_utils/__init__.py View File

@ -22,6 +22,7 @@
from .checks import check_cppcheck, check_printsize from .checks import check_cppcheck, check_printsize
from .float_support import remove_float_support from .float_support import remove_float_support
from .ldscripts import ldscripts_inject_libpath from .ldscripts import ldscripts_inject_libpath
from .libalgobsec import libalgobsec_inject_patcher
from .lwip import lwip_inject_patcher from .lwip import lwip_inject_patcher
from .postmortem import dummy_ets_printf from .postmortem import dummy_ets_printf
from .git import app_inject_revision from .git import app_inject_revision
@ -33,6 +34,7 @@ __all__ = [
"check_printsize", "check_printsize",
"remove_float_support", "remove_float_support",
"ldscripts_inject_libpath", "ldscripts_inject_libpath",
"libalgobsec_inject_patcher",
"lwip_inject_patcher", "lwip_inject_patcher",
"dummy_ets_printf", "dummy_ets_printf",
"app_inject_revision", "app_inject_revision",


+ 0
- 1
code/scripts/espurna_utils/ldscripts.py View File

@ -1,6 +1,5 @@
import os import os
def ldscripts_inject_libpath(env): def ldscripts_inject_libpath(env):
platform = env.PioPlatform() platform = env.PioPlatform()


+ 69
- 0
code/scripts/espurna_utils/libalgobsec.py View File

@ -0,0 +1,69 @@
import os
import sys
def libalgobsec_inject_patcher(env):
libalgobsec_builder = next(
(
builder
for builder in env.GetLibBuilders()
if builder.name == "BSEC Software Library"
),
None,
)
if libalgobsec_builder is None:
return
def process_archive(target, source, env):
import subprocess
# Allows `import espurna_utils` for external scripts, where we might not be within scons runtime
from SCons.Script import Delete, Mkdir
tmpdir = env.get(
"ESPURNA_LIBALGOBSEC_PATCHER_TMPDIR",
os.path.join(str(target[0].get_dir()), "_tmpdir"),
)
env.Execute(Mkdir(tmpdir))
# XXX: $AR does not support output argument for the extraction
# always switch into tmpdir when running commands
def run(cmd):
sys.stdout.write(" ".join(cmd))
sys.stdout.write("\n")
subprocess.check_call(cmd, cwd=tmpdir)
run([env.subst("$AR"), "x", source[0].abspath])
names = []
for infilename in os.listdir(tmpdir):
newname = infilename
if not infilename.endswith(".c.o"):
newname = infilename.replace(".o", ".c.o")
os.rename(os.path.join(tmpdir, infilename), os.path.join(tmpdir, newname))
names.append(newname)
pack_cmd = [env.subst("$AR"), "cr", target[0].abspath]
pack_cmd.extend(names)
run(pack_cmd)
env.Execute(Delete(tmpdir))
# Instead of replacing the file in-place, link with the patched version
libalgobsec_dir = os.path.join(libalgobsec_builder.src_dir, "esp8266")
target = env.File(
"libalgobsec.a", directory=env.subst("$BUILD_DIR/libalgobsec_patched")
)
source = env.File("libalgobsec.a", directory=libalgobsec_dir)
command = env.Command(target, source, process_archive)
patcher = env.Alias("patch-libalgobsec", command)
env.Append(LIBPATH=[target.get_dir()])
env.Append(LIBS=["algobsec"])
env.Depends("$BUILD_DIR/${PROGNAME}.elf", patcher)

+ 4
- 0
code/scripts/pio_main.py View File

@ -13,6 +13,7 @@ from espurna_utils import (
check_printsize, check_printsize,
remove_float_support, remove_float_support,
ldscripts_inject_libpath, ldscripts_inject_libpath,
libalgobsec_inject_patcher,
lwip_inject_patcher, lwip_inject_patcher,
app_inject_revision, app_inject_revision,
dummy_ets_printf, dummy_ets_printf,
@ -48,6 +49,9 @@ if "DISABLE_POSTMORTEM_STACKDUMP" in env["CPPFLAGS"]:
"$BUILD_DIR/FrameworkArduino/core_esp8266_postmortem.cpp.o", dummy_ets_printf "$BUILD_DIR/FrameworkArduino/core_esp8266_postmortem.cpp.o", dummy_ets_printf
) )
# place bsec's libalgobsec.a sections in the flash to avoid "section ‘.text' will not fit in region 'iram1_0_seg'" error
libalgobsec_inject_patcher(env)
# patch lwip1 sources conditionally: # patch lwip1 sources conditionally:
# https://github.com/xoseperez/espurna/issues/1610 # https://github.com/xoseperez/espurna/issues/1610
lwip_inject_patcher(env) lwip_inject_patcher(env)


+ 1
- 0
code/test/build/sensor.h View File

@ -3,6 +3,7 @@
#define BH1750_SUPPORT 1 #define BH1750_SUPPORT 1
#define BMP180_SUPPORT 1 #define BMP180_SUPPORT 1
#define BMX280_SUPPORT 1 #define BMX280_SUPPORT 1
#define BME680_SUPPORT 1
#define DALLAS_SUPPORT 1 #define DALLAS_SUPPORT 1
#define DHT_SUPPORT 1 #define DHT_SUPPORT 1
#define DIGITAL_SUPPORT 1 #define DIGITAL_SUPPORT 1


Loading…
Cancel
Save