diff options
-rw-r--r-- | src/app/bme680-bsec/Kconfig | 6 | ||||
-rw-r--r-- | src/app/bme680-bsec/Makefile.inc | 0 | ||||
-rw-r--r-- | src/app/bme680-bsec/main.cc | 272 |
3 files changed, 278 insertions, 0 deletions
diff --git a/src/app/bme680-bsec/Kconfig b/src/app/bme680-bsec/Kconfig new file mode 100644 index 0000000..6c60688 --- /dev/null +++ b/src/app/bme680-bsec/Kconfig @@ -0,0 +1,6 @@ +# Copyright 2020 Daniel Friesel +# +# SPDX-License-Identifier: CC0-1.0 + +prompt "BME680 BSEC logger" +depends on driver_bme680_bsec && meta_driver_uptime && loop && !wakeup diff --git a/src/app/bme680-bsec/Makefile.inc b/src/app/bme680-bsec/Makefile.inc new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/app/bme680-bsec/Makefile.inc diff --git a/src/app/bme680-bsec/main.cc b/src/app/bme680-bsec/main.cc new file mode 100644 index 0000000..2474fa3 --- /dev/null +++ b/src/app/bme680-bsec/main.cc @@ -0,0 +1,272 @@ +/* + * Copyright 2020 Daniel Friesel + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include "arch.h" +#include "driver/gpio.h" +#include "driver/stdout.h" +#include "driver/uptime.h" + +#if defined(CONFIG_meta_driver_hardware_i2c) +#include "driver/i2c.h" +#elif defined(CONFIG_driver_softi2c) +#include "driver/soft_i2c.h" +#endif + +#include "driver/bme680.h" +#include "driver/bme680_util.h" +#include "driver/bme680-bsec-armv6/bsec_interface.h" + +const char* accuracy[] = {"(unreliable)", "(calibration required)", "(auto-trim in progress)", ""}; + +void loop(void) +{ + static bsec_bme_settings_t sensor_settings; + + struct bme680_field_data data; + bsec_input_t bsec_inputs[BSEC_MAX_PHYSICAL_SENSOR]; + bsec_output_t bsec_outputs[BSEC_NUMBER_OUTPUTS]; + + uint8_t num_bsec_inputs = 0; + uint8_t num_bsec_outputs = BSEC_NUMBER_OUTPUTS; + + int64_t now = uptime.get_us() * 1000; + uint32_t now_s = uptime.get_s(); + + /* + * BSEC expects the application to observe precise timing constraints. + * After each call to bsec_sensor_control, sensor_settings.next_call is set + * to the nanosecond timestamp of the next call. Significant violations + * cause bsec_sensor_control to return a BSEC_W_SC_CALL_TIMING_VIOLATION + * warning. + */ + + if (now < sensor_settings.next_call) { + if (sensor_settings.next_call - now < 1000000000) { + // less than one second -> sleep + arch.delay_us((sensor_settings.next_call - now) / 1000); + now = uptime.get_us() * 1000; + } else { + // more than one second -> next loop() call is sufficient + return; + } + } + + /* + * Retrieve sensor configuration from BSEC. In our case, it's fairly simple: + * all virtual sensors are set to the same sample rate, so we expect + * physical sensor readings to be the same for each BSEC call. However, + * if we disable virtual sensors, or change their sample rate, this is + * no longer the case. + * Also, bsec_sensor_control controls sensor_settings.next_call, so we + * need to call it anyways. + */ + bsec_library_return_t status = bsec_sensor_control(now, &sensor_settings); + + if (status < 0) { + kout << "bsec_sensor_control error: " << status << endl; + return; + } + if (status > 0) { + kout << "bsec_sensor_control warning: " << status << endl; + } + + /* + * bsec_sensor_control tells us whether it needs new sensor data or + * not. If so: configure the sensor as indicated and perform a measurement. + */ + if (sensor_settings.trigger_measurement) { + bme680.tph_sett.os_hum = sensor_settings.humidity_oversampling; + bme680.tph_sett.os_pres = sensor_settings.pressure_oversampling; + bme680.tph_sett.os_temp = sensor_settings.temperature_oversampling; + bme680.gas_sett.run_gas = sensor_settings.run_gas; + bme680.gas_sett.heatr_temp = sensor_settings.heater_temperature; + bme680.gas_sett.heatr_dur = sensor_settings.heating_duration; + + bme680.power_mode = BME680_FORCED_MODE; + bme680.setSensorSettings(BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_GAS_SENSOR_SEL); + + bme680.setSensorMode(); + + /* + * TODO recent versions of the bme680 open-source driver are able to + * calculate the required delay. + */ + arch.delay_ms(250); + + do { + arch.delay_ms(5); + bme680.getSensorMode(); + } while (bme680.power_mode == BME680_FORCED_MODE); + + if (sensor_settings.process_data) { + bme680.getSensorData(&data); + if (data.status & BME680_NEW_DATA_MSK) { + if (sensor_settings.process_data & BSEC_PROCESS_TEMPERATURE) { + bsec_inputs[num_bsec_inputs].sensor_id = BSEC_INPUT_TEMPERATURE; + bsec_inputs[num_bsec_inputs].signal = data.temperature / 100.0f; + bsec_inputs[num_bsec_inputs].time_stamp = now; + num_bsec_inputs++; + } + if (sensor_settings.process_data & BSEC_PROCESS_HUMIDITY) { + bsec_inputs[num_bsec_inputs].sensor_id = BSEC_INPUT_HUMIDITY; + bsec_inputs[num_bsec_inputs].signal = data.humidity / 1000.0f; + bsec_inputs[num_bsec_inputs].time_stamp = now; + num_bsec_inputs++; + } + if (sensor_settings.process_data & BSEC_PROCESS_PRESSURE) { + bsec_inputs[num_bsec_inputs].sensor_id = BSEC_INPUT_PRESSURE; + bsec_inputs[num_bsec_inputs].signal = data.pressure; + bsec_inputs[num_bsec_inputs].time_stamp = now; + num_bsec_inputs++; + } + if (sensor_settings.process_data & BSEC_PROCESS_GAS) { + bsec_inputs[num_bsec_inputs].sensor_id = BSEC_INPUT_GASRESISTOR; + bsec_inputs[num_bsec_inputs].signal = data.gas_resistance; + bsec_inputs[num_bsec_inputs].time_stamp = now; + num_bsec_inputs++; + } + } + } + + if (num_bsec_inputs > 0) { + status = bsec_do_steps(bsec_inputs, num_bsec_inputs, bsec_outputs, &num_bsec_outputs); + + if (status < 0) { + kout << "bsec_do_steps error: " << status << endl; + return; + } + if (status > 0) { + kout << "bsec_do_steps warning: " << status << endl; + } + + for (uint8_t i = 0; i < num_bsec_outputs; i++) { + switch (bsec_outputs[i].sensor_id) { + case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE: + kout << now_s << " BME680 temperature " << bsec_outputs[i].signal << " °c" << endl; + break; + case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY: + kout << now_s << " BME680 humidity " << bsec_outputs[i].signal << " %" << endl; + break; + case BSEC_OUTPUT_RAW_PRESSURE: + kout << now_s << " BME680 pressure " << bsec_outputs[i].signal / 100 << " hPa" << endl; + break; + case BSEC_OUTPUT_RAW_GAS: + kout << now_s << " BME680 gas resistance " << bsec_outputs[i].signal << " Ω" << endl; + break; + case BSEC_OUTPUT_IAQ: + if (bsec_outputs[i].accuracy > 0) { + kout << now_s << " BME680 IAQ: " << bsec_outputs[i].signal << " " << accuracy[bsec_outputs[i].accuracy] << endl; + } + break; + case BSEC_OUTPUT_STABILIZATION_STATUS: + if (bsec_outputs[i].signal < 1) { + kout << now_s << " BME680 IAQ initial stabilization in progress" << endl; + } + break; + case BSEC_OUTPUT_RUN_IN_STATUS: + if (bsec_outputs[i].signal < 1) { + kout << now_s << " BME680 IAQ power-on stabilization in progress" << endl; + } + break; + default: + continue; + } + } + kout << endl; + } + } +} + +int main(void) +{ + + arch.setup(); + gpio.setup(); + kout.setup(); + +#if defined(CONFIG_meta_driver_i2c) + while (i2c.setup() != 0) { + kout << "I2C setup FAILED" << endl; + arch.delay_ms(1000); + } + kout << "I2C setup OK" << endl; +#endif + + // Set up (open-source) BME680 driver + bme680.intf = BME680_I2C_INTF; + bme680.read = bme680_i2c_read; + bme680.write = bme680_i2c_write; + bme680.delay_ms = bme680_delay_ms; + + int8_t bme680_status = bme680.init(); + while (bme680_status != 0) { + kout << "BME680 init failed: " << (uint8_t)bme680_status << endl; + arch.delay_ms(1000); + } + kout << "BME680 init OK" << endl; + + // Initialize proprietary BSEC library + bsec_library_return_t bsec_status = bsec_init(); + while (bsec_status != BSEC_OK) { + kout << "BSEC init failed: " << bsec_status << endl; + arch.delay_ms(1000); + } + kout << "BSEC init OK" << endl; + + /* + * Output configuration. The BME680 BSEC library supports several virtual + * sensors such as raw temperature, compensated temperature, or IAQ. Each + * virtual sensor is calculated based on past observations and the + * TPH+Gas readings obtained from the BME680 sinsor. + * + * Here, we are interested in seven different types of readings. + */ + bsec_sensor_configuration_t virtual_sensors[7]; + unsigned char n_virtual_sensors = 7; + + /* + * bsec_update_subscription writes required sensor settings to the + * sensor_configs array. We're not interested in them at the moment. + */ + bsec_sensor_configuration_t sensor_configs[BSEC_MAX_PHYSICAL_SENSOR]; + unsigned char n_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR; + + /* + * Low Power mode -> 1/3 Hz (i.e., one sample every three seconds). + */ + float sample_rate = BSEC_SAMPLE_RATE_LP; + + /* + * We're interested in the following readings. + * See bsec_virtual_sensor_t definition in bsec_datatypes for a list of + * supported virtual sensor types ("sensor_ids"). + */ + virtual_sensors[0].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE; + virtual_sensors[0].sample_rate = sample_rate; + virtual_sensors[1].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY; + virtual_sensors[1].sample_rate = sample_rate; + virtual_sensors[2].sensor_id = BSEC_OUTPUT_RAW_PRESSURE; + virtual_sensors[2].sample_rate = sample_rate; + virtual_sensors[3].sensor_id = BSEC_OUTPUT_RAW_GAS; + virtual_sensors[3].sample_rate = sample_rate; + virtual_sensors[4].sensor_id = BSEC_OUTPUT_IAQ; + virtual_sensors[4].sample_rate = sample_rate; + virtual_sensors[5].sensor_id = BSEC_OUTPUT_STABILIZATION_STATUS; + virtual_sensors[5].sample_rate = sample_rate; + virtual_sensors[6].sensor_id = BSEC_OUTPUT_RUN_IN_STATUS; + virtual_sensors[6].sample_rate = sample_rate; + + bsec_status = bsec_update_subscription(virtual_sensors, n_virtual_sensors, sensor_configs, &n_sensor_settings); + + while (bsec_status != BSEC_OK) { + kout << "bsec_update_subscription error: " << bsec_status << endl; + arch.delay_ms(1000); + } + kout << "bsec_update_subscription OK" << endl; + + arch.idle_loop(); + + return 0; +} |