05-11-2020 02:16 PM
Dear all,
I am trying to integrate a Bosch BME680 sensor running on the precompiled, closed source "BSEC" library as a custom sensor for ESPHome for an ESP32 for getting an IAQ reading instead of the resistance reading only. In other words, I do not want to use the ESPHome-driver for the sensor but the precompiled Bosch BSEC library wrapped in a custom ESPHome sensor component.
However, I cannot get ESPHome to compile/link the libraries correctly. What am I missing here?
Thank you very much for getting me kickstarted with with the precompiled library on ESPHome.
Best regards,
Chris
Configuration YAML:
esphome:
name: multisensor_1
platform: ESP32
board: nodemcu-32s
includes:
- 'CE_BSEC.h'
libraries:
- 'BSEC Software Library'
i2c:
sda: 21
scl: 22
scan: True
id: id_i2c
sensor:
## this is the BOSCH Implementation
- platform: custom
lambda: |-
auto IAQSensor = new CE_BSEC();
App.register_component(IAQSensor);
return { IAQSensor->TSensor, IAQSensor->PSensor, IAQSensor->HSensor, IAQSensor->AQSensor };
sensors:
- name: "BME680 Temperature"
unit_of_measurement: 'V'
accuracy_decimals: 1
- name: "BME680 Pressure"
unit_of_measurement: 'hPa'
accuracy_decimals: 1
- name: "BME680 relative Humidity"
unit_of_measurement: '%'
accuracy_decimals: 1
- name: "BME680 IAQ"
accuracy_decimals: 1
Custom sensor CE_BSEC.h
#include "esphome.h"
#include "bsec.h"
class CE_BSEC : public PollingComponent, public Sensor {
public:
// constructor
Bsec iaqSensor;
Sensor *TSensor = new Sensor();
Sensor *PSensor = new Sensor();
Sensor *HSensor = new Sensor();
Sensor *AQSensor = new Sensor();
CE_BSEC() : PollingComponent(60000) { }
void setup() override {
// This will be called by App.setup()
iaqSensor.begin(0x77, Wire);
}
void update() override {
float Temp = iaqSensor.temperature;
TSensor->publish_state(Temp);
float Press = iaqSensor.pressure;
PSensor->publish_state(Press);
float Hum = iaqSensor.humidity;
HSensor->publish_state(Hum);
float Qual = iaqSensor.iaq;
AQSensor->publish_state(Qual);
}
};
Compiling .pioenvs/multisensor_1/src/main.cpp.o
Linking .pioenvs/multisensor_1/firmware.elf
.pioenvs/multisensor_1/lib0fa/libBSEC Software Library_ID6979.a(bsec.cpp.o):(.literal._ZN4Bsec11beginCommonEv+0x4): undefined reference to `bsec_init'
.pioenvs/multisensor_1/lib0fa/libBSEC Software Library_ID6979.a(bsec.cpp.o):(.literal._ZN4Bsec11beginCommonEv+0x8): undefined reference to `bsec_get_version'
.pioenvs/multisensor_1/lib0fa/libBSEC Software Library_ID6979.a(bsec.cpp.o): In function `Bsec::beginCommon()':
/config/multisensor_1/.piolibdeps/multisensor_1/BSEC Software Library_ID6979/src/bsec.cpp:257: undefined reference to `bsec_init'
.pioenvs/multisensor_1/lib0fa/libBSEC Software Library_ID6979.a(bsec.cpp.o): In function `Bsec::getVersion()':
/config/multisensor_1/.piolibdeps/multisensor_1/BSEC Software Library_ID6979/src/bsec.cpp:257: undefined reference to `bsec_get_version'
collect2: error: ld returned 1 exit status
*** [.pioenvs/multisensor_1/firmware.elf] Error 1
Solved! Go to Solution.
08-31-2020 12:54 PM
Hi,
Can you please share some more details about your working solution?
I'm stuck with ESPhome and BSEC library as I don't get any other values than 25 from iaqSensor.iaq.
Thanks in advance,
Andreas
09-05-2020 11:19 AM
I am using the BME680 connected in I2C with ESP32 with ESPHOME and BSEC library installed.
But I have the same problems.
I can read temperature, humidity, pressure and gas resistance, but the IAQ is always 25 and the accuracy of the IAQ is 0.
Any suggestions ?
Thank
09-07-2020 05:58 PM - edited 09-07-2020 05:59 PM
In case it helps anyone this is my setup which I've had running without issue for a while now on a couple of ESP8266 wemos d1 minis.
bsecsensor.h
#include <esphome.h>
#include <bsec.h>
#include <Wire.h>
#include <EEPROM.h>
const uint8_t bsec_config_iaq[] = {
#include "config/generic_33v_3s_28d/bsec_iaq.txt"
};
#define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) // 360 minutes - 4 times a day
static const char *TAG = "bsec";
class BSECSensor : public Component {
private:
Bsec iaqSensor;
bsec_library_return_t bsecStatus = BSEC_OK;
int8_t bme680Status = BME680_OK;
uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE] = {0};
uint16_t stateUpdateCounter = 0;
uint8_t sensorPushNum = 0;
bool checkIaqSensorStatus(void) {
if (iaqSensor.status != BSEC_OK && iaqSensor.status != bsecStatus) {
if (iaqSensor.status < BSEC_OK) {
ESP_LOGE(TAG, "BSEC error code: %d", iaqSensor.status);
status_set_error();
} else {
ESP_LOGW(TAG, "BSEC warning code: %d", iaqSensor.status);
status_set_warning();
}
}
if (iaqSensor.bme680Status != BME680_OK && iaqSensor.bme680Status != bme680Status) {
if (iaqSensor.bme680Status < BME680_OK) {
ESP_LOGE(TAG, "BME680 error code: %d", iaqSensor.bme680Status);
status_set_error();
} else {
ESP_LOGW(TAG, "BME680 warning code: %d", iaqSensor.bme680Status);
status_set_warning();
}
}
bsecStatus = iaqSensor.status;
bme680Status = iaqSensor.bme680Status;
if (bsecStatus < BSEC_OK || bme680Status < BME680_OK) {
return false;
}
status_clear_error();
status_clear_warning();
return true;
}
void loadState(void) {
if (EEPROM.read(0) == BSEC_MAX_STATE_BLOB_SIZE) {
// Existing state in EEPROM
ESP_LOGI(TAG, "Reading state from EEPROM");
for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE; i++) {
bsecState[i] = EEPROM.read(i + 1);
}
iaqSensor.setState(bsecState);
checkIaqSensorStatus();
// Don't immediately update after initial load
stateUpdateCounter++;
} else {
// Erase the EEPROM with zeroes
ESP_LOGI(TAG, "Erasing EEPROM");
for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE + 1; i++) {
EEPROM.write(i, 0);
}
EEPROM.commit();
}
}
void updateState(void) {
if (
// First state update when IAQ accuracy is >= 3
(stateUpdateCounter == 0 && iaqSensor.staticIaqAccuracy >= 3)
// Then update every STATE_SAVE_PERIOD minutes
|| ((stateUpdateCounter * STATE_SAVE_PERIOD) < millis())
) {
stateUpdateCounter++;
} else {
return;
}
iaqSensor.getState(bsecState);
if (!checkIaqSensorStatus()) {
return;
}
ESP_LOGI(TAG, "Writing state to EEPROM");
for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE ; i++) {
EEPROM.write(i + 1, bsecState[i]);
}
EEPROM.write(0, BSEC_MAX_STATE_BLOB_SIZE);
EEPROM.commit();
}
public:
Sensor *temperature = new Sensor();
Sensor *humidity = new Sensor();
Sensor *pressure = new Sensor();
Sensor *gasResistance = new Sensor();
Sensor *staticIaq = new Sensor();
Sensor *staticIaqAccuracy = new Sensor();
Sensor *co2Equivalent = new Sensor();
Sensor *breathVocEquivalent = new Sensor();
void setup() override {
EEPROM.begin(BSEC_MAX_STATE_BLOB_SIZE + 1);
iaqSensor.begin(BME680_I2C_ADDR_PRIMARY, Wire);
ESP_LOGI(
TAG, "BSEC Version: %d.%d.%d.%d",
iaqSensor.version.major, iaqSensor.version.minor,
iaqSensor.version.major_bugfix, iaqSensor.version.minor_bugfix
);
iaqSensor.setConfig(bsec_config_iaq);
// Load state from EEPROM
loadState();
// Subscribe to sensor values
bsec_virtual_sensor_t sensorList[7] = {
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
BSEC_OUTPUT_RAW_PRESSURE,
BSEC_OUTPUT_RAW_GAS,
BSEC_OUTPUT_STATIC_IAQ,
BSEC_OUTPUT_CO2_EQUIVALENT,
BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
};
iaqSensor.updateSubscription(sensorList, 7, BSEC_SAMPLE_RATE_LP);
}
void setTemperatureOffset(float temp) {
iaqSensor.setTemperatureOffset(temp);
}
void loop() override {
if (checkIaqSensorStatus() && iaqSensor.run()) {
sensorPushNum = 1;
updateState();
}
// In order not to block here, spread the sensor state pushes
// across subsequent calls otherwise we end up with API disconnects
if (sensorPushNum > 0 && sensorPushNum <= {
switch (sensorPushNum++) {
case 1: temperature->publish_state(iaqSensor.temperature); break;
case 2: humidity->publish_state(iaqSensor.humidity); break;
case 3: pressure->publish_state(iaqSensor.pressure); break;
case 4: gasResistance->publish_state(iaqSensor.gasResistance); break;
case 5: staticIaq->publish_state(iaqSensor.staticIaq); break;
case 6: staticIaqAccuracy->publish_state(iaqSensor.staticIaqAccuracy); break;
case 7: co2Equivalent->publish_state(iaqSensor.co2Equivalent); break;
case 8: breathVocEquivalent->publish_state(iaqSensor.breathVocEquivalent); break;
}
}
}
};
.bsecsensor.yaml
Note: I have to specifiy a specific patched arduino version due to the ESP8266 memory layout per the library readme. If you're going to use this please fork the Arduino repo and modify the platform_packages directive accordingly as I can't guarantuee what state my copy will be in long term. I imagine on an ESP32 it would "just work" with no need specify any arduino options.
esphome:
name: ${location_id}_climate
platform: ESP8266
board: d1_mini
includes:
- bsecsensor.h
libraries:
- 'BSEC Software Library'
arduino_version: '2.5.2'
platformio_options:
# Use patched arduino to modify BSEC library memory location
# https://github.com/BoschSensortec/BSEC-Arduino-library#4-additional-core-specific-modifications
platform_packages: framework-arduinoespressif8266 @ https://github.com/trvrnrth/Arduino.git
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: ${location} Climate Sensor
password: !secret ap_password
captive_portal:
logger:
level: INFO
esp8266_store_log_strings_in_flash: False
api:
ota:
status_led:
pin:
number: 2
inverted: True
i2c:
sda: 4
scl: 5
scan: False
sensor:
- platform: custom
lambda: |-
auto sensor = new BSECSensor();
sensor->setTemperatureOffset(${temp_offset});
App.register_component(sensor);
return {
sensor->temperature,
sensor->humidity,
sensor->pressure,
sensor->gasResistance,
sensor->staticIaq,
sensor->staticIaqAccuracy,
sensor->co2Equivalent,
sensor->breathVocEquivalent,
};
sensors:
- name: ${location} Temperature
unit_of_measurement: '°C'
accuracy_decimals: 1
icon: mdi:thermometer
filters:
- median
- name: ${location} Humidity
unit_of_measurement: '%'
accuracy_decimals: 1
icon: mdi:water-percent
filters:
- median
- name: ${location} Pressure
unit_of_measurement: 'hPa'
icon: mdi:gauge
filters:
- median
- name: ${location} Gas Resistance
unit_of_measurement: 'Ω'
icon: mdi:gas-cylinder
filters:
- median
- name: ${location} Static IAQ
unit_of_measurement: IAQ
accuracy_decimals: 1
icon: mdi:gauge
filters:
- median
- id: ${location_id}_raw_static_iaq_accuracy
internal: True
on_value:
- text_sensor.template.publish:
id: ${location_id}_static_iaq_accuracy
state: !lambda |-
if (x == 0) return "Stabilizing";
if (x == 1) return "Uncertain";
if (x == 2) return "Calibrating";
if (x == 3) return "Calibrated";
return "Invalid";
- name: ${location} CO2 Equivalent
unit_of_measurement: 'ppm'
accuracy_decimals: 1
icon: mdi:test-tube
filters:
- median
- name: ${location} Breath VOC Equivalent
unit_of_measurement: 'ppm'
accuracy_decimals: 1
icon: mdi:test-tube
filters:
- median
text_sensor:
- platform: template
id: ${location_id}_static_iaq_accuracy
name: ${location} Static IAQ Accuracy
icon: mdi:checkbox-marked-circle-outline
test_climate.yaml
substitutions:
location_id: test
location: Test
temp_offset: '0'
<<: !include .bsecsensor.yaml
09-07-2020 11:41 PM
Thank you very much trvrnrth, function !!!! 😀
I got some errors, but I was able to continue.
There is an error in this line
if (sensorPushNum > 0 && sensorPushNum <=
I think there is a missing value after <=
Another mistake is here
- text_sensor.template.publish:
here I get: Unable to find action with the name 'text_sensor.template.publish'.
But in any case I found a way to get it running.
So again thank you.
09-08-2020 06:52 PM - edited 09-08-2020 06:55 PM
Good to hear it's working for you!
There seems to be some emoji detection or something mangling things. The line in error should read:
if (sensorPushNum > 0 && sensorPushNum <= 8 ) {
Weird that the text sensor publish isn't working for you as that's being used as detailed in the docs. You might want to double check your editor hasn't messed up the indentation there.