summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/app/bme680-bsec/Kconfig6
-rw-r--r--src/app/bme680-bsec/Makefile.inc0
-rw-r--r--src/app/bme680-bsec/main.cc272
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;
+}