/* * 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; }