From 76600a86b156c1d5ebaf54e15166c5774e984b54 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 27 Dec 2018 17:09:40 +0100 Subject: [PATCH] first fully functional integration (experimental) --- include/globals.h | 1 + lib/Bosch-BSEC/src/bme680/bme680_defs.h | 2 +- lib/Bosch-BSEC/src/bsec.cpp | 641 ++++++++++++------------ platformio.ini | 2 +- src/bme680mems.cpp | 117 +++-- src/cyclic.cpp | 7 +- src/display.cpp | 136 ++--- src/hal/ttgobeam_new.h | 8 +- src/hal/ttgobeam_old.h | 2 +- src/main.cpp | 18 +- 10 files changed, 488 insertions(+), 446 deletions(-) diff --git a/include/globals.h b/include/globals.h index 8c429048..7fda0d29 100644 --- a/include/globals.h +++ b/include/globals.h @@ -89,6 +89,7 @@ extern uint8_t volatile channel; // wifi channel rotation counter extern uint16_t volatile macs_total, macs_wifi, macs_ble, batt_voltage; // display values extern hw_timer_t *channelSwitch, *sendCycle, *displaytimer; +extern SemaphoreHandle_t I2Caccess; extern std::set, Mallocator> macs; extern std::array::iterator it; diff --git a/lib/Bosch-BSEC/src/bme680/bme680_defs.h b/lib/Bosch-BSEC/src/bme680/bme680_defs.h index 495edfe0..79fd7782 100644 --- a/lib/Bosch-BSEC/src/bme680/bme680_defs.h +++ b/lib/Bosch-BSEC/src/bme680/bme680_defs.h @@ -103,7 +103,7 @@ /** BME680 configuration macros */ /** Enable or un-comment the macro to provide floating point data output */ #ifndef BME680_FLOAT_POINT_COMPENSATION -/* #define BME680_FLOAT_POINT_COMPENSATION */ +//#define BME680_FLOAT_POINT_COMPENSATION #endif /** BME680 General config */ diff --git a/lib/Bosch-BSEC/src/bsec.cpp b/lib/Bosch-BSEC/src/bsec.cpp index d9ac1868..cf85d77f 100644 --- a/lib/Bosch-BSEC/src/bsec.cpp +++ b/lib/Bosch-BSEC/src/bsec.cpp @@ -47,186 +47,185 @@ #include "bsec.h" -TwoWire* Bsec::wireObj = NULL; -SPIClass* Bsec::spiObj = NULL; +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(); +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; +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(); + 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; +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(); + Bsec::wireObj = &i2c; + Bsec::wireObj->begin(); - beginCommon(); + 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; +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(); + pinMode(chipSelect, OUTPUT); + digitalWrite(chipSelect, HIGH); + Bsec::spiObj = &spi; + Bsec::spiObj->begin(); - beginCommon(); + beginCommon(); } /** * @brief Common code for the begin function */ -void Bsec::beginCommon(void) -{ - status = bsec_init(); +void Bsec::beginCommon(void) { + status = bsec_init(); - getVersion(); - - bme680Status = bme680_init(&_bme680); + 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; +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++; - } + 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; + 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 + * @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; +bool Bsec::run(void) { + bool newData = false; + /* Check if the time has arrived to call do_steps() */ + int64_t callTimeMs = getTimeMs(); - int64_t callTimeNs = callTimeMs * INT64_C(1000000); + if (callTimeMs >= nextCall) { - status = bsec_sensor_control(callTimeNs, &bme680Settings); - if (status < BSEC_OK) - return false; + bsec_bme_settings_t bme680Settings; - nextCall = bme680Settings.next_call / INT64_C(1000000); // Convert from ns to ms + int64_t callTimeNs = callTimeMs * INT64_C(1000000); - bme680Status = setBme680Config(bme680Settings); - if (bme680Status != BME680_OK) { - return false; - } + status = bsec_sensor_control(callTimeNs, &bme680Settings); + if (status < BSEC_OK) + return false; - bme680Status = bme680_set_sensor_mode(&_bme680); - if (bme680Status != BME680_OK) { - return false; - } + nextCall = + bme680Settings.next_call / INT64_C(1000000); // Convert from ns to ms - /* Wait for measurement to complete */ - uint16_t meas_dur = 0; + bme680Status = setBme680Config(bme680Settings); + if (bme680Status != BME680_OK) { + return false; + } - bme680_get_profile_dur(&meas_dur, &_bme680); - delay_ms(meas_dur); + bme680Status = bme680_set_sensor_mode(&_bme680); + if (bme680Status != BME680_OK) { + return false; + } - newData = readProcessData(callTimeNs, bme680Settings); - } - - return newData; + /* 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 + * @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); +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]; +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); + 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]; +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)); + status = bsec_set_configuration(state, BSEC_MAX_PROPERTY_BLOB_SIZE, + workBuffer, sizeof(workBuffer)); } /* Private functions */ @@ -234,260 +233,266 @@ void Bsec::setConfig(const uint8_t *state) /** * @brief Get the version of the BSEC library */ -void Bsec::getVersion(void) -{ - bsec_get_version(&version); -} +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; - } +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; + 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; - inputs[nInputs].signal = _data.temperature; - 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; - inputs[nInputs].signal = _data.humidity; - 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 (_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]; + 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; + status = bsec_do_steps(inputs, nInputs, _outputs, &nOutputs); + if (status != BSEC_OK) + return false; - zeroOutputs(); + zeroOutputs(); - if (nOutputs > 0) { - outputTimestamp = _outputs[0].time_stamp / 1000000; // Convert from ns to ms + 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; - } - } + 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; + 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); +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; +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(); +int64_t Bsec::getTimeMs(void) { + int64_t timeMs = millis(); - if (lastTime > timeMs) { // An overflow occured - lastTime = timeMs; - millisOverflowCounter++; - } + if (lastTime > timeMs) { // An overflow occured + lastTime = timeMs; + millisOverflowCounter++; + } - return timeMs + (millisOverflowCounter * 0xFFFFFFFF); + 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); +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; +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; - } +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; + 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); +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 - 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, LOW); - digitalWrite(devId, HIGH); - Bsec::spiObj->endTransaction(); - } else { - rslt = -1; - } + 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]); - return rslt;; + digitalWrite(devId, HIGH); + Bsec::spiObj->endTransaction(); + } else { + rslt = -1; + } + + return rslt; + ; } \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 0922cb7a..ecbbf0de 100644 --- a/platformio.ini +++ b/platformio.ini @@ -33,7 +33,7 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng release_version = 1.7.03 ; 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 -debug_level = 4 +debug_level = 3 ; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA upload_protocol = esptool ;upload_protocol = custom diff --git a/src/bme680mems.cpp b/src/bme680mems.cpp index d5f08662..58570dda 100644 --- a/src/bme680mems.cpp +++ b/src/bme680mems.cpp @@ -7,60 +7,63 @@ static const char TAG[] = "main"; bmeStatus_t bme_status; TaskHandle_t BmeTask; -float bme_offset = (float)BME_TEMP_OFFSET; Bsec iaqSensor; +bsec_virtual_sensor_t sensorList[10] = { + BSEC_OUTPUT_RAW_TEMPERATURE, + BSEC_OUTPUT_RAW_PRESSURE, + BSEC_OUTPUT_RAW_HUMIDITY, + BSEC_OUTPUT_RAW_GAS, + BSEC_OUTPUT_IAQ, + BSEC_OUTPUT_STATIC_IAQ, + BSEC_OUTPUT_CO2_EQUIVALENT, + BSEC_OUTPUT_BREATH_VOC_EQUIVALENT, + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, +}; + // initialize BME680 sensor int bme_init(void) { - Wire.begin(HAS_BME); - iaqSensor.begin(BME_ADDR, Wire); + // block i2c bus access + if (xSemaphoreTake(I2Caccess, (DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) == + pdTRUE) { - ESP_LOGI(TAG, "BSEC v%d.%d.%d.%d", iaqSensor.version.major, - iaqSensor.version.minor, iaqSensor.version.major_bugfix, - iaqSensor.version.minor_bugfix); + Wire.begin(HAS_BME); + iaqSensor.begin(BME_ADDR, Wire); - iaqSensor.setConfig(bsec_config_iaq); + ESP_LOGI(TAG, "BSEC v%d.%d.%d.%d", iaqSensor.version.major, + iaqSensor.version.minor, iaqSensor.version.major_bugfix, + iaqSensor.version.minor_bugfix); - if (checkIaqSensorStatus()) - ESP_LOGI(TAG, "BME680 sensor found and initialized"); - else { - ESP_LOGE(TAG, "BME680 sensor not found"); + iaqSensor.setConfig(bsec_config_iaq); + + if (checkIaqSensorStatus()) + ESP_LOGI(TAG, "BME680 sensor found and initialized"); + else { + ESP_LOGE(TAG, "BME680 sensor not found"); + return 1; + } + + 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; + } + + xSemaphoreGive(I2Caccess); // release i2c bus access + + } else { + ESP_LOGE(TAG, "I2c bus busy - BME680 initialization error"); return 1; } - bsec_virtual_sensor_t sensorList[10] = { - BSEC_OUTPUT_RAW_TEMPERATURE, - BSEC_OUTPUT_RAW_PRESSURE, - BSEC_OUTPUT_RAW_HUMIDITY, - BSEC_OUTPUT_RAW_GAS, - BSEC_OUTPUT_IAQ, - BSEC_OUTPUT_STATIC_IAQ, - BSEC_OUTPUT_CO2_EQUIVALENT, - BSEC_OUTPUT_BREATH_VOC_EQUIVALENT, - BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, - BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, - }; - - 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; - } - - iaqSensor.setTemperatureOffset(bme_offset); - - if (checkIaqSensorStatus()) - ESP_LOGI(TAG, "Ttemperature offset initialized succesful"); - else { - ESP_LOGE(TAG, "Temperature offset initialization error"); - return 1; - } -} +} // bme_init() // Helper function definitions int checkIaqSensorStatus(void) { @@ -83,7 +86,7 @@ int checkIaqSensorStatus(void) { } return rslt; -} +} // checkIaqSensorStatus() // loop function which reads and processes data based on sensor settings void bme_loop(void *pvParameters) { @@ -92,16 +95,24 @@ void bme_loop(void *pvParameters) { #ifdef HAS_BME while (checkIaqSensorStatus()) { - 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; + + // block i2c bus access + 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 diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 80e16f1f..0ce0c657 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -52,7 +52,12 @@ void doHousekeeping() { // read battery voltage into global variable #ifdef HAS_BATTERY_PROBE 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 // check free heap memory diff --git a/src/display.cpp b/src/display.cpp index 7427562d..861328fd 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -98,96 +98,104 @@ void init_display(const char *Productname, const char *Version) { void refreshtheDisplay() { - // set display on/off according to current device configuration - if (DisplayState != cfg.screenon) { - DisplayState = cfg.screenon; - u8x8.setPowerSave(!cfg.screenon); - } + // block i2c bus access + if (xSemaphoreTake(I2Caccess, (DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) == + pdTRUE) { - // if display is switched off we don't refresh it and save time - if (!DisplayState) - return; + // set display on/off according to current device configuration + if (DisplayState != cfg.screenon) { + DisplayState = cfg.screenon; + u8x8.setPowerSave(!cfg.screenon); + } - uint8_t msgWaiting; - char buff[16]; // 16 chars line buffer + // if display is switched off we don't refresh it and save time + if (!DisplayState) + return; - // update counter (lines 0-1) - snprintf( - buff, sizeof(buff), "PAX:%-4d", - (int)macs.size()); // convert 16-bit MAC counter to decimal counter value - u8x8.draw2x2String(0, 0, - buff); // display number on unique macs total Wifi + BLE + uint8_t msgWaiting; + char buff[16]; // 16 chars line buffer + + // update counter (lines 0-1) + snprintf( + buff, sizeof(buff), "PAX:%-4d", + (int) + macs.size()); // convert 16-bit MAC counter to decimal counter value + u8x8.draw2x2String(0, 0, + buff); // display number on unique macs total Wifi + BLE // update Battery status (line 2) #ifdef HAS_BATTERY_PROBE - u8x8.setCursor(0, 2); - u8x8.printf("B:%.1fV", batt_voltage / 1000.0); + u8x8.setCursor(0, 2); + u8x8.printf("B:%.1fV", batt_voltage / 1000.0); #endif // update GPS status (line 2) #ifdef HAS_GPS - u8x8.setCursor(9, 2); - if (!gps.location.isValid()) // if no fix then display Sats value inverse - { - u8x8.setInverseFont(1); - u8x8.printf("Sats:%.2d", gps.satellites.value()); - u8x8.setInverseFont(0); - } else - u8x8.printf("Sats:%.2d", gps.satellites.value()); + u8x8.setCursor(9, 2); + if (!gps.location.isValid()) // if no fix then display Sats value inverse + { + u8x8.setInverseFont(1); + u8x8.printf("Sats:%.2d", gps.satellites.value()); + u8x8.setInverseFont(0); + } else + u8x8.printf("Sats:%.2d", gps.satellites.value()); #endif - // update bluetooth counter + LoRa SF (line 3) + // update bluetooth counter + LoRa SF (line 3) #ifdef BLECOUNTER - u8x8.setCursor(0, 3); - if (cfg.blescan) - u8x8.printf("BLTH:%-4d", macs_ble); - else - u8x8.printf("%s", "BLTH:off"); + u8x8.setCursor(0, 3); + if (cfg.blescan) + u8x8.printf("BLTH:%-4d", macs_ble); + else + u8x8.printf("%s", "BLTH:off"); #endif #ifdef HAS_LORA - u8x8.setCursor(11, 3); - u8x8.printf("SF:"); - if (cfg.adrmode) // if ADR=on then display SF value inverse - u8x8.setInverseFont(1); - u8x8.printf("%c%c", lora_datarate[LMIC.datarate * 2], - lora_datarate[LMIC.datarate * 2 + 1]); - if (cfg.adrmode) // switch off inverse if it was turned on - u8x8.setInverseFont(0); + u8x8.setCursor(11, 3); + u8x8.printf("SF:"); + if (cfg.adrmode) // if ADR=on then display SF value inverse + u8x8.setInverseFont(1); + u8x8.printf("%c%c", lora_datarate[LMIC.datarate * 2], + lora_datarate[LMIC.datarate * 2 + 1]); + if (cfg.adrmode) // switch off inverse if it was turned on + u8x8.setInverseFont(0); #endif // HAS_LORA - // update wifi counter + channel display (line 4) - u8x8.setCursor(0, 4); - u8x8.printf("WIFI:%-4d", macs_wifi); - u8x8.setCursor(11, 4); - u8x8.printf("ch:%02d", channel); + // update wifi counter + channel display (line 4) + u8x8.setCursor(0, 4); + u8x8.printf("WIFI:%-4d", macs_wifi); + u8x8.setCursor(11, 4); + u8x8.printf("ch:%02d", channel); - // update RSSI limiter status & free memory display (line 5) - u8x8.setCursor(0, 5); - u8x8.printf(!cfg.rssilimit ? "RLIM:off " : "RLIM:%-4d", cfg.rssilimit); - u8x8.setCursor(10, 5); - u8x8.printf("%4dKB", getFreeRAM() / 1024); + // update RSSI limiter status & free memory display (line 5) + u8x8.setCursor(0, 5); + u8x8.printf(!cfg.rssilimit ? "RLIM:off " : "RLIM:%-4d", cfg.rssilimit); + u8x8.setCursor(10, 5); + u8x8.printf("%4dKB", getFreeRAM() / 1024); #ifdef HAS_LORA - // update LoRa status display (line 6) - u8x8.setCursor(0, 6); - u8x8.printf("%-16s", display_line6); + // update LoRa status display (line 6) + u8x8.setCursor(0, 6); + u8x8.printf("%-16s", display_line6); - // update LMiC event display (line 7) - u8x8.setCursor(0, 7); - u8x8.printf("%-14s", display_line7); + // update LMiC event display (line 7) + u8x8.setCursor(0, 7); + u8x8.printf("%-14s", display_line7); - // update LoRa send queue display (line 7) - msgWaiting = uxQueueMessagesWaiting(LoraSendQueue); - if (msgWaiting) { - sprintf(buff, "%2d", msgWaiting); - u8x8.setCursor(14, 7); - u8x8.printf("%-2s", msgWaiting == SEND_QUEUE_SIZE ? "<>" : buff); - } else - u8x8.printf(" "); + // update LoRa send queue display (line 7) + msgWaiting = uxQueueMessagesWaiting(LoraSendQueue); + if (msgWaiting) { + sprintf(buff, "%2d", msgWaiting); + u8x8.setCursor(14, 7); + u8x8.printf("%-2s", msgWaiting == SEND_QUEUE_SIZE ? "<>" : buff); + } else + u8x8.printf(" "); #endif // HAS_LORA + xSemaphoreGive(I2Caccess); // release i2c bus access + } + } // refreshDisplay() #endif // HAS_DISPLAY \ No newline at end of file diff --git a/src/hal/ttgobeam_new.h b/src/hal/ttgobeam_new.h index 814e59c1..d177e033 100644 --- a/src/hal/ttgobeam_new.h +++ b/src/hal/ttgobeam_new.h @@ -19,10 +19,10 @@ // 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 MY_OLED_SDA (21) -//#define MY_OLED_SCL (22) -//#define MY_OLED_RST (NOT_A_PIN) +#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C +#define MY_OLED_SDA (21) +#define MY_OLED_SCL (22) +#define MY_OLED_RST U8X8_PIN_NONE //#define DISPLAY_FLIP 1 // use if display is rotated #define HAS_LORA 1 // comment out if device shall not send data via LoRa diff --git a/src/hal/ttgobeam_old.h b/src/hal/ttgobeam_old.h index f8e530fc..f6fd4531 100644 --- a/src/hal/ttgobeam_old.h +++ b/src/hal/ttgobeam_old.h @@ -22,7 +22,7 @@ //#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C //#define MY_OLED_SDA (21) //#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 HAS_LORA 1 // comment out if device shall not send data via LoRa diff --git a/src/main.cpp b/src/main.cpp index 37d71674..29aa32cb 100644 --- a/src/main.cpp +++ b/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 irqhandler 1 1 executes tasks triggered by irq 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 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 ========================== 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, *displaytimer = NULL; // irq tasks TaskHandle_t irqHandlerTask, wifiSwitchTask; +SemaphoreHandle_t I2Caccess; // container holding unique MAC address hashes with Memory Alloctor using PSRAM, // if present @@ -78,6 +82,14 @@ void setup() { 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 #ifdef DISABLE_BROWNOUT // register with brownout is at address DR_REG_RTCCNTL_BASE + 0xd4 @@ -343,11 +355,11 @@ void setup() { "bmeloop", // name of task 4096, // stack size of 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 1); // CPU core } - delay(2000); // time for initializing i2c sensor #endif // start timer triggered interrupts