Merge pull request #233 from cyberman54/development-bme680-2

BME680 integration refactored
This commit is contained in:
Verkehrsrot 2018-12-28 19:17:44 +01:00 committed by GitHub
commit 5bdc354a07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 967 additions and 968 deletions

View File

@ -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

View File

@ -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;

View File

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

View File

@ -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__ */
/*! @}*/

View File

@ -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
View 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
View 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

View File

@ -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}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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