Merge pull request #233 from cyberman54/development-bme680-2
BME680 integration refactored
This commit is contained in:
commit
5bdc354a07
@ -3,30 +3,52 @@
|
|||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
#include "bsec_integration.h"
|
|
||||||
#include "irqhandler.h"
|
#include "irqhandler.h"
|
||||||
|
#include "../lib/Bosch-BSEC/src/bsec.h"
|
||||||
extern const uint8_t bsec_config_iaq[454];
|
|
||||||
|
|
||||||
extern bmeStatus_t
|
extern bmeStatus_t
|
||||||
bme_status; // Make struct for storing gps data globally available
|
bme_status; // Make struct for storing gps data globally available
|
||||||
extern TaskHandle_t BmeTask;
|
extern TaskHandle_t BmeTask;
|
||||||
|
|
||||||
|
// --- Bosch BSEC library configuration ---
|
||||||
|
// 3,3V supply voltage; 3s max time between sensor_control calls; 4 days
|
||||||
|
// calibration. Change this const if not applicable for your application (see
|
||||||
|
// BME680 datasheet)
|
||||||
|
const uint8_t bsec_config_iaq[454] = {
|
||||||
|
1, 7, 4, 1, 61, 0, 0, 0, 0, 0, 0, 0, 174, 1, 0,
|
||||||
|
0, 48, 0, 1, 0, 137, 65, 0, 63, 205, 204, 204, 62, 0, 0,
|
||||||
|
64, 63, 205, 204, 204, 62, 0, 0, 225, 68, 0, 192, 168, 71, 64,
|
||||||
|
49, 119, 76, 0, 0, 0, 0, 0, 80, 5, 95, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 28, 0, 2, 0, 0, 244, 1, 225, 0, 25, 0,
|
||||||
|
0, 128, 64, 0, 0, 32, 65, 144, 1, 0, 0, 112, 65, 0, 0,
|
||||||
|
0, 63, 16, 0, 3, 0, 10, 215, 163, 60, 10, 215, 35, 59, 10,
|
||||||
|
215, 35, 59, 9, 0, 5, 0, 0, 0, 0, 0, 1, 88, 0, 9,
|
||||||
|
0, 229, 208, 34, 62, 0, 0, 0, 0, 0, 0, 0, 0, 218, 27,
|
||||||
|
156, 62, 225, 11, 67, 64, 0, 0, 160, 64, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 94, 75, 72, 189, 93, 254, 159, 64, 66, 62, 160, 191,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 33, 31, 180, 190, 138, 176, 97,
|
||||||
|
64, 65, 241, 99, 190, 0, 0, 0, 0, 0, 0, 0, 0, 167, 121,
|
||||||
|
71, 61, 165, 189, 41, 192, 184, 30, 189, 64, 12, 0, 10, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 229, 0, 254, 0, 2, 1, 5, 48,
|
||||||
|
117, 100, 0, 44, 1, 112, 23, 151, 7, 132, 3, 197, 0, 92, 4,
|
||||||
|
144, 1, 64, 1, 64, 1, 144, 1, 48, 117, 48, 117, 48, 117, 48,
|
||||||
|
117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117, 48, 117, 100, 0,
|
||||||
|
100, 0, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 100, 0, 48,
|
||||||
|
117, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117,
|
||||||
|
100, 0, 100, 0, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44,
|
||||||
|
1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1,
|
||||||
|
44, 1, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8,
|
||||||
|
7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7,
|
||||||
|
112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112,
|
||||||
|
23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 220, 5, 220, 5, 220, 5, 255, 255, 255,
|
||||||
|
255, 255, 255, 220, 5, 220, 5, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 44, 1, 0, 0, 0, 0,
|
||||||
|
239, 79, 0, 0};
|
||||||
|
|
||||||
int bme_init();
|
int bme_init();
|
||||||
void bme_loop(void *pvParameters);
|
void bme_loop(void *pvParameters);
|
||||||
int8_t i2c_read(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data,
|
int checkIaqSensorStatus(void);
|
||||||
uint16_t len);
|
|
||||||
int8_t i2c_write(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data,
|
|
||||||
uint16_t len);
|
|
||||||
void output_ready(int64_t timestamp, float iaq, uint8_t iaq_accuracy,
|
|
||||||
float temperature, float humidity, float pressure,
|
|
||||||
float raw_temperature, float raw_humidity, float gas,
|
|
||||||
bsec_library_return_t bsec_status, float static_iaq,
|
|
||||||
float co2_equivalent, float breath_voc_equivalent);
|
|
||||||
uint32_t state_load(uint8_t *state_buffer, uint32_t n_buffer);
|
|
||||||
void state_save(const uint8_t *state_buffer, uint32_t length);
|
|
||||||
uint32_t config_load(uint8_t *config_buffer, uint32_t n_buffer);
|
|
||||||
void user_delay_ms(uint32_t period);
|
|
||||||
int64_t get_timestamp_us();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -89,6 +89,7 @@ extern uint8_t volatile channel; // wifi channel rotation counter
|
|||||||
extern uint16_t volatile macs_total, macs_wifi, macs_ble,
|
extern uint16_t volatile macs_total, macs_wifi, macs_ble,
|
||||||
batt_voltage; // display values
|
batt_voltage; // display values
|
||||||
extern hw_timer_t *channelSwitch, *sendCycle, *displaytimer;
|
extern hw_timer_t *channelSwitch, *sendCycle, *displaytimer;
|
||||||
|
extern SemaphoreHandle_t I2Caccess;
|
||||||
|
|
||||||
extern std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs;
|
extern std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs;
|
||||||
extern std::array<uint64_t, 0xff>::iterator it;
|
extern std::array<uint64_t, 0xff>::iterator it;
|
||||||
|
@ -1,559 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 Robert Bosch. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Disclaimer
|
|
||||||
*
|
|
||||||
* Common:
|
|
||||||
* Bosch Sensortec products are developed for the consumer goods industry. They may only be used
|
|
||||||
* within the parameters of the respective valid product data sheet. Bosch Sensortec products are
|
|
||||||
* provided with the express understanding that there is no warranty of fitness for a particular purpose.
|
|
||||||
* They are not fit for use in life-sustaining, safety or security sensitive systems or any system or device
|
|
||||||
* that may lead to bodily harm or property damage if the system or device malfunctions. In addition,
|
|
||||||
* Bosch Sensortec products are not fit for use in products which interact with motor vehicle systems.
|
|
||||||
* The resale and/or use of products are at the purchasers own risk and his own responsibility. The
|
|
||||||
* examination of fitness for the intended use is the sole responsibility of the Purchaser.
|
|
||||||
*
|
|
||||||
* The purchaser shall indemnify Bosch Sensortec from all third party claims, including any claims for
|
|
||||||
* incidental, or consequential damages, arising from any product use not covered by the parameters of
|
|
||||||
* the respective valid product data sheet or not approved by Bosch Sensortec and reimburse Bosch
|
|
||||||
* Sensortec for all costs in connection with such claims.
|
|
||||||
*
|
|
||||||
* The purchaser must monitor the market for the purchased products, particularly with regard to
|
|
||||||
* product safety and inform Bosch Sensortec without delay of all security relevant incidents.
|
|
||||||
*
|
|
||||||
* Engineering Samples are marked with an asterisk (*) or (e). Samples may vary from the valid
|
|
||||||
* technical specifications of the product series. They are therefore not intended or fit for resale to third
|
|
||||||
* parties or for use in end products. Their sole purpose is internal client testing. The testing of an
|
|
||||||
* engineering sample may in no way replace the testing of a product series. Bosch Sensortec
|
|
||||||
* assumes no liability for the use of engineering samples. By accepting the engineering samples, the
|
|
||||||
* Purchaser agrees to indemnify Bosch Sensortec from all claims arising from the use of engineering
|
|
||||||
* samples.
|
|
||||||
*
|
|
||||||
* Special:
|
|
||||||
* This software module (hereinafter called "Software") and any information on application-sheets
|
|
||||||
* (hereinafter called "Information") is provided free of charge for the sole purpose to support your
|
|
||||||
* application work. The Software and Information is subject to the following terms and conditions:
|
|
||||||
*
|
|
||||||
* The Software is specifically designed for the exclusive use for Bosch Sensortec products by
|
|
||||||
* personnel who have special experience and training. Do not use this Software if you do not have the
|
|
||||||
* proper experience or training.
|
|
||||||
*
|
|
||||||
* This Software package is provided `` as is `` and without any expressed or implied warranties,
|
|
||||||
* including without limitation, the implied warranties of merchantability and fitness for a particular
|
|
||||||
* purpose.
|
|
||||||
*
|
|
||||||
* Bosch Sensortec and their representatives and agents deny any liability for the functional impairment
|
|
||||||
* of this Software in terms of fitness, performance and safety. Bosch Sensortec and their
|
|
||||||
* representatives and agents shall not be liable for any direct or indirect damages or injury, except as
|
|
||||||
* otherwise stipulated in mandatory applicable law.
|
|
||||||
*
|
|
||||||
* The Information provided is believed to be accurate and reliable. Bosch Sensortec assumes no
|
|
||||||
* responsibility for the consequences of use of such Information nor for any infringement of patents or
|
|
||||||
* other rights of third parties which may result from its use. No license is granted by implication or
|
|
||||||
* otherwise under any patent or patent rights of Bosch. Specifications mentioned in the Information are
|
|
||||||
* subject to change without notice.
|
|
||||||
*
|
|
||||||
* It is not allowed to deliver the source code of the Software to any third party without permission of
|
|
||||||
* Bosch Sensortec.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* @file bsec_integration.c
|
|
||||||
*
|
|
||||||
* @brief
|
|
||||||
* Private part of the example for using of BSEC library.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* @addtogroup bsec_examples BSEC Examples
|
|
||||||
* @brief BSEC usage examples
|
|
||||||
* @{*/
|
|
||||||
|
|
||||||
/**********************************************************************************************************************/
|
|
||||||
/* header files */
|
|
||||||
/**********************************************************************************************************************/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "bsec_integration.h"
|
|
||||||
|
|
||||||
/**********************************************************************************************************************/
|
|
||||||
/* local macro definitions */
|
|
||||||
/**********************************************************************************************************************/
|
|
||||||
|
|
||||||
#define NUM_USED_OUTPUTS 8
|
|
||||||
|
|
||||||
/**********************************************************************************************************************/
|
|
||||||
/* global variable declarations */
|
|
||||||
/**********************************************************************************************************************/
|
|
||||||
|
|
||||||
/* Global sensor APIs data structure */
|
|
||||||
static struct bme680_dev bme680_g;
|
|
||||||
|
|
||||||
/* Global temperature offset to be subtracted */
|
|
||||||
static float bme680_temperature_offset_g = 0.0f;
|
|
||||||
|
|
||||||
/**********************************************************************************************************************/
|
|
||||||
/* functions */
|
|
||||||
/**********************************************************************************************************************/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief Virtual sensor subscription
|
|
||||||
* Please call this function before processing of data using bsec_do_steps function
|
|
||||||
*
|
|
||||||
* @param[in] sample_rate mode to be used (either BSEC_SAMPLE_RATE_ULP or BSEC_SAMPLE_RATE_LP)
|
|
||||||
*
|
|
||||||
* @return subscription result, zero when successful
|
|
||||||
*/
|
|
||||||
static bsec_library_return_t bme680_bsec_update_subscription(float sample_rate)
|
|
||||||
{
|
|
||||||
bsec_sensor_configuration_t requested_virtual_sensors[NUM_USED_OUTPUTS];
|
|
||||||
uint8_t n_requested_virtual_sensors = NUM_USED_OUTPUTS;
|
|
||||||
|
|
||||||
bsec_sensor_configuration_t required_sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
|
|
||||||
uint8_t n_required_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
|
|
||||||
|
|
||||||
bsec_library_return_t status = BSEC_OK;
|
|
||||||
|
|
||||||
/* note: Virtual sensors as desired to be added here */
|
|
||||||
requested_virtual_sensors[0].sensor_id = BSEC_OUTPUT_IAQ;
|
|
||||||
requested_virtual_sensors[0].sample_rate = sample_rate;
|
|
||||||
requested_virtual_sensors[1].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE;
|
|
||||||
requested_virtual_sensors[1].sample_rate = sample_rate;
|
|
||||||
requested_virtual_sensors[2].sensor_id = BSEC_OUTPUT_RAW_PRESSURE;
|
|
||||||
requested_virtual_sensors[2].sample_rate = sample_rate;
|
|
||||||
requested_virtual_sensors[3].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY;
|
|
||||||
requested_virtual_sensors[3].sample_rate = sample_rate;
|
|
||||||
requested_virtual_sensors[4].sensor_id = BSEC_OUTPUT_RAW_GAS;
|
|
||||||
requested_virtual_sensors[4].sample_rate = sample_rate;
|
|
||||||
requested_virtual_sensors[5].sensor_id = BSEC_OUTPUT_RAW_TEMPERATURE;
|
|
||||||
requested_virtual_sensors[5].sample_rate = sample_rate;
|
|
||||||
requested_virtual_sensors[6].sensor_id = BSEC_OUTPUT_RAW_HUMIDITY;
|
|
||||||
requested_virtual_sensors[6].sample_rate = sample_rate;
|
|
||||||
requested_virtual_sensors[7].sensor_id = BSEC_OUTPUT_STATIC_IAQ;
|
|
||||||
requested_virtual_sensors[7].sample_rate = sample_rate;
|
|
||||||
|
|
||||||
/* Call bsec_update_subscription() to enable/disable the requested virtual sensors */
|
|
||||||
status = bsec_update_subscription(requested_virtual_sensors, n_requested_virtual_sensors, required_sensor_settings,
|
|
||||||
&n_required_sensor_settings);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief Initialize the BME680 sensor and the BSEC library
|
|
||||||
*
|
|
||||||
* @param[in] sample_rate mode to be used (either BSEC_SAMPLE_RATE_ULP or BSEC_SAMPLE_RATE_LP)
|
|
||||||
* @param[in] temperature_offset device-specific temperature offset (due to self-heating)
|
|
||||||
* @param[in] bus_write pointer to the bus writing function
|
|
||||||
* @param[in] bus_read pointer to the bus reading function
|
|
||||||
* @param[in] sleep pointer to the system specific sleep function
|
|
||||||
* @param[in] state_load pointer to the system-specific state load function
|
|
||||||
* @param[in] config_load pointer to the system-specific config load function
|
|
||||||
*
|
|
||||||
* @return zero if successful, negative otherwise
|
|
||||||
*/
|
|
||||||
return_values_init bsec_iot_init(float sample_rate, float temperature_offset, bme680_com_fptr_t bus_write,
|
|
||||||
bme680_com_fptr_t bus_read, sleep_fct sleep, state_load_fct state_load, config_load_fct config_load)
|
|
||||||
{
|
|
||||||
return_values_init ret = {BME680_OK, BSEC_OK};
|
|
||||||
bsec_library_return_t bsec_status = BSEC_OK;
|
|
||||||
|
|
||||||
uint8_t bsec_state[BSEC_MAX_PROPERTY_BLOB_SIZE] = {0};
|
|
||||||
uint8_t bsec_config[BSEC_MAX_PROPERTY_BLOB_SIZE] = {0};
|
|
||||||
uint8_t work_buffer[BSEC_MAX_PROPERTY_BLOB_SIZE] = {0};
|
|
||||||
int bsec_state_len, bsec_config_len;
|
|
||||||
|
|
||||||
/* Fixed I2C configuration */
|
|
||||||
bme680_g.dev_id = BME680_I2C_ADDR_PRIMARY;
|
|
||||||
bme680_g.intf = BME680_I2C_INTF;
|
|
||||||
/* User configurable I2C configuration */
|
|
||||||
bme680_g.write = bus_write;
|
|
||||||
bme680_g.read = bus_read;
|
|
||||||
bme680_g.delay_ms = sleep;
|
|
||||||
|
|
||||||
/* Initialize BME680 API */
|
|
||||||
ret.bme680_status = bme680_init(&bme680_g);
|
|
||||||
if (ret.bme680_status != BME680_OK)
|
|
||||||
{
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize BSEC library */
|
|
||||||
ret.bsec_status = bsec_init();
|
|
||||||
if (ret.bsec_status != BSEC_OK)
|
|
||||||
{
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load library config, if available */
|
|
||||||
bsec_config_len = config_load(bsec_config, sizeof(bsec_config));
|
|
||||||
if (bsec_config_len != 0)
|
|
||||||
{
|
|
||||||
ret.bsec_status = bsec_set_configuration(bsec_config, bsec_config_len, work_buffer, sizeof(work_buffer));
|
|
||||||
if (ret.bsec_status != BSEC_OK)
|
|
||||||
{
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load previous library state, if available */
|
|
||||||
bsec_state_len = state_load(bsec_state, sizeof(bsec_state));
|
|
||||||
if (bsec_state_len != 0)
|
|
||||||
{
|
|
||||||
ret.bsec_status = bsec_set_state(bsec_state, bsec_state_len, work_buffer, sizeof(work_buffer));
|
|
||||||
if (ret.bsec_status != BSEC_OK)
|
|
||||||
{
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set temperature offset */
|
|
||||||
bme680_temperature_offset_g = temperature_offset;
|
|
||||||
|
|
||||||
/* Call to the function which sets the library with subscription information */
|
|
||||||
ret.bsec_status = bme680_bsec_update_subscription(sample_rate);
|
|
||||||
if (ret.bsec_status != BSEC_OK)
|
|
||||||
{
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief Trigger the measurement based on sensor settings
|
|
||||||
*
|
|
||||||
* @param[in] sensor_settings settings of the BME680 sensor adopted by sensor control function
|
|
||||||
* @param[in] sleep pointer to the system specific sleep function
|
|
||||||
*
|
|
||||||
* @return none
|
|
||||||
*/
|
|
||||||
static void bme680_bsec_trigger_measurement(bsec_bme_settings_t *sensor_settings, sleep_fct sleep)
|
|
||||||
{
|
|
||||||
uint16_t meas_period;
|
|
||||||
uint8_t set_required_settings;
|
|
||||||
int8_t bme680_status = BME680_OK;
|
|
||||||
|
|
||||||
/* Check if a forced-mode measurement should be triggered now */
|
|
||||||
if (sensor_settings->trigger_measurement)
|
|
||||||
{
|
|
||||||
/* Set sensor configuration */
|
|
||||||
|
|
||||||
bme680_g.tph_sett.os_hum = sensor_settings->humidity_oversampling;
|
|
||||||
bme680_g.tph_sett.os_pres = sensor_settings->pressure_oversampling;
|
|
||||||
bme680_g.tph_sett.os_temp = sensor_settings->temperature_oversampling;
|
|
||||||
bme680_g.gas_sett.run_gas = sensor_settings->run_gas;
|
|
||||||
bme680_g.gas_sett.heatr_temp = sensor_settings->heater_temperature; /* degree Celsius */
|
|
||||||
bme680_g.gas_sett.heatr_dur = sensor_settings->heating_duration; /* milliseconds */
|
|
||||||
|
|
||||||
/* Select the power mode */
|
|
||||||
/* Must be set before writing the sensor configuration */
|
|
||||||
bme680_g.power_mode = BME680_FORCED_MODE;
|
|
||||||
/* Set the required sensor settings needed */
|
|
||||||
set_required_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_GAS_SENSOR_SEL;
|
|
||||||
|
|
||||||
/* Set the desired sensor configuration */
|
|
||||||
bme680_status = bme680_set_sensor_settings(set_required_settings, &bme680_g);
|
|
||||||
|
|
||||||
/* Set power mode as forced mode and trigger forced mode measurement */
|
|
||||||
bme680_status = bme680_set_sensor_mode(&bme680_g);
|
|
||||||
|
|
||||||
/* Get the total measurement duration so as to sleep or wait till the measurement is complete */
|
|
||||||
bme680_get_profile_dur(&meas_period, &bme680_g);
|
|
||||||
|
|
||||||
/* Delay till the measurement is ready. Timestamp resolution in ms */
|
|
||||||
sleep((uint32_t)meas_period);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Call the API to get current operation mode of the sensor */
|
|
||||||
bme680_status = bme680_get_sensor_mode(&bme680_g);
|
|
||||||
/* When the measurement is completed and data is ready for reading, the sensor must be in BME680_SLEEP_MODE.
|
|
||||||
* Read operation mode to check whether measurement is completely done and wait until the sensor is no more
|
|
||||||
* in BME680_FORCED_MODE. */
|
|
||||||
while (bme680_g.power_mode == BME680_FORCED_MODE)
|
|
||||||
{
|
|
||||||
/* sleep for 5 ms */
|
|
||||||
sleep(5);
|
|
||||||
bme680_status = bme680_get_sensor_mode(&bme680_g);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief Read the data from registers and populate the inputs structure to be passed to do_steps function
|
|
||||||
*
|
|
||||||
* @param[in] time_stamp_trigger settings of the sensor returned from sensor control function
|
|
||||||
* @param[in] inputs input structure containing the information on sensors to be passed to do_steps
|
|
||||||
* @param[in] num_bsec_inputs number of inputs to be passed to do_steps
|
|
||||||
* @param[in] bsec_process_data process data variable returned from sensor_control
|
|
||||||
*
|
|
||||||
* @return none
|
|
||||||
*/
|
|
||||||
static void bme680_bsec_read_data(int64_t time_stamp_trigger, bsec_input_t *inputs, uint8_t *num_bsec_inputs,
|
|
||||||
int32_t bsec_process_data)
|
|
||||||
{
|
|
||||||
static struct bme680_field_data data;
|
|
||||||
int8_t bme680_status = BME680_OK;
|
|
||||||
|
|
||||||
/* We only have to read data if the previous call the bsec_sensor_control() actually asked for it */
|
|
||||||
if (bsec_process_data)
|
|
||||||
{
|
|
||||||
bme680_status = bme680_get_sensor_data(&data, &bme680_g);
|
|
||||||
|
|
||||||
if (data.status & BME680_NEW_DATA_MSK)
|
|
||||||
{
|
|
||||||
/* Pressure to be processed by BSEC */
|
|
||||||
if (bsec_process_data & BSEC_PROCESS_PRESSURE)
|
|
||||||
{
|
|
||||||
/* Place presssure sample into input struct */
|
|
||||||
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_PRESSURE;
|
|
||||||
inputs[*num_bsec_inputs].signal = data.pressure;
|
|
||||||
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
|
|
||||||
(*num_bsec_inputs)++;
|
|
||||||
}
|
|
||||||
/* Temperature to be processed by BSEC */
|
|
||||||
if (bsec_process_data & BSEC_PROCESS_TEMPERATURE)
|
|
||||||
{
|
|
||||||
/* Place temperature sample into input struct */
|
|
||||||
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
|
|
||||||
#ifdef BME680_FLOAT_POINT_COMPENSATION
|
|
||||||
inputs[*num_bsec_inputs].signal = data.temperature;
|
|
||||||
#else
|
|
||||||
inputs[*num_bsec_inputs].signal = data.temperature / 100.0f;
|
|
||||||
#endif
|
|
||||||
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
|
|
||||||
(*num_bsec_inputs)++;
|
|
||||||
|
|
||||||
/* Also add optional heatsource input which will be subtracted from the temperature reading to
|
|
||||||
* compensate for device-specific self-heating (supported in BSEC IAQ solution)*/
|
|
||||||
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
|
|
||||||
inputs[*num_bsec_inputs].signal = bme680_temperature_offset_g;
|
|
||||||
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
|
|
||||||
(*num_bsec_inputs)++;
|
|
||||||
}
|
|
||||||
/* Humidity to be processed by BSEC */
|
|
||||||
if (bsec_process_data & BSEC_PROCESS_HUMIDITY)
|
|
||||||
{
|
|
||||||
/* Place humidity sample into input struct */
|
|
||||||
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
|
|
||||||
#ifdef BME680_FLOAT_POINT_COMPENSATION
|
|
||||||
inputs[*num_bsec_inputs].signal = data.humidity;
|
|
||||||
#else
|
|
||||||
inputs[*num_bsec_inputs].signal = data.humidity / 1000.0f;
|
|
||||||
#endif
|
|
||||||
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
|
|
||||||
(*num_bsec_inputs)++;
|
|
||||||
}
|
|
||||||
/* Gas to be processed by BSEC */
|
|
||||||
if (bsec_process_data & BSEC_PROCESS_GAS)
|
|
||||||
{
|
|
||||||
/* Check whether gas_valid flag is set */
|
|
||||||
if(data.status & BME680_GASM_VALID_MSK)
|
|
||||||
{
|
|
||||||
/* Place sample into input struct */
|
|
||||||
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
|
|
||||||
inputs[*num_bsec_inputs].signal = data.gas_resistance;
|
|
||||||
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
|
|
||||||
(*num_bsec_inputs)++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief This function is written to process the sensor data for the requested virtual sensors
|
|
||||||
*
|
|
||||||
* @param[in] bsec_inputs input structure containing the information on sensors to be passed to do_steps
|
|
||||||
* @param[in] num_bsec_inputs number of inputs to be passed to do_steps
|
|
||||||
* @param[in] output_ready pointer to the function processing obtained BSEC outputs
|
|
||||||
*
|
|
||||||
* @return none
|
|
||||||
*/
|
|
||||||
static void bme680_bsec_process_data(bsec_input_t *bsec_inputs, uint8_t num_bsec_inputs, output_ready_fct output_ready)
|
|
||||||
{
|
|
||||||
/* Output buffer set to the maximum virtual sensor outputs supported */
|
|
||||||
bsec_output_t bsec_outputs[BSEC_NUMBER_OUTPUTS];
|
|
||||||
uint8_t num_bsec_outputs = 0;
|
|
||||||
uint8_t index = 0;
|
|
||||||
|
|
||||||
bsec_library_return_t bsec_status = BSEC_OK;
|
|
||||||
|
|
||||||
int64_t timestamp = 0;
|
|
||||||
float iaq = 0.0f;
|
|
||||||
uint8_t iaq_accuracy = 0;
|
|
||||||
float temp = 0.0f;
|
|
||||||
float raw_temp = 0.0f;
|
|
||||||
float raw_pressure = 0.0f;
|
|
||||||
float humidity = 0.0f;
|
|
||||||
float raw_humidity = 0.0f;
|
|
||||||
float raw_gas = 0.0f;
|
|
||||||
float static_iaq = 0.0f;
|
|
||||||
uint8_t static_iaq_accuracy = 0;
|
|
||||||
float co2_equivalent = 0.0f;
|
|
||||||
uint8_t co2_accuracy = 0;
|
|
||||||
float breath_voc_equivalent = 0.0f;
|
|
||||||
uint8_t breath_voc_accuracy = 0;
|
|
||||||
float comp_gas_value = 0.0f;
|
|
||||||
uint8_t comp_gas_accuracy = 0;
|
|
||||||
float gas_percentage = 0.0f;
|
|
||||||
uint8_t gas_percentage_acccuracy = 0;
|
|
||||||
|
|
||||||
/* Check if something should be processed by BSEC */
|
|
||||||
if (num_bsec_inputs > 0)
|
|
||||||
{
|
|
||||||
/* Set number of outputs to the size of the allocated buffer */
|
|
||||||
/* BSEC_NUMBER_OUTPUTS to be defined */
|
|
||||||
num_bsec_outputs = BSEC_NUMBER_OUTPUTS;
|
|
||||||
|
|
||||||
/* Perform processing of the data by BSEC
|
|
||||||
Note:
|
|
||||||
* The number of outputs you get depends on what you asked for during bsec_update_subscription(). This is
|
|
||||||
handled under bme680_bsec_update_subscription() function in this example file.
|
|
||||||
* The number of actual outputs that are returned is written to num_bsec_outputs. */
|
|
||||||
bsec_status = bsec_do_steps(bsec_inputs, num_bsec_inputs, bsec_outputs, &num_bsec_outputs);
|
|
||||||
|
|
||||||
/* Iterate through the outputs and extract the relevant ones. */
|
|
||||||
for (index = 0; index < num_bsec_outputs; index++)
|
|
||||||
{
|
|
||||||
switch (bsec_outputs[index].sensor_id)
|
|
||||||
{
|
|
||||||
case BSEC_OUTPUT_IAQ:
|
|
||||||
iaq = bsec_outputs[index].signal;
|
|
||||||
iaq_accuracy = bsec_outputs[index].accuracy;
|
|
||||||
break;
|
|
||||||
case BSEC_OUTPUT_STATIC_IAQ:
|
|
||||||
static_iaq = bsec_outputs[index].signal;
|
|
||||||
static_iaq_accuracy = bsec_outputs[index].accuracy;
|
|
||||||
break;
|
|
||||||
case BSEC_OUTPUT_CO2_EQUIVALENT:
|
|
||||||
co2_equivalent = bsec_outputs[index].signal;
|
|
||||||
co2_accuracy = bsec_outputs[index].accuracy;
|
|
||||||
break;
|
|
||||||
case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
|
|
||||||
breath_voc_equivalent = bsec_outputs[index].signal;
|
|
||||||
breath_voc_accuracy = bsec_outputs[index].accuracy;
|
|
||||||
break;
|
|
||||||
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
|
|
||||||
temp = bsec_outputs[index].signal;
|
|
||||||
break;
|
|
||||||
case BSEC_OUTPUT_RAW_PRESSURE:
|
|
||||||
raw_pressure = bsec_outputs[index].signal;
|
|
||||||
break;
|
|
||||||
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
|
|
||||||
humidity = bsec_outputs[index].signal;
|
|
||||||
break;
|
|
||||||
case BSEC_OUTPUT_RAW_GAS:
|
|
||||||
raw_gas = bsec_outputs[index].signal;
|
|
||||||
break;
|
|
||||||
case BSEC_OUTPUT_RAW_TEMPERATURE:
|
|
||||||
raw_temp = bsec_outputs[index].signal;
|
|
||||||
break;
|
|
||||||
case BSEC_OUTPUT_RAW_HUMIDITY:
|
|
||||||
raw_humidity = bsec_outputs[index].signal;
|
|
||||||
break;
|
|
||||||
case BSEC_OUTPUT_COMPENSATED_GAS:
|
|
||||||
comp_gas_value = bsec_outputs[index].signal;
|
|
||||||
comp_gas_accuracy = bsec_outputs[index].accuracy;
|
|
||||||
break;
|
|
||||||
case BSEC_OUTPUT_GAS_PERCENTAGE:
|
|
||||||
gas_percentage = bsec_outputs[index].signal;
|
|
||||||
gas_percentage_acccuracy = bsec_outputs[index].accuracy;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assume that all the returned timestamps are the same */
|
|
||||||
timestamp = bsec_outputs[index].time_stamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pass the extracted outputs to the user provided output_ready() function. */
|
|
||||||
output_ready(timestamp, iaq, iaq_accuracy, temp, humidity, raw_pressure, raw_temp,
|
|
||||||
raw_humidity, raw_gas, bsec_status, static_iaq, co2_equivalent, breath_voc_equivalent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief Runs the main (endless) loop that queries sensor settings, applies them, and processes the measured data
|
|
||||||
*
|
|
||||||
* @param[in] sleep pointer to the system specific sleep function
|
|
||||||
* @param[in] get_timestamp_us pointer to the system specific timestamp derivation function
|
|
||||||
* @param[in] output_ready pointer to the function processing obtained BSEC outputs
|
|
||||||
* @param[in] state_save pointer to the system-specific state save function
|
|
||||||
* @param[in] save_intvl interval at which BSEC state should be saved (in samples)
|
|
||||||
*
|
|
||||||
* @return none
|
|
||||||
*/
|
|
||||||
void bsec_iot_loop(sleep_fct sleep, get_timestamp_us_fct get_timestamp_us, output_ready_fct output_ready,
|
|
||||||
state_save_fct state_save, uint32_t save_intvl)
|
|
||||||
{
|
|
||||||
/* Timestamp variables */
|
|
||||||
int64_t time_stamp = 0;
|
|
||||||
int64_t time_stamp_interval_ms = 0;
|
|
||||||
|
|
||||||
/* Allocate enough memory for up to BSEC_MAX_PHYSICAL_SENSOR physical inputs*/
|
|
||||||
bsec_input_t bsec_inputs[BSEC_MAX_PHYSICAL_SENSOR];
|
|
||||||
|
|
||||||
/* Number of inputs to BSEC */
|
|
||||||
uint8_t num_bsec_inputs = 0;
|
|
||||||
|
|
||||||
/* BSEC sensor settings struct */
|
|
||||||
bsec_bme_settings_t sensor_settings;
|
|
||||||
|
|
||||||
/* Save state variables */
|
|
||||||
uint8_t bsec_state[BSEC_MAX_STATE_BLOB_SIZE];
|
|
||||||
uint8_t work_buffer[BSEC_MAX_STATE_BLOB_SIZE];
|
|
||||||
uint32_t bsec_state_len = 0;
|
|
||||||
uint32_t n_samples = 0;
|
|
||||||
|
|
||||||
bsec_library_return_t bsec_status = BSEC_OK;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/* get the timestamp in nanoseconds before calling bsec_sensor_control() */
|
|
||||||
time_stamp = get_timestamp_us() * 1000;
|
|
||||||
|
|
||||||
/* Retrieve sensor settings to be used in this time instant by calling bsec_sensor_control */
|
|
||||||
bsec_sensor_control(time_stamp, &sensor_settings);
|
|
||||||
|
|
||||||
/* Trigger a measurement if necessary */
|
|
||||||
bme680_bsec_trigger_measurement(&sensor_settings, sleep);
|
|
||||||
|
|
||||||
/* Read data from last measurement */
|
|
||||||
num_bsec_inputs = 0;
|
|
||||||
bme680_bsec_read_data(time_stamp, bsec_inputs, &num_bsec_inputs, sensor_settings.process_data);
|
|
||||||
|
|
||||||
/* Time to invoke BSEC to perform the actual processing */
|
|
||||||
bme680_bsec_process_data(bsec_inputs, num_bsec_inputs, output_ready);
|
|
||||||
|
|
||||||
/* Increment sample counter */
|
|
||||||
n_samples++;
|
|
||||||
|
|
||||||
/* Retrieve and store state if the passed save_intvl */
|
|
||||||
if (n_samples >= save_intvl)
|
|
||||||
{
|
|
||||||
bsec_status = bsec_get_state(0, bsec_state, sizeof(bsec_state), work_buffer, sizeof(work_buffer), &bsec_state_len);
|
|
||||||
if (bsec_status == BSEC_OK)
|
|
||||||
{
|
|
||||||
state_save(bsec_state, bsec_state_len);
|
|
||||||
}
|
|
||||||
n_samples = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Compute how long we can sleep until we need to call bsec_sensor_control() next */
|
|
||||||
/* Time_stamp is converted from microseconds to nanoseconds first and then the difference to milliseconds */
|
|
||||||
time_stamp_interval_ms = (sensor_settings.next_call - get_timestamp_us() * 1000) / 1000000;
|
|
||||||
if (time_stamp_interval_ms > 0)
|
|
||||||
{
|
|
||||||
sleep((uint32_t)time_stamp_interval_ms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! @}*/
|
|
||||||
|
|
@ -1,165 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 Robert Bosch. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Disclaimer
|
|
||||||
*
|
|
||||||
* Common:
|
|
||||||
* Bosch Sensortec products are developed for the consumer goods industry. They may only be used
|
|
||||||
* within the parameters of the respective valid product data sheet. Bosch Sensortec products are
|
|
||||||
* provided with the express understanding that there is no warranty of fitness for a particular purpose.
|
|
||||||
* They are not fit for use in life-sustaining, safety or security sensitive systems or any system or device
|
|
||||||
* that may lead to bodily harm or property damage if the system or device malfunctions. In addition,
|
|
||||||
* Bosch Sensortec products are not fit for use in products which interact with motor vehicle systems.
|
|
||||||
* The resale and/or use of products are at the purchasers own risk and his own responsibility. The
|
|
||||||
* examination of fitness for the intended use is the sole responsibility of the Purchaser.
|
|
||||||
*
|
|
||||||
* The purchaser shall indemnify Bosch Sensortec from all third party claims, including any claims for
|
|
||||||
* incidental, or consequential damages, arising from any product use not covered by the parameters of
|
|
||||||
* the respective valid product data sheet or not approved by Bosch Sensortec and reimburse Bosch
|
|
||||||
* Sensortec for all costs in connection with such claims.
|
|
||||||
*
|
|
||||||
* The purchaser must monitor the market for the purchased products, particularly with regard to
|
|
||||||
* product safety and inform Bosch Sensortec without delay of all security relevant incidents.
|
|
||||||
*
|
|
||||||
* Engineering Samples are marked with an asterisk (*) or (e). Samples may vary from the valid
|
|
||||||
* technical specifications of the product series. They are therefore not intended or fit for resale to third
|
|
||||||
* parties or for use in end products. Their sole purpose is internal client testing. The testing of an
|
|
||||||
* engineering sample may in no way replace the testing of a product series. Bosch Sensortec
|
|
||||||
* assumes no liability for the use of engineering samples. By accepting the engineering samples, the
|
|
||||||
* Purchaser agrees to indemnify Bosch Sensortec from all claims arising from the use of engineering
|
|
||||||
* samples.
|
|
||||||
*
|
|
||||||
* Special:
|
|
||||||
* This software module (hereinafter called "Software") and any information on application-sheets
|
|
||||||
* (hereinafter called "Information") is provided free of charge for the sole purpose to support your
|
|
||||||
* application work. The Software and Information is subject to the following terms and conditions:
|
|
||||||
*
|
|
||||||
* The Software is specifically designed for the exclusive use for Bosch Sensortec products by
|
|
||||||
* personnel who have special experience and training. Do not use this Software if you do not have the
|
|
||||||
* proper experience or training.
|
|
||||||
*
|
|
||||||
* This Software package is provided `` as is `` and without any expressed or implied warranties,
|
|
||||||
* including without limitation, the implied warranties of merchantability and fitness for a particular
|
|
||||||
* purpose.
|
|
||||||
*
|
|
||||||
* Bosch Sensortec and their representatives and agents deny any liability for the functional impairment
|
|
||||||
* of this Software in terms of fitness, performance and safety. Bosch Sensortec and their
|
|
||||||
* representatives and agents shall not be liable for any direct or indirect damages or injury, except as
|
|
||||||
* otherwise stipulated in mandatory applicable law.
|
|
||||||
*
|
|
||||||
* The Information provided is believed to be accurate and reliable. Bosch Sensortec assumes no
|
|
||||||
* responsibility for the consequences of use of such Information nor for any infringement of patents or
|
|
||||||
* other rights of third parties which may result from its use. No license is granted by implication or
|
|
||||||
* otherwise under any patent or patent rights of Bosch. Specifications mentioned in the Information are
|
|
||||||
* subject to change without notice.
|
|
||||||
*
|
|
||||||
* It is not allowed to deliver the source code of the Software to any third party without permission of
|
|
||||||
* Bosch Sensortec.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* @file bsec_integration.h
|
|
||||||
*
|
|
||||||
* @brief
|
|
||||||
* Contains BSEC integration API
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* @addtogroup bsec_examples BSEC Examples
|
|
||||||
* @brief BSEC usage examples
|
|
||||||
* @{*/
|
|
||||||
|
|
||||||
#ifndef __BSEC_INTEGRATION_H__
|
|
||||||
#define __BSEC_INTEGRATION_H__
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**********************************************************************************************************************/
|
|
||||||
/* header files */
|
|
||||||
/**********************************************************************************************************************/
|
|
||||||
|
|
||||||
/* Use the following bme680 driver: https://github.com/BoschSensortec/BME680_driver/releases/tag/bme680_v3.5.1 */
|
|
||||||
#include "bme680.h"
|
|
||||||
/* BSEC header files are available in the inc/ folder of the release package */
|
|
||||||
#include "bsec_interface.h"
|
|
||||||
#include "bsec_datatypes.h"
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************************************************************/
|
|
||||||
/* type definitions */
|
|
||||||
/**********************************************************************************************************************/
|
|
||||||
|
|
||||||
/* function pointer to the system specific sleep function */
|
|
||||||
typedef void (*sleep_fct)(uint32_t t_ms);
|
|
||||||
|
|
||||||
/* function pointer to the system specific timestamp derivation function */
|
|
||||||
typedef int64_t (*get_timestamp_us_fct)();
|
|
||||||
|
|
||||||
/* function pointer to the function processing obtained BSEC outputs */
|
|
||||||
typedef void (*output_ready_fct)(int64_t timestamp, float iaq, uint8_t iaq_accuracy, float temperature, float humidity,
|
|
||||||
float pressure, float raw_temperature, float raw_humidity, float gas, bsec_library_return_t bsec_status,
|
|
||||||
float static_iaq, float co2_equivalent, float breath_voc_equivalent);
|
|
||||||
|
|
||||||
/* function pointer to the function loading a previous BSEC state from NVM */
|
|
||||||
typedef uint32_t (*state_load_fct)(uint8_t *state_buffer, uint32_t n_buffer);
|
|
||||||
|
|
||||||
/* function pointer to the function saving BSEC state to NVM */
|
|
||||||
typedef void (*state_save_fct)(const uint8_t *state_buffer, uint32_t length);
|
|
||||||
|
|
||||||
/* function pointer to the function loading the BSEC configuration string from NVM */
|
|
||||||
typedef uint32_t (*config_load_fct)(uint8_t *state_buffer, uint32_t n_buffer);
|
|
||||||
|
|
||||||
/* structure definitions */
|
|
||||||
|
|
||||||
/* Structure with the return value from bsec_iot_init() */
|
|
||||||
typedef struct{
|
|
||||||
/*! Result of API execution status */
|
|
||||||
int8_t bme680_status;
|
|
||||||
/*! Result of BSEC library */
|
|
||||||
bsec_library_return_t bsec_status;
|
|
||||||
}return_values_init;
|
|
||||||
/**********************************************************************************************************************/
|
|
||||||
/* function declarations */
|
|
||||||
/**********************************************************************************************************************/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief Initialize the BME680 sensor and the BSEC library
|
|
||||||
*
|
|
||||||
* @param[in] sample_rate mode to be used (either BSEC_SAMPLE_RATE_ULP or BSEC_SAMPLE_RATE_LP)
|
|
||||||
* @param[in] temperature_offset device-specific temperature offset (due to self-heating)
|
|
||||||
* @param[in] bus_write pointer to the bus writing function
|
|
||||||
* @param[in] bus_read pointer to the bus reading function
|
|
||||||
* @param[in] sleep pointer to the system-specific sleep function
|
|
||||||
* @param[in] state_load pointer to the system-specific state load function
|
|
||||||
*
|
|
||||||
* @return zero if successful, negative otherwise
|
|
||||||
*/
|
|
||||||
return_values_init bsec_iot_init(float sample_rate, float temperature_offset, bme680_com_fptr_t bus_write, bme680_com_fptr_t bus_read,
|
|
||||||
sleep_fct sleep, state_load_fct state_load, config_load_fct config_load);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief Runs the main (endless) loop that queries sensor settings, applies them, and processes the measured data
|
|
||||||
*
|
|
||||||
* @param[in] sleep pointer to the system-specific sleep function
|
|
||||||
* @param[in] get_timestamp_us pointer to the system-specific timestamp derivation function
|
|
||||||
* @param[in] output_ready pointer to the function processing obtained BSEC outputs
|
|
||||||
* @param[in] state_save pointer to the system-specific state save function
|
|
||||||
* @param[in] save_intvl interval at which BSEC state should be saved (in samples)
|
|
||||||
*
|
|
||||||
* @return return_values_init struct with the result of the API and the BSEC library
|
|
||||||
*/
|
|
||||||
void bsec_iot_loop(sleep_fct sleep, get_timestamp_us_fct get_timestamp_us, output_ready_fct output_ready,
|
|
||||||
state_save_fct state_save, uint32_t save_intvl);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __BSEC_INTEGRATION_H__ */
|
|
||||||
|
|
||||||
/*! @}*/
|
|
||||||
|
|
@ -103,7 +103,7 @@
|
|||||||
/** BME680 configuration macros */
|
/** BME680 configuration macros */
|
||||||
/** Enable or un-comment the macro to provide floating point data output */
|
/** Enable or un-comment the macro to provide floating point data output */
|
||||||
#ifndef BME680_FLOAT_POINT_COMPENSATION
|
#ifndef BME680_FLOAT_POINT_COMPENSATION
|
||||||
/* #define BME680_FLOAT_POINT_COMPENSATION */
|
//#define BME680_FLOAT_POINT_COMPENSATION
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** BME680 General config */
|
/** BME680 General config */
|
498
lib/Bosch-BSEC/src/bsec.cpp
Normal file
498
lib/Bosch-BSEC/src/bsec.cpp
Normal file
@ -0,0 +1,498 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2017 - 2018 Bosch Sensortec GmbH
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Neither the name of the copyright holder nor the names of the
|
||||||
|
* 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 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
|
||||||
|
*
|
||||||
|
* The information provided is believed to be accurate and reliable.
|
||||||
|
* The copyright holder assumes no responsibility
|
||||||
|
* for the consequences of use
|
||||||
|
* of such information nor for any infringement of patents or
|
||||||
|
* other rights of third parties which may result from its use.
|
||||||
|
* No license is granted by implication or otherwise under any patent or
|
||||||
|
* patent rights of the copyright holder.
|
||||||
|
*
|
||||||
|
* @file bsec.cpp
|
||||||
|
* @date 31 Jan 2018
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "bsec.h"
|
||||||
|
|
||||||
|
TwoWire *Bsec::wireObj = NULL;
|
||||||
|
SPIClass *Bsec::spiObj = NULL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructor
|
||||||
|
*/
|
||||||
|
Bsec::Bsec() {
|
||||||
|
nextCall = 0;
|
||||||
|
version.major = 0;
|
||||||
|
version.minor = 0;
|
||||||
|
version.major_bugfix = 0;
|
||||||
|
version.minor_bugfix = 0;
|
||||||
|
millisOverflowCounter = 0;
|
||||||
|
lastTime = 0;
|
||||||
|
bme680Status = BME680_OK;
|
||||||
|
outputTimestamp = 0;
|
||||||
|
_tempOffset = 0.0f;
|
||||||
|
status = BSEC_OK;
|
||||||
|
zeroOutputs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to initialize the BSEC library and the BME680 sensor
|
||||||
|
*/
|
||||||
|
void Bsec::begin(uint8_t devId, enum bme680_intf intf, bme680_com_fptr_t read,
|
||||||
|
bme680_com_fptr_t write, bme680_delay_fptr_t idleTask) {
|
||||||
|
_bme680.dev_id = devId;
|
||||||
|
_bme680.intf = intf;
|
||||||
|
_bme680.read = read;
|
||||||
|
_bme680.write = write;
|
||||||
|
_bme680.delay_ms = idleTask;
|
||||||
|
_bme680.amb_temp = 25;
|
||||||
|
_bme680.power_mode = BME680_FORCED_MODE;
|
||||||
|
|
||||||
|
beginCommon();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to initialize the BSEC library and the BME680 sensor
|
||||||
|
*/
|
||||||
|
void Bsec::begin(uint8_t i2cAddr, TwoWire &i2c) {
|
||||||
|
_bme680.dev_id = i2cAddr;
|
||||||
|
_bme680.intf = BME680_I2C_INTF;
|
||||||
|
_bme680.read = Bsec::i2cRead;
|
||||||
|
_bme680.write = Bsec::i2cWrite;
|
||||||
|
_bme680.delay_ms = Bsec::delay_ms;
|
||||||
|
_bme680.amb_temp = 25;
|
||||||
|
_bme680.power_mode = BME680_FORCED_MODE;
|
||||||
|
|
||||||
|
Bsec::wireObj = &i2c;
|
||||||
|
Bsec::wireObj->begin();
|
||||||
|
|
||||||
|
beginCommon();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to initialize the BSEC library and the BME680 sensor
|
||||||
|
*/
|
||||||
|
void Bsec::begin(uint8_t chipSelect, SPIClass &spi) {
|
||||||
|
_bme680.dev_id = chipSelect;
|
||||||
|
_bme680.intf = BME680_SPI_INTF;
|
||||||
|
_bme680.read = Bsec::spiTransfer;
|
||||||
|
_bme680.write = Bsec::spiTransfer;
|
||||||
|
_bme680.delay_ms = Bsec::delay_ms;
|
||||||
|
_bme680.amb_temp = 25;
|
||||||
|
_bme680.power_mode = BME680_FORCED_MODE;
|
||||||
|
|
||||||
|
pinMode(chipSelect, OUTPUT);
|
||||||
|
digitalWrite(chipSelect, HIGH);
|
||||||
|
Bsec::spiObj = &spi;
|
||||||
|
Bsec::spiObj->begin();
|
||||||
|
|
||||||
|
beginCommon();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Common code for the begin function
|
||||||
|
*/
|
||||||
|
void Bsec::beginCommon(void) {
|
||||||
|
status = bsec_init();
|
||||||
|
|
||||||
|
getVersion();
|
||||||
|
|
||||||
|
bme680Status = bme680_init(&_bme680);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function that sets the desired sensors and the sample rates
|
||||||
|
*/
|
||||||
|
void Bsec::updateSubscription(bsec_virtual_sensor_t sensorList[],
|
||||||
|
uint8_t nSensors, float sampleRate) {
|
||||||
|
bsec_sensor_configuration_t virtualSensors[BSEC_NUMBER_OUTPUTS],
|
||||||
|
sensorSettings[BSEC_MAX_PHYSICAL_SENSOR];
|
||||||
|
uint8_t nVirtualSensors = 0, nSensorSettings = BSEC_MAX_PHYSICAL_SENSOR;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < nSensors; i++) {
|
||||||
|
virtualSensors[nVirtualSensors].sensor_id = sensorList[i];
|
||||||
|
virtualSensors[nVirtualSensors].sample_rate = sampleRate;
|
||||||
|
nVirtualSensors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = bsec_update_subscription(virtualSensors, nVirtualSensors,
|
||||||
|
sensorSettings, &nSensorSettings);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback from the user to trigger reading of data from the BME680,
|
||||||
|
* process and store outputs
|
||||||
|
*/
|
||||||
|
bool Bsec::run(void) {
|
||||||
|
bool newData = false;
|
||||||
|
/* Check if the time has arrived to call do_steps() */
|
||||||
|
int64_t callTimeMs = getTimeMs();
|
||||||
|
|
||||||
|
if (callTimeMs >= nextCall) {
|
||||||
|
|
||||||
|
bsec_bme_settings_t bme680Settings;
|
||||||
|
|
||||||
|
int64_t callTimeNs = callTimeMs * INT64_C(1000000);
|
||||||
|
|
||||||
|
status = bsec_sensor_control(callTimeNs, &bme680Settings);
|
||||||
|
if (status < BSEC_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
nextCall =
|
||||||
|
bme680Settings.next_call / INT64_C(1000000); // Convert from ns to ms
|
||||||
|
|
||||||
|
bme680Status = setBme680Config(bme680Settings);
|
||||||
|
if (bme680Status != BME680_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bme680Status = bme680_set_sensor_mode(&_bme680);
|
||||||
|
if (bme680Status != BME680_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for measurement to complete */
|
||||||
|
uint16_t meas_dur = 0;
|
||||||
|
|
||||||
|
bme680_get_profile_dur(&meas_dur, &_bme680);
|
||||||
|
delay_ms(meas_dur);
|
||||||
|
|
||||||
|
newData = readProcessData(callTimeNs, bme680Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to get the state of the algorithm to save to non-volatile
|
||||||
|
* memory
|
||||||
|
*/
|
||||||
|
void Bsec::getState(uint8_t *state) {
|
||||||
|
uint8_t workBuffer[BSEC_MAX_STATE_BLOB_SIZE];
|
||||||
|
uint32_t n_serialized_state = BSEC_MAX_STATE_BLOB_SIZE;
|
||||||
|
status = bsec_get_state(0, state, BSEC_MAX_STATE_BLOB_SIZE, workBuffer,
|
||||||
|
BSEC_MAX_STATE_BLOB_SIZE, &n_serialized_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to set the state of the algorithm from non-volatile memory
|
||||||
|
*/
|
||||||
|
void Bsec::setState(uint8_t *state) {
|
||||||
|
uint8_t workBuffer[BSEC_MAX_STATE_BLOB_SIZE];
|
||||||
|
|
||||||
|
status = bsec_set_state(state, BSEC_MAX_STATE_BLOB_SIZE, workBuffer,
|
||||||
|
BSEC_MAX_STATE_BLOB_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to set the configuration of the algorithm from memory
|
||||||
|
*/
|
||||||
|
void Bsec::setConfig(const uint8_t *state) {
|
||||||
|
uint8_t workBuffer[BSEC_MAX_PROPERTY_BLOB_SIZE];
|
||||||
|
|
||||||
|
status = bsec_set_configuration(state, BSEC_MAX_PROPERTY_BLOB_SIZE,
|
||||||
|
workBuffer, sizeof(workBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Private functions */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the version of the BSEC library
|
||||||
|
*/
|
||||||
|
void Bsec::getVersion(void) { bsec_get_version(&version); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read data from the BME680 and process it
|
||||||
|
*/
|
||||||
|
bool Bsec::readProcessData(int64_t currTimeNs,
|
||||||
|
bsec_bme_settings_t bme680Settings) {
|
||||||
|
bme680Status = bme680_get_sensor_data(&_data, &_bme680);
|
||||||
|
if (bme680Status != BME680_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR]; // Temp, Pres, Hum & Gas
|
||||||
|
uint8_t nInputs = 0, nOutputs = 0;
|
||||||
|
|
||||||
|
if (_data.status & BME680_NEW_DATA_MSK) {
|
||||||
|
if (bme680Settings.process_data & BSEC_PROCESS_TEMPERATURE) {
|
||||||
|
inputs[nInputs].sensor_id = BSEC_INPUT_TEMPERATURE;
|
||||||
|
#ifdef BME680_FLOAT_POINT_COMPENSATION
|
||||||
|
inputs[nInputs].signal = _data.temperature;
|
||||||
|
#else
|
||||||
|
inputs[nInputs].signal = _data.temperature / 100.0f;
|
||||||
|
#endif
|
||||||
|
inputs[nInputs].time_stamp = currTimeNs;
|
||||||
|
nInputs++;
|
||||||
|
/* Temperature offset from the real temperature due to external heat
|
||||||
|
* sources */
|
||||||
|
inputs[nInputs].sensor_id = BSEC_INPUT_HEATSOURCE;
|
||||||
|
inputs[nInputs].signal = _tempOffset;
|
||||||
|
inputs[nInputs].time_stamp = currTimeNs;
|
||||||
|
nInputs++;
|
||||||
|
}
|
||||||
|
if (bme680Settings.process_data & BSEC_PROCESS_HUMIDITY) {
|
||||||
|
inputs[nInputs].sensor_id = BSEC_INPUT_HUMIDITY;
|
||||||
|
#ifdef BME680_FLOAT_POINT_COMPENSATION
|
||||||
|
inputs[nInputs].signal = _data.humidity;
|
||||||
|
#else
|
||||||
|
inputs[nInputs].signal = _data.humidity / 1000.0f;
|
||||||
|
#endif
|
||||||
|
inputs[nInputs].time_stamp = currTimeNs;
|
||||||
|
nInputs++;
|
||||||
|
}
|
||||||
|
if (bme680Settings.process_data & BSEC_PROCESS_PRESSURE) {
|
||||||
|
inputs[nInputs].sensor_id = BSEC_INPUT_PRESSURE;
|
||||||
|
inputs[nInputs].signal = _data.pressure;
|
||||||
|
inputs[nInputs].time_stamp = currTimeNs;
|
||||||
|
nInputs++;
|
||||||
|
}
|
||||||
|
if (bme680Settings.process_data & BSEC_PROCESS_GAS) {
|
||||||
|
inputs[nInputs].sensor_id = BSEC_INPUT_GASRESISTOR;
|
||||||
|
inputs[nInputs].signal = _data.gas_resistance;
|
||||||
|
inputs[nInputs].time_stamp = currTimeNs;
|
||||||
|
nInputs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nInputs > 0) {
|
||||||
|
nOutputs = BSEC_NUMBER_OUTPUTS;
|
||||||
|
bsec_output_t _outputs[BSEC_NUMBER_OUTPUTS];
|
||||||
|
|
||||||
|
status = bsec_do_steps(inputs, nInputs, _outputs, &nOutputs);
|
||||||
|
if (status != BSEC_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
zeroOutputs();
|
||||||
|
|
||||||
|
if (nOutputs > 0) {
|
||||||
|
outputTimestamp =
|
||||||
|
_outputs[0].time_stamp / 1000000; // Convert from ns to ms
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < nOutputs; i++) {
|
||||||
|
switch (_outputs[i].sensor_id) {
|
||||||
|
case BSEC_OUTPUT_IAQ:
|
||||||
|
iaqEstimate = _outputs[i].signal;
|
||||||
|
iaqAccuracy = _outputs[i].accuracy;
|
||||||
|
break;
|
||||||
|
case BSEC_OUTPUT_STATIC_IAQ:
|
||||||
|
staticIaq = _outputs[i].signal;
|
||||||
|
staticIaqAccuracy = _outputs[i].accuracy;
|
||||||
|
break;
|
||||||
|
case BSEC_OUTPUT_CO2_EQUIVALENT:
|
||||||
|
co2Equivalent = _outputs[i].signal;
|
||||||
|
co2Accuracy = _outputs[i].accuracy;
|
||||||
|
break;
|
||||||
|
case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
|
||||||
|
breathVocEquivalent = _outputs[i].signal;
|
||||||
|
breathVocAccuracy = _outputs[i].accuracy;
|
||||||
|
break;
|
||||||
|
case BSEC_OUTPUT_RAW_TEMPERATURE:
|
||||||
|
rawTemperature = _outputs[i].signal;
|
||||||
|
break;
|
||||||
|
case BSEC_OUTPUT_RAW_PRESSURE:
|
||||||
|
pressure = _outputs[i].signal;
|
||||||
|
break;
|
||||||
|
case BSEC_OUTPUT_RAW_HUMIDITY:
|
||||||
|
rawHumidity = _outputs[i].signal;
|
||||||
|
break;
|
||||||
|
case BSEC_OUTPUT_RAW_GAS:
|
||||||
|
gasResistance = _outputs[i].signal;
|
||||||
|
break;
|
||||||
|
case BSEC_OUTPUT_STABILIZATION_STATUS:
|
||||||
|
stabStatus = _outputs[i].signal;
|
||||||
|
break;
|
||||||
|
case BSEC_OUTPUT_RUN_IN_STATUS:
|
||||||
|
runInStatus = _outputs[i].signal;
|
||||||
|
break;
|
||||||
|
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
|
||||||
|
temperature = _outputs[i].signal;
|
||||||
|
break;
|
||||||
|
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
|
||||||
|
humidity = _outputs[i].signal;
|
||||||
|
break;
|
||||||
|
case BSEC_OUTPUT_COMPENSATED_GAS:
|
||||||
|
compGasValue = _outputs[i].signal;
|
||||||
|
compGasAccuracy = _outputs[i].accuracy;
|
||||||
|
break;
|
||||||
|
case BSEC_OUTPUT_GAS_PERCENTAGE:
|
||||||
|
gasPercentage = _outputs[i].signal;
|
||||||
|
gasPercentageAcccuracy = _outputs[i].accuracy;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the BME680 sensor's configuration
|
||||||
|
*/
|
||||||
|
int8_t Bsec::setBme680Config(bsec_bme_settings_t bme680Settings) {
|
||||||
|
_bme680.gas_sett.run_gas = bme680Settings.run_gas;
|
||||||
|
_bme680.tph_sett.os_hum = bme680Settings.humidity_oversampling;
|
||||||
|
_bme680.tph_sett.os_temp = bme680Settings.temperature_oversampling;
|
||||||
|
_bme680.tph_sett.os_pres = bme680Settings.pressure_oversampling;
|
||||||
|
_bme680.gas_sett.heatr_temp = bme680Settings.heater_temperature;
|
||||||
|
_bme680.gas_sett.heatr_dur = bme680Settings.heating_duration;
|
||||||
|
uint16_t desired_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL |
|
||||||
|
BME680_FILTER_SEL | BME680_GAS_SENSOR_SEL;
|
||||||
|
return bme680_set_sensor_settings(desired_settings, &_bme680);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to zero the outputs
|
||||||
|
*/
|
||||||
|
void Bsec::zeroOutputs(void) {
|
||||||
|
temperature = 0.0f;
|
||||||
|
pressure = 0.0f;
|
||||||
|
humidity = 0.0f;
|
||||||
|
gasResistance = 0.0f;
|
||||||
|
rawTemperature = 0.0f;
|
||||||
|
rawHumidity = 0.0f;
|
||||||
|
stabStatus = 0.0f;
|
||||||
|
runInStatus = 0.0f;
|
||||||
|
iaqEstimate = 0.0f;
|
||||||
|
iaqAccuracy = 0;
|
||||||
|
staticIaq = 0.0f;
|
||||||
|
staticIaqAccuracy = 0;
|
||||||
|
co2Equivalent = 0.0f;
|
||||||
|
co2Accuracy = 0;
|
||||||
|
breathVocEquivalent = 0.0f;
|
||||||
|
breathVocAccuracy = 0;
|
||||||
|
compGasValue = 0.0f;
|
||||||
|
compGasAccuracy = 0;
|
||||||
|
gasPercentage = 0.0f;
|
||||||
|
gasPercentageAcccuracy = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to calculate an int64_t timestamp in milliseconds
|
||||||
|
*/
|
||||||
|
int64_t Bsec::getTimeMs(void) {
|
||||||
|
int64_t timeMs = millis();
|
||||||
|
|
||||||
|
if (lastTime > timeMs) { // An overflow occured
|
||||||
|
lastTime = timeMs;
|
||||||
|
millisOverflowCounter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return timeMs + (millisOverflowCounter * 0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Task that delays for a ms period of time
|
||||||
|
*/
|
||||||
|
void Bsec::delay_ms(uint32_t period) {
|
||||||
|
// Wait for a period amount of ms
|
||||||
|
// The system may simply idle, sleep or even perform background tasks
|
||||||
|
delay(period);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Callback function for reading registers over I2C
|
||||||
|
*/
|
||||||
|
int8_t Bsec::i2cRead(uint8_t devId, uint8_t regAddr, uint8_t *regData,
|
||||||
|
uint16_t length) {
|
||||||
|
uint16_t i;
|
||||||
|
int8_t rslt = 0;
|
||||||
|
if (Bsec::wireObj) {
|
||||||
|
Bsec::wireObj->beginTransmission(devId);
|
||||||
|
Bsec::wireObj->write(regAddr);
|
||||||
|
rslt = Bsec::wireObj->endTransmission();
|
||||||
|
Bsec::wireObj->requestFrom((int)devId, (int)length);
|
||||||
|
for (i = 0; (i < length) && Bsec::wireObj->available(); i++) {
|
||||||
|
regData[i] = Bsec::wireObj->read();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rslt = -1;
|
||||||
|
}
|
||||||
|
return rslt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback function for writing registers over I2C
|
||||||
|
*/
|
||||||
|
int8_t Bsec::i2cWrite(uint8_t devId, uint8_t regAddr, uint8_t *regData,
|
||||||
|
uint16_t length) {
|
||||||
|
uint16_t i;
|
||||||
|
int8_t rslt = 0;
|
||||||
|
if (Bsec::wireObj) {
|
||||||
|
Bsec::wireObj->beginTransmission(devId);
|
||||||
|
Bsec::wireObj->write(regAddr);
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
Bsec::wireObj->write(regData[i]);
|
||||||
|
}
|
||||||
|
rslt = Bsec::wireObj->endTransmission();
|
||||||
|
} else {
|
||||||
|
rslt = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rslt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback function for reading and writing registers over SPI
|
||||||
|
*/
|
||||||
|
int8_t Bsec::spiTransfer(uint8_t devId, uint8_t regAddr, uint8_t *regData,
|
||||||
|
uint16_t length) {
|
||||||
|
int8_t rslt = 0;
|
||||||
|
if (Bsec::spiObj) {
|
||||||
|
Bsec::spiObj->beginTransaction(
|
||||||
|
SPISettings(4000000, MSBFIRST, SPI_MODE0)); // Can be upto 10MHz
|
||||||
|
|
||||||
|
digitalWrite(devId, LOW);
|
||||||
|
|
||||||
|
Bsec::spiObj->transfer(
|
||||||
|
regAddr); // Write the register address, ignore the return
|
||||||
|
for (uint16_t i = 0; i < length; i++)
|
||||||
|
regData[i] = Bsec::spiObj->transfer(regData[i]);
|
||||||
|
|
||||||
|
digitalWrite(devId, HIGH);
|
||||||
|
Bsec::spiObj->endTransaction();
|
||||||
|
} else {
|
||||||
|
rslt = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rslt;
|
||||||
|
;
|
||||||
|
}
|
230
lib/Bosch-BSEC/src/bsec.h
Normal file
230
lib/Bosch-BSEC/src/bsec.h
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2017 - 2018 Bosch Sensortec GmbH
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Neither the name of the copyright holder nor the names of the
|
||||||
|
* 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 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
|
||||||
|
*
|
||||||
|
* The information provided is believed to be accurate and reliable.
|
||||||
|
* The copyright holder assumes no responsibility
|
||||||
|
* for the consequences of use
|
||||||
|
* of such information nor for any infringement of patents or
|
||||||
|
* other rights of third parties which may result from its use.
|
||||||
|
* No license is granted by implication or otherwise under any patent or
|
||||||
|
* patent rights of the copyright holder.
|
||||||
|
*
|
||||||
|
* @file bsec.h
|
||||||
|
* @date 31 Jan 2018
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BSEC_CLASS_H
|
||||||
|
#define BSEC_CLASS_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "Wire.h"
|
||||||
|
#include "SPI.h"
|
||||||
|
#include "inc/bsec_datatypes.h"
|
||||||
|
#include "inc/bsec_interface.h"
|
||||||
|
#include "bme680/bme680.h"
|
||||||
|
|
||||||
|
/* BSEC class definition */
|
||||||
|
class Bsec
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/* Public variables */
|
||||||
|
bsec_version_t version; // Stores the version of the BSEC algorithm
|
||||||
|
int64_t nextCall; // Stores the time when the algorithm has to be called next in ms
|
||||||
|
int8_t bme680Status; // Placeholder for the BME680 driver's error codes
|
||||||
|
bsec_library_return_t status;
|
||||||
|
float iaqEstimate, rawTemperature, pressure, rawHumidity, gasResistance, stabStatus, runInStatus, temperature, humidity,
|
||||||
|
staticIaq, co2Equivalent, breathVocEquivalent, compGasValue, gasPercentage;
|
||||||
|
uint8_t iaqAccuracy, staticIaqAccuracy, co2Accuracy, breathVocAccuracy, compGasAccuracy, gasPercentageAcccuracy;
|
||||||
|
int64_t outputTimestamp; // Timestamp in ms of the output
|
||||||
|
static TwoWire *wireObj;
|
||||||
|
static SPIClass *spiObj;
|
||||||
|
|
||||||
|
/* Public APIs */
|
||||||
|
/**
|
||||||
|
* @brief Constructor
|
||||||
|
*/
|
||||||
|
Bsec();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to initialize the BSEC library and the BME680 sensor
|
||||||
|
* @param devId : Device identifier parameter for the read/write interface functions
|
||||||
|
* @param intf : Physical communication interface
|
||||||
|
* @param read : Pointer to the read function
|
||||||
|
* @param write : Pointer to the write function
|
||||||
|
* @param idleTask : Pointer to the idling task
|
||||||
|
*/
|
||||||
|
void begin(uint8_t devId, enum bme680_intf intf, bme680_com_fptr_t read, bme680_com_fptr_t write, bme680_delay_fptr_t idleTask);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to initialize the BSEC library and the BME680 sensor
|
||||||
|
* @param i2cAddr : I2C address
|
||||||
|
* @param i2c : Pointer to the TwoWire object
|
||||||
|
*/
|
||||||
|
void begin(uint8_t i2cAddr, TwoWire &i2c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to initialize the BSEC library and the BME680 sensor
|
||||||
|
* @param chipSelect : SPI chip select
|
||||||
|
* @param spi : Pointer to the SPIClass object
|
||||||
|
*/
|
||||||
|
void begin(uint8_t chipSelect, SPIClass &spi);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function that sets the desired sensors and the sample rates
|
||||||
|
* @param sensorList : The list of output sensors
|
||||||
|
* @param nSensors : Number of outputs requested
|
||||||
|
* @param sampleRate : The sample rate of requested sensors
|
||||||
|
*/
|
||||||
|
void updateSubscription(bsec_virtual_sensor_t sensorList[], uint8_t nSensors, float sampleRate = BSEC_SAMPLE_RATE_ULP);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback from the user to trigger reading of data from the BME680, process and store outputs
|
||||||
|
* @return true if there are new outputs. false otherwise
|
||||||
|
*/
|
||||||
|
bool run(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to get the state of the algorithm to save to non-volatile memory
|
||||||
|
* @param state : Pointer to a memory location that contains the state
|
||||||
|
*/
|
||||||
|
void getState(uint8_t *state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to set the state of the algorithm from non-volatile memory
|
||||||
|
* @param state : Pointer to a memory location that contains the state
|
||||||
|
*/
|
||||||
|
void setState(uint8_t *state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to set the configuration of the algorithm from memory
|
||||||
|
* @param state : Pointer to a memory location that contains the configuration
|
||||||
|
*/
|
||||||
|
void setConfig(const uint8_t *config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to set the temperature offset
|
||||||
|
* @param tempOffset : Temperature offset in degree Celsius
|
||||||
|
*/
|
||||||
|
void setTemperatureOffset(float tempOffset)
|
||||||
|
{
|
||||||
|
_tempOffset = tempOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to calculate an int64_t timestamp in milliseconds
|
||||||
|
*/
|
||||||
|
int64_t getTimeMs(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Task that delays for a ms period of time
|
||||||
|
* @param period : Period of time in ms
|
||||||
|
*/
|
||||||
|
static void delay_ms(uint32_t period);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback function for reading registers over I2C
|
||||||
|
* @param devId : Library agnostic parameter to identify the device to communicate with
|
||||||
|
* @param regAddr : Register address
|
||||||
|
* @param regData : Pointer to the array containing the data to be read
|
||||||
|
* @param length : Length of the array of data
|
||||||
|
* @return Zero for success, non-zero otherwise
|
||||||
|
*/
|
||||||
|
static int8_t i2cRead(uint8_t devId, uint8_t regAddr, uint8_t *regData, uint16_t length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback function for writing registers over I2C
|
||||||
|
* @param devId : Library agnostic parameter to identify the device to communicate with
|
||||||
|
* @param regAddr : Register address
|
||||||
|
* @param regData : Pointer to the array containing the data to be written
|
||||||
|
* @param length : Length of the array of data
|
||||||
|
* @return Zero for success, non-zero otherwise
|
||||||
|
*/
|
||||||
|
static int8_t i2cWrite(uint8_t devId, uint8_t regAddr, uint8_t *regData, uint16_t length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback function for reading and writing registers over SPI
|
||||||
|
* @param devId : Library agnostic parameter to identify the device to communicate with
|
||||||
|
* @param regAddr : Register address
|
||||||
|
* @param regData : Pointer to the array containing the data to be read or written
|
||||||
|
* @param length : Length of the array of data
|
||||||
|
* @return Zero for success, non-zero otherwise
|
||||||
|
*/
|
||||||
|
static int8_t spiTransfer(uint8_t devId, uint8_t regAddr, uint8_t *regData, uint16_t length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* Private variables */
|
||||||
|
struct bme680_dev _bme680;
|
||||||
|
struct bme680_field_data _data;
|
||||||
|
float _tempOffset;
|
||||||
|
// Global variables to help create a millisecond timestamp that doesn't overflow every 51 days.
|
||||||
|
// If it overflows, it will have a negative value. Something that should never happen.
|
||||||
|
uint32_t millisOverflowCounter;
|
||||||
|
uint32_t lastTime;
|
||||||
|
|
||||||
|
/* Private APIs */
|
||||||
|
/**
|
||||||
|
* @brief Get the version of the BSEC library
|
||||||
|
*/
|
||||||
|
void getVersion(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read data from the BME680 and process it
|
||||||
|
* @param currTimeNs: Current time in ns
|
||||||
|
* @param bme680Settings: BME680 sensor's settings
|
||||||
|
* @return true if there are new outputs. false otherwise
|
||||||
|
*/
|
||||||
|
bool readProcessData(int64_t currTimeNs, bsec_bme_settings_t bme680Settings);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the BME680 sensor's configuration
|
||||||
|
* @param bme680Settings: Settings to configure the BME680
|
||||||
|
* @return BME680 return code. BME680_OK for success, failure otherwise
|
||||||
|
*/
|
||||||
|
int8_t setBme680Config(bsec_bme_settings_t bme680Settings);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Common code for the begin function
|
||||||
|
*/
|
||||||
|
void beginCommon(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function to zero the outputs
|
||||||
|
*/
|
||||||
|
void zeroOutputs(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
; ---> SELECT TARGET PLATFORM HERE! <---
|
; ---> SELECT TARGET PLATFORM HERE! <---
|
||||||
[platformio]
|
[platformio]
|
||||||
env_default = generic
|
;env_default = generic
|
||||||
;env_default = ebox
|
;env_default = ebox
|
||||||
;env_default = eboxtube
|
;env_default = eboxtube
|
||||||
;env_default = heltec
|
;env_default = heltec
|
||||||
@ -16,7 +16,7 @@ env_default = generic
|
|||||||
;env_default = ttgov21old
|
;env_default = ttgov21old
|
||||||
;env_default = ttgov21new
|
;env_default = ttgov21new
|
||||||
;env_default = ttgobeam_old
|
;env_default = ttgobeam_old
|
||||||
;env_default = ttgobeam_new
|
env_default = ttgobeam_new
|
||||||
;env_default = lopy
|
;env_default = lopy
|
||||||
;env_default = lopy4
|
;env_default = lopy4
|
||||||
;env_default = fipy
|
;env_default = fipy
|
||||||
@ -30,10 +30,10 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng
|
|||||||
|
|
||||||
[common]
|
[common]
|
||||||
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
||||||
release_version = 1.7.01
|
release_version = 1.7.03
|
||||||
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
||||||
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
||||||
debug_level = 0
|
debug_level = 3
|
||||||
; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA
|
; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA
|
||||||
upload_protocol = esptool
|
upload_protocol = esptool
|
||||||
;upload_protocol = custom
|
;upload_protocol = custom
|
||||||
@ -70,8 +70,8 @@ build_flags_basic =
|
|||||||
'-DBINTRAY_PACKAGE="${PIOENV}"'
|
'-DBINTRAY_PACKAGE="${PIOENV}"'
|
||||||
'-DPROGVERSION="${common.release_version}"'
|
'-DPROGVERSION="${common.release_version}"'
|
||||||
build_flags_sensors =
|
build_flags_sensors =
|
||||||
-Llib/Bosch-BSEC
|
-Llib/Bosch-BSEC/src/esp32/
|
||||||
-llibalgobsec.a
|
-lalgobsec
|
||||||
build_flags_all =
|
build_flags_all =
|
||||||
${common.build_flags_basic}
|
${common.build_flags_basic}
|
||||||
${common.build_flags_sensors}
|
${common.build_flags_sensors}
|
||||||
|
@ -8,167 +8,117 @@ static const char TAG[] = "main";
|
|||||||
bmeStatus_t bme_status;
|
bmeStatus_t bme_status;
|
||||||
TaskHandle_t BmeTask;
|
TaskHandle_t BmeTask;
|
||||||
|
|
||||||
float bme_offset = (float)BME_TEMP_OFFSET;
|
Bsec iaqSensor;
|
||||||
|
|
||||||
// --- Bosch BSEC library configuration ---
|
bsec_virtual_sensor_t sensorList[10] = {
|
||||||
// 3,3V supply voltage; 3s max time between sensor_control calls; 4 days
|
BSEC_OUTPUT_RAW_TEMPERATURE,
|
||||||
// calibration. Change this const if not applicable for your application (see
|
BSEC_OUTPUT_RAW_PRESSURE,
|
||||||
// BME680 datasheet)
|
BSEC_OUTPUT_RAW_HUMIDITY,
|
||||||
const uint8_t bsec_config_iaq[454] = {
|
BSEC_OUTPUT_RAW_GAS,
|
||||||
1, 7, 4, 1, 61, 0, 0, 0, 0, 0, 0, 0, 174, 1, 0,
|
BSEC_OUTPUT_IAQ,
|
||||||
0, 48, 0, 1, 0, 137, 65, 0, 63, 205, 204, 204, 62, 0, 0,
|
BSEC_OUTPUT_STATIC_IAQ,
|
||||||
64, 63, 205, 204, 204, 62, 0, 0, 225, 68, 0, 192, 168, 71, 64,
|
BSEC_OUTPUT_CO2_EQUIVALENT,
|
||||||
49, 119, 76, 0, 0, 0, 0, 0, 80, 5, 95, 0, 0, 0, 0,
|
BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
|
||||||
0, 0, 0, 0, 28, 0, 2, 0, 0, 244, 1, 225, 0, 25, 0,
|
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
|
||||||
0, 128, 64, 0, 0, 32, 65, 144, 1, 0, 0, 112, 65, 0, 0,
|
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
|
||||||
0, 63, 16, 0, 3, 0, 10, 215, 163, 60, 10, 215, 35, 59, 10,
|
};
|
||||||
215, 35, 59, 9, 0, 5, 0, 0, 0, 0, 0, 1, 88, 0, 9,
|
|
||||||
0, 229, 208, 34, 62, 0, 0, 0, 0, 0, 0, 0, 0, 218, 27,
|
|
||||||
156, 62, 225, 11, 67, 64, 0, 0, 160, 64, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 94, 75, 72, 189, 93, 254, 159, 64, 66, 62, 160, 191,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 33, 31, 180, 190, 138, 176, 97,
|
|
||||||
64, 65, 241, 99, 190, 0, 0, 0, 0, 0, 0, 0, 0, 167, 121,
|
|
||||||
71, 61, 165, 189, 41, 192, 184, 30, 189, 64, 12, 0, 10, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 229, 0, 254, 0, 2, 1, 5, 48,
|
|
||||||
117, 100, 0, 44, 1, 112, 23, 151, 7, 132, 3, 197, 0, 92, 4,
|
|
||||||
144, 1, 64, 1, 64, 1, 144, 1, 48, 117, 48, 117, 48, 117, 48,
|
|
||||||
117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117, 48, 117, 100, 0,
|
|
||||||
100, 0, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 100, 0, 48,
|
|
||||||
117, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117,
|
|
||||||
100, 0, 100, 0, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44,
|
|
||||||
1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1,
|
|
||||||
44, 1, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8,
|
|
||||||
7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7,
|
|
||||||
112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112,
|
|
||||||
23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 220, 5, 220, 5, 220, 5, 255, 255, 255,
|
|
||||||
255, 255, 255, 220, 5, 220, 5, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 44, 1, 0, 0, 0, 0,
|
|
||||||
239, 79, 0, 0};
|
|
||||||
|
|
||||||
// initialize BME680 sensor
|
// initialize BME680 sensor
|
||||||
int bme_init(void) {
|
int bme_init(void) {
|
||||||
|
|
||||||
// struct bme680_dev gas_sensor;
|
// block i2c bus access
|
||||||
Wire.begin(HAS_BME, 400000); // I2C connect to BME680 sensor with 400 KHz
|
if (xSemaphoreTake(I2Caccess, (DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) ==
|
||||||
|
pdTRUE) {
|
||||||
|
|
||||||
// Call to the function which initializes the BSEC library
|
Wire.begin(HAS_BME);
|
||||||
// Switch on low-power mode and provide no temperature offset
|
iaqSensor.begin(BME_ADDR, Wire);
|
||||||
|
|
||||||
return_values_init ret =
|
ESP_LOGI(TAG, "BSEC v%d.%d.%d.%d", iaqSensor.version.major,
|
||||||
bsec_iot_init(BSEC_SAMPLE_RATE_LP, bme_offset, i2c_write, i2c_read,
|
iaqSensor.version.minor, iaqSensor.version.major_bugfix,
|
||||||
user_delay_ms, state_load, config_load);
|
iaqSensor.version.minor_bugfix);
|
||||||
|
|
||||||
if ((int)ret.bme680_status) {
|
iaqSensor.setConfig(bsec_config_iaq);
|
||||||
ESP_LOGE(TAG, "Could not initialize BME680, error %d",
|
|
||||||
(int)ret.bme680_status);
|
if (checkIaqSensorStatus())
|
||||||
} else if ((int)ret.bsec_status) {
|
|
||||||
ESP_LOGE(TAG, "Could not initialize BSEC library, error %d",
|
|
||||||
(int)ret.bsec_status);
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG, "BME680 sensor found and initialized");
|
ESP_LOGI(TAG, "BME680 sensor found and initialized");
|
||||||
|
else {
|
||||||
|
ESP_LOGE(TAG, "BME680 sensor not found");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
iaqSensor.setTemperatureOffset((float)BME_TEMP_OFFSET);
|
||||||
|
iaqSensor.updateSubscription(sensorList, 10, BSEC_SAMPLE_RATE_LP);
|
||||||
|
|
||||||
|
if (checkIaqSensorStatus())
|
||||||
|
ESP_LOGI(TAG, "BSEC subscription succesful");
|
||||||
|
else {
|
||||||
|
ESP_LOGE(TAG, "BSEC subscription error");
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void output_ready(int64_t timestamp, float iaq, uint8_t iaq_accuracy,
|
xSemaphoreGive(I2Caccess); // release i2c bus access
|
||||||
float temperature, float humidity, float pressure,
|
|
||||||
float raw_temperature, float raw_humidity, float gas,
|
|
||||||
bsec_library_return_t bsec_status, float static_iaq,
|
|
||||||
float co2_equivalent, float breath_voc_equivalent) {
|
|
||||||
|
|
||||||
bme_status.temperature = temperature;
|
} else {
|
||||||
bme_status.humidity = humidity;
|
ESP_LOGE(TAG, "I2c bus busy - BME680 initialization error");
|
||||||
bme_status.pressure = (pressure / 100.0); // conversion Pa -> hPa
|
return 1;
|
||||||
bme_status.iaq = iaq;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // bme_init()
|
||||||
|
|
||||||
|
// Helper function definitions
|
||||||
|
int checkIaqSensorStatus(void) {
|
||||||
|
int rslt = 1; // true = 1 = no error, false = 0 = error
|
||||||
|
|
||||||
|
if (iaqSensor.status != BSEC_OK) {
|
||||||
|
rslt = 0;
|
||||||
|
if (iaqSensor.status < BSEC_OK)
|
||||||
|
ESP_LOGE(TAG, "BSEC error %d", iaqSensor.status);
|
||||||
|
else
|
||||||
|
ESP_LOGW(TAG, "BSEC warning %d", iaqSensor.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iaqSensor.bme680Status != BME680_OK) {
|
||||||
|
rslt = 0;
|
||||||
|
if (iaqSensor.bme680Status < BME680_OK)
|
||||||
|
ESP_LOGE(TAG, "BME680 error %d", iaqSensor.bme680Status);
|
||||||
|
else
|
||||||
|
ESP_LOGW(TAG, "BME680 warning %d", iaqSensor.bme680Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rslt;
|
||||||
|
} // checkIaqSensorStatus()
|
||||||
|
|
||||||
// loop function which reads and processes data based on sensor settings
|
// loop function which reads and processes data based on sensor settings
|
||||||
void bme_loop(void *pvParameters) {
|
void bme_loop(void *pvParameters) {
|
||||||
|
|
||||||
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
||||||
|
|
||||||
#ifdef HAS_BME
|
#ifdef HAS_BME
|
||||||
// State is saved every 10.000 samples, which means every 10.000 * 3 secs =
|
while (checkIaqSensorStatus()) {
|
||||||
// 500 minutes
|
|
||||||
bsec_iot_loop(user_delay_ms, get_timestamp_us, output_ready, state_save,
|
// block i2c bus access
|
||||||
10000);
|
if (xSemaphoreTake(I2Caccess, (DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) ==
|
||||||
|
pdTRUE) {
|
||||||
|
|
||||||
|
if (iaqSensor.run()) { // If new data is available
|
||||||
|
bme_status.raw_temperature = iaqSensor.rawTemperature;
|
||||||
|
bme_status.raw_humidity = iaqSensor.rawHumidity;
|
||||||
|
bme_status.temperature = iaqSensor.temperature;
|
||||||
|
bme_status.humidity = iaqSensor.humidity;
|
||||||
|
bme_status.pressure =
|
||||||
|
(iaqSensor.pressure / 100.0); // conversion Pa -> hPa
|
||||||
|
bme_status.iaq = iaqSensor.iaqEstimate;
|
||||||
|
bme_status.iaq_accuracy = iaqSensor.iaqAccuracy;
|
||||||
|
bme_status.gas = iaqSensor.gasResistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
xSemaphoreGive(I2Caccess); // release i2c bus access
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
ESP_LOGE(TAG, "BME task ended");
|
||||||
vTaskDelete(BmeTask); // should never be reached
|
vTaskDelete(BmeTask); // should never be reached
|
||||||
|
|
||||||
} // bme_loop()
|
} // bme_loop()
|
||||||
|
|
||||||
int8_t i2c_read(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data,
|
|
||||||
uint16_t len) {
|
|
||||||
int8_t rslt = 0;
|
|
||||||
Wire.beginTransmission(dev_id);
|
|
||||||
Wire.write(reg_addr);
|
|
||||||
rslt = Wire.endTransmission(false);
|
|
||||||
|
|
||||||
Wire.requestFrom((int)dev_id, (int)len);
|
|
||||||
for (uint16_t i = 0; (i < len) && Wire.available(); i++) {
|
|
||||||
reg_data[i] = Wire.read();
|
|
||||||
}
|
|
||||||
// return 0 for success, non-zero for failure
|
|
||||||
return rslt;
|
|
||||||
}
|
|
||||||
|
|
||||||
int8_t i2c_write(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data,
|
|
||||||
uint16_t len) {
|
|
||||||
Wire.beginTransmission(dev_id);
|
|
||||||
Wire.write(reg_addr);
|
|
||||||
for (uint16_t i = 0; i < len; i++) {
|
|
||||||
Wire.write(reg_data[i]);
|
|
||||||
}
|
|
||||||
// return 0 for success, non-zero for failure
|
|
||||||
return Wire.endTransmission(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief Load previous library state from non-volatile memory
|
|
||||||
*
|
|
||||||
* @param[in,out] state_buffer buffer to hold the loaded state string
|
|
||||||
* @param[in] n_buffer size of the allocated state buffer
|
|
||||||
*
|
|
||||||
* @return number of bytes copied to state_buffer
|
|
||||||
*/
|
|
||||||
uint32_t state_load(uint8_t *state_buffer, uint32_t n_buffer) {
|
|
||||||
// ...
|
|
||||||
// Load a previous library state from non-volatile memory, if available.
|
|
||||||
//
|
|
||||||
// Return zero if loading was unsuccessful or no state was available,
|
|
||||||
// otherwise return length of loaded state string.
|
|
||||||
// ...
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief Save library state to non-volatile memory
|
|
||||||
*
|
|
||||||
* @param[in] state_buffer buffer holding the state to be stored
|
|
||||||
* @param[in] length length of the state string to be stored
|
|
||||||
*
|
|
||||||
* @return none
|
|
||||||
*/
|
|
||||||
void state_save(const uint8_t *state_buffer, uint32_t length) {
|
|
||||||
// ...
|
|
||||||
// Save the string some form of non-volatile memory, if possible.
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t config_load(uint8_t *config_buffer, uint32_t n_buffer) {
|
|
||||||
|
|
||||||
// Load a library config from non-volatile memory, if available.
|
|
||||||
// Return zero if loading was unsuccessful or no config was available,
|
|
||||||
// otherwise return length of loaded config string.
|
|
||||||
|
|
||||||
memcpy(config_buffer, bsec_config_iaq, sizeof(bsec_config_iaq));
|
|
||||||
return sizeof(bsec_config_iaq);
|
|
||||||
}
|
|
||||||
|
|
||||||
void user_delay_ms(uint32_t period) { delay(period); }
|
|
||||||
|
|
||||||
int64_t get_timestamp_us() { return (int64_t)millis() * 1000; }
|
|
||||||
|
|
||||||
#endif // HAS_BME
|
#endif // HAS_BME
|
@ -52,7 +52,12 @@ void doHousekeeping() {
|
|||||||
// read battery voltage into global variable
|
// read battery voltage into global variable
|
||||||
#ifdef HAS_BATTERY_PROBE
|
#ifdef HAS_BATTERY_PROBE
|
||||||
batt_voltage = read_voltage();
|
batt_voltage = read_voltage();
|
||||||
ESP_LOGI(TAG, "Measured Voltage: %dmV", batt_voltage);
|
ESP_LOGI(TAG, "Voltage: %dmV", batt_voltage);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// display BME sensor data if present
|
||||||
|
#ifdef HAS_BME
|
||||||
|
ESP_LOGI(TAG, "BME680 Temp: %.2f°C | IAQ: %.2f", bme_status.temperature, bme_status.iaq);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// check free heap memory
|
// check free heap memory
|
||||||
|
@ -98,6 +98,10 @@ void init_display(const char *Productname, const char *Version) {
|
|||||||
|
|
||||||
void refreshtheDisplay() {
|
void refreshtheDisplay() {
|
||||||
|
|
||||||
|
// block i2c bus access
|
||||||
|
if (xSemaphoreTake(I2Caccess, (DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) ==
|
||||||
|
pdTRUE) {
|
||||||
|
|
||||||
// set display on/off according to current device configuration
|
// set display on/off according to current device configuration
|
||||||
if (DisplayState != cfg.screenon) {
|
if (DisplayState != cfg.screenon) {
|
||||||
DisplayState = cfg.screenon;
|
DisplayState = cfg.screenon;
|
||||||
@ -114,7 +118,8 @@ void refreshtheDisplay() {
|
|||||||
// update counter (lines 0-1)
|
// update counter (lines 0-1)
|
||||||
snprintf(
|
snprintf(
|
||||||
buff, sizeof(buff), "PAX:%-4d",
|
buff, sizeof(buff), "PAX:%-4d",
|
||||||
(int)macs.size()); // convert 16-bit MAC counter to decimal counter value
|
(int)
|
||||||
|
macs.size()); // convert 16-bit MAC counter to decimal counter value
|
||||||
u8x8.draw2x2String(0, 0,
|
u8x8.draw2x2String(0, 0,
|
||||||
buff); // display number on unique macs total Wifi + BLE
|
buff); // display number on unique macs total Wifi + BLE
|
||||||
|
|
||||||
@ -188,6 +193,9 @@ void refreshtheDisplay() {
|
|||||||
|
|
||||||
#endif // HAS_LORA
|
#endif // HAS_LORA
|
||||||
|
|
||||||
|
xSemaphoreGive(I2Caccess); // release i2c bus access
|
||||||
|
}
|
||||||
|
|
||||||
} // refreshDisplay()
|
} // refreshDisplay()
|
||||||
|
|
||||||
#endif // HAS_DISPLAY
|
#endif // HAS_DISPLAY
|
@ -17,8 +17,8 @@
|
|||||||
|
|
||||||
// enable only if device has these sensors, otherwise comment these lines
|
// enable only if device has these sensors, otherwise comment these lines
|
||||||
// BME680 sensor on I2C bus
|
// BME680 sensor on I2C bus
|
||||||
// don't forget to connect SDIO of BME680 to GND for selecting i2c addr 0x76
|
|
||||||
#define HAS_BME GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL
|
#define HAS_BME GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL
|
||||||
|
#define BME_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND
|
||||||
|
|
||||||
// user defined sensors
|
// user defined sensors
|
||||||
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
|
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
|
||||||
|
@ -11,11 +11,9 @@
|
|||||||
// disable brownout detection (avoid unexpected reset on some boards)
|
// disable brownout detection (avoid unexpected reset on some boards)
|
||||||
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
||||||
|
|
||||||
// enable only if device has these sensors, otherwise comment these lines
|
|
||||||
// BME680 sensor on I2C bus
|
|
||||||
// Octopus32 has a pre-populated BME680 on i2c addr 0x76
|
// Octopus32 has a pre-populated BME680 on i2c addr 0x76
|
||||||
#define HAS_BME GPIO_NUM_23, GPIO_NUM_22 // SDA, SCL
|
#define HAS_BME GPIO_NUM_23, GPIO_NUM_22 // SDA, SCL
|
||||||
//#define HAS_BME 0x76
|
#define BME_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND
|
||||||
|
|
||||||
// user defined sensors
|
// user defined sensors
|
||||||
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
|
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
|
||||||
|
@ -8,22 +8,21 @@
|
|||||||
// Hardware related definitions for TTGO T-Beam board
|
// Hardware related definitions for TTGO T-Beam board
|
||||||
//
|
//
|
||||||
// pinouts taken from http://tinymicros.com/wiki/TTGO_T-Beam
|
// pinouts taken from http://tinymicros.com/wiki/TTGO_T-Beam
|
||||||
//
|
|
||||||
// enable only if device has these sensors, otherwise comment these lines
|
// enable only if device has these sensors, otherwise comment these lines
|
||||||
// BME680 sensor on I2C bus
|
// BME680 sensor on I2C bus
|
||||||
// don't forget to connect SDIO of BME680 to GND for selecting i2c addr 0x76
|
#define HAS_BME GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL
|
||||||
//
|
#define BME_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND
|
||||||
//#define HAS_BME GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL
|
|
||||||
|
|
||||||
#define HAS_LED GPIO_NUM_14 // on board green LED
|
#define HAS_LED GPIO_NUM_14 // on board green LED
|
||||||
|
|
||||||
// user defined sensors
|
// user defined sensors
|
||||||
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
|
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
|
||||||
|
|
||||||
//#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||||
//#define MY_OLED_SDA (21)
|
#define MY_OLED_SDA (21)
|
||||||
//#define MY_OLED_SCL (22)
|
#define MY_OLED_SCL (22)
|
||||||
//#define MY_OLED_RST (NOT_A_PIN)
|
#define MY_OLED_RST U8X8_PIN_NONE
|
||||||
//#define DISPLAY_FLIP 1 // use if display is rotated
|
//#define DISPLAY_FLIP 1 // use if display is rotated
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
//#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
//#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||||
//#define MY_OLED_SDA (21)
|
//#define MY_OLED_SDA (21)
|
||||||
//#define MY_OLED_SCL (22)
|
//#define MY_OLED_SCL (22)
|
||||||
//#define MY_OLED_RST (NOT_A_PIN)
|
//#define MY_OLED_RST U8X8_PIN_NONE
|
||||||
//#define DISPLAY_FLIP 1 // use if display is rotated
|
//#define DISPLAY_FLIP 1 // use if display is rotated
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
|
18
src/main.cpp
18
src/main.cpp
@ -35,11 +35,14 @@ IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
|
|||||||
looptask 1 1 arduino core -> runs the LMIC LoRa stack
|
looptask 1 1 arduino core -> runs the LMIC LoRa stack
|
||||||
irqhandler 1 1 executes tasks triggered by irq
|
irqhandler 1 1 executes tasks triggered by irq
|
||||||
gpsloop 1 2 reads data from GPS via serial or i2c
|
gpsloop 1 2 reads data from GPS via serial or i2c
|
||||||
bmeloop 1 0 reads data from BME sensor via i2c
|
bmeloop 1 1 reads data from BME sensor via i2c
|
||||||
IDLE 1 0 ESP32 arduino scheduler
|
IDLE 1 0 ESP32 arduino scheduler
|
||||||
|
|
||||||
Low priority numbers denote low priority tasks.
|
Low priority numbers denote low priority tasks.
|
||||||
|
|
||||||
|
Tasks using i2c bus all must have same priority, because using mutex semaphore
|
||||||
|
(irqhandler, bmeloop)
|
||||||
|
|
||||||
ESP32 hardware timers
|
ESP32 hardware timers
|
||||||
==========================
|
==========================
|
||||||
0 Trigger display refresh
|
0 Trigger display refresh
|
||||||
@ -60,6 +63,7 @@ uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
|
|||||||
hw_timer_t *channelSwitch = NULL, *sendCycle = NULL, *homeCycle = NULL,
|
hw_timer_t *channelSwitch = NULL, *sendCycle = NULL, *homeCycle = NULL,
|
||||||
*displaytimer = NULL; // irq tasks
|
*displaytimer = NULL; // irq tasks
|
||||||
TaskHandle_t irqHandlerTask, wifiSwitchTask;
|
TaskHandle_t irqHandlerTask, wifiSwitchTask;
|
||||||
|
SemaphoreHandle_t I2Caccess;
|
||||||
|
|
||||||
// container holding unique MAC address hashes with Memory Alloctor using PSRAM,
|
// container holding unique MAC address hashes with Memory Alloctor using PSRAM,
|
||||||
// if present
|
// if present
|
||||||
@ -78,6 +82,14 @@ void setup() {
|
|||||||
|
|
||||||
char features[100] = "";
|
char features[100] = "";
|
||||||
|
|
||||||
|
if (I2Caccess == NULL) // Check that semaphore has not already been created
|
||||||
|
{
|
||||||
|
I2Caccess = xSemaphoreCreateMutex(); // Create a mutex semaphore we will use
|
||||||
|
// to manage the i2c bus
|
||||||
|
if ((I2Caccess) != NULL)
|
||||||
|
xSemaphoreGive((I2Caccess)); // Flag the i2c bus available for use
|
||||||
|
}
|
||||||
|
|
||||||
// disable brownout detection
|
// disable brownout detection
|
||||||
#ifdef DISABLE_BROWNOUT
|
#ifdef DISABLE_BROWNOUT
|
||||||
// register with brownout is at address DR_REG_RTCCNTL_BASE + 0xd4
|
// register with brownout is at address DR_REG_RTCCNTL_BASE + 0xd4
|
||||||
@ -343,11 +355,11 @@ void setup() {
|
|||||||
"bmeloop", // name of task
|
"bmeloop", // name of task
|
||||||
4096, // stack size of task
|
4096, // stack size of task
|
||||||
(void *)1, // parameter of the task
|
(void *)1, // parameter of the task
|
||||||
0, // priority of the task
|
//0, // priority of the task
|
||||||
|
1, // priority of the task
|
||||||
&BmeTask, // task handle
|
&BmeTask, // task handle
|
||||||
1); // CPU core
|
1); // CPU core
|
||||||
}
|
}
|
||||||
delay(2000); // time for initializing i2c sensor
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// start timer triggered interrupts
|
// start timer triggered interrupts
|
||||||
|
Loading…
Reference in New Issue
Block a user