first commit
This commit is contained in:
commit
5893b00dd2
1669 changed files with 1982740 additions and 0 deletions
|
|
@ -0,0 +1,81 @@
|
|||
# BME688 Development Kit-Firmware-Quick-Start-Guide
|
||||
|
||||
## Pre-requisites
|
||||
|
||||
Follow the Installation and getting started in the [README.md](../../README.md).
|
||||
|
||||
## Instructions to build the **bme68x_demo_sample**
|
||||
|
||||
1. Install the following Arduino libraries under Sketch->Include Library->Manage Libraries
|
||||
|
||||
- ArduinoJson (tested for 6.19.4 by Benoit Blanchon)
|
||||
|
||||
- RTClib (tested for 2.0.3 by Adafruit)
|
||||
|
||||
- Click OK, when asked to install needed dependencies
|
||||
|
||||
- SdFat (tested for 2.1.2 by Bill Greiman)
|
||||
|
||||
2. Open the 'bme68x_demo_sample.ino' file from the path - Bosch_BSEC2_Library/examples/bme68x_demo_sample
|
||||
|
||||
3. Connect your board via USB
|
||||
|
||||
- Select "Adafruit ESP32 Feather" in the Arduino IDE under Tools->Board
|
||||
|
||||
- Select your COM Port under Tools->Port
|
||||
|
||||
5. Upload the firmware
|
||||
|
||||
- If you have a coin cell (CR1220) for the RTC, make sure it is inserted before flashing the board
|
||||
|
||||
- When the board is flashed, don't unmount the coin cell, if not necessary
|
||||
|
||||
5. Wait for the board to be flashed and then disconnect it
|
||||
|
||||
- Last line in the Arduino command line should be: "Hard resetting via RTS pin..."
|
||||
|
||||
6. To start the data logging, insert an SD-card with a '.bmeconfig file' in it (generated using BME AI studio) and connect power supply
|
||||
|
||||
7. A red LED blinking with 1Hz indicates a successful data logging, a faster blinking indicates an error during the initialization (RTC, SD or Bluetooth)
|
||||
|
||||
## Additional Note
|
||||
|
||||
The 'Bosch_BSEC2_Library' has a 'src' folder with the following files essential for running application-
|
||||
|
||||
- header files - bsec_datatypes.h, bsec_interface.h and bsec_interface_multi.h(under folder 'inc')
|
||||
|
||||
- esp32 library - static library (under 'esp32')
|
||||
|
||||
## More about Demo Application Code (Bme68x_demo_sample.ino Sketch):
|
||||
|
||||
This is an example code for datalogging and integration of BSEC2x library in BME688 development kit,
|
||||
which has been designed to work with Adafruit ESP32 Feather Board.
|
||||
|
||||
The example code will primarily operate in two modes-
|
||||
|
||||
**a. DEMO_RECORDING_MODE**
|
||||
|
||||
**b. DEMO_TEST_ALGORITHM_MODE**
|
||||
|
||||
- Multi sensor support is available in DEMO_RECORDING_MODE upto 8 sensors.
|
||||
- Modify NUM_BME68X_UNITS between 0 to 8, where 0 to 7 is for sensor numbers 1 to 8 respectively and 8 is for running all 8 sensors parallely.
|
||||
- Multi sensor support is available in DEMO_TEST_ALGORITHM_MODE upto 4 sensors.
|
||||
- Modify NUM_BME68X_UNITS between 0 to 4, where 0 to 3 is for sensor numbers 1 to 4 respectively and 4 is for running all 4 sensors parallely.
|
||||
|
||||
### Note:
|
||||
|
||||
- After successfully flashing the firmware, "DEMO_RECORDING_MODE" is the default mode set in the example.
|
||||
|
||||
- An initial check for the availability of sensor board configuration file (.bmeconfig) inside SD card is performed.
|
||||
|
||||
- For DEMO_RECORDING_MODE, once .bmeconfig file has been detected, it will initialize and set the heater profiles for all the bme688 sensors with the provided configuration and create bme68x datalogger output file (with '.bmerawdata' extension).
|
||||
|
||||
- Until the user connects the board to the BME688 demo application through Bluetooth LE and switches to "DEMO_TEST_ALGORITHM_MODE", it will continue to collect the data in DEMO_RECORDING_MODE.
|
||||
|
||||
- When it comes to 'DEMO_TEST_ALGORITHM_MODE' (connecting over bluetooth and set to "DEMO_TEST_ALGORITHM_MODE"), initialization of sensor and BSEC library is undertaken.
|
||||
|
||||
- Furthermore, configuring with the BSEC config file (generated out of training through BME AI Studio or a default configuration from the BSEC website release package) and subscribing for the desired virtual outputs with the supported sample rate is complete, an output data file is created with the '.aiprediction' extension.
|
||||
|
||||
- If the Bluetooth is disconnected, the board continuous to work in the existing mode unless power is reset.
|
||||
|
||||
- The default bootup mode is "DEMO_RECORDING_MODE".
|
||||
409
libraries/bsec2/examples/bme68x_demo_sample/ble_controller.cpp
Normal file
409
libraries/bsec2/examples/bme68x_demo_sample/ble_controller.cpp
Normal file
|
|
@ -0,0 +1,409 @@
|
|||
/*!
|
||||
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
|
||||
*
|
||||
* BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 THE
|
||||
* 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.
|
||||
*
|
||||
* @file ble_controller.cpp
|
||||
* @date 03 Jan 2024
|
||||
* @version 2.1.5
|
||||
*
|
||||
* @brief ble_controller
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* own header include */
|
||||
#include "ble_controller.h"
|
||||
|
||||
bleController::ble_cmd bleController::cmd_list[] = {
|
||||
{"setlabel", &bleController::parse_cmd_set_label, bleController::SET_LABEL},
|
||||
{"setlabelinfo", &bleController::parse_cmd_set_label_info, bleController::SET_LABEL_INFO},
|
||||
{"getlabelinfo", &bleController::parse_cmd_get_label_info, bleController::GET_LABEL_INFO},
|
||||
{"setrtctime", &bleController::parse_cmd_set_rtc_time, bleController::SET_RTC_TIME},
|
||||
{"getrtctime", &bleController::parse_cmd_get_rtc_time, bleController::GET_RTC_TIME},
|
||||
{"start", &bleController::parse_cmd_start_streaming, bleController::START_STREAMING},
|
||||
{"stop", &bleController::parse_cmd_stop_streaming, bleController::STOP_STREAMING},
|
||||
{"readconfig", &bleController::parse_cmd_read_config, bleController::READ_CONFIG},
|
||||
{"setappmode", &bleController::parse_cmd_set_appmode, bleController::SET_APPMODE},
|
||||
{"getappmode", &bleController::parse_cmd_get_appmode, bleController::GET_APPMODE},
|
||||
{"setgroundtruth", &bleController::parse_cmd_set_groundtruth, bleController::SET_GROUNDTRUTH},
|
||||
{"getfwversion", &bleController::parse_cmd_get_fw_version, bleController::GET_FW_VERSION},
|
||||
};
|
||||
|
||||
QueueHandle_t bleController::msg_queue = nullptr;
|
||||
BLECharacteristic *bleController::ble_char_tx = nullptr, *bleController::ble_char_rx = nullptr;
|
||||
BLEServer *bleController::pServer = nullptr;
|
||||
|
||||
/*!
|
||||
* @brief bleController class Constructor
|
||||
*/
|
||||
bleController::bleController(bleCallBack callBack) : _callBack(callBack)
|
||||
{}
|
||||
|
||||
/*!
|
||||
* @brief function to initialize the ble controller
|
||||
*/
|
||||
demo_ret_code bleController::begin()
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
|
||||
msg_queue = xQueueCreate(BLE_MSG_QUEUE_LEN, sizeof(ble_msg));
|
||||
|
||||
/* Initialize BLE with Device name */
|
||||
BLEDevice::init("BME688 Development Kit");
|
||||
/* Create Server */
|
||||
pServer = BLEDevice::createServer();
|
||||
pServer->setCallbacks(new serverCallbacks());
|
||||
|
||||
/* Create UART Service */
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
/* add characteristics for transmitting and receiving */
|
||||
ble_char_tx = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID_TX,
|
||||
BLECharacteristic::PROPERTY_NOTIFY
|
||||
);
|
||||
ble_char_tx->addDescriptor(new BLE2902());
|
||||
|
||||
ble_char_rx = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID_RX,
|
||||
BLECharacteristic::PROPERTY_WRITE
|
||||
);
|
||||
/* set callback functions */
|
||||
ble_char_rx->setCallbacks(this);
|
||||
/* start advertising */
|
||||
pService->start();
|
||||
pServer->getAdvertising()->start();
|
||||
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief : This function fetches the RTC time which is requested through ble command
|
||||
*/
|
||||
bleController::cmd_status bleController::parse_cmd_get_rtc_time(std::stringstream& ss, ble_msg& msg)
|
||||
{
|
||||
return CMD_VALID;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief : This function parses the RTC time received from the ble device and updates
|
||||
* the RTC time to the ble structure
|
||||
*/
|
||||
bleController::cmd_status bleController::parse_cmd_set_rtc_time(std::stringstream& ss, ble_msg& msg)
|
||||
{
|
||||
uint32_t rtc;
|
||||
|
||||
if (ss >> rtc)
|
||||
{
|
||||
msg.rtc_time = rtc;
|
||||
return CMD_VALID;
|
||||
}
|
||||
return CMD_INVALID;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief : This function updates the received label to the ble structure
|
||||
*/
|
||||
bleController::cmd_status bleController::parse_cmd_set_label(std::stringstream& ss, ble_msg& msg)
|
||||
{
|
||||
uint32_t label;
|
||||
|
||||
if (ss >> label)
|
||||
{
|
||||
msg.label = label;
|
||||
return CMD_VALID;
|
||||
}
|
||||
return CMD_INVALID;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief : This function fetches the current label information
|
||||
*/
|
||||
bleController::cmd_status bleController::parse_cmd_get_label_info(std::stringstream& ss, ble_msg& msg)
|
||||
{
|
||||
return CMD_VALID;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief : This function updates the received label information to the ble structure
|
||||
*/
|
||||
bleController::cmd_status bleController::parse_cmd_set_label_info(std::stringstream& ss, ble_msg& msg)
|
||||
{
|
||||
uint32_t label;
|
||||
std::string lbl_name, lbl_desc;
|
||||
|
||||
if (ss >> label)
|
||||
{
|
||||
msg.label_info.label = label;
|
||||
|
||||
/* read label name until comma */
|
||||
if (std::getline(ss, lbl_name, ','))
|
||||
{
|
||||
lbl_name.erase(lbl_name.begin());
|
||||
|
||||
if (lbl_name.length() > LABEL_NAME_SIZE)
|
||||
{
|
||||
return MAX_LABEL_NAME_REACHED;
|
||||
}
|
||||
memset(msg.label_info.label_name, 0, (LABEL_NAME_SIZE + 1));
|
||||
strncpy(msg.label_info.label_name, lbl_name.c_str(), (LABEL_NAME_SIZE - 1));
|
||||
|
||||
/* read label description until dot */
|
||||
if(std::getline(ss, lbl_desc, '.'))
|
||||
{
|
||||
|
||||
if (lbl_desc.length() > LABEL_DESC_SIZE)
|
||||
{
|
||||
return MAX_LABEL_DESCRIPTION_REACHED;
|
||||
}
|
||||
memset(msg.label_info.label_desc, 0, (LABEL_DESC_SIZE + 1));
|
||||
strncpy(msg.label_info.label_desc, lbl_desc.c_str(), (LABEL_DESC_SIZE - 1));
|
||||
return CMD_VALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
return CMD_INVALID;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief : This function launches sensor data or sensor data and BSEC output streaming through ble
|
||||
* based on the app mode
|
||||
*/
|
||||
bleController::cmd_status bleController::parse_cmd_start_streaming(std::stringstream& ss, ble_msg& msg)
|
||||
{
|
||||
int32_t sensor_num, sample_rate, output_id;
|
||||
|
||||
if (ss >> sensor_num)
|
||||
{
|
||||
msg.bsec.selected_sensor = static_cast<uint8_t>(sensor_num);
|
||||
|
||||
if (ss >> sample_rate)
|
||||
{
|
||||
msg.bsec.sample_rate = static_cast<uint8_t>(sample_rate);
|
||||
msg.bsec.len = 0;
|
||||
|
||||
while ((msg.bsec.len < BSEC_NUMBER_OUTPUTS) && (ss >> output_id))
|
||||
{
|
||||
msg.bsec.output_id[msg.bsec.len++] = static_cast<uint8_t>(output_id);
|
||||
}
|
||||
|
||||
if (ss >> output_id)
|
||||
{
|
||||
return BSEC_OUTPUT_EXCESS_ERROR;
|
||||
}
|
||||
return CMD_VALID;
|
||||
}
|
||||
}
|
||||
return CMD_INVALID;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief : This function stops ble streaming
|
||||
*/
|
||||
bleController::cmd_status bleController::parse_cmd_stop_streaming(std::stringstream& ss, ble_msg& msg)
|
||||
{
|
||||
return CMD_VALID;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief : This function launches the config file data through ble
|
||||
*/
|
||||
bleController::cmd_status bleController::parse_cmd_read_config(std::stringstream& ss, ble_msg& msg)
|
||||
{
|
||||
int32_t file_type;
|
||||
|
||||
if (ss >> file_type)
|
||||
{
|
||||
msg.file_type = static_cast<config_file>(file_type);
|
||||
return CMD_VALID;
|
||||
}
|
||||
return CMD_INVALID;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief : This function updates the current Appmode
|
||||
*/
|
||||
bleController::cmd_status bleController::parse_cmd_set_appmode(std::stringstream& ss, ble_msg& msg)
|
||||
{
|
||||
int32_t mode;
|
||||
|
||||
if (ss >> mode)
|
||||
{
|
||||
msg.mode = static_cast<uint8_t>(mode);
|
||||
return CMD_VALID;
|
||||
}
|
||||
return CMD_INVALID;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief : This function retrieves the current Appmode through ble
|
||||
*/
|
||||
bleController::cmd_status bleController::parse_cmd_get_appmode(std::stringstream& ss, ble_msg& msg)
|
||||
{
|
||||
return CMD_VALID;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* @brief : This function updates the Groundtruth
|
||||
*/
|
||||
bleController::cmd_status bleController::parse_cmd_set_groundtruth(std::stringstream& ss, ble_msg& msg)
|
||||
{
|
||||
int32_t ground_truth;
|
||||
|
||||
if (ss >> ground_truth)
|
||||
{
|
||||
msg.ground_truth = static_cast<uint32_t>(ground_truth);
|
||||
return CMD_VALID;
|
||||
}
|
||||
return CMD_INVALID;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief : This function retrieves the current firmware version through ble
|
||||
*/
|
||||
bleController::cmd_status bleController::parse_cmd_get_fw_version(std::stringstream& ss, ble_msg& msg)
|
||||
{
|
||||
return CMD_VALID;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief function gets called when data is received from a bluetooth device.
|
||||
* It will read in the sent bluetooth command
|
||||
*/
|
||||
void bleController::onWrite(BLECharacteristic *pCharacteristic)
|
||||
{
|
||||
std::string rx_value = pCharacteristic->getValue(), cmd_name;
|
||||
std::stringstream ss(rx_value);
|
||||
cmd_status status = CMD_INVALID;
|
||||
ble_msg msg;
|
||||
|
||||
if (ss >> cmd_name)
|
||||
{
|
||||
StaticJsonDocument<BLE_JSON_DOC_SIZE> jsonDoc;
|
||||
|
||||
for (auto& cmd : cmd_list)
|
||||
{
|
||||
|
||||
if (cmd_name == cmd.name)
|
||||
{
|
||||
status = cmd.parse(ss, msg);
|
||||
|
||||
if (status == CMD_VALID)
|
||||
{
|
||||
msg.name = cmd.name;
|
||||
msg.id = cmd.id;
|
||||
|
||||
if (xQueueSendFromISR(msg_queue, (const void*)&msg, 0) == pdPASS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = CONTROLLER_QUEUE_FULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
jsonDoc[cmd_name.c_str()] = status;
|
||||
send_notification(jsonDoc);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief : This function checks the ble connection status, restarts advertising if disconnected
|
||||
*/
|
||||
void bleController::check_ble_connection_sts()
|
||||
{
|
||||
/* disconnecting */
|
||||
if (!device_connected && old_device_connected)
|
||||
{
|
||||
pServer->startAdvertising(); /* restart advertising, when ble is disconnected */
|
||||
old_device_connected = device_connected;
|
||||
}
|
||||
/* connecting */
|
||||
if (device_connected && !old_device_connected)
|
||||
{
|
||||
old_device_connected = device_connected;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief function dequeues the last received ble message. The ble callBack is
|
||||
* called if a new message is available.
|
||||
*/
|
||||
bool bleController::dequeue_ble_msg(void)
|
||||
{
|
||||
ble_msg msg;
|
||||
|
||||
if (xQueueReceive(msg_queue, &msg, 0) == pdPASS)
|
||||
{
|
||||
StaticJsonDocument<BLE_JSON_DOC_SIZE> jsonDoc;
|
||||
|
||||
if (_callBack != nullptr)
|
||||
{
|
||||
_callBack(msg, jsonDoc);
|
||||
|
||||
send_notification(jsonDoc);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief function to send a json formatted notification
|
||||
*/
|
||||
void bleController::send_notification(JsonDocument& jsonDoc)
|
||||
{
|
||||
String notif, msg;
|
||||
|
||||
serializeJson(jsonDoc, notif);
|
||||
|
||||
size_t not_len = notif.length(), begin_msg = 0, end_msg = BLE_CONTROLLER_NOTIF_SIZE;
|
||||
|
||||
while (begin_msg < not_len)
|
||||
{
|
||||
|
||||
if (end_msg > not_len)
|
||||
{
|
||||
end_msg = not_len;
|
||||
}
|
||||
msg = notif.substring(begin_msg, end_msg);
|
||||
|
||||
begin_msg += BLE_CONTROLLER_NOTIF_SIZE;
|
||||
end_msg += BLE_CONTROLLER_NOTIF_SIZE;
|
||||
|
||||
ble_char_tx->setValue((const char*)msg.c_str());
|
||||
ble_char_tx->notify();
|
||||
}
|
||||
}
|
||||
327
libraries/bsec2/examples/bme68x_demo_sample/ble_controller.h
Normal file
327
libraries/bsec2/examples/bme68x_demo_sample/ble_controller.h
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
/*!
|
||||
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
|
||||
*
|
||||
* BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 THE
|
||||
* 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.
|
||||
*
|
||||
* @file ble_controller.h
|
||||
* @date 03 Jan 2024
|
||||
* @version 2.1.5
|
||||
*
|
||||
* @brief Header file for the ble controller
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BLE_CONTROLLER_H
|
||||
#define BLE_CONTROLLER_H
|
||||
|
||||
/* Include Arduino Core */
|
||||
#include <Arduino.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <sstream>
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLE2902.h>
|
||||
#include "BLECharacteristic.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include "label_provider.h"
|
||||
#include "demo_app.h"
|
||||
|
||||
/* Bluetooth UART UUID's */
|
||||
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
#define BLE_MSG_QUEUE_LEN UINT8_C(3)
|
||||
#define BLE_JSON_DOC_SIZE UINT16_C(2048)
|
||||
#define BLE_CONTROLLER_NOTIF_SIZE UINT16_C(600)
|
||||
#define BLE_MAX_MTU_SIZE UINT16_C(512)
|
||||
|
||||
static bool device_connected = false;
|
||||
static bool old_device_connected = false;
|
||||
|
||||
/*!
|
||||
* @brief Class library for the ble controller
|
||||
*/
|
||||
class bleController: public BLECharacteristicCallbacks
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* @brief ble communication status
|
||||
*/
|
||||
enum cmd_status
|
||||
{
|
||||
CMD_VALID,
|
||||
CMD_INVALID,
|
||||
CONTROLLER_QUEUE_FULL,
|
||||
LABEL_INVALID,
|
||||
BSEC_SELECTED_SENSOR_INVALID,
|
||||
BSEC_CONFIG_FILE_ERROR,
|
||||
BSEC_INIT_ERROR,
|
||||
BSEC_SET_CONFIG_ERROR,
|
||||
BSEC_UPDATE_SUBSCRIPTION_ERROR,
|
||||
BSEC_RUN_ERROR,
|
||||
BSEC_OUTPUT_EXCESS_ERROR,
|
||||
SENSOR_CONFIG_MISSING,
|
||||
SENSOR_INITIALIZATION_FAILED,
|
||||
SD_CARD_INIT_ERROR,
|
||||
CONFIG_FILE_ERROR,
|
||||
APP_ALREADY_IN_STREAMING_MODE,
|
||||
SENSOR_READ_ERROR,
|
||||
BSEC_CONFIG_FILE_MISSING,
|
||||
AI_CONFIG_FILE_MISSING,
|
||||
LABEL_INFO_FILE_MISSING,
|
||||
INVALID_APP_MODE,
|
||||
LABEL_FILE_OPEN_FAILED,
|
||||
MAX_LABEL_NAME_REACHED,
|
||||
MAX_LABEL_DESCRIPTION_REACHED,
|
||||
FILE_OPEN_ERROR,
|
||||
DESERIALIZATION_FAILED,
|
||||
LABEL_NOT_FOUND,
|
||||
AI_CONFIG_AND_SUBSCRIPTION_MISSMATCH
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief bsec sample rate enumeration
|
||||
*/
|
||||
enum bsec_sample_rate
|
||||
{
|
||||
ULP,
|
||||
LP,
|
||||
HP
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief config file type enumeration
|
||||
*/
|
||||
enum config_file
|
||||
{
|
||||
BMECONFIG,
|
||||
AICONFIG
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief bluetooth message id enumeration
|
||||
*/
|
||||
enum ble_msg_id
|
||||
{
|
||||
GET_LABEL_INFO,
|
||||
SET_LABEL_INFO,
|
||||
SET_LABEL,
|
||||
GET_RTC_TIME,
|
||||
SET_RTC_TIME,
|
||||
START_STREAMING,
|
||||
STOP_STREAMING,
|
||||
READ_CONFIG,
|
||||
SET_APPMODE,
|
||||
GET_APPMODE,
|
||||
SET_GROUNDTRUTH,
|
||||
GET_FW_VERSION
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief bluetooth bsec message
|
||||
*/
|
||||
struct ble_bsec_msg
|
||||
{
|
||||
uint8_t selected_sensor;
|
||||
uint8_t sample_rate;
|
||||
uint8_t len;
|
||||
uint8_t output_id[BSEC_NUMBER_OUTPUTS];
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief label information
|
||||
*/
|
||||
struct ble_label_info
|
||||
{
|
||||
uint32_t label;
|
||||
char label_name[LABEL_NAME_SIZE + 1];
|
||||
char label_desc[LABEL_DESC_SIZE + 1];
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* @brief bluetooth message structure
|
||||
*/
|
||||
struct ble_msg
|
||||
{
|
||||
const char *name;
|
||||
ble_msg_id id;
|
||||
union
|
||||
{
|
||||
ble_bsec_msg bsec;
|
||||
uint32_t label;
|
||||
uint32_t rtc_time;
|
||||
config_file file_type;
|
||||
uint8_t mode;
|
||||
ble_label_info label_info;
|
||||
uint32_t ground_truth;
|
||||
};
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief bluetooth command structure
|
||||
*/
|
||||
struct ble_cmd
|
||||
{
|
||||
const char *name;
|
||||
cmd_status (*parse)(std::stringstream& ss, ble_msg& msg);
|
||||
ble_msg_id id;
|
||||
};
|
||||
|
||||
typedef void (*bleCallBack)(const ble_msg &msg, JsonDocument& jsonDoc);
|
||||
|
||||
/*!
|
||||
* @brief : The constructor of the bleController class creates an instance of the class
|
||||
*
|
||||
* @param[in] callBack : ble callBack called when a message is dequeued.
|
||||
*/
|
||||
bleController(bleCallBack callBack);
|
||||
|
||||
/*!
|
||||
* @brief : This function initializes the ble controller.
|
||||
*
|
||||
* @return bosch error code
|
||||
*/
|
||||
demo_ret_code begin();
|
||||
|
||||
/*!
|
||||
* @brief : This function dequeues the last received ble message. The ble callBack
|
||||
* is called if a new message is available.
|
||||
*
|
||||
* @return true if a new message is available else false
|
||||
*/
|
||||
bool dequeue_ble_msg(void);
|
||||
|
||||
/*!
|
||||
* @brief : This function send a json formatted notification.
|
||||
*
|
||||
* @param[out] jsonDoc : json formatted message
|
||||
*/
|
||||
void send_notification(JsonDocument& jsonDoc);
|
||||
|
||||
/*!
|
||||
* @brief : This function gets called when data is received from a bluetooth device.
|
||||
* It will read in the sent bluetooth command.
|
||||
*/
|
||||
void onWrite(BLECharacteristic *pCharacteristic);
|
||||
|
||||
/*!
|
||||
* @brief : This function checks the ble connection status, restarts advertising if disconnected
|
||||
*/
|
||||
void check_ble_connection_sts();
|
||||
|
||||
private:
|
||||
bleCallBack _callBack;
|
||||
|
||||
static QueueHandle_t msg_queue;
|
||||
static ble_cmd cmd_list[];
|
||||
static BLECharacteristic *ble_char_tx, *ble_char_rx;
|
||||
static BLEServer *pServer;
|
||||
|
||||
/*!
|
||||
* @brief : This function fetches the RTC time which is requested through ble command
|
||||
*/
|
||||
static cmd_status parse_cmd_get_rtc_time(std::stringstream& ss, ble_msg& msg);
|
||||
|
||||
/*!
|
||||
* @brief : This function parses the RTC time received from the ble device and updates
|
||||
* the RTC time to the ble structure
|
||||
*/
|
||||
static cmd_status parse_cmd_set_rtc_time(std::stringstream& ss, ble_msg& msg);
|
||||
|
||||
/*!
|
||||
* @brief : This function fetches the current label information from the .bmelabelinfo file
|
||||
*/
|
||||
static cmd_status parse_cmd_get_label_info(std::stringstream& ss, ble_msg& msg);
|
||||
|
||||
/*!
|
||||
* @brief : This function updates the received label information to the ble structure
|
||||
*/
|
||||
static cmd_status parse_cmd_set_label_info(std::stringstream& ss, ble_msg& msg);
|
||||
|
||||
/*!
|
||||
* @brief : This function updates the received label to the ble structure
|
||||
*/
|
||||
static cmd_status parse_cmd_set_label(std::stringstream& ss, ble_msg& msg);
|
||||
|
||||
/*!
|
||||
* @brief : This function launches sensor data or sensor data and BSEC output streaming through ble
|
||||
* based on the app mode
|
||||
*/
|
||||
static cmd_status parse_cmd_start_streaming(std::stringstream& ss, ble_msg& msg);
|
||||
|
||||
/*!
|
||||
* @brief : This function stops ble streaming
|
||||
*/
|
||||
static cmd_status parse_cmd_stop_streaming(std::stringstream& ss, ble_msg& msg);
|
||||
|
||||
/*!
|
||||
* @brief : This function launches the config file data through ble
|
||||
*/
|
||||
static cmd_status parse_cmd_read_config(std::stringstream& ss, ble_msg& msg);
|
||||
|
||||
/*!
|
||||
* @brief : This function updates the current Appmode
|
||||
*/
|
||||
static cmd_status parse_cmd_set_appmode(std::stringstream& ss, ble_msg& msg);
|
||||
|
||||
/*!
|
||||
* @brief : This function retrieves the current Appmode through ble
|
||||
*/
|
||||
static cmd_status parse_cmd_get_appmode(std::stringstream& ss, ble_msg& msg);
|
||||
|
||||
/*!
|
||||
* @brief : This function updates the Groundtruth through ble
|
||||
*/
|
||||
static cmd_status parse_cmd_set_groundtruth(std::stringstream& ss, ble_msg& msg);
|
||||
|
||||
/*!
|
||||
* @brief : This function retrieves the current firmware version through ble
|
||||
*/
|
||||
static cmd_status parse_cmd_get_fw_version(std::stringstream& ss, ble_msg& msg);
|
||||
};
|
||||
|
||||
class serverCallbacks: public BLEServerCallbacks
|
||||
{
|
||||
void onConnect(BLEServer* pServer)
|
||||
{
|
||||
device_connected = true;
|
||||
}
|
||||
|
||||
void onDisconnect(BLEServer* pServer)
|
||||
{
|
||||
device_connected = false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,438 @@
|
|||
/*!
|
||||
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
|
||||
*
|
||||
* BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 THE
|
||||
* 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.
|
||||
*
|
||||
* @file bme68x_datalogger.cpp
|
||||
* @date 03 Jan 2024
|
||||
* @version 2.1.5
|
||||
*
|
||||
* @brief bme68x_datalogger
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* own header include */
|
||||
#include "bme68x_datalogger.h"
|
||||
#include <Esp.h>
|
||||
|
||||
/*!
|
||||
* @brief The constructor of the bme68xDataLogger class
|
||||
*/
|
||||
bme68xDataLogger::bme68xDataLogger() : _file_counter(1)
|
||||
{}
|
||||
|
||||
/*!
|
||||
* @brief Function to configure the datalogger using the provided sensor config file
|
||||
*/
|
||||
demo_ret_code bme68xDataLogger::begin(const String& config_name)
|
||||
{
|
||||
demo_ret_code ret_code = utils::begin();
|
||||
|
||||
_config_name = config_name;
|
||||
|
||||
if (ret_code >= EDK_OK)
|
||||
{
|
||||
/* Resets the file counter when seed file is generated */
|
||||
_file_counter = 1;
|
||||
ret_code = create_log_file();
|
||||
|
||||
if (ret_code >= EDK_OK)
|
||||
{
|
||||
ret_code = create_label_info_file();
|
||||
}
|
||||
_ss.setf(std::ios::fixed, std::ios::floatfield);
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Function which flushes the buffered sensor data to the current log file
|
||||
*/
|
||||
demo_ret_code bme68xDataLogger::flush()
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
File logFile;
|
||||
std::string txt;
|
||||
|
||||
if (_ss.rdbuf()->in_avail())
|
||||
{
|
||||
txt = _ss.str();
|
||||
_ss.str(std::string());
|
||||
|
||||
if (_file_counter && logFile.open(_log_file_name.c_str(), O_RDWR | O_AT_END))
|
||||
{
|
||||
logFile.seek(_sensor_data_pos);
|
||||
logFile.print(txt.c_str());
|
||||
_sensor_data_pos = logFile.position();
|
||||
logFile.println("\n\t ]\n\t}\n}");
|
||||
|
||||
if (logFile.size() >= FILE_SIZE_LIMIT)
|
||||
{
|
||||
logFile.close();
|
||||
++_file_counter;
|
||||
ret_code = create_log_file();
|
||||
|
||||
if (ret_code >= EDK_OK)
|
||||
{
|
||||
ret_code = create_label_info_file();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logFile.close();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_code = EDK_DATALOGGER_LOG_FILE_ERROR;
|
||||
}
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Function writes the sensor data to the current log file
|
||||
*/
|
||||
demo_ret_code bme68xDataLogger::write_sensor_data(const uint8_t* num, const uint32_t* sensor_id, const uint8_t* sensorMode,
|
||||
const bme68x_data* bme68xData, const uint32_t* scan_cycle_index,
|
||||
gas_label label, demo_ret_code code)
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
uint32_t rtc_tsp = utils::get_rtc().now().unixtime();
|
||||
uint32_t time_since_power_on = millis();
|
||||
|
||||
if (_end_of_line)
|
||||
{
|
||||
_ss << ",\n";
|
||||
}
|
||||
_ss << "\t\t\t[\n\t\t\t\t";
|
||||
(num != nullptr) ? (_ss << (uint32_t)*num) : (_ss << "null");
|
||||
_ss << ",\n\t\t\t\t";
|
||||
(sensor_id != nullptr) ? (_ss << (uint32_t)*sensor_id) : (_ss << "null");
|
||||
_ss << ",\n\t\t\t\t";
|
||||
_ss << time_since_power_on;
|
||||
_ss << ",\n\t\t\t\t";
|
||||
_ss << rtc_tsp;
|
||||
_ss << ",\n\t\t\t\t";
|
||||
(bme68xData != nullptr) ? (_ss << bme68xData->temperature) : (_ss << "null");
|
||||
_ss << ",\n\t\t\t\t";
|
||||
(bme68xData != nullptr) ? (_ss << (bme68xData->pressure * .01f)) : (_ss << "null");
|
||||
_ss << ",\n\t\t\t\t";
|
||||
(bme68xData != nullptr) ? (_ss << bme68xData->humidity) : (_ss << "null");
|
||||
_ss << ",\n\t\t\t\t";
|
||||
(bme68xData != nullptr) ? (_ss << bme68xData->gas_resistance) : (_ss << "null");
|
||||
_ss << ",\n\t\t\t\t";
|
||||
(bme68xData != nullptr) ? (_ss << (uint32_t)bme68xData->gas_index) : (_ss << "null");
|
||||
_ss << ",\n\t\t\t\t";
|
||||
_ss << (bool)(BME68X_PARALLEL_MODE);
|
||||
_ss << ",\n\t\t\t\t";
|
||||
(scan_cycle_index != nullptr) ? (_ss << (uint32_t)(*scan_cycle_index)) : (_ss << "null");
|
||||
_ss << ",\n\t\t\t\t";
|
||||
_ss << (uint32_t)label;
|
||||
_ss << ",\n\t\t\t\t";
|
||||
_ss << (uint32_t)code;
|
||||
_ss << "\n\t\t\t]";
|
||||
|
||||
_end_of_line = true;
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Function stores the labelTag, labelName and labelDescription to the .bmelabelinfo file
|
||||
*/
|
||||
demo_ret_code bme68xDataLogger::set_label_info(int32_t label_tag, const String& label_name, const String& label_desc)
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
|
||||
File logFile;
|
||||
|
||||
if (logFile.open(_label_file_name.c_str(), O_READ))
|
||||
{
|
||||
DeserializationError error = deserializeJson(label_doc, logFile);
|
||||
logFile.close();
|
||||
|
||||
if (error)
|
||||
{
|
||||
Serial.println(error.c_str());
|
||||
return EDK_SENSOR_MANAGER_JSON_DESERIAL_ERROR;
|
||||
}
|
||||
JsonArray lblInfo = label_doc["labelInformation"].as<JsonArray>();
|
||||
|
||||
JsonObject obj = lblInfo.createNestedObject();
|
||||
obj["labelTag"] = label_tag;
|
||||
obj["labelName"] = label_name;
|
||||
obj["labelDescription"] = label_desc;
|
||||
|
||||
if (logFile.open(_label_file_name.c_str(), O_RDWR | O_TRUNC))
|
||||
{
|
||||
serializeJsonPretty(label_doc, logFile);
|
||||
logFile.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_code = EDK_DATALOGGER_LABEL_INFO_FILE_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_code = EDK_DATALOGGER_LABEL_INFO_FILE_ERROR;
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Function to create a bme68x datalogger output file with .bmerawdata extension
|
||||
*/
|
||||
demo_ret_code bme68xDataLogger::create_log_file()
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
String mac_str = utils::get_mac_address();
|
||||
String log_file_base_name = "_Board_" + mac_str + "_PowerOnOff_1_";
|
||||
|
||||
_log_file_name = utils::get_date_time() + log_file_base_name + utils::get_file_seed() +
|
||||
"_File_" + String(_file_counter) + BME68X_RAWDATA_FILE_EXT;
|
||||
|
||||
File configFile, logFile;
|
||||
|
||||
if (_config_name.length() && !configFile.open(_config_name.c_str(), O_RDWR))
|
||||
{
|
||||
ret_code = EDK_DATALOGGER_SENSOR_CONFIG_FILE_ERROR;
|
||||
}
|
||||
else if (!logFile.open(_log_file_name.c_str(), O_RDWR | O_CREAT))
|
||||
{
|
||||
ret_code = EDK_DATALOGGER_LOG_FILE_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_config_name.length())
|
||||
{
|
||||
String line_buffer;
|
||||
|
||||
/* read in each line from the config file and copy it to the log file */
|
||||
while (configFile.available())
|
||||
{
|
||||
line_buffer = configFile.readStringUntil('\n');
|
||||
|
||||
/* skip the last closing curly bracket of the JSON document */
|
||||
if (line_buffer == "}")
|
||||
{
|
||||
logFile.println("\t,");
|
||||
break;
|
||||
}
|
||||
logFile.println(line_buffer);
|
||||
}
|
||||
configFile.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
logFile.println("{");
|
||||
}
|
||||
|
||||
/* write data header / skeleton */
|
||||
/* raw data header */
|
||||
logFile.println("\t\"rawDataHeader\": {");
|
||||
logFile.println("\t\t\"counterPowerOnOff\": 1,");
|
||||
logFile.println("\t\t\"seedPowerOnOff\": \"" + utils::get_file_seed() + "\",");
|
||||
logFile.println("\t\t\"counterFileLimit\": " + String(_file_counter) + ",");
|
||||
logFile.println("\t\t\"dateCreated\": \"" + String(utils::get_rtc().now().unixtime()) + "\",");
|
||||
logFile.println("\t\t\"dateCreated_ISO\": \"" + utils::get_rtc().now().timestamp() + "+00:00\",");
|
||||
logFile.println("\t\t\"firmwareVersion\": \"" + String(FIRMWARE_VERSION) + "\",");
|
||||
logFile.println("\t\t\"boardId\": \"" + mac_str + "\"");
|
||||
logFile.println("\t},");
|
||||
logFile.println("\t\"rawDataBody\": {");
|
||||
logFile.println("\t\t\"dataColumns\": [");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Sensor Index\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"sensor_index\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 1");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Sensor ID\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"sensor_id\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 2");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Time Since PowerOn\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"Milliseconds\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"timestamp_since_poweron\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 3");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Real time clock\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"Unix Timestamp: seconds since Jan 01 1970. (UTC); 0 = missing\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"real_time_clock\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 4");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Temperature\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"DegreesCelcius\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"float\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"temperature\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 5");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Pressure\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"Hectopascals\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"float\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"pressure\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 6");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Relative Humidity\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"Percent\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"float\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"relative_humidity\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 7");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Resistance Gassensor\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"Ohms\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"float\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"resistance_gassensor\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 8");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Heater Profile Step Index\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"heater_profile_step_index\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 9");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Scanning Mode Enabled\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"boolean\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"scanning_enabled\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 10");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Scanning Cycle Index\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"scanning_cycle_index\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 11");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Label Tag\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"label_tag\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 12");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Error Code\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"error_code\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 13");
|
||||
logFile.println("\t\t\t}");
|
||||
logFile.println("\t\t],");
|
||||
|
||||
/* data block */
|
||||
logFile.println("\t\t\"dataBlock\": [");
|
||||
/* save position in file, where to write the first data set */
|
||||
_sensor_data_pos = logFile.position();
|
||||
logFile.println("\t\t]");
|
||||
logFile.println("\t}");
|
||||
logFile.println("}");
|
||||
|
||||
/* close log file */
|
||||
logFile.close();
|
||||
|
||||
_end_of_line = false;
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Function to create a bme68x label information file with .bmelabelinfo extension
|
||||
*/
|
||||
demo_ret_code bme68xDataLogger::create_label_info_file()
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
String mac_str = utils::get_mac_address();
|
||||
String label_file_base_name = "_Board_" + mac_str + "_PowerOnOff_1_";
|
||||
|
||||
_label_file_name = utils::get_date_time() + label_file_base_name + utils::get_file_seed() +
|
||||
"_File_" + String(_file_counter) + BME68X_LABEL_INFO_FILE_EXT;
|
||||
File logFile;
|
||||
|
||||
if (!logFile.open(_label_file_name.c_str(), O_RDWR | O_CREAT))
|
||||
{
|
||||
ret_code = EDK_DATALOGGER_LABEL_INFO_FILE_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* write labelinfo header / skeleton */
|
||||
logFile.println("{");
|
||||
logFile.println("\t\"labelInfoHeader\": {");
|
||||
logFile.println("\t\t\"counterPowerOnOff\": 1,");
|
||||
logFile.println("\t\t\"seedPowerOnOff\": \"" + utils::get_file_seed() + "\",");
|
||||
logFile.println("\t\t\"dateCreated\": \"" + String(utils::get_rtc().now().unixtime()) + "\",");
|
||||
logFile.println("\t\t\"dateCreated_ISO\": \"" + utils::get_rtc().now().timestamp() + "+00:00\",");
|
||||
logFile.println("\t\t\"firmwareVersion\": \"" + String(FIRMWARE_VERSION) + "\",");
|
||||
logFile.println("\t\t\"boardId\": \"" + mac_str + "\"");
|
||||
logFile.println("\t},");
|
||||
logFile.println("\t\"labelInformation\": [");
|
||||
logFile.println("\t\t{");
|
||||
logFile.println("\t\t\t\"labelTag\": 0,");
|
||||
logFile.println("\t\t\t\"labelName\": \"Initial\",");
|
||||
logFile.println("\t\t\t\"labelDescription\": \"Standard label for no label has been set\"");
|
||||
logFile.println("\t\t},");
|
||||
logFile.println("\t\t{");
|
||||
logFile.println("\t\t\t\"labelTag\": 1,");
|
||||
logFile.println("\t\t\t\"labelName\": \"Button 1\",");
|
||||
logFile.println("\t\t\t\"labelDescription\": \"Standard label for hardware button 1 pressed\"");
|
||||
logFile.println("\t\t},");
|
||||
logFile.println("\t\t{");
|
||||
logFile.println("\t\t\t\"labelTag\": 2,");
|
||||
logFile.println("\t\t\t\"labelName\": \"Button 2\",");
|
||||
logFile.println("\t\t\t\"labelDescription\": \"Standard label for hardware button 2 pressed\"");
|
||||
logFile.println("\t\t},");
|
||||
logFile.println("\t\t{");
|
||||
logFile.println("\t\t\t\"labelTag\": 3,");
|
||||
logFile.println("\t\t\t\"labelName\": \"Button 1+2\",");
|
||||
logFile.println("\t\t\t\"labelDescription\": \"Standard label for hardware button 1 and button 2 pressed\"");
|
||||
logFile.print("\t\t}");
|
||||
logFile.println("\n\t]");
|
||||
logFile.print("}");
|
||||
|
||||
/* close log file */
|
||||
logFile.close();
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
137
libraries/bsec2/examples/bme68x_demo_sample/bme68x_datalogger.h
Normal file
137
libraries/bsec2/examples/bme68x_demo_sample/bme68x_datalogger.h
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/*!
|
||||
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
|
||||
*
|
||||
* BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 THE
|
||||
* 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.
|
||||
*
|
||||
* @file bme68x_datalogger.h
|
||||
* @date 03 Jan 2024
|
||||
* @version 2.1.5
|
||||
*
|
||||
* @brief Header file for the bme68x datalogger
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BME68X_DATALOGGER_H
|
||||
#define BME68X_DATALOGGER_H
|
||||
|
||||
/* Include of Arduino Core */
|
||||
#include "Arduino.h"
|
||||
#include <SdFat.h>
|
||||
#include <RTClib.h>
|
||||
#include <base64.h>
|
||||
#include "utils.h"
|
||||
#include "demo_app.h"
|
||||
#include "label_provider.h"
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#define DOC_SIZE UINT32_C(50000)
|
||||
|
||||
/*!
|
||||
* @brief : Class library that holds functionality of the bme68x datalogger
|
||||
*/
|
||||
class bme68xDataLogger
|
||||
{
|
||||
private:
|
||||
String _config_name, _log_file_name, _label_file_name;
|
||||
std::stringstream _ss;
|
||||
uint32_t _sensor_data_pos = 0;
|
||||
uint32_t _file_counter = 1;
|
||||
bool _end_of_line = false;
|
||||
|
||||
/*!
|
||||
* @brief : This function creates a bme68x datalogger output file with .bmerawdata extension
|
||||
*
|
||||
* @return bosch error code
|
||||
*/
|
||||
demo_ret_code create_log_file();
|
||||
|
||||
/*!
|
||||
* @brief : This function creates a bme68x label information file with .bmelabelinfo extension
|
||||
*
|
||||
* @return bosch error code
|
||||
*/
|
||||
demo_ret_code create_label_info_file();
|
||||
public:
|
||||
StaticJsonDocument<DOC_SIZE> label_doc;
|
||||
|
||||
/*!
|
||||
* @brief : The constructor of the bme68xDataLogger class
|
||||
* Creates an instance of the class
|
||||
*/
|
||||
bme68xDataLogger();
|
||||
|
||||
/*!
|
||||
* @brief : This function configures the datalogger using the provided sensor config file
|
||||
*
|
||||
* @param[in] configName : sensor configuration file
|
||||
*
|
||||
* @return bosch error code
|
||||
*/
|
||||
demo_ret_code begin(const String& configName = "");
|
||||
|
||||
/*!
|
||||
* @brief : This function flushes the buffered sensor data to the current log file
|
||||
*
|
||||
* @return bosch error code
|
||||
*/
|
||||
demo_ret_code flush();
|
||||
|
||||
/*!
|
||||
* @brief : This function writes the sensor data to the current log file.
|
||||
*
|
||||
* @param[in] num : sensor number
|
||||
* @param[in] sensorId : pointer to sensor id, if NULL a null json object is inserted
|
||||
* @param[in] sensorMode : pointer to sensor operation mode, if NULL a null json object is inserted
|
||||
* @param[in] bme68xData : pointer to bme68x data, if NULL a null json object is inserted
|
||||
* @param[in] scanCycleIndex : pointer to sensor scanning cycle index
|
||||
* @param[in] label : class label
|
||||
* @param[in] code : application return code
|
||||
*
|
||||
* @return bosch error code
|
||||
*/
|
||||
demo_ret_code write_sensor_data(const uint8_t* num, const uint32_t* sensor_id, const uint8_t* sensor_mode,
|
||||
const bme68x_data* bme68xData, const uint32_t* scan_cycle_index,
|
||||
gas_label label, demo_ret_code code);
|
||||
/*!
|
||||
* @brief : This function stores the labelTag, labelName and labelDescription to the .bmelabelinfo file.
|
||||
*
|
||||
* @param[in] labelTag : label tag
|
||||
* @param[in] labelName : reference to the label name
|
||||
* @param[in] labelDesc : reference to the label description
|
||||
*
|
||||
* @return bosch error code
|
||||
*/
|
||||
demo_ret_code set_label_info(int32_t label_tag,const String& label_name, const String& label_desc);
|
||||
};
|
||||
|
||||
#endif
|
||||
1088
libraries/bsec2/examples/bme68x_demo_sample/bme68x_demo_sample.ino
Normal file
1088
libraries/bsec2/examples/bme68x_demo_sample/bme68x_demo_sample.ino
Normal file
File diff suppressed because it is too large
Load diff
988
libraries/bsec2/examples/bme68x_demo_sample/bsec_datalogger.cpp
Normal file
988
libraries/bsec2/examples/bme68x_demo_sample/bsec_datalogger.cpp
Normal file
|
|
@ -0,0 +1,988 @@
|
|||
/*!
|
||||
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
|
||||
*
|
||||
* BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 THE
|
||||
* 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.
|
||||
*
|
||||
* @file bsec_datalogger.cpp
|
||||
* @date 03 Jan 2024
|
||||
* @version 2.1.5
|
||||
*
|
||||
* @brief bsec_datalogger
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* own header include */
|
||||
#include "bsec_datalogger.h"
|
||||
#include <math.h>
|
||||
#include <Esp.h>
|
||||
|
||||
StaticJsonDocument<80> filter1;
|
||||
StaticJsonDocument<1024> doc;
|
||||
StaticJsonDocument<70> filter2;
|
||||
StaticJsonDocument<512> label_doc;
|
||||
StaticJsonDocument<2048> config;
|
||||
|
||||
/*!
|
||||
* @brief The constructor of the bsec_datalogger class
|
||||
*/
|
||||
bsecDataLogger::bsecDataLogger() : _bsec_file_counter(1)
|
||||
{}
|
||||
|
||||
/*!
|
||||
* @brief This function configures the bsec datalogger using the provided bsec config string file
|
||||
*/
|
||||
demo_ret_code bsecDataLogger::begin(const String& config_name, const bsec_version_t& bsec_version, uint8_t sensor_num)
|
||||
{
|
||||
demo_ret_code ret_code = utils::begin();
|
||||
|
||||
_ai_config_name = config_name;
|
||||
_version = bsec_version;
|
||||
|
||||
if (ret_code >= EDK_OK)
|
||||
{
|
||||
ret_code = create_bsec_file();
|
||||
|
||||
if (ret_code >= EDK_OK)
|
||||
{
|
||||
ret_code = create_raw_data_file(sensor_num);
|
||||
|
||||
if (ret_code >= EDK_OK)
|
||||
{
|
||||
ret_code = create_label_info_file();
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function creates a bsec output file
|
||||
*/
|
||||
demo_ret_code bsecDataLogger::create_bsec_file()
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
String mac_str = utils::get_mac_address();
|
||||
String ai_file_base_name = "_Board_" + mac_str + "_PowerOnOff_1_";
|
||||
|
||||
_ai_file_name = utils::get_date_time() + ai_file_base_name + utils::get_file_seed() + "_File_" +
|
||||
String(_bsec_file_counter) + AI_DATA_FILE_EXT;
|
||||
|
||||
File configFile, logFile;
|
||||
|
||||
if (_ai_config_name.length() && !configFile.open(_ai_config_name.c_str(), O_RDWR))
|
||||
{
|
||||
ret_code = EDK_DATALOGGER_AI_CONFIG_FILE_ERROR;
|
||||
}
|
||||
else if (!logFile.open(_ai_file_name.c_str(), O_RDWR | O_CREAT))
|
||||
{
|
||||
ret_code = EDK_DATALOGGER_LOG_FILE_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (_ai_config_name.length())
|
||||
{
|
||||
String line_buffer;
|
||||
|
||||
/* read in each line from the config file and copy it to the log file */
|
||||
while (configFile.available())
|
||||
{
|
||||
line_buffer = configFile.readStringUntil('\n');
|
||||
|
||||
/* skip the last closing curly bracket of the JSON document */
|
||||
if (line_buffer == "}")
|
||||
{
|
||||
logFile.println("\t,");
|
||||
break;
|
||||
}
|
||||
logFile.println(line_buffer);
|
||||
}
|
||||
configFile.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
logFile.println("{");
|
||||
}
|
||||
logFile.println("\t\"aiPredictionsDataHeader\": {");
|
||||
logFile.println("\t\t\"counterPowerOnOff\": 1,");
|
||||
logFile.println("\t\t\"seedPowerOnOff\": \"" + utils::get_file_seed() + "\",");
|
||||
logFile.println("\t\t\"counterFileLimit\": " + String(_bsec_file_counter) + ",");
|
||||
logFile.println("\t\t\"dateCreated\": \"" + String(utils::get_rtc().now().unixtime()) + "\",");
|
||||
logFile.println("\t\t\"dateCreated_ISO\": \"" + utils::get_rtc().now().timestamp() + "+00:00\",");
|
||||
logFile.println("\t\t\"firmwareVersion\": \"" + String(FIRMWARE_VERSION) + "\",");
|
||||
logFile.println("\t\t\"bsecVersion\": \"" + String(_version.major) + "." + String(_version.minor) + \
|
||||
"." + String(_version.major_bugfix) + "." + String(_version.minor_bugfix) + "\",");
|
||||
logFile.println("\t\t\"boardId\": \"" + mac_str + "\"");
|
||||
logFile.println("\t},");
|
||||
logFile.println("\t\"aiPredictionsDataBody\": {");
|
||||
logFile.println("\t\t\"dataColumns\": [");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Sensor Index\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"sensor_index\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 1");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Sensor ID\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"sensor_id\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 2");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Start Time Since PowerOn\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"Milliseconds\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"start_time_since_poweron\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 3");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"End Time Since PowerOn\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"Milliseconds\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"end_time_since_poweron\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 4");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Class/Target 1 prediction\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"float\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"classtarget_1_prediction\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 5");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Class/Target 2 prediction\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"float\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"classtarget_2_prediction\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 6");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Class/Target 3 prediction\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"float\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"classtarget_3_prediction\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 7");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Class/Target 4 prediction\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"float\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"classtarget_4_prediction\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 8");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Prediction accuracy\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"prediction_accuracy\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 9");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"IAQ\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"float\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"iaq\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 10");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"IAQ accuracy\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"iaq_accuracy\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 11");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Error code\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"error_code\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 12");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Ground truth\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"classId\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"ground_truth\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 13");
|
||||
logFile.println("\t\t\t}");
|
||||
logFile.println("\t\t],");
|
||||
|
||||
/* data block */
|
||||
logFile.println("\t\t\"dataBlock\": [");
|
||||
/* save position in file, where to write the first data set */
|
||||
_ai_data_pos = logFile.position();
|
||||
logFile.println("\t\t]");
|
||||
logFile.println("\t}");
|
||||
logFile.println("}");
|
||||
|
||||
/* close log file */
|
||||
logFile.close();
|
||||
|
||||
_first_line = true;
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function creates a bme68x datalogger output file
|
||||
*/
|
||||
demo_ret_code bsecDataLogger::create_raw_data_file(uint8_t sensor_num)
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
String mac_str = utils::get_mac_address();
|
||||
String bme_file_base_name = "_Board_" + mac_str + "_PowerOnOff_1_";
|
||||
|
||||
_bme_file_name = utils::get_date_time() + bme_file_base_name + utils::get_file_seed() + "_File_" +
|
||||
String(_bsec_file_counter) + BME68X_RAWDATA_FILE_EXT;
|
||||
|
||||
File logFile;
|
||||
|
||||
if (!logFile.open(_bme_file_name.c_str(), O_RDWR | O_CREAT))
|
||||
{
|
||||
ret_code = EDK_DATALOGGER_LOG_FILE_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_code = prepare_config_content(sensor_num);
|
||||
|
||||
if (ret_code != EDK_OK)
|
||||
{
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
/* Writes the config header and body to the logfile */
|
||||
logFile.print(config_string);
|
||||
/* write data header / skeleton */
|
||||
/* raw data header */
|
||||
logFile.println("\t,\n\t\"rawDataHeader\": {");
|
||||
logFile.println("\t\t\"counterPowerOnOff\": 1,");
|
||||
logFile.println("\t\t\"seedPowerOnOff\": \"" + utils::get_file_seed() + "\",");
|
||||
logFile.println("\t\t\"counterFileLimit\": " + String(_bsec_file_counter) + ",");
|
||||
logFile.println("\t\t\"dateCreated\": \"" + String(utils::get_rtc().now().unixtime()) + "\",");
|
||||
logFile.println("\t\t\"dateCreated_ISO\": \"" + utils::get_rtc().now().timestamp() + "+00:00\",");
|
||||
logFile.println("\t\t\"firmwareVersion\": \"" + String(FIRMWARE_VERSION) + "\",");
|
||||
logFile.println("\t\t\"boardId\": \"" + mac_str + "\"");
|
||||
logFile.println("\t},");
|
||||
logFile.println("\t\"rawDataBody\": {");
|
||||
logFile.println("\t\t\"dataColumns\": [");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Sensor Index\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"sensor_index\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 1");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Sensor ID\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"sensor_id\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 2");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Time Since PowerOn\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"Milliseconds\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"timestamp_since_poweron\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 3");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Real time clock\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"Unix Timestamp: seconds since Jan 01 1970. (UTC); 0 = missing\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"real_time_clock\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 4");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Temperature\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"DegreesCelcius\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"float\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"temperature\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 5");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Pressure\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"Hectopascals\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"float\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"pressure\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 6");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Relative Humidity\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"Percent\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"float\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"relative_humidity\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 7");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Resistance Gassensor\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"Ohms\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"float\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"resistance_gassensor\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 8");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Heater Profile Step Index\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"heater_profile_step_index\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 9");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Scanning Mode Enabled\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"boolean\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"scanning_enabled\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 10");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Scanning Cycle Index\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"scanning_cycle_index\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 11");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Label Tag\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"label_tag\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 12");
|
||||
logFile.println("\t\t\t},");
|
||||
logFile.println("\t\t\t{");
|
||||
logFile.println("\t\t\t\t\"name\": \"Error Code\",");
|
||||
logFile.println("\t\t\t\t\"unit\": \"\",");
|
||||
logFile.println("\t\t\t\t\"format\": \"integer\",");
|
||||
logFile.println("\t\t\t\t\"key\": \"error_code\",");
|
||||
logFile.println("\t\t\t\t\"colId\": 13");
|
||||
logFile.println("\t\t\t}");
|
||||
logFile.println("\t\t],");
|
||||
|
||||
/* data block */
|
||||
logFile.println("\t\t\"dataBlock\": [");
|
||||
/* save position in file, where to write the first data set */
|
||||
_bme_data_pos = logFile.position();
|
||||
logFile.println("\t\t]");
|
||||
logFile.println("\t}");
|
||||
logFile.println("}");
|
||||
|
||||
_end_of_line = false;
|
||||
/* close log file */
|
||||
logFile.close();
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
demo_ret_code bsecDataLogger::prepare_config_content(uint8_t sensor_num)
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
filter1.clear();
|
||||
doc.clear();
|
||||
config.clear();
|
||||
config_string = "\0";
|
||||
|
||||
File configFile;
|
||||
|
||||
if (_ai_config_name.length() && !configFile.open(_ai_config_name.c_str(), O_RDWR))
|
||||
{
|
||||
return EDK_DATALOGGER_AI_CONFIG_FILE_ERROR;
|
||||
}
|
||||
|
||||
filter1["aiConfigHeader"]["appVersion"] = true;
|
||||
JsonObject aiConfigBody = filter1.createNestedObject("aiConfigBody");
|
||||
aiConfigBody["heaterProfile"] = true;
|
||||
aiConfigBody["dutyCycleProfile"] = true;
|
||||
|
||||
DeserializationError error = deserializeJson(doc, configFile, DeserializationOption::Filter(filter1));
|
||||
|
||||
if (error)
|
||||
{
|
||||
Serial.println(error.c_str());
|
||||
return EDK_SENSOR_MANAGER_JSON_DESERIAL_ERROR;
|
||||
}
|
||||
|
||||
/* parcing configuration details from aiConfig file in to local buffers */
|
||||
const char* app_version = doc["aiConfigHeader"]["appVersion"];
|
||||
|
||||
JsonObject heaterProfile = doc["aiConfigBody"]["heaterProfile"];
|
||||
const char* heater_profile_id = heaterProfile["id"];
|
||||
int32_t heater_profile_time_base = heaterProfile["timeBase"];
|
||||
|
||||
JsonArray tempTimeVector = heaterProfile["temperatureTimeVectors"];
|
||||
|
||||
int32_t temp_time_vector_0_0 = tempTimeVector[0][0];
|
||||
int32_t temp_time_vector_0_1 = tempTimeVector[0][1];
|
||||
|
||||
int32_t temp_time_vector_1_0 = tempTimeVector[1][0];
|
||||
int32_t temp_time_vector_1_1 = tempTimeVector[1][1];
|
||||
|
||||
int32_t temp_time_vector_2_0 = tempTimeVector[2][0];
|
||||
int32_t temp_time_vector_2_1 = tempTimeVector[2][1];
|
||||
|
||||
int32_t temp_time_vector_3_0 = tempTimeVector[3][0];
|
||||
int32_t temp_time_vector_3_1 = tempTimeVector[3][1];
|
||||
|
||||
int32_t temp_time_vector_4_0 = tempTimeVector[4][0];
|
||||
int32_t temp_time_vector_4_1 = tempTimeVector[4][1];
|
||||
|
||||
int32_t temp_time_vector_5_0 = tempTimeVector[5][0];
|
||||
int32_t temp_time_vector_5_1 = tempTimeVector[5][1];
|
||||
|
||||
int32_t temp_time_vector_6_0 = tempTimeVector[6][0];
|
||||
int32_t temp_time_vector_6_1 = tempTimeVector[6][1];
|
||||
|
||||
int32_t temp_time_vector_7_0 = tempTimeVector[7][0];
|
||||
int32_t temp_time_vector_7_1 = tempTimeVector[7][1];
|
||||
|
||||
int32_t temp_time_vector_8_0 = tempTimeVector[8][0];
|
||||
int32_t temp_time_vector_8_1 = tempTimeVector[8][1];
|
||||
|
||||
int32_t temp_time_vector_9_0 = tempTimeVector[9][0];
|
||||
int32_t temp_time_vector_9_1 = tempTimeVector[9][1];
|
||||
|
||||
JsonObject dutyCycleProfile = doc["aiConfigBody"]["dutyCycleProfile"];
|
||||
const char* duty_cycle_profile_id = dutyCycleProfile["id"];
|
||||
int32_t number_scanning_cycles = dutyCycleProfile["numberScanningCycles"];
|
||||
int32_t number_sleeping_cycles = dutyCycleProfile["numberSleepingCycles"];
|
||||
|
||||
/* creating configuration header and body */
|
||||
JsonObject configHeader = config.createNestedObject("configHeader");
|
||||
configHeader["dateCreated_ISO"] = utils::get_rtc().now().timestamp() + "+00:00";
|
||||
configHeader["appVersion"] = app_version;
|
||||
configHeader["boardType"] = "board_8";
|
||||
configHeader["boardMode"] = "live_test_algorithm";
|
||||
configHeader["boardLayout"] = "custom";
|
||||
|
||||
JsonObject configBody = config.createNestedObject("configBody");
|
||||
|
||||
JsonObject heaterProfiles = configBody["heaterProfiles"].createNestedObject();
|
||||
heaterProfiles["id"] = heater_profile_id;
|
||||
heaterProfiles["timeBase"] = heater_profile_time_base;
|
||||
|
||||
JsonArray tempTimeVectors = heaterProfiles.createNestedArray("temperatureTimeVectors");
|
||||
|
||||
JsonArray tempTimeVectors_0 = tempTimeVectors.createNestedArray();
|
||||
tempTimeVectors_0.add(temp_time_vector_0_0);
|
||||
tempTimeVectors_0.add(temp_time_vector_0_1);
|
||||
|
||||
JsonArray tempTimeVectors_1 = tempTimeVectors.createNestedArray();
|
||||
tempTimeVectors_1.add(temp_time_vector_1_0);
|
||||
tempTimeVectors_1.add(temp_time_vector_1_1);
|
||||
|
||||
JsonArray tempTimeVectors_2 = tempTimeVectors.createNestedArray();
|
||||
tempTimeVectors_2.add(temp_time_vector_2_0);
|
||||
tempTimeVectors_2.add(temp_time_vector_2_1);
|
||||
|
||||
JsonArray tempTimeVectors_3 = tempTimeVectors.createNestedArray();
|
||||
tempTimeVectors_3.add(temp_time_vector_3_0);
|
||||
tempTimeVectors_3.add(temp_time_vector_3_1);
|
||||
|
||||
JsonArray tempTimeVectors_4 = tempTimeVectors.createNestedArray();
|
||||
tempTimeVectors_4.add(temp_time_vector_4_0);
|
||||
tempTimeVectors_4.add(temp_time_vector_4_1);
|
||||
|
||||
JsonArray tempTimeVectors_5 = tempTimeVectors.createNestedArray();
|
||||
tempTimeVectors_5.add(temp_time_vector_5_0);
|
||||
tempTimeVectors_5.add(temp_time_vector_5_1);
|
||||
|
||||
JsonArray tempTimeVectors_6 = tempTimeVectors.createNestedArray();
|
||||
tempTimeVectors_6.add(temp_time_vector_6_0);
|
||||
tempTimeVectors_6.add(temp_time_vector_6_1);
|
||||
|
||||
JsonArray tempTimeVectors_7 = tempTimeVectors.createNestedArray();
|
||||
tempTimeVectors_7.add(temp_time_vector_7_0);
|
||||
tempTimeVectors_7.add(temp_time_vector_7_1);
|
||||
|
||||
JsonArray tempTimeVectors_8 = tempTimeVectors.createNestedArray();
|
||||
tempTimeVectors_8.add(temp_time_vector_8_0);
|
||||
tempTimeVectors_8.add(temp_time_vector_8_1);
|
||||
|
||||
JsonArray tempTimeVectors_9 = tempTimeVectors.createNestedArray();
|
||||
tempTimeVectors_9.add(temp_time_vector_9_0);
|
||||
tempTimeVectors_9.add(temp_time_vector_9_1);
|
||||
|
||||
JsonArray configBody_dutyCycleProfiles = configBody.createNestedArray("dutyCycleProfiles");
|
||||
|
||||
JsonObject dutyCycleProfiles = configBody_dutyCycleProfiles.createNestedObject();
|
||||
dutyCycleProfiles["id"] = duty_cycle_profile_id;
|
||||
dutyCycleProfiles["numberScanningCycles"] = number_scanning_cycles;
|
||||
dutyCycleProfiles["numberSleepingCycles"] = number_sleeping_cycles;
|
||||
|
||||
JsonArray sensorConfigurations = configBody.createNestedArray("sensorConfigurations");
|
||||
|
||||
JsonObject sensorConfiguration_0 = sensorConfigurations.createNestedObject();
|
||||
sensorConfiguration_0["sensorIndex"] = 0;
|
||||
|
||||
if ( (sensorConfiguration_0["sensorIndex"] == sensor_num) || (sensor_num == NUM_OF_SENS) )
|
||||
{
|
||||
sensorConfiguration_0["active"] = true;
|
||||
sensorConfiguration_0["heaterProfile"] = heater_profile_id;
|
||||
sensorConfiguration_0["dutyCycleProfile"] = duty_cycle_profile_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
sensorConfiguration_0["active"] = false;
|
||||
sensorConfiguration_0["heaterProfile"] = (char*)0;
|
||||
sensorConfiguration_0["dutyCycleProfile"] = (char*)0;
|
||||
}
|
||||
|
||||
JsonObject sensorConfiguration_1 = sensorConfigurations.createNestedObject();
|
||||
sensorConfiguration_1["sensorIndex"] = 1;
|
||||
|
||||
if ( (sensorConfiguration_1["sensorIndex"] == sensor_num) || (sensor_num == NUM_OF_SENS) )
|
||||
{
|
||||
sensorConfiguration_1["active"] = true;
|
||||
sensorConfiguration_1["heaterProfile"] = heater_profile_id;
|
||||
sensorConfiguration_1["dutyCycleProfile"] = duty_cycle_profile_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
sensorConfiguration_1["active"] = false;
|
||||
sensorConfiguration_1["heaterProfile"] = (char*)0;
|
||||
sensorConfiguration_1["dutyCycleProfile"] = (char*)0;
|
||||
}
|
||||
|
||||
JsonObject sensorConfiguration_2 = sensorConfigurations.createNestedObject();
|
||||
sensorConfiguration_2["sensorIndex"] = 2;
|
||||
|
||||
if ( (sensorConfiguration_2["sensorIndex"] == sensor_num) || (sensor_num == NUM_OF_SENS) )
|
||||
{
|
||||
sensorConfiguration_2["active"] = true;
|
||||
sensorConfiguration_2["heaterProfile"] = heater_profile_id;
|
||||
sensorConfiguration_2["dutyCycleProfile"] = duty_cycle_profile_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
sensorConfiguration_2["active"] = false;
|
||||
sensorConfiguration_2["heaterProfile"] = (char*)0;
|
||||
sensorConfiguration_2["dutyCycleProfile"] = (char*)0;
|
||||
}
|
||||
|
||||
JsonObject sensorConfiguration_3 = sensorConfigurations.createNestedObject();
|
||||
sensorConfiguration_3["sensorIndex"] = 3;
|
||||
|
||||
if ( (sensorConfiguration_3["sensorIndex"] == sensor_num) || (sensor_num == NUM_OF_SENS) )
|
||||
{
|
||||
sensorConfiguration_3["active"] = true;
|
||||
sensorConfiguration_3["heaterProfile"] = heater_profile_id;
|
||||
sensorConfiguration_3["dutyCycleProfile"] = duty_cycle_profile_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
sensorConfiguration_3["active"] = false;
|
||||
sensorConfiguration_3["heaterProfile"] = (char*)0;
|
||||
sensorConfiguration_3["dutyCycleProfile"] = (char*)0;
|
||||
}
|
||||
|
||||
/* Sensor number 4 in start command is used to test sensors 0 - 3 in multi instance mode*/
|
||||
JsonObject sensorConfiguration_4 = sensorConfigurations.createNestedObject();
|
||||
sensorConfiguration_4["sensorIndex"] = 4;
|
||||
|
||||
if ( (sensorConfiguration_4["sensorIndex"] == sensor_num) && (sensor_num != NUM_OF_SENS) )
|
||||
{
|
||||
sensorConfiguration_4["active"] = true;
|
||||
sensorConfiguration_4["heaterProfile"] = heater_profile_id;
|
||||
sensorConfiguration_4["dutyCycleProfile"] = duty_cycle_profile_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
sensorConfiguration_4["active"] = false;
|
||||
sensorConfiguration_4["heaterProfile"] = (char*)0;
|
||||
sensorConfiguration_4["dutyCycleProfile"] = (char*)0;
|
||||
}
|
||||
|
||||
JsonObject sensorConfiguration_5 = sensorConfigurations.createNestedObject();
|
||||
sensorConfiguration_5["sensorIndex"] = 5;
|
||||
|
||||
if (sensorConfiguration_5["sensorIndex"] == sensor_num)
|
||||
{
|
||||
sensorConfiguration_5["active"] = true;
|
||||
sensorConfiguration_5["heaterProfile"] = heater_profile_id;
|
||||
sensorConfiguration_5["dutyCycleProfile"] = duty_cycle_profile_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
sensorConfiguration_5["active"] = false;
|
||||
sensorConfiguration_5["heaterProfile"] = (char*)0;
|
||||
sensorConfiguration_5["dutyCycleProfile"] = (char*)0;
|
||||
}
|
||||
|
||||
JsonObject sensorConfiguration_6 = sensorConfigurations.createNestedObject();
|
||||
sensorConfiguration_6["sensorIndex"] = 6;
|
||||
|
||||
if (sensorConfiguration_6["sensorIndex"] == sensor_num)
|
||||
{
|
||||
sensorConfiguration_6["active"] = true;
|
||||
sensorConfiguration_6["heaterProfile"] = heater_profile_id;
|
||||
sensorConfiguration_6["dutyCycleProfile"] = duty_cycle_profile_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
sensorConfiguration_6["active"] = false;
|
||||
sensorConfiguration_6["heaterProfile"] = (char*)0;
|
||||
sensorConfiguration_6["dutyCycleProfile"] = (char*)0;
|
||||
}
|
||||
|
||||
JsonObject sensorConfiguration_7 = sensorConfigurations.createNestedObject();
|
||||
sensorConfiguration_7["sensorIndex"] = 7;
|
||||
|
||||
if (sensorConfiguration_7["sensorIndex"] == sensor_num)
|
||||
{
|
||||
sensorConfiguration_7["active"] = true;
|
||||
sensorConfiguration_7["heaterProfile"] = heater_profile_id;
|
||||
sensorConfiguration_7["dutyCycleProfile"] = duty_cycle_profile_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
sensorConfiguration_7["active"] = false;
|
||||
sensorConfiguration_7["heaterProfile"] = (char*)0;
|
||||
sensorConfiguration_7["dutyCycleProfile"] = (char*)0;
|
||||
}
|
||||
serializeJsonPretty(config, config_string);
|
||||
config_string = config_string.substring(0,config_string.length()-1);
|
||||
|
||||
return EDK_OK;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Function to create a bme68x label information file with .bmelabelinfo extension
|
||||
*/
|
||||
demo_ret_code bsecDataLogger::create_label_info_file()
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
String mac_str = utils::get_mac_address();
|
||||
String label_file_base_name = "_Board_" + mac_str + "_PowerOnOff_1_";
|
||||
uint16_t label_tag = 1001; //since labelTag starts with 1001
|
||||
|
||||
_label_file_name = utils::get_date_time() + label_file_base_name + utils::get_file_seed() +
|
||||
"_File_" + String(_bsec_file_counter) + BME68X_LABEL_INFO_FILE_EXT;
|
||||
File logFile;
|
||||
|
||||
if (!logFile.open(_label_file_name.c_str(), O_RDWR | O_CREAT))
|
||||
{
|
||||
ret_code = EDK_DATALOGGER_LABEL_INFO_FILE_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
filter2.clear();
|
||||
label_doc.clear();
|
||||
|
||||
/* write labelinfo header / skeleton */
|
||||
logFile.println("{");
|
||||
logFile.println("\t\"labelInfoHeader\": {");
|
||||
logFile.println("\t\t\"counterPowerOnOff\": 1,");
|
||||
logFile.println("\t\t\"seedPowerOnOff\": \"" + utils::get_file_seed() + "\",");
|
||||
logFile.println("\t\t\"dateCreated\": \"" + String(utils::get_rtc().now().unixtime()) + "\",");
|
||||
logFile.println("\t\t\"dateCreated_ISO\": \"" + utils::get_rtc().now().timestamp() + "+00:00\",");
|
||||
logFile.println("\t\t\"firmwareVersion\": \"" + String(FIRMWARE_VERSION) + "\",");
|
||||
logFile.println("\t\t\"boardId\": \"" + mac_str + "\"");
|
||||
logFile.println("\t},");
|
||||
logFile.println("\t\"labelInformation\": [");
|
||||
logFile.println("\t\t{");
|
||||
logFile.println("\t\t\t\"labelTag\": 0,");
|
||||
logFile.println("\t\t\t\"labelName\": \"Initial\",");
|
||||
logFile.println("\t\t\t\"labelDescription\": \"Standard label for no label has been set\"");
|
||||
logFile.println("\t\t},");
|
||||
logFile.println("\t\t{");
|
||||
logFile.println("\t\t\t\"labelTag\": 1,");
|
||||
logFile.println("\t\t\t\"labelName\": \"Button 1\",");
|
||||
logFile.println("\t\t\t\"labelDescription\": \"Standard label for hardware button 1 pressed\"");
|
||||
logFile.println("\t\t},");
|
||||
logFile.println("\t\t{");
|
||||
logFile.println("\t\t\t\"labelTag\": 2,");
|
||||
logFile.println("\t\t\t\"labelName\": \"Button 2\",");
|
||||
logFile.println("\t\t\t\"labelDescription\": \"Standard label for hardware button 2 pressed\"");
|
||||
logFile.println("\t\t},");
|
||||
logFile.println("\t\t{");
|
||||
logFile.println("\t\t\t\"labelTag\": 3,");
|
||||
logFile.println("\t\t\t\"labelName\": \"Button 1+2\",");
|
||||
logFile.println("\t\t\t\"labelDescription\": \"Standard label for hardware button 1 and button 2 pressed\"");
|
||||
logFile.print("\t\t}");
|
||||
|
||||
filter2["aiConfigBody"]["type"] = true;
|
||||
filter2["aiConfigBody"]["classes"] = true;
|
||||
filter2["aiConfigBody"]["targets"] = true;
|
||||
|
||||
File configFile;
|
||||
configFile.open(_ai_config_name.c_str(), O_RDWR);
|
||||
|
||||
DeserializationError error = deserializeJson(label_doc, configFile, DeserializationOption::Filter(filter2));
|
||||
configFile.close();
|
||||
|
||||
if (error)
|
||||
{
|
||||
Serial.println(error.c_str());
|
||||
return EDK_SENSOR_MANAGER_JSON_DESERIAL_ERROR;
|
||||
}
|
||||
|
||||
// reading configuration type
|
||||
const char* type = label_doc["aiConfigBody"]["type"];
|
||||
|
||||
strcpy(ai_config_type, type);
|
||||
|
||||
if (strcmp(type, "classification") == 0)
|
||||
{
|
||||
|
||||
for (JsonObject classes : label_doc["aiConfigBody"]["classes"].as<JsonArray>())
|
||||
{
|
||||
const char* class_name = classes["name"];
|
||||
|
||||
logFile.println(",\n\t\t{");
|
||||
logFile.println("\t\t\t\"labelTag\": " + String(label_tag++) + ",");
|
||||
logFile.println("\t\t\t\"labelName\": \"" + String(class_name) + "\",");
|
||||
logFile.println("\t\t\t\"labelDescription\": \"Predefined by algorithm classes (class name used as labelName)\"");
|
||||
logFile.print("\t\t}");
|
||||
}
|
||||
}
|
||||
else if (strcmp(type, "regression") == 0)
|
||||
{
|
||||
|
||||
for (JsonObject target_name : label_doc["aiConfigBody"]["targets"].as<JsonArray>())
|
||||
{
|
||||
const char* targets = target_name["name"];
|
||||
|
||||
logFile.println(",\n\t\t{");
|
||||
logFile.println("\t\t\t\"labelTag\": " + String(label_tag++) + ",");
|
||||
logFile.println("\t\t\t\"labelName\": \"" + String(targets) + "\",");
|
||||
logFile.println("\t\t\t\"labelDescription\": \"Predefined by algorithm Targets (target name used as labelName)\"");
|
||||
logFile.print("\t\t}");
|
||||
}
|
||||
}
|
||||
logFile.println("\n\t]");
|
||||
logFile.print("}");
|
||||
|
||||
/* close log file */
|
||||
logFile.close();
|
||||
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Function which flushes the buffered sensor data to the current log file
|
||||
*/
|
||||
demo_ret_code bsecDataLogger::flush_sensor_data(uint8_t sensor_num)
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
File logFile;
|
||||
std::string txt;
|
||||
|
||||
if (_bs.rdbuf()->in_avail())
|
||||
{
|
||||
txt = _bs.str();
|
||||
_bs.str(std::string());
|
||||
|
||||
if (_bme_file_counter && logFile.open(_bme_file_name.c_str(), O_RDWR | O_AT_END))
|
||||
{
|
||||
logFile.seek(_bme_data_pos);
|
||||
logFile.print(txt.c_str());
|
||||
_bme_data_pos = logFile.position();
|
||||
logFile.print("\n\t\t]\n\t}\n}");
|
||||
|
||||
if (logFile.size() >= FILE_SIZE_LIMIT)
|
||||
{
|
||||
logFile.close();
|
||||
++_bme_file_counter;
|
||||
ret_code = create_raw_data_file(sensor_num);
|
||||
|
||||
if (ret_code >= EDK_OK)
|
||||
{
|
||||
ret_code = create_label_info_file();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logFile.close();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_code = EDK_DATALOGGER_LOG_FILE_ERROR;
|
||||
}
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function writes the bsec output to the current log file
|
||||
*/
|
||||
demo_ret_code bsecDataLogger::write_bsec_output(sensor_io_data& buff_data)
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
File logFile;
|
||||
|
||||
if (_bsec_file_counter && logFile.open(_ai_file_name.c_str(), O_RDWR | O_AT_END))
|
||||
{
|
||||
/* set writing position to end of data block */
|
||||
logFile.seek(_ai_data_pos);
|
||||
|
||||
float gas_signal[4] = {NAN, NAN, NAN, NAN}, iaq_signal = NAN;
|
||||
uint8_t iaq_accuracy = 0xFF; //, gasAccuracy = 0xFF;
|
||||
uint8_t gas_accuracy[4] = {0xFF, 0xFF, 0xFF, 0xFF};
|
||||
uint8_t is_reg_class_subscribe = 0;
|
||||
uint8_t Gas_accuracy = 0;
|
||||
uint8_t index = 0;
|
||||
|
||||
for (uint8_t i = 0; ((buff_data.outputs).output != nullptr) && (i < (buff_data.outputs).nOutputs); i++)
|
||||
{
|
||||
const bsec_output_t& output = (buff_data.outputs).output[i];
|
||||
|
||||
if (output.sensor_id >= BSEC_OUTPUT_GAS_ESTIMATE_1 && output.sensor_id <= BSEC_OUTPUT_GAS_ESTIMATE_4)
|
||||
{
|
||||
index = output.sensor_id - BSEC_OUTPUT_GAS_ESTIMATE_1;
|
||||
gas_signal[index] = output.signal;
|
||||
gas_accuracy[index] = (output.accuracy > gas_accuracy[index]) ? gas_accuracy[index] : output.accuracy;
|
||||
Gas_accuracy |= ((gas_accuracy[index] & 0x03) << (index * 2));
|
||||
is_reg_class_subscribe = 1;
|
||||
}
|
||||
else if (output.sensor_id >= BSEC_OUTPUT_REGRESSION_ESTIMATE_1 && output.sensor_id <= BSEC_OUTPUT_REGRESSION_ESTIMATE_4)
|
||||
{
|
||||
index = output.sensor_id - BSEC_OUTPUT_REGRESSION_ESTIMATE_1;
|
||||
gas_signal[index] = output.signal;
|
||||
gas_accuracy[index] = (output.accuracy > gas_accuracy[index]) ? gas_accuracy[index] : output.accuracy;
|
||||
Gas_accuracy |= ((gas_accuracy[index] & 0x03) << (index * 2));
|
||||
is_reg_class_subscribe = 1;
|
||||
}
|
||||
else if(output.sensor_id == BSEC_OUTPUT_IAQ)
|
||||
{
|
||||
iaq_signal = output.signal;
|
||||
iaq_accuracy = output.accuracy;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_first_line)
|
||||
{
|
||||
logFile.println(",");
|
||||
}
|
||||
logFile.print("\t\t\t[\n\t\t\t\t");
|
||||
logFile.print(buff_data.sensor_num);
|
||||
logFile.print(",\n\t\t\t\t");
|
||||
logFile.print(buff_data.sensor_id);
|
||||
logFile.print(",\n\t\t\t\t");
|
||||
logFile.print(buff_data.start_time_since_power_on);
|
||||
logFile.print(",\n\t\t\t\t");
|
||||
logFile.print(buff_data.end_time_since_power_on);
|
||||
logFile.print(",\n\t\t\t\t");
|
||||
(!isnan(gas_signal[0])) ? logFile.print(gas_signal[0]) : logFile.print("null");
|
||||
logFile.print(",\n\t\t\t\t");
|
||||
(!isnan(gas_signal[1])) ? logFile.print(gas_signal[1]) : logFile.print("null");
|
||||
logFile.print(",\n\t\t\t\t");
|
||||
(!isnan(gas_signal[2])) ? logFile.print(gas_signal[2]) : logFile.print("null");
|
||||
logFile.print(",\n\t\t\t\t");
|
||||
(!isnan(gas_signal[3])) ? logFile.print(gas_signal[3]) : logFile.print("null");
|
||||
logFile.print(",\n\t\t\t\t");
|
||||
(is_reg_class_subscribe == 1) ? logFile.print(Gas_accuracy) : logFile.print("null");
|
||||
logFile.print(",\n\t\t\t\t");
|
||||
(!isnan(iaq_signal)) ? logFile.print(iaq_signal) : logFile.print("null");
|
||||
logFile.print(",\n\t\t\t\t");
|
||||
(iaq_accuracy != 0xFF) ? logFile.print(iaq_accuracy) : logFile.print("null");
|
||||
logFile.print(",\n\t\t\t\t");
|
||||
logFile.print(buff_data.code);
|
||||
logFile.print(",\n\t\t\t\t");
|
||||
logFile.println(buff_data.ground_truth);
|
||||
logFile.print("\t\t\t]");
|
||||
|
||||
_ai_data_pos = logFile.position();
|
||||
_first_line = false;
|
||||
logFile.println("\n\t\t]\n\t}\n}");
|
||||
|
||||
logFile.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_code = EDK_DATALOGGER_LOG_FILE_ERROR;
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function writes the sensor data to the current log file.
|
||||
*/
|
||||
demo_ret_code bsecDataLogger::write_sensor_data(const uint8_t* num, const uint32_t* sensor_id,
|
||||
const bme68x_data* bme68xData, const uint32_t* scan_cycle_index,
|
||||
uint32_t ground_truth, demo_ret_code code, sensor_io_data& buff_data)
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
uint32_t rtc_tsp = utils::get_rtc().now().unixtime();
|
||||
uint32_t time_since_power_on = millis();
|
||||
|
||||
//Copying time for sync with aiprediction file
|
||||
if (bme68xData != nullptr)
|
||||
{
|
||||
|
||||
if (bme68xData->gas_index == 0)
|
||||
{
|
||||
buff_data.start_time_since_power_on = time_since_power_on;
|
||||
}
|
||||
else
|
||||
{
|
||||
buff_data.end_time_since_power_on = time_since_power_on;
|
||||
}
|
||||
}
|
||||
|
||||
if (_end_of_line)
|
||||
{
|
||||
_bs << ",\n";
|
||||
}
|
||||
_bs << "\t\t\t[\n\t\t\t\t";
|
||||
(num != nullptr) ? (_bs << (int32_t)*num) : (_bs << "null");
|
||||
_bs << ",\n\t\t\t\t";
|
||||
(sensor_id != nullptr) ? (_bs << (int32_t)*sensor_id) : (_bs << "null");
|
||||
_bs << ",\n\t\t\t\t";
|
||||
_bs << time_since_power_on;
|
||||
_bs << ",\n\t\t\t\t";
|
||||
_bs << rtc_tsp;
|
||||
_bs << ",\n\t\t\t\t";
|
||||
(bme68xData != nullptr) ? (_bs << bme68xData->temperature) : (_bs << "null");
|
||||
_bs << ",\n\t\t\t\t";
|
||||
(bme68xData != nullptr) ? (_bs << bme68xData->pressure) : (_bs << "null");
|
||||
_bs << ",\n\t\t\t\t";
|
||||
(bme68xData != nullptr) ? (_bs << bme68xData->humidity) : (_bs << "null");
|
||||
_bs << ",\n\t\t\t\t";
|
||||
(bme68xData != nullptr) ? (_bs << bme68xData->gas_resistance) : (_bs << "null");
|
||||
_bs << ",\n\t\t\t\t";
|
||||
(bme68xData != nullptr) ? (_bs << (int32_t)bme68xData->gas_index) : (_bs << "null");
|
||||
_bs << ",\n\t\t\t\t";
|
||||
_bs << (bool)(BME68X_PARALLEL_MODE);
|
||||
_bs << ",\n\t\t\t\t";
|
||||
(scan_cycle_index != nullptr) ? (_bs << (int32_t)(*scan_cycle_index)) : (_bs << "null");
|
||||
_bs << ",\n\t\t\t\t";
|
||||
_bs << ground_truth;
|
||||
_bs << ",\n\t\t\t\t";
|
||||
_bs << (int32_t)code;
|
||||
_bs << "\n\t\t\t]";
|
||||
|
||||
_end_of_line = true;
|
||||
|
||||
return ret_code;
|
||||
}
|
||||
195
libraries/bsec2/examples/bme68x_demo_sample/bsec_datalogger.h
Normal file
195
libraries/bsec2/examples/bme68x_demo_sample/bsec_datalogger.h
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
/*!
|
||||
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
|
||||
*
|
||||
* BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 THE
|
||||
* 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.
|
||||
*
|
||||
* @file bsec_datalogger.h
|
||||
* @date 03 Jan 2024
|
||||
* @version 2.1.5
|
||||
*
|
||||
* @brief Header file for the bsec datalogger
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BSEC_DATALOGGER_H
|
||||
#define BSEC_DATALOGGER_H
|
||||
|
||||
/* Include of Arduino Core */
|
||||
#include "Arduino.h"
|
||||
#include <SdFat.h>
|
||||
#include <RTClib.h>
|
||||
#include <base64.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include "utils.h"
|
||||
#include "demo_app.h"
|
||||
#include "label_provider.h"
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
/* Number of sensors to operate*/
|
||||
#define NUM_OF_SENS UINT8_C(4)
|
||||
#define COUNT_MAX_SIZE UINT8_C(2)
|
||||
|
||||
/*!
|
||||
* @brief Class library that holds functionality of the bsec datalogger
|
||||
*/
|
||||
class bsecDataLogger
|
||||
{
|
||||
private:
|
||||
String _ai_config_name, _ai_file_name, _bme_file_name, _label_file_name;
|
||||
std::stringstream _bs, _ba;
|
||||
uint32_t _ai_data_pos = 0, _bme_data_pos = 0;
|
||||
uint32_t _bme_file_counter = 1;
|
||||
uint32_t _bsec_file_counter = 1;
|
||||
bool _first_line = false;
|
||||
bsec_version_t _version;
|
||||
bool _end_of_line = false;
|
||||
String config_string;
|
||||
|
||||
/*!
|
||||
* @brief : This function creates a bsec output file
|
||||
*
|
||||
* @return bosch error code
|
||||
*/
|
||||
demo_ret_code create_bsec_file();
|
||||
|
||||
/*!
|
||||
* @brief : This function creates a bme68x datalogger output file
|
||||
*
|
||||
* @param[in] sensorNum : Selected sensor number
|
||||
*
|
||||
* @return bosch error code
|
||||
*/
|
||||
demo_ret_code create_raw_data_file(uint8_t sensor_num);
|
||||
|
||||
/*!
|
||||
* @brief : Fuction that parses the config data from aiConfig file and creates config header and body
|
||||
*
|
||||
* @param[in] sensorNum : Selected sensor number
|
||||
*
|
||||
* @return bosch error code
|
||||
*/
|
||||
demo_ret_code prepare_config_content(uint8_t sensor_num);
|
||||
|
||||
public:
|
||||
/*!
|
||||
* @brief structure which comprises sensor input and output data to write into SD card
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/*! sensor number */
|
||||
uint8_t sensor_num;
|
||||
|
||||
/*! sensor Index */
|
||||
uint32_t sensor_id;
|
||||
|
||||
/*! bsec output structure*/
|
||||
bsecOutputs outputs;
|
||||
|
||||
/*! return code */
|
||||
demo_ret_code code;
|
||||
|
||||
/*! start time since power on */
|
||||
uint32_t start_time_since_power_on;
|
||||
|
||||
/*! end time since power on */
|
||||
uint32_t end_time_since_power_on;
|
||||
|
||||
/*! ground truth */
|
||||
uint16_t ground_truth;
|
||||
|
||||
} sensor_io_data;
|
||||
|
||||
uint8_t scanCycles = 0;
|
||||
|
||||
/* Holds the aiconfig type (clasification / regression) */
|
||||
char ai_config_type[15] = {};
|
||||
|
||||
/*!
|
||||
* @brief :The constructor of the bsec_datalogger class
|
||||
* Creates an instance of the class
|
||||
*/
|
||||
bsecDataLogger();
|
||||
|
||||
/*!
|
||||
* @brief : This function configures the bsec datalogger using the provided bsec config string file.
|
||||
*
|
||||
* @param[in] configName : sensor configuration file
|
||||
* @param[in] bsecVersion : reference to bsec version
|
||||
* @param[in] sensorNum : selected sensor number
|
||||
*
|
||||
* @return bosch error code
|
||||
*/
|
||||
demo_ret_code begin(const String& config_name, const bsec_version_t& bsec_version, uint8_t sensor_num);
|
||||
|
||||
/*!
|
||||
* @brief : This function flushes the buffered sensor data to the current log file
|
||||
*
|
||||
* @return bosch error code
|
||||
*/
|
||||
demo_ret_code flush_sensor_data(uint8_t sensor_num);
|
||||
|
||||
/*!
|
||||
* @brief : This function writes the bsec output to the current log file.
|
||||
*
|
||||
* @param[in] buffData : reference to the buffered data
|
||||
*
|
||||
* @return bosch error code
|
||||
*/
|
||||
|
||||
demo_ret_code write_bsec_output(sensor_io_data& buff_data);
|
||||
|
||||
/*!
|
||||
* @brief : This function writes the sensor data to the current log file.
|
||||
*
|
||||
* @param[in] num : sensor number
|
||||
* @param[in] sensorId : pointer to sensor id, if NULL a null json object is inserted
|
||||
* @param[in] sensorMode : pointer to sensor operation mode, if NULL a null json object is inserted
|
||||
* @param[in] bme68xData : pointer to bme68x data, if NULL a null json object is inserted
|
||||
* @param[in] scanCycleIndex : pointer to sensor scanning cycle index
|
||||
* @param[in] label : class label
|
||||
* @param[in] code : application return code
|
||||
*
|
||||
* @return bosch error code
|
||||
*/
|
||||
demo_ret_code write_sensor_data(const uint8_t* num, const uint32_t* sensor_id, const bme68x_data* bme68xData,
|
||||
const uint32_t* scan_cycle_index, uint32_t ground_truth, demo_ret_code code,
|
||||
sensor_io_data& buff_data);
|
||||
|
||||
/*!
|
||||
* @brief : This function creates a bme68x label information file with .bmelabelinfo extension
|
||||
*
|
||||
* @return bosch error code
|
||||
*/
|
||||
demo_ret_code create_label_info_file();
|
||||
};
|
||||
|
||||
#endif
|
||||
137
libraries/bsec2/examples/bme68x_demo_sample/demo_app.h
Normal file
137
libraries/bsec2/examples/bme68x_demo_sample/demo_app.h
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/*!
|
||||
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
|
||||
*
|
||||
* BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 THE
|
||||
* 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.
|
||||
*
|
||||
* @file demo_app.h
|
||||
* @date 03 Jan 2024
|
||||
* @version 2.1.5
|
||||
*
|
||||
* @brief Header file for the bosch application definitions
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DEMO_APPLICATION_H
|
||||
#define DEMO_APPLICATION_H
|
||||
|
||||
#include "bsec2.h"
|
||||
|
||||
#define FIRMWARE_VERSION "2.1.5"
|
||||
|
||||
/*!
|
||||
* @brief Enumeration for demo app mode
|
||||
*/
|
||||
enum demo_app_mode
|
||||
{
|
||||
DEMO_RECORDING_MODE,
|
||||
DEMO_TEST_ALGORITHM_MODE,
|
||||
DEMO_IDLE_MODE
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Enumeration for the demo app return code
|
||||
*/
|
||||
enum demo_ret_code
|
||||
{
|
||||
EDK_LABEL_NOT_FOUND = -28,
|
||||
EDK_DATALOGGER_LABEL_INFO_FILE_ERROR = -27,
|
||||
EDK_DATALOGGER_AI_CONFIG_FILE_ERROR = -26,
|
||||
EDK_SENSOR_INITIALIZATION_FAILED = -25,
|
||||
EDK_EXTENSION_NOT_AVAILABLE = -24,
|
||||
EDK_END_OF_FILE = -23,
|
||||
EDK_FILE_OPEN_ERROR = -22,
|
||||
EDK_BUFFER_DATA_ERROR = -21,
|
||||
EDK_BLE_CONTROLLER_FULL_QUEUE = -20,
|
||||
EDK_BLE_CONTROLLER_INVALID_CMD = -19,
|
||||
EDK_BLE_CONTROLLER_OUT_OF_RANGE = -18,
|
||||
EDK_BLE_CONTROLLER_OUT_OF_BOUNDS = -17,
|
||||
|
||||
EDK_BSEC_INIT_ERROR = -16,
|
||||
EDK_BSEC_SET_CONFIG_ERROR = -15,
|
||||
EDK_BSEC_UPDATE_SUBSCRIPTION_ERROR = -14,
|
||||
EDK_BSEC_RUN_ERROR = -13,
|
||||
|
||||
EDK_DATALOGGER_LOG_FILE_ERROR = -12,
|
||||
EDK_DATALOGGER_SENSOR_CONFIG_FILE_ERROR = -11,
|
||||
|
||||
EDK_BME68X_DRIVER_ERROR = -10,
|
||||
|
||||
EDK_SENSOR_MANAGER_CONFIG_FILE_ERROR = -9,
|
||||
EDK_SENSOR_MANAGER_SENSOR_INDEX_ERROR = -8,
|
||||
EDK_SENSOR_MANAGER_JSON_DESERIAL_ERROR = -7,
|
||||
EDK_SENSOR_MANAGER_JSON_FORMAT_ERROR = -6,
|
||||
|
||||
EDK_BSEC_CONFIG_STR_FILE_ERROR = -5,
|
||||
EDK_BSEC_CONFIG_STR_READ_ERROR = -4,
|
||||
EDK_BSEC_CONFIG_STR_SIZE_ERROR = -3,
|
||||
|
||||
EDK_SD_CARD_INIT_ERROR = -2,
|
||||
EDK_SENSOR_CONFIG_MISSING_ERROR = -1,
|
||||
|
||||
EDK_OK = 0,
|
||||
|
||||
EDK_SENSOR_MANAGER_DATA_MISS_WARNING = 1,
|
||||
|
||||
EDK_DATALOGGER_RTC_BEGIN_WARNING = 2,
|
||||
EDK_DATALOGGER_RTC_ADJUST_WARNING = 3
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Structure to hold heater profile data
|
||||
*/
|
||||
struct bme68x_heater_profile
|
||||
{
|
||||
uint64_t sleep_duration;
|
||||
uint16_t temperature[10];
|
||||
uint16_t duration[10];
|
||||
uint8_t nb_repetitions;
|
||||
uint8_t length;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Structure to hold sensor state information
|
||||
*/
|
||||
struct bme68x_sensor
|
||||
{
|
||||
bme68xDev device;
|
||||
bme68x_heater_profile heater_profile;
|
||||
|
||||
uint64_t wake_up_time;
|
||||
uint32_t id;
|
||||
bool is_configured;
|
||||
uint8_t mode;
|
||||
uint8_t cycle_pos;
|
||||
uint8_t next_gas_index;
|
||||
int8_t i2c_mask;
|
||||
uint32_t scan_cycle_index;
|
||||
};
|
||||
|
||||
#endif
|
||||
144
libraries/bsec2/examples/bme68x_demo_sample/label_provider.cpp
Normal file
144
libraries/bsec2/examples/bme68x_demo_sample/label_provider.cpp
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/*!
|
||||
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
|
||||
*
|
||||
* BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 THE
|
||||
* 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.
|
||||
*
|
||||
* @file label_provider.cpp
|
||||
* @date 03 Jan 2024
|
||||
* @version 2.1.5
|
||||
*
|
||||
* @brief label provider
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* own header include */
|
||||
#include "label_provider.h"
|
||||
|
||||
volatile gas_label labelProvider::_label;
|
||||
volatile bool labelProvider::_but1_pressed, labelProvider::_but2_pressed;
|
||||
|
||||
QueueHandle_t labelProvider::_queue = nullptr;
|
||||
|
||||
/*!
|
||||
* @brief The constructor of the label_provider class
|
||||
*/
|
||||
labelProvider::labelProvider()
|
||||
{}
|
||||
|
||||
/*!
|
||||
* @brief This function initializes the label provider module
|
||||
*/
|
||||
void labelProvider::begin()
|
||||
{
|
||||
_but1_pressed = false;
|
||||
_but2_pressed = false;
|
||||
|
||||
_queue = xQueueCreate(2, sizeof(gas_label));
|
||||
|
||||
/* Button interrupts setup and attachment */
|
||||
pinMode(PIN_BUTTON_1, INPUT_PULLUP);
|
||||
pinMode(PIN_BUTTON_2, INPUT_PULLUP);
|
||||
|
||||
attachInterrupt(digitalPinToInterrupt(PIN_BUTTON_1), isr_button1, CHANGE);
|
||||
attachInterrupt(digitalPinToInterrupt(PIN_BUTTON_2), isr_button2, CHANGE);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function is the interrupt function, that handles the button press of the first button
|
||||
*/
|
||||
void labelProvider::isr_button1()
|
||||
{
|
||||
/* check if button is pressed or idle */
|
||||
if (_but1_pressed == false)
|
||||
{
|
||||
/* determine if only this button or both are pressed and set helper button label */
|
||||
_but1_pressed = true;
|
||||
|
||||
if (_but2_pressed)
|
||||
{
|
||||
_label = BSEC_CLASS_3;
|
||||
}
|
||||
else
|
||||
{
|
||||
_label = BSEC_CLASS_1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if both buttons are released, user label according to helper button label */
|
||||
_but1_pressed = false;
|
||||
|
||||
if (!_but2_pressed)
|
||||
{
|
||||
xQueueSendFromISR(_queue, (const void*)&_label, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function is the interrupt function, that handles the button press of the second button
|
||||
*/
|
||||
void labelProvider::isr_button2()
|
||||
{
|
||||
/* check if button is pressed or idle */
|
||||
if (_but2_pressed == false)
|
||||
{
|
||||
/* determine if only this button or both are pressed and set helper button label */
|
||||
_but2_pressed = true;
|
||||
|
||||
if (_but1_pressed)
|
||||
{
|
||||
_label = BSEC_CLASS_3;
|
||||
}
|
||||
else
|
||||
{
|
||||
_label = BSEC_CLASS_2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if both buttons are released, user label according to helper button label */
|
||||
_but2_pressed = false;
|
||||
|
||||
if (!_but1_pressed)
|
||||
{
|
||||
xQueueSendFromISR(_queue, (const void*)&_label, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function retrieves the current label
|
||||
*/
|
||||
bool labelProvider::get_label(gas_label &label)
|
||||
{
|
||||
return xQueueReceive(_queue, &label, 0);
|
||||
}
|
||||
113
libraries/bsec2/examples/bme68x_demo_sample/label_provider.h
Normal file
113
libraries/bsec2/examples/bme68x_demo_sample/label_provider.h
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/*!
|
||||
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
|
||||
*
|
||||
* BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 THE
|
||||
* 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.
|
||||
*
|
||||
* @file label_provider.h
|
||||
* @date 03 Jan 2024
|
||||
* @version 2.1.5
|
||||
*
|
||||
* @brief Header file for the label provider
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LABEL_PROVIDER_H
|
||||
#define LABEL_PROVIDER_H
|
||||
|
||||
/* Include of Arduino Core */
|
||||
#include "Arduino.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
|
||||
/* Pins connected to interrupt buttons */
|
||||
#define PIN_BUTTON_1 UINT8_C(32)
|
||||
#define PIN_BUTTON_2 UINT8_C(14)
|
||||
#define LABEL_NAME_SIZE UINT8_C(50)
|
||||
#define LABEL_DESC_SIZE UINT8_C(200)
|
||||
#define LABEL_TAG_MAX_RANGE UINT32_C(2147483647)
|
||||
#define LABEL_TAG_MIN_RANGE UINT16_C(1001)
|
||||
|
||||
enum gas_label
|
||||
{
|
||||
BSEC_NO_CLASS,
|
||||
BSEC_CLASS_1,
|
||||
BSEC_CLASS_2,
|
||||
BSEC_CLASS_3,
|
||||
BSEC_CLASS_4
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief : Class library that holds functionality of the label provider
|
||||
*/
|
||||
class labelProvider
|
||||
{
|
||||
private:
|
||||
/* variable for temporarily holding the new set user label */
|
||||
static volatile gas_label _label;
|
||||
/* variables to store information about the current buttons states */
|
||||
static volatile bool _but1_pressed, _but2_pressed;
|
||||
|
||||
static QueueHandle_t _queue;
|
||||
|
||||
/*!
|
||||
* @brief : This function is the interrupt handler of the first button
|
||||
*/
|
||||
static void isr_button1();
|
||||
|
||||
/*!
|
||||
* @brief : This function is the interrupt handler of the second button
|
||||
*/
|
||||
static void isr_button2();
|
||||
|
||||
public:
|
||||
|
||||
/*!
|
||||
* @brief : The constructor of the label_provider class
|
||||
* Creates an instance of the class
|
||||
*/
|
||||
labelProvider();
|
||||
|
||||
/*!
|
||||
* @brief : This function initializes the label provider module
|
||||
*/
|
||||
void begin();
|
||||
|
||||
/*!
|
||||
* @brief : This function retrieves the current label.
|
||||
*
|
||||
* @param[out] label : reference to the label
|
||||
*
|
||||
* @return true if a new label is available else false
|
||||
*/
|
||||
bool get_label(gas_label &label);
|
||||
};
|
||||
|
||||
#endif
|
||||
110
libraries/bsec2/examples/bme68x_demo_sample/led_controller.cpp
Normal file
110
libraries/bsec2/examples/bme68x_demo_sample/led_controller.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/*!
|
||||
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
|
||||
*
|
||||
* BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 THE
|
||||
* 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.
|
||||
*
|
||||
* @file led_controller.cpp
|
||||
* @date 03 Jan 2024
|
||||
* @version 2.1.5
|
||||
*
|
||||
* @brief led controller
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* own header include */
|
||||
#include "led_controller.h"
|
||||
|
||||
/*!
|
||||
* @brief The constructor of the led_controller class
|
||||
*/
|
||||
ledController::ledController()
|
||||
{
|
||||
previous_ret_code = EDK_OK;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function initializes the led controller module
|
||||
*/
|
||||
void ledController::begin()
|
||||
{
|
||||
/* LED pin initialization for runtime monitoring and error indication */
|
||||
pinMode(PIN_LED, OUTPUT);
|
||||
digitalWrite(PIN_LED, LOW);
|
||||
|
||||
_led_on = false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function updates the led blinking pattern according to the
|
||||
* provided period
|
||||
*/
|
||||
void ledController::switch_led(uint32_t period)
|
||||
{
|
||||
uint32_t time_stamp = millis();
|
||||
|
||||
if ((time_stamp - _time_stamp) >= period)
|
||||
{
|
||||
|
||||
if (_led_on)
|
||||
{
|
||||
digitalWrite(PIN_LED, LOW);
|
||||
_led_on = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
digitalWrite(PIN_LED, HIGH);
|
||||
_led_on = true;
|
||||
}
|
||||
_time_stamp = time_stamp;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function updates the led controller status
|
||||
*/
|
||||
void ledController::update(demo_ret_code retcode)
|
||||
{
|
||||
|
||||
if (retcode >= EDK_OK)
|
||||
{
|
||||
switch_led(LED_OK_PERIOD);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (retcode != previous_ret_code)
|
||||
{
|
||||
Serial.println("Error code = " + String((int32_t) retcode));
|
||||
previous_ret_code = retcode;
|
||||
}
|
||||
switch_led(LED_ERROR_PERIOD);
|
||||
}
|
||||
}
|
||||
95
libraries/bsec2/examples/bme68x_demo_sample/led_controller.h
Normal file
95
libraries/bsec2/examples/bme68x_demo_sample/led_controller.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*!
|
||||
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
|
||||
*
|
||||
* BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 THE
|
||||
* 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.
|
||||
*
|
||||
* @file led_controller.h
|
||||
* @date 03 Jan 2024
|
||||
* @version 2.1.5
|
||||
*
|
||||
* @brief Header file for the led_controller
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LED_CONTROLLER_H
|
||||
#define LED_CONTROLLER_H
|
||||
|
||||
/* Include of Arduino Core */
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "demo_app.h"
|
||||
|
||||
#define PIN_LED UINT8_C(13)
|
||||
#define LED_ERROR_PERIOD UINT8_C(100)
|
||||
#define LED_OK_PERIOD UINT16_C(1000)
|
||||
|
||||
/*!
|
||||
* @brief : Class library that holds functionality of the led controller
|
||||
*/
|
||||
class ledController
|
||||
{
|
||||
private:
|
||||
bool _led_on = false;
|
||||
uint32_t _time_stamp = 0;
|
||||
demo_ret_code previous_ret_code;
|
||||
|
||||
/*!
|
||||
* @brief : This function updates the led blinking pattern according to
|
||||
* the provided period.
|
||||
*
|
||||
* @param[in] period : blinking period
|
||||
*/
|
||||
void switch_led(uint32_t period);
|
||||
|
||||
public:
|
||||
/*!
|
||||
* @brief : The constructor of the led_controller class
|
||||
* Creates an instance of the class
|
||||
*/
|
||||
ledController();
|
||||
|
||||
/*!
|
||||
* @brief : This function initializes the led controller module
|
||||
*/
|
||||
void begin();
|
||||
|
||||
/*!
|
||||
* @brief : This function updates the led controller status. It should be
|
||||
* called at least 10 times per second to ensure that the correct
|
||||
* blinking pattern is generated.
|
||||
*
|
||||
* @param[in] retCode : error code
|
||||
*/
|
||||
void update(demo_ret_code ret_code);
|
||||
};
|
||||
|
||||
#endif
|
||||
354
libraries/bsec2/examples/bme68x_demo_sample/sensor_manager.cpp
Normal file
354
libraries/bsec2/examples/bme68x_demo_sample/sensor_manager.cpp
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
/*!
|
||||
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
|
||||
*
|
||||
* BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 THE
|
||||
* 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.
|
||||
*
|
||||
* @file sensor_manager.cpp
|
||||
* @date 03 Jan 2024
|
||||
* @version 2.1.5
|
||||
*
|
||||
* @brief sensor manager
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* own header include */
|
||||
#include "sensor_manager.h"
|
||||
|
||||
bme68x_sensor sensorManager::_sensors[NUM_BME68X_UNITS];
|
||||
comm_mux comm_setup[NUM_BME68X_UNITS];
|
||||
|
||||
/*!
|
||||
* @brief The constructor of the sensorManager class
|
||||
*/
|
||||
sensorManager::sensorManager()
|
||||
{}
|
||||
|
||||
/*!
|
||||
* @brief This function initializes the given BME688 sensor
|
||||
*/
|
||||
int8_t sensorManager::initialize_sensor(uint8_t sensor_number, uint32_t& sensor_id)
|
||||
{
|
||||
int8_t bme68x_rslt;
|
||||
|
||||
bme68xSensors[sensor_number].begin(BME68X_SPI_INTF, comm_mux_read, comm_mux_write, comm_mux_delay,
|
||||
&comm_setup[sensor_number]);
|
||||
bme68x_rslt = bme68xSensors[sensor_number].status;
|
||||
|
||||
if (bme68x_rslt != BME68X_OK)
|
||||
{
|
||||
return bme68x_rslt;
|
||||
}
|
||||
sensor_id = bme68xSensors[sensor_number].getUniqueId();
|
||||
bme68x_rslt = bme68xSensors[sensor_number].status;
|
||||
return bme68x_rslt;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function configures the heater settings of the sensor
|
||||
*/
|
||||
int8_t sensorManager::set_heater_profile(const String& heater_profile_str, const String& duty_cycle_str,
|
||||
bme68x_heater_profile& heater_profile, uint8_t sensor_number)
|
||||
{
|
||||
/* get heater profiles from parsed object */
|
||||
JsonArray heaterProfilesJson = _configDoc["configBody"]["heaterProfiles"].as<JsonArray>();
|
||||
|
||||
/* iterate over all heater profiles */
|
||||
for (JsonVariant _heaterProfileJson: heaterProfilesJson)
|
||||
{
|
||||
|
||||
/* compare with profile of given sensor */
|
||||
if (heater_profile_str == _heaterProfileJson["id"].as<String>())
|
||||
{
|
||||
/* on match, save heater temperature and duration vectors */
|
||||
heater_profile.length = _heaterProfileJson["temperatureTimeVectors"].size();
|
||||
|
||||
for (uint32_t i = 0; i < heater_profile.length; i++)
|
||||
{
|
||||
heater_profile.temperature[i] = _heaterProfileJson["temperatureTimeVectors"][i][0].as<uint16_t>();
|
||||
heater_profile.duration[i] = _heaterProfileJson["temperatureTimeVectors"][i][1].as<uint16_t>();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* get duty cycle profiles from parsed object */
|
||||
JsonArray dutyCycleProfilesJson = _configDoc["configBody"]["dutyCycleProfiles"].as<JsonArray>();
|
||||
|
||||
/* iterate over all duty cycle profiles */
|
||||
for (JsonVariant _dutyCycleProfileJson : dutyCycleProfilesJson)
|
||||
{
|
||||
/* compare with profile of the given sensor */
|
||||
if (duty_cycle_str == _dutyCycleProfileJson["id"].as<String>())
|
||||
{
|
||||
/* on match, save duty cycle information to the sensor profile */
|
||||
heater_profile.nb_repetitions = _dutyCycleProfileJson["numberScanningCycles"].as<uint8_t>();
|
||||
heater_profile.sleep_duration = _dutyCycleProfileJson["numberSleepingCycles"].as<uint8_t>();
|
||||
|
||||
uint64_t sleep_duration = 0;
|
||||
|
||||
for (uint16_t dur : heater_profile.duration)
|
||||
{
|
||||
sleep_duration += (uint64_t)dur * HEATER_TIME_BASE;
|
||||
}
|
||||
heater_profile.sleep_duration *= sleep_duration;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return configure_sensor(heater_profile, sensor_number);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function configures the bme688 sensor
|
||||
*/
|
||||
int8_t sensorManager::configure_sensor(bme68x_heater_profile& heater_profile, uint8_t sensor_number)
|
||||
{
|
||||
bme68xSensors[sensor_number].setTPH();
|
||||
int8_t bme68x_rslt = bme68xSensors[sensor_number].status;
|
||||
|
||||
if (bme68x_rslt != BME68X_OK)
|
||||
{
|
||||
return bme68x_rslt;
|
||||
}
|
||||
|
||||
/* getMeasDur() returns Measurement duration in micro sec. to convert to milli sec. '/ INT64_C(1000)' */
|
||||
uint32_t shared_heatr_dur = HEATER_TIME_BASE -
|
||||
(bme68xSensors[sensor_number].getMeasDur(BME68X_PARALLEL_MODE) / INT64_C(1000));
|
||||
/* sets the heater configuration of the sensor */
|
||||
bme68xSensors[sensor_number].setHeaterProf(heater_profile.temperature, heater_profile.duration,
|
||||
shared_heatr_dur, heater_profile.length);
|
||||
return bme68xSensors[sensor_number].status;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function initializes all bme688 sensors
|
||||
*/
|
||||
demo_ret_code sensorManager::initialize_all_sensors()
|
||||
{
|
||||
int8_t bme68x_rslt = BME68X_OK;
|
||||
comm_mux_begin(Wire, SPI);
|
||||
|
||||
for (uint8_t i = 0; i < NUM_BME68X_UNITS; i++)
|
||||
{
|
||||
bme68x_sensor* sensor = get_sensor(i);
|
||||
/* Communication interface set for all the 8 sensors */
|
||||
comm_setup[i] = comm_mux_set_config(Wire, SPI, i, comm_setup[i]);
|
||||
|
||||
if (sensor != nullptr)
|
||||
{
|
||||
sensor->i2c_mask = ((0x01 << i) ^ 0xFF);//TODO
|
||||
bme68x_rslt = initialize_sensor(i, sensor->id);
|
||||
|
||||
if (bme68x_rslt != BME68X_OK)
|
||||
{
|
||||
return EDK_BME68X_DRIVER_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return EDK_OK;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function configures the sensor manager using the provided config file
|
||||
*/
|
||||
demo_ret_code sensorManager::begin(const String& config_name)
|
||||
{
|
||||
int8_t bme68xRslt = BME68X_OK;
|
||||
|
||||
comm_mux_begin(Wire, SPI);
|
||||
|
||||
/* Communication interface set for all the 8 sensors in the development kit */
|
||||
for (uint8_t i = 0; i < NUM_BME68X_UNITS; i++)
|
||||
{
|
||||
comm_setup[i] = comm_mux_set_config(Wire, SPI, i, comm_setup[i]);
|
||||
}
|
||||
|
||||
/* open config file */
|
||||
File configFile;
|
||||
|
||||
if (configFile.open(config_name.c_str(), O_READ))
|
||||
{
|
||||
/* read in configuration and parse to JSON object */
|
||||
DeserializationError error = deserializeJson(_configDoc, configFile);
|
||||
/* close config file */
|
||||
configFile.close();
|
||||
|
||||
if (error)
|
||||
{
|
||||
Serial.println(error.c_str());
|
||||
return EDK_SENSOR_MANAGER_JSON_DESERIAL_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return EDK_SENSOR_MANAGER_CONFIG_FILE_ERROR;
|
||||
}
|
||||
|
||||
memset(_sensors, 0, sizeof(_sensors));
|
||||
|
||||
JsonArray devicefigurations = _configDoc["configBody"]["sensorConfigurations"].as<JsonArray>();
|
||||
|
||||
for (JsonVariant devicefig : devicefigurations)
|
||||
{
|
||||
/* save config information to sensor profile */
|
||||
uint8_t sensor_number = devicefig["sensorIndex"].as<uint8_t>();
|
||||
bme68x_sensor* sensor = get_sensor(sensor_number);
|
||||
|
||||
if (sensor == nullptr)
|
||||
{
|
||||
return EDK_SENSOR_MANAGER_SENSOR_INDEX_ERROR;
|
||||
}
|
||||
|
||||
String heater_profile_str = devicefig["heaterProfile"].as<String>();
|
||||
String duty_cycle_str = devicefig["dutyCycleProfile"].as<String>();
|
||||
|
||||
sensor->is_configured = false;
|
||||
sensor->wake_up_time = 0;
|
||||
sensor->mode = BME68X_SLEEP_MODE;
|
||||
sensor->cycle_pos = 0;
|
||||
sensor->next_gas_index = 0;
|
||||
sensor->i2c_mask = ((0x01 << sensor_number) ^ 0xFF);
|
||||
sensor->scan_cycle_index = 1;
|
||||
|
||||
/* initialize the sensor */
|
||||
bme68xRslt = initialize_sensor(sensor_number, sensor->id);
|
||||
|
||||
if (bme68xRslt != BME68X_OK)
|
||||
{
|
||||
return EDK_BME68X_DRIVER_ERROR;
|
||||
}
|
||||
|
||||
/* set the heater profile */
|
||||
bme68xRslt = set_heater_profile(heater_profile_str, duty_cycle_str, sensor->heater_profile, sensor_number);
|
||||
|
||||
if (bme68xRslt != BME68X_OK)
|
||||
{
|
||||
return EDK_BME68X_DRIVER_ERROR;
|
||||
}
|
||||
|
||||
sensor->is_configured = true;
|
||||
}
|
||||
return EDK_OK;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function retrieves the selected sensor data
|
||||
*/
|
||||
demo_ret_code sensorManager::collect_data(uint8_t num, bme68x_data* data[3])
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
int8_t bme68x_rslt = BME68X_OK;
|
||||
|
||||
data[0] = data[1] = data[2] = nullptr;
|
||||
bme68x_sensor* sensor = get_sensor(num);
|
||||
|
||||
if (sensor == nullptr)
|
||||
{
|
||||
return EDK_SENSOR_MANAGER_SENSOR_INDEX_ERROR;
|
||||
}
|
||||
|
||||
uint64_t time_stamp = utils::get_tick_ms();
|
||||
|
||||
if (sensor->is_configured && (time_stamp >= sensor->wake_up_time))
|
||||
{
|
||||
|
||||
/* Wake up the sensor if necessary */
|
||||
if (sensor->mode == BME68X_SLEEP_MODE)
|
||||
{
|
||||
sensor->mode = BME68X_PARALLEL_MODE;
|
||||
bme68xSensors[num].setOpMode(BME68X_PARALLEL_MODE);
|
||||
bme68x_rslt = bme68xSensors[num].status;
|
||||
sensor->next_gas_index = 0;
|
||||
sensor->wake_up_time = time_stamp + GAS_WAIT_SHARED;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t nFields, j = 0;
|
||||
|
||||
nFields = bme68xSensors[num].fetchData();
|
||||
bme68x_data *sensor_data = bme68xSensors[num].getAllData();
|
||||
|
||||
for (uint8_t k = 0; k < 3; k++)
|
||||
{
|
||||
_field_data[k] = sensor_data[k];
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < nFields; i++)
|
||||
{
|
||||
|
||||
if (_field_data[i].status & BME68X_GASM_VALID_MSK)
|
||||
{
|
||||
uint8_t delta_index = _field_data[i].gas_index - sensor->next_gas_index;
|
||||
|
||||
if (delta_index > sensor->heater_profile.length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (delta_index > 0)
|
||||
{
|
||||
ret_code = EDK_SENSOR_MANAGER_DATA_MISS_WARNING;
|
||||
}
|
||||
|
||||
data[j++] = &_field_data[i];
|
||||
|
||||
sensor->next_gas_index = _field_data[i].gas_index + 1;
|
||||
|
||||
if (sensor->next_gas_index == sensor->heater_profile.length)
|
||||
{
|
||||
sensor->next_gas_index = 0;
|
||||
|
||||
if (++sensor->cycle_pos >= sensor->heater_profile.nb_repetitions)
|
||||
{
|
||||
sensor->cycle_pos = 0;
|
||||
sensor->mode = BME68X_SLEEP_MODE;
|
||||
sensor->wake_up_time = utils::get_tick_ms() + sensor->heater_profile.sleep_duration;
|
||||
bme68xSensors[num].setOpMode(BME68X_SLEEP_MODE);
|
||||
bme68x_rslt = bme68xSensors[num].status;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sensor->wake_up_time = time_stamp + GAS_WAIT_SHARED;
|
||||
}
|
||||
}
|
||||
|
||||
if (data[0] == nullptr)
|
||||
{
|
||||
sensor->wake_up_time = time_stamp + GAS_WAIT_SHARED;
|
||||
}
|
||||
}
|
||||
|
||||
if (bme68x_rslt < BME68X_OK)
|
||||
{
|
||||
ret_code = EDK_BME68X_DRIVER_ERROR;
|
||||
}
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
207
libraries/bsec2/examples/bme68x_demo_sample/sensor_manager.h
Normal file
207
libraries/bsec2/examples/bme68x_demo_sample/sensor_manager.h
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
/*!
|
||||
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
|
||||
*
|
||||
* BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 THE
|
||||
* 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.
|
||||
*
|
||||
* @file sensor_manager.h
|
||||
* @date 03 Jan 2024
|
||||
* @version 2.1.5
|
||||
*
|
||||
* @brief Header file for the sensor manager
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SENSOR_MANAGER_H
|
||||
#define SENSOR_MANAGER_H
|
||||
|
||||
/* Include of Arduino Core */
|
||||
#include <Arduino.h>
|
||||
#include <SdFat.h>
|
||||
#include <RTClib.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <SPI.h>
|
||||
#include <Wire.h>
|
||||
#include "utils.h"
|
||||
#include "demo_app.h"
|
||||
#include <bme68xLibrary.h>
|
||||
#include <commMux\commMux.h>
|
||||
|
||||
/* I2C-Expander masks */
|
||||
#define I2C_EXPANDER_ADDR 0x20
|
||||
#define I2C_EXPANDER_OUTPUT_REG_ADDR 0x01
|
||||
#define I2C_EXPANDER_CONFIG_REG_ADDR 0x03
|
||||
#define I2C_EXPANDER_CONFIG_REG_MASK 0x00
|
||||
#define SPI_COMM_SPEED UINT32_C(4000000)
|
||||
#define NUM_BME68X_UNITS UINT8_C(8)
|
||||
#define HEATER_TIME_BASE UINT8_C(140)
|
||||
#define MAX_HEATER_DURATION UINT8_C(200)
|
||||
#define GAS_WAIT_SHARED UINT8_C(140)
|
||||
/* Size of Json document in bytes */
|
||||
#define JSON_DOC_SIZE UINT16_C(5000)
|
||||
|
||||
/*!
|
||||
* @brief : Class library that holds the functionality of the sensor manager
|
||||
*/
|
||||
class sensorManager
|
||||
{
|
||||
private:
|
||||
static bme68x_sensor _sensors[NUM_BME68X_UNITS];
|
||||
Bme68x bme68xSensors[NUM_BME68X_UNITS];
|
||||
bme68x_data _field_data[3];
|
||||
|
||||
StaticJsonDocument<JSON_DOC_SIZE> _configDoc;
|
||||
|
||||
/*!
|
||||
* @brief : This function initializes the given BME688 sensor
|
||||
*
|
||||
* @param[in] sensorNumber : The sensor number
|
||||
* @param[in] sensorId : The sensor id
|
||||
*
|
||||
* @return 0 on success, lessthan zero otherwise
|
||||
*/
|
||||
int8_t initialize_sensor(uint8_t sensor_number, uint32_t& sensor_id);
|
||||
/*!
|
||||
* @brief : This function configures the heater settings of the sensor
|
||||
*
|
||||
* @param[in] heaterProfileStr : The heater profile string id
|
||||
* @param[in] dutyCycleStr : The duty cycle string id
|
||||
* @param[in] heaterProfile : The heater profile structure
|
||||
* @param[in] sensorNumber : The sensor number
|
||||
*
|
||||
* @return bme68x return code
|
||||
*/
|
||||
int8_t set_heater_profile(const String& heater_profile_str, const String& duty_cycle_str,
|
||||
bme68x_heater_profile& heater_profile, uint8_t sensor_number);
|
||||
|
||||
/*!
|
||||
* @brief : This function configures the bme688 sensor
|
||||
*
|
||||
* @param[in] heaterProfile : The heater profile structure
|
||||
* @param[in] sensorNumber : The sensor number
|
||||
*
|
||||
* @return bme68x return code
|
||||
*/
|
||||
int8_t configure_sensor(bme68x_heater_profile& heater_profile, uint8_t sensor_number);
|
||||
public:
|
||||
/*!
|
||||
* @brief : This function retrieves the selected sensor.
|
||||
*
|
||||
* @param[in] num : Sensor number
|
||||
*
|
||||
* @return Pointer to sensor if it exists, else nullptr
|
||||
*/
|
||||
static inline bme68x_sensor* get_sensor(uint8_t num)
|
||||
{
|
||||
bme68x_sensor* sensor = nullptr;
|
||||
|
||||
if (num < NUM_BME68X_UNITS)
|
||||
{
|
||||
sensor = &_sensors[num];
|
||||
}
|
||||
return sensor;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief : This function selects next readable bme688 sensor in given operation mode
|
||||
*
|
||||
* @param[inout] num : Reference to the sensor number
|
||||
* @param[in] mode : The sensor operation mode
|
||||
*
|
||||
* @return True if available
|
||||
*/
|
||||
static inline bool select_next_sensor(uint64_t& wake_up_time, uint8_t& num, uint8_t mode)
|
||||
{
|
||||
num = (uint8_t)0xFF;
|
||||
|
||||
for (uint8_t i = 0; i < NUM_BME68X_UNITS; i++)
|
||||
{
|
||||
|
||||
if ((_sensors[i].mode == mode) && (_sensors[i].wake_up_time < wake_up_time))
|
||||
{
|
||||
wake_up_time = _sensors[i].wake_up_time;
|
||||
num = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (num < NUM_BME68X_UNITS)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief : This function schedules the next readable bme688 sensor
|
||||
*
|
||||
* @param[inout] num : Reference to the sensor number
|
||||
*
|
||||
* @return True if available
|
||||
*/
|
||||
static inline bool schedule_sensor(uint8_t& num)
|
||||
{
|
||||
uint64_t wake_up_time = utils::get_tick_ms() + 20;
|
||||
return (select_next_sensor(wake_up_time, num, BME68X_PARALLEL_MODE) ||
|
||||
select_next_sensor(wake_up_time, num, BME68X_SLEEP_MODE));
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief : The constructor of the sensorManager class
|
||||
* Creates an instance of the class
|
||||
*/
|
||||
sensorManager();
|
||||
|
||||
/*!
|
||||
* @brief : This function initializes all bme688 sensors. It does not configure the sensor manager for data collection,
|
||||
* the begin function should be used instead for this purpose.
|
||||
*/
|
||||
demo_ret_code initialize_all_sensors();
|
||||
|
||||
/*!
|
||||
* @brief : This function configures the sensor manager using the provided config file.
|
||||
*
|
||||
* @param[in] config : sensor configuration file
|
||||
*
|
||||
* @return error code
|
||||
*/
|
||||
demo_ret_code begin(const String& config);
|
||||
|
||||
/*!
|
||||
* @brief : This function retrieves the selected sensor data.
|
||||
*
|
||||
* @param[in] num : Sensor number
|
||||
* @param[in] data : Pointer to sensor data if it is available, else nullptr
|
||||
*
|
||||
* @return error code
|
||||
*/
|
||||
demo_ret_code collect_data(uint8_t num, bme68x_data* data[3]);
|
||||
};
|
||||
|
||||
#endif
|
||||
332
libraries/bsec2/examples/bme68x_demo_sample/utils.cpp
Normal file
332
libraries/bsec2/examples/bme68x_demo_sample/utils.cpp
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
/*!
|
||||
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
|
||||
*
|
||||
* BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 THE
|
||||
* 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.
|
||||
*
|
||||
* @file utils.cpp
|
||||
* @date 03 Jan 2024
|
||||
* @version 2.1.5
|
||||
*
|
||||
* @brief utils
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
/* own header include */
|
||||
#include "utils.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <math.h>
|
||||
|
||||
uint64_t utils::_tick_ms;
|
||||
uint64_t utils::_tick_over_flow_cnt;
|
||||
SdFat utils::_sd;
|
||||
RTC_PCF8523 utils::_rtc;
|
||||
char utils::_file_seed[DATA_LOG_FILE_SEED_SIZE];
|
||||
uint32_t utils::_file_data_pos = 0;
|
||||
bool utils::_is_conf_available;
|
||||
|
||||
/*!
|
||||
* @brief This function creates the random alphanumeric file seed for the log file
|
||||
*/
|
||||
void utils::create_file_seed()
|
||||
{
|
||||
/* setup an alphanumeric characters buffer to choose from */
|
||||
const char *letters = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
/* for each character of the file seed, randomly select one from the buffer */
|
||||
for (uint32_t seed_char = 0; seed_char < 16; seed_char++)
|
||||
{
|
||||
uint32_t rand_c = random(0, 36);
|
||||
_file_seed[seed_char] = letters[rand_c];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function is a callback function to set the correct date for modified SD-card files
|
||||
*/
|
||||
void utils::date_time(uint16_t* date, uint16_t* time)
|
||||
{
|
||||
DateTime now = _rtc.now();
|
||||
/* return date using FAT_DATE macro to format fields */
|
||||
*date = FAT_DATE(now.year(), now.month(), now.day());
|
||||
/* return time using FAT_TIME macro to format fields */
|
||||
*time = FAT_TIME(now.hour(), now.minute(), now.second());
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function initializes the module
|
||||
*/
|
||||
demo_ret_code utils::begin()
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
|
||||
if (!_sd.begin(PIN_SD_CS, SPI_EIGHTH_SPEED))
|
||||
{
|
||||
ret_code = EDK_SD_CARD_INIT_ERROR;
|
||||
}
|
||||
else if (!_rtc.begin())
|
||||
{
|
||||
ret_code = EDK_DATALOGGER_RTC_BEGIN_WARNING;
|
||||
}
|
||||
else if (!_rtc.initialized() || _rtc.lostPower())
|
||||
{
|
||||
_rtc.adjust(DateTime(F(__DATE__), F(__TIME__)) - TimeSpan((int32_t)(TIMEZONE * 3600)));
|
||||
|
||||
ret_code = EDK_DATALOGGER_RTC_ADJUST_WARNING;
|
||||
}
|
||||
|
||||
randomSeed(analogRead(0));
|
||||
|
||||
create_file_seed();
|
||||
|
||||
SdFile::dateTimeCallback(date_time);
|
||||
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function retrieves the rtc handle
|
||||
*/
|
||||
RTC_PCF8523& utils::get_rtc()
|
||||
{
|
||||
return _rtc;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function retrieves the created file seed
|
||||
*/
|
||||
String utils::get_file_seed()
|
||||
{
|
||||
return String(_file_seed);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function creates a mac address string
|
||||
*/
|
||||
String utils::get_mac_address()
|
||||
{
|
||||
uint64_t mac = ESP.getEfuseMac();
|
||||
char *mac_ptr = (char*)&mac;
|
||||
char mac_str[13];
|
||||
|
||||
sprintf(mac_str, "%02X%02X%02X%02X%02X%02X", mac_ptr[0], mac_ptr[1], mac_ptr[2], mac_ptr[3], mac_ptr[4], mac_ptr[5]);
|
||||
|
||||
return String(mac_str);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function creates a date string
|
||||
*/
|
||||
String utils::get_date_time()
|
||||
{
|
||||
char time_buffer[20];
|
||||
DateTime date = _rtc.now();
|
||||
|
||||
sprintf(time_buffer, "%d_%02d_%02d_%02d_%02d", date.year(), date.month(), date.day(), date.hour(), date.minute());
|
||||
|
||||
return String(time_buffer);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function retrieves the first file with provided file extension
|
||||
*/
|
||||
bool utils::get_file_with_extension(String& fName, const String& extension)
|
||||
{
|
||||
File root;
|
||||
File file;
|
||||
char file_name[90];
|
||||
|
||||
if (root.open("/"))
|
||||
{
|
||||
|
||||
while (file.openNext(&root, O_READ))
|
||||
{
|
||||
|
||||
if (file.isFile())
|
||||
{
|
||||
file.getName(file_name, sizeof(file_name));
|
||||
|
||||
if (String(file_name).endsWith(extension))
|
||||
{
|
||||
file.close();
|
||||
fName = String(file_name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function retrieves the latest file with provided file extension
|
||||
*/
|
||||
bool utils::get_latest_file_with_extension(String& fName, const String& extension)
|
||||
{
|
||||
File root;
|
||||
File file;
|
||||
char file_name[90];
|
||||
bool flag = false;
|
||||
|
||||
if (root.open("/"))
|
||||
{
|
||||
|
||||
while (file.openNext(&root, O_READ))
|
||||
{
|
||||
|
||||
if (file.isFile())
|
||||
{
|
||||
file.getName(file_name, sizeof(file_name));
|
||||
|
||||
if (String(file_name).endsWith(extension))
|
||||
{
|
||||
file.close();
|
||||
fName = String(file_name);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function retrives the bsec configuration string from the provided file
|
||||
*/
|
||||
demo_ret_code utils::get_bsec_config(const String& file_name, uint8_t config_str[BSEC_MAX_PROPERTY_BLOB_SIZE])
|
||||
{
|
||||
demo_ret_code ret_code = EDK_OK;
|
||||
uint32_t config_str_len;
|
||||
|
||||
File configFile;
|
||||
if (!configFile.open(file_name.c_str(), O_RDWR))
|
||||
{
|
||||
ret_code = EDK_BSEC_CONFIG_STR_FILE_ERROR;
|
||||
}
|
||||
else if (configFile.read(&config_str_len, sizeof(uint32_t)) != sizeof(uint32_t))
|
||||
{
|
||||
ret_code = EDK_BSEC_CONFIG_STR_READ_ERROR;
|
||||
}
|
||||
else if (config_str_len != BSEC_MAX_PROPERTY_BLOB_SIZE)
|
||||
{
|
||||
ret_code = EDK_BSEC_CONFIG_STR_SIZE_ERROR;
|
||||
}
|
||||
else if (configFile.read(config_str, config_str_len) != config_str_len)
|
||||
{
|
||||
ret_code = EDK_BSEC_CONFIG_STR_READ_ERROR;
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief This function returns the tick value (ms)
|
||||
*/
|
||||
uint64_t utils::get_tick_ms(void)
|
||||
{
|
||||
uint64_t time_ms = millis();
|
||||
|
||||
if (_tick_ms > time_ms) /* An overflow occurred */
|
||||
{
|
||||
_tick_over_flow_cnt++;
|
||||
}
|
||||
_tick_ms = time_ms;
|
||||
return time_ms + (_tick_over_flow_cnt * INT64_C(0xFFFFFFFF));
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief : This function reads the size of bytes from the file of given fileExtension
|
||||
*/
|
||||
demo_ret_code utils::read_file(const String& file_extension, size_t size, char *file_data)
|
||||
{
|
||||
File file;
|
||||
demo_ret_code ret_code;
|
||||
static String file_name;
|
||||
static bool first_time = true;
|
||||
memset(file_data, 0, size); /* Clears the previous data if any */
|
||||
ret_code = utils::begin(); /* Initializes the SD card module */
|
||||
|
||||
size = size - 1;
|
||||
|
||||
if (ret_code >= EDK_OK)
|
||||
{
|
||||
|
||||
if (first_time)
|
||||
{
|
||||
|
||||
if (file_extension == BME68X_LABEL_INFO_FILE_EXT)
|
||||
{
|
||||
/* check for a file with given extension */
|
||||
_is_conf_available = utils::get_latest_file_with_extension(file_name, file_extension);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check for a file with given extension */
|
||||
_is_conf_available = utils::get_file_with_extension(file_name, file_extension);
|
||||
}
|
||||
}
|
||||
|
||||
if (_is_conf_available)
|
||||
{
|
||||
|
||||
if (!file.open(file_name.c_str(), O_READ)) /* open the given file in read mode */
|
||||
{
|
||||
return EDK_FILE_OPEN_ERROR;
|
||||
}
|
||||
first_time = false;
|
||||
file.seek(_file_data_pos); /* sets the position of the file to a particular byte */
|
||||
|
||||
if (file.available())
|
||||
{
|
||||
file.read(file_data, size); /* reads the given number of bytes of data */
|
||||
_file_data_pos = file.position(); /* save the file position indicator for next read */
|
||||
file.close(); /* closes the file */
|
||||
return EDK_OK;
|
||||
}
|
||||
file.close();
|
||||
_file_data_pos = 0; /* resets the file position indicator */
|
||||
first_time = true;
|
||||
return EDK_END_OF_FILE;
|
||||
}
|
||||
return EDK_EXTENSION_NOT_AVAILABLE;
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
168
libraries/bsec2/examples/bme68x_demo_sample/utils.h
Normal file
168
libraries/bsec2/examples/bme68x_demo_sample/utils.h
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
/*!
|
||||
* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
|
||||
*
|
||||
* BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 THE
|
||||
* 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.
|
||||
*
|
||||
* @file utils.h
|
||||
* @date 03 Jan 2024
|
||||
* @version 2.1.5
|
||||
*
|
||||
* @brief Header file for the datalogger utils
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DATALOGGER_UTILS_H
|
||||
#define DATALOGGER_UTILS_H
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <SdFat.h>
|
||||
#include <RTClib.h>
|
||||
#include "demo_app.h"
|
||||
|
||||
#define BME68X_RAWDATA_FILE_EXT ".bmerawdata"
|
||||
#define BME68X_LABEL_INFO_FILE_EXT ".bmelabelinfo"
|
||||
#define BME68X_CONFIG_FILE_EXT ".bmeconfig"
|
||||
#define BSEC_CONFIG_FILE_EXT ".config"
|
||||
#define AI_CONFIG_FILE_EXT ".aiconfig"
|
||||
#define AI_DATA_FILE_EXT ".aipredictions"
|
||||
#define FILE_SIZE_LIMIT UINT32_C(311427072)
|
||||
#define TIMEZONE 2.0
|
||||
#define DATA_LOG_FILE_SEED_SIZE UINT8_C(17)
|
||||
#define PIN_SD_CS UINT8_C(33)
|
||||
|
||||
|
||||
class utils
|
||||
{
|
||||
private:
|
||||
static uint64_t _tick_ms;
|
||||
static uint64_t _tick_over_flow_cnt;
|
||||
static SdFat _sd;
|
||||
static RTC_PCF8523 _rtc;
|
||||
static char _file_seed[DATA_LOG_FILE_SEED_SIZE];
|
||||
static uint32_t _file_data_pos;
|
||||
static bool _is_conf_available;
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
* @brief : This function creates the random alphanumeric file seed for the log file
|
||||
*/
|
||||
static void create_file_seed();
|
||||
public:
|
||||
/*!
|
||||
* @brief : This function is a callback function to set the correct date for modified SD-card files
|
||||
*/
|
||||
static void date_time(uint16_t* date, uint16_t* time);
|
||||
|
||||
/*!
|
||||
* @brief : This function initializes the module
|
||||
*
|
||||
* @return a bosch return code
|
||||
*/
|
||||
static demo_ret_code begin();
|
||||
|
||||
/*!
|
||||
* @brief : This function retrieves the rtc handle
|
||||
*
|
||||
* @return a reference to the rtc handle
|
||||
*/
|
||||
static RTC_PCF8523& get_rtc();
|
||||
|
||||
/*!
|
||||
* @brief : This function retrieves the created file seed
|
||||
*
|
||||
* @return the created file seed (16 random alphanumerical characters)
|
||||
*/
|
||||
static String get_file_seed();
|
||||
|
||||
/*!
|
||||
* @brief : This function creates a mac address string
|
||||
*
|
||||
* @return the mac address string
|
||||
*/
|
||||
static String get_mac_address();
|
||||
|
||||
/*!
|
||||
* @brief : This function creates a date string
|
||||
*
|
||||
* @return the date string
|
||||
*/
|
||||
static String get_date_time();
|
||||
|
||||
/*!
|
||||
* @brief : This function retrieves the first file with provided file extension
|
||||
*
|
||||
* @param[out] fName : the filename found
|
||||
* @param[in] extension : the file extension
|
||||
*
|
||||
* @return the date string
|
||||
*/
|
||||
static bool get_file_with_extension(String& fName, const String& extension);
|
||||
|
||||
/*!
|
||||
* @brief : This function retrieves the latest file with provided file extension
|
||||
*
|
||||
* @param[out] fName : the filename found
|
||||
* @param[in] extension : the file extension
|
||||
*
|
||||
* @return the date string
|
||||
*/
|
||||
static bool get_latest_file_with_extension(String& fName, const String& extension);
|
||||
|
||||
/*!
|
||||
* @brief : This function retrives the bsec configuration string from the provided file
|
||||
*
|
||||
* @param[in] fileName : the bsec configuration filename
|
||||
* @param[out] configStr: the configuration string
|
||||
*
|
||||
* @return a bosch return code
|
||||
*/
|
||||
static demo_ret_code get_bsec_config(const String& file_name, uint8_t config_str[BSEC_MAX_PROPERTY_BLOB_SIZE]);
|
||||
|
||||
/*!
|
||||
* @brief : This function returns the tick value (ms)
|
||||
*
|
||||
* @return tick value in milliseconds
|
||||
*/
|
||||
static uint64_t get_tick_ms(void);
|
||||
|
||||
/*!
|
||||
* @brief : This function reads size of bytes from the file of given fileExtension
|
||||
*
|
||||
* @param[in] fileExtension : file extension to search for a file in SD card
|
||||
* @param[in] size : number of bytes to read
|
||||
* @param[in] fileData : pointer to store the read data
|
||||
*
|
||||
* @return a bosch return code
|
||||
*/
|
||||
static demo_ret_code read_file(const String& file_extension, size_t size, char *file_data);
|
||||
};
|
||||
#endif
|
||||
12
libraries/bsec2/examples/generic_examples/Readme.md
Normal file
12
libraries/bsec2/examples/generic_examples/Readme.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
The generic_examples are provided only for a single sensor implementation.
|
||||
If multiple sensors are used kindly refer to x8_board_examples where support for 8 sensors are demonstrated.
|
||||
|
||||
|
||||
basic_config_state.ino example supports regression as well as classification outputs.
|
||||
However BSEC supports only one mode at any given point of time.
|
||||
Ensure to define the output mode required in the example code before compilation.
|
||||
|
||||
Set the OUTPUT_MODE macro to CLASSIFICATION for the classification output of the BSEC algorithm (default).
|
||||
#define OUTPUT_MODE CLASSIFICATION
|
||||
Set the OUTPUT_MODE macro to REGRESSION for the regression output of the BSEC algorithm.
|
||||
#define OUTPUT_MODE REGRESSION
|
||||
223
libraries/bsec2/examples/generic_examples/basic/basic.ino
Normal file
223
libraries/bsec2/examples/generic_examples/basic/basic.ino
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
/**
|
||||
* Copyright (C) 2021 Bosch Sensortec GmbH
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
/* If compiling this examples leads to an 'undefined reference error', refer to the README
|
||||
* at https://github.com/BoschSensortec/Bosch-BSEC2-Library
|
||||
*/
|
||||
/* The new sensor needs to be conditioned before the example can work reliably. You may run this
|
||||
* example for 24hrs to let the sensor stabilize.
|
||||
*/
|
||||
|
||||
/**
|
||||
* basic.ino sketch :
|
||||
* This is an example for illustrating the BSEC virtual outputs and
|
||||
* which has been designed to work with Adafruit ESP8266 Board
|
||||
*/
|
||||
|
||||
#include <bsec2.h>
|
||||
|
||||
/* Macros used */
|
||||
#define PANIC_LED LED_BUILTIN
|
||||
#define ERROR_DUR 1000
|
||||
|
||||
#define SAMPLE_RATE BSEC_SAMPLE_RATE_ULP
|
||||
|
||||
/* Helper functions declarations */
|
||||
/**
|
||||
* @brief : This function toggles the led when a fault was detected
|
||||
*/
|
||||
void errLeds(void);
|
||||
|
||||
/**
|
||||
* @brief : This function checks the BSEC status, prints the respective error code. Halts in case of error
|
||||
* @param[in] bsec : Bsec2 class object
|
||||
*/
|
||||
void checkBsecStatus(Bsec2 bsec);
|
||||
|
||||
/**
|
||||
* @brief : This function is called by the BSEC library when a new output is available
|
||||
* @param[in] input : BME68X sensor data before processing
|
||||
* @param[in] outputs : Processed BSEC BSEC output data
|
||||
* @param[in] bsec : Instance of BSEC2 calling the callback
|
||||
*/
|
||||
void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec);
|
||||
|
||||
/* Create an object of the class Bsec2 */
|
||||
Bsec2 envSensor;
|
||||
|
||||
/* Entry point for the example */
|
||||
void setup(void)
|
||||
{
|
||||
/* Desired subscription list of BSEC2 outputs */
|
||||
bsecSensor sensorList[] = {
|
||||
BSEC_OUTPUT_IAQ,
|
||||
BSEC_OUTPUT_RAW_TEMPERATURE,
|
||||
BSEC_OUTPUT_RAW_PRESSURE,
|
||||
BSEC_OUTPUT_RAW_HUMIDITY,
|
||||
BSEC_OUTPUT_RAW_GAS,
|
||||
BSEC_OUTPUT_STABILIZATION_STATUS,
|
||||
BSEC_OUTPUT_RUN_IN_STATUS,
|
||||
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
|
||||
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
|
||||
BSEC_OUTPUT_STATIC_IAQ,
|
||||
BSEC_OUTPUT_CO2_EQUIVALENT,
|
||||
BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
|
||||
BSEC_OUTPUT_GAS_PERCENTAGE,
|
||||
BSEC_OUTPUT_COMPENSATED_GAS
|
||||
};
|
||||
|
||||
/* Initialize the communication interfaces */
|
||||
Serial.begin(115200);
|
||||
Wire.begin();
|
||||
pinMode(PANIC_LED, OUTPUT);
|
||||
|
||||
/* Valid for boards with USB-COM. Wait until the port is open */
|
||||
while(!Serial) delay(10);
|
||||
|
||||
/* Initialize the library and interfaces */
|
||||
if (!envSensor.begin(BME68X_I2C_ADDR_LOW, Wire))
|
||||
{
|
||||
checkBsecStatus(envSensor);
|
||||
}
|
||||
|
||||
/*
|
||||
* The default offset provided has been determined by testing the sensor in LP and ULP mode on application board 3.0
|
||||
* Please update the offset value after testing this on your product
|
||||
*/
|
||||
if (SAMPLE_RATE == BSEC_SAMPLE_RATE_ULP)
|
||||
{
|
||||
envSensor.setTemperatureOffset(TEMP_OFFSET_ULP);
|
||||
}
|
||||
else if (SAMPLE_RATE == BSEC_SAMPLE_RATE_LP)
|
||||
{
|
||||
envSensor.setTemperatureOffset(TEMP_OFFSET_LP);
|
||||
}
|
||||
|
||||
/* Subsribe to the desired BSEC2 outputs */
|
||||
if (!envSensor.updateSubscription(sensorList, ARRAY_LEN(sensorList), SAMPLE_RATE))
|
||||
{
|
||||
checkBsecStatus(envSensor);
|
||||
}
|
||||
|
||||
/* Whenever new data is available call the newDataCallback function */
|
||||
envSensor.attachCallback(newDataCallback);
|
||||
|
||||
Serial.println("BSEC library version " + \
|
||||
String(envSensor.version.major) + "." \
|
||||
+ String(envSensor.version.minor) + "." \
|
||||
+ String(envSensor.version.major_bugfix) + "." \
|
||||
+ String(envSensor.version.minor_bugfix));
|
||||
}
|
||||
|
||||
/* Function that is looped forever */
|
||||
void loop(void)
|
||||
{
|
||||
/* Call the run function often so that the library can
|
||||
* check if it is time to read new data from the sensor
|
||||
* and process it.
|
||||
*/
|
||||
if (!envSensor.run())
|
||||
{
|
||||
checkBsecStatus(envSensor);
|
||||
}
|
||||
}
|
||||
|
||||
void errLeds(void)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
digitalWrite(PANIC_LED, HIGH);
|
||||
delay(ERROR_DUR);
|
||||
digitalWrite(PANIC_LED, LOW);
|
||||
delay(ERROR_DUR);
|
||||
}
|
||||
}
|
||||
|
||||
void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec)
|
||||
{
|
||||
if (!outputs.nOutputs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("BSEC outputs:\n\tTime stamp = " + String((int) (outputs.output[0].time_stamp / INT64_C(1000000))));
|
||||
for (uint8_t i = 0; i < outputs.nOutputs; i++)
|
||||
{
|
||||
const bsecData output = outputs.output[i];
|
||||
switch (output.sensor_id)
|
||||
{
|
||||
case BSEC_OUTPUT_IAQ:
|
||||
Serial.println("\tIAQ = " + String(output.signal));
|
||||
Serial.println("\tIAQ accuracy = " + String((int) output.accuracy));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_TEMPERATURE:
|
||||
Serial.println("\tTemperature = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_PRESSURE:
|
||||
Serial.println("\tPressure = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_HUMIDITY:
|
||||
Serial.println("\tHumidity = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_GAS:
|
||||
Serial.println("\tGas resistance = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_STABILIZATION_STATUS:
|
||||
Serial.println("\tStabilization status = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RUN_IN_STATUS:
|
||||
Serial.println("\tRun in status = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
|
||||
Serial.println("\tCompensated temperature = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
|
||||
Serial.println("\tCompensated humidity = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_STATIC_IAQ:
|
||||
Serial.println("\tStatic IAQ = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_CO2_EQUIVALENT:
|
||||
Serial.println("\tCO2 Equivalent = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
|
||||
Serial.println("\tbVOC equivalent = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_GAS_PERCENTAGE:
|
||||
Serial.println("\tGas percentage = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_COMPENSATED_GAS:
|
||||
Serial.println("\tCompensated gas = " + String(output.signal));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void checkBsecStatus(Bsec2 bsec)
|
||||
{
|
||||
if (bsec.status < BSEC_OK)
|
||||
{
|
||||
Serial.println("BSEC error code : " + String(bsec.status));
|
||||
errLeds(); /* Halt in case of failure */
|
||||
}
|
||||
else if (bsec.status > BSEC_OK)
|
||||
{
|
||||
Serial.println("BSEC warning code : " + String(bsec.status));
|
||||
}
|
||||
|
||||
if (bsec.sensor.status < BME68X_OK)
|
||||
{
|
||||
Serial.println("BME68X error code : " + String(bsec.sensor.status));
|
||||
errLeds(); /* Halt in case of failure */
|
||||
}
|
||||
else if (bsec.sensor.status > BME68X_OK)
|
||||
{
|
||||
Serial.println("BME68X warning code : " + String(bsec.sensor.status));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,360 @@
|
|||
/**
|
||||
* Copyright (C) 2021 Bosch Sensortec GmbH
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
/* If compiling this examples leads to an 'undefined reference error', refer to the README
|
||||
* at https://github.com/BoschSensortec/Bosch-BSEC2-Library
|
||||
*/
|
||||
/* The new sensor needs to be conditioned before the example can work reliably. You may run this
|
||||
* example for 24hrs to let the sensor stabilize.
|
||||
*/
|
||||
|
||||
/**
|
||||
* basic_config_state.ino sketch :
|
||||
* This is an example for integration of BSEC2x library using configuration setting and has been
|
||||
* tested with Adafruit ESP8266 Board
|
||||
*
|
||||
* For quick integration test, example code can be used with configuration file under folder
|
||||
* Bosch_BSEC2_Library/src/config/FieldAir_HandSanitizer (Configuration file added as simple
|
||||
* code example for integration but not optimized on classification performance)
|
||||
* Config string for H2S and NonH2S target classes is also kept for reference (Suitable for
|
||||
* lab-based characterization of the sensor)
|
||||
*/
|
||||
|
||||
/* Use the Espressif EEPROM library. Skip otherwise */
|
||||
#if defined(ARDUINO_ARCH_ESP32) || (ARDUINO_ARCH_ESP8266)
|
||||
#include <EEPROM.h>
|
||||
#define USE_EEPROM
|
||||
#endif
|
||||
|
||||
#include <bsec2.h>
|
||||
|
||||
#define CLASSIFICATION 1
|
||||
#define REGRESSION 2
|
||||
|
||||
/* Configuration for two class classification used here
|
||||
* For four class classification please use configuration under config/FieldAir_HandSanitizer_Onion_Cinnamon
|
||||
*/
|
||||
/* Note :
|
||||
For the classification output from BSEC algorithm set OUTPUT_MODE macro to CLASSIFICATION.
|
||||
For the regression output from BSEC algorithm set OUTPUT_MODE macro to REGRESSION.
|
||||
*/
|
||||
#define OUTPUT_MODE CLASSIFICATION
|
||||
|
||||
#if (OUTPUT_MODE == CLASSIFICATION)
|
||||
const uint8_t bsec_config[] = {
|
||||
#include "config/FieldAir_HandSanitizer/bsec_selectivity.txt"
|
||||
};
|
||||
#elif (OUTPUT_MODE == REGRESSION)
|
||||
const uint8_t bsec_config[] = {
|
||||
#include "config/bme688/bme688_reg_18v_300s_4d/bsec_selectivity.txt"
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Macros used */
|
||||
#define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) /* 360 minutes - 4 times a day */
|
||||
#define PANIC_LED LED_BUILTIN
|
||||
#define ERROR_DUR 1000
|
||||
|
||||
/* Helper functions declarations */
|
||||
/**
|
||||
* @brief : This function toggles the led continuously with one second delay
|
||||
*/
|
||||
void errLeds(void);
|
||||
|
||||
/**
|
||||
* @brief : This function checks the BSEC status, prints the respective error code. Halts in case of error
|
||||
* @param[in] bsec : Bsec2 class object
|
||||
*/
|
||||
void checkBsecStatus(Bsec2 bsec);
|
||||
|
||||
/**
|
||||
* @brief : This function updates/saves BSEC state
|
||||
* @param[in] bsec : Bsec2 class object
|
||||
*/
|
||||
void updateBsecState(Bsec2 bsec);
|
||||
|
||||
/**
|
||||
* @brief : This function is called by the BSEC library when a new output is available
|
||||
* @param[in] input : BME68X sensor data before processing
|
||||
* @param[in] outputs : Processed BSEC BSEC output data
|
||||
* @param[in] bsec : Instance of BSEC2 calling the callback
|
||||
*/
|
||||
void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec);
|
||||
|
||||
/**
|
||||
* @brief : This function retrieves the existing state
|
||||
* @param : Bsec2 class object
|
||||
*/
|
||||
bool loadState(Bsec2 bsec);
|
||||
|
||||
/**
|
||||
* @brief : This function writes the state into EEPROM
|
||||
* @param : Bsec2 class object
|
||||
*/
|
||||
bool saveState(Bsec2 bsec);
|
||||
|
||||
/* Create an object of the class Bsec2 */
|
||||
Bsec2 envSensor;
|
||||
#ifdef USE_EEPROM
|
||||
static uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE];
|
||||
#endif
|
||||
/* Gas estimate names will be according to the configuration classes used */
|
||||
const String gasName[] = { "Field Air", "Hand sanitizer", "Undefined 3", "Undefined 4"};
|
||||
|
||||
/* Entry point for the example */
|
||||
void setup(void)
|
||||
{
|
||||
#if (OUTPUT_MODE == CLASSIFICATION)
|
||||
/* Desired subscription list of BSEC2 outputs */
|
||||
bsecSensor sensorList[] = {
|
||||
BSEC_OUTPUT_RAW_TEMPERATURE,
|
||||
BSEC_OUTPUT_RAW_PRESSURE,
|
||||
BSEC_OUTPUT_RAW_HUMIDITY,
|
||||
BSEC_OUTPUT_RAW_GAS,
|
||||
BSEC_OUTPUT_RAW_GAS_INDEX,
|
||||
BSEC_OUTPUT_GAS_ESTIMATE_1,
|
||||
BSEC_OUTPUT_GAS_ESTIMATE_2,
|
||||
BSEC_OUTPUT_GAS_ESTIMATE_3,
|
||||
BSEC_OUTPUT_GAS_ESTIMATE_4
|
||||
};
|
||||
#elif (OUTPUT_MODE == REGRESSION)
|
||||
/* Desired subscription list of BSEC2 outputs */
|
||||
bsecSensor sensorList[] = {
|
||||
BSEC_OUTPUT_RAW_TEMPERATURE,
|
||||
BSEC_OUTPUT_RAW_PRESSURE,
|
||||
BSEC_OUTPUT_RAW_HUMIDITY,
|
||||
BSEC_OUTPUT_RAW_GAS,
|
||||
BSEC_OUTPUT_RAW_GAS_INDEX,
|
||||
BSEC_OUTPUT_REGRESSION_ESTIMATE_1,
|
||||
BSEC_OUTPUT_REGRESSION_ESTIMATE_2,
|
||||
BSEC_OUTPUT_REGRESSION_ESTIMATE_3,
|
||||
BSEC_OUTPUT_REGRESSION_ESTIMATE_4
|
||||
};
|
||||
#endif
|
||||
|
||||
Serial.begin(115200);
|
||||
#ifdef USE_EEPROM
|
||||
EEPROM.begin(BSEC_MAX_STATE_BLOB_SIZE + 1);
|
||||
#endif
|
||||
Wire.begin();
|
||||
pinMode(PANIC_LED, OUTPUT);
|
||||
|
||||
/* Valid for boards with USB-COM. Wait until the port is open */
|
||||
while (!Serial) delay(10);
|
||||
|
||||
/* Initialize the library and interfaces */
|
||||
if (!envSensor.begin(BME68X_I2C_ADDR_LOW, Wire))
|
||||
{
|
||||
checkBsecStatus(envSensor);
|
||||
}
|
||||
|
||||
/* Load the configuration string that stores information on how to classify the detected gas */
|
||||
if (!envSensor.setConfig(bsec_config))
|
||||
{
|
||||
checkBsecStatus (envSensor);
|
||||
}
|
||||
|
||||
/* Copy state from the EEPROM to the algorithm */
|
||||
if (!loadState(envSensor))
|
||||
{
|
||||
checkBsecStatus (envSensor);
|
||||
}
|
||||
|
||||
/* Subscribe for the desired BSEC2 outputs */
|
||||
if (!envSensor.updateSubscription(sensorList, ARRAY_LEN(sensorList), BSEC_SAMPLE_RATE_SCAN))
|
||||
{
|
||||
checkBsecStatus (envSensor);
|
||||
}
|
||||
|
||||
/* Whenever new data is available call the newDataCallback function */
|
||||
envSensor.attachCallback(newDataCallback);
|
||||
|
||||
Serial.println("\nBSEC library version " + \
|
||||
String(envSensor.version.major) + "." \
|
||||
+ String(envSensor.version.minor) + "." \
|
||||
+ String(envSensor.version.major_bugfix) + "." \
|
||||
+ String(envSensor.version.minor_bugfix));
|
||||
}
|
||||
|
||||
/* Function that is looped forever */
|
||||
void loop(void)
|
||||
{
|
||||
/* Call the run function often so that the library can
|
||||
* check if it is time to read new data from the sensor
|
||||
* and process it.
|
||||
*/
|
||||
if (!envSensor.run()) {
|
||||
checkBsecStatus (envSensor);
|
||||
}
|
||||
}
|
||||
|
||||
void errLeds(void)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
digitalWrite(PANIC_LED, HIGH);
|
||||
delay(ERROR_DUR);
|
||||
digitalWrite(PANIC_LED, LOW);
|
||||
delay(ERROR_DUR);
|
||||
}
|
||||
}
|
||||
|
||||
void updateBsecState(Bsec2 bsec)
|
||||
{
|
||||
static uint16_t stateUpdateCounter = 0;
|
||||
bool update = false;
|
||||
|
||||
if (!stateUpdateCounter || (stateUpdateCounter * STATE_SAVE_PERIOD) < millis())
|
||||
{
|
||||
/* Update every STATE_SAVE_PERIOD minutes */
|
||||
update = true;
|
||||
stateUpdateCounter++;
|
||||
}
|
||||
|
||||
if (update && !saveState(bsec))
|
||||
checkBsecStatus(bsec);
|
||||
}
|
||||
|
||||
void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec)
|
||||
{
|
||||
if (!outputs.nOutputs)
|
||||
return;
|
||||
|
||||
Serial.println("BSEC outputs:\n\tTime stamp = " + String((int) (outputs.output[0].time_stamp / INT64_C(1000000))));
|
||||
uint8_t index = 0;
|
||||
|
||||
for (uint8_t i = 0; i < outputs.nOutputs; i++)
|
||||
{
|
||||
const bsecData output = outputs.output[i];
|
||||
switch (output.sensor_id)
|
||||
{
|
||||
case BSEC_OUTPUT_RAW_TEMPERATURE:
|
||||
Serial.println("\tTemperature = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_PRESSURE:
|
||||
Serial.println("\tPressure = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_HUMIDITY:
|
||||
Serial.println("\tHumidity = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_GAS:
|
||||
Serial.println("\tGas resistance = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_GAS_INDEX:
|
||||
Serial.println("\tGas index = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
|
||||
Serial.println("\tCompensated temperature = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
|
||||
Serial.println("\tCompensated humidity = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_GAS_ESTIMATE_1:
|
||||
case BSEC_OUTPUT_GAS_ESTIMATE_2:
|
||||
case BSEC_OUTPUT_GAS_ESTIMATE_3:
|
||||
case BSEC_OUTPUT_GAS_ESTIMATE_4:
|
||||
index = (output.sensor_id - BSEC_OUTPUT_GAS_ESTIMATE_1);
|
||||
if (index == 0) // The four classes are updated from BSEC with same accuracy, thus printing is done just once.
|
||||
{
|
||||
Serial.println("\tAccuracy = " + String(output.accuracy));
|
||||
}
|
||||
Serial.println("\tClass " + String(index + 1) + " probability = " + String(output.signal * 100) + "%");
|
||||
break;
|
||||
case BSEC_OUTPUT_REGRESSION_ESTIMATE_1:
|
||||
case BSEC_OUTPUT_REGRESSION_ESTIMATE_2:
|
||||
case BSEC_OUTPUT_REGRESSION_ESTIMATE_3:
|
||||
case BSEC_OUTPUT_REGRESSION_ESTIMATE_4:
|
||||
index = (output.sensor_id - BSEC_OUTPUT_REGRESSION_ESTIMATE_1);
|
||||
if (index == 0) // The four targets are updated from BSEC with same accuracy, thus printing is done just once.
|
||||
{
|
||||
Serial.println("\tAccuracy = " + String(output.accuracy));
|
||||
}
|
||||
Serial.println("\tTarget " + String(index + 1) + " = " + String(output.signal * 100));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
updateBsecState(envSensor);
|
||||
}
|
||||
|
||||
void checkBsecStatus(Bsec2 bsec)
|
||||
{
|
||||
if (bsec.status < BSEC_OK)
|
||||
{
|
||||
Serial.println("BSEC error code : " + String(bsec.status));
|
||||
errLeds(); /* Halt in case of failure */
|
||||
} else if (bsec.status > BSEC_OK)
|
||||
{
|
||||
Serial.println("BSEC warning code : " + String(bsec.status));
|
||||
}
|
||||
|
||||
if (bsec.sensor.status < BME68X_OK)
|
||||
{
|
||||
Serial.println("BME68X error code : " + String(bsec.sensor.status));
|
||||
errLeds(); /* Halt in case of failure */
|
||||
} else if (bsec.sensor.status > BME68X_OK)
|
||||
{
|
||||
Serial.println("BME68X warning code : " + String(bsec.sensor.status));
|
||||
}
|
||||
}
|
||||
|
||||
bool loadState(Bsec2 bsec)
|
||||
{
|
||||
#ifdef USE_EEPROM
|
||||
|
||||
|
||||
if (EEPROM.read(0) == BSEC_MAX_STATE_BLOB_SIZE)
|
||||
{
|
||||
/* Existing state in EEPROM */
|
||||
Serial.println("Reading state from EEPROM");
|
||||
Serial.print("State file: ");
|
||||
for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE; i++)
|
||||
{
|
||||
bsecState[i] = EEPROM.read(i + 1);
|
||||
Serial.print(String(bsecState[i], HEX) + ", ");
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
if (!bsec.setState(bsecState))
|
||||
return false;
|
||||
} else
|
||||
{
|
||||
/* Erase the EEPROM with zeroes */
|
||||
Serial.println("Erasing EEPROM");
|
||||
|
||||
for (uint8_t i = 0; i <= BSEC_MAX_STATE_BLOB_SIZE; i++)
|
||||
EEPROM.write(i, 0);
|
||||
|
||||
EEPROM.commit();
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool saveState(Bsec2 bsec)
|
||||
{
|
||||
#ifdef USE_EEPROM
|
||||
if (!bsec.getState(bsecState))
|
||||
return false;
|
||||
|
||||
Serial.println("Writing state to EEPROM");
|
||||
Serial.print("State file: ");
|
||||
|
||||
for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE; i++)
|
||||
{
|
||||
EEPROM.write(i + 1, bsecState[i]);
|
||||
Serial.print(String(bsecState[i], HEX) + ", ");
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
EEPROM.write(0, BSEC_MAX_STATE_BLOB_SIZE);
|
||||
EEPROM.commit();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:esp32dev]
|
||||
platform = espressif32@6.6.0
|
||||
board = esp32dev
|
||||
framework = espidf
|
||||
platform_packages =
|
||||
platformio/framework-espidf@~3.50201.0
|
||||
lib_deps =
|
||||
../../../
|
||||
BME68x Sensor library=https://github.com/luar123/Bosch-BME68x-Library.git
|
||||
https://github.com/natanaeljr/esp32-I2Cbus
|
||||
monitor_speed = 115200
|
||||
274
libraries/bsec2/examples/generic_examples/basic_idf/src/main.cpp
Normal file
274
libraries/bsec2/examples/generic_examples/basic_idf/src/main.cpp
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
/**
|
||||
* Copyright (C) 2021 Bosch Sensortec GmbH
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
/* If compiling this examples leads to an 'undefined reference error', refer to the README
|
||||
* at https://github.com/BoschSensortec/Bosch-BSEC2-Library
|
||||
*/
|
||||
/* The new sensor needs to be conditioned before the example can work reliably. You may run this
|
||||
* example for 24hrs to let the sensor stabilize.
|
||||
*/
|
||||
|
||||
/**
|
||||
* basic.ino sketch :
|
||||
* This is an example for illustrating the BSEC virtual outputs and
|
||||
* which has been designed to work with Adafruit ESP8266 Board
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "I2Cbus.hpp"
|
||||
|
||||
#define BME688_ADD 0x77
|
||||
|
||||
static const char* TAG = "main";
|
||||
|
||||
#include <bsec2.h>
|
||||
|
||||
/* Macros used */
|
||||
#define PANIC_LED GPIO_NUM_5
|
||||
#define ERROR_DUR 1000
|
||||
|
||||
#define SAMPLE_RATE BSEC_SAMPLE_RATE_LP
|
||||
|
||||
/* Helper functions declarations */
|
||||
/**
|
||||
* @brief : This function toggles the led when a fault was detected
|
||||
*/
|
||||
void errLeds(void);
|
||||
|
||||
/**
|
||||
* @brief : This function checks the BSEC status, prints the respective error code. Halts in case of error
|
||||
* @param[in] bsec : Bsec2 class object
|
||||
*/
|
||||
void checkBsecStatus(Bsec2 bsec);
|
||||
|
||||
/**
|
||||
* @brief : This function is called by the BSEC library when a new output is available
|
||||
* @param[in] input : BME68X sensor data before processing
|
||||
* @param[in] outputs : Processed BSEC BSEC output data
|
||||
* @param[in] bsec : Instance of BSEC2 calling the callback
|
||||
*/
|
||||
void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec);
|
||||
|
||||
/* Create an object of the class Bsec2 */
|
||||
Bsec2 envSensor;
|
||||
|
||||
/* Function wrappers */
|
||||
int8_t read_bytes_wrapper(uint8_t a_register, uint8_t *data, uint32_t len, void *intfPtr) {
|
||||
return static_cast<I2C_t *>(intfPtr)->readBytes(BME688_ADD, a_register, len, data)==ESP_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
int8_t write_bytes_wrapper(uint8_t a_register, const uint8_t *data, uint32_t len,
|
||||
void *intfPtr) {
|
||||
return static_cast<I2C_t *>(intfPtr)->writeBytes(BME688_ADD, a_register, len, data)==ESP_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
uint32_t IRAM_ATTR millis() { return (uint32_t) (esp_timer_get_time() / 1000ULL); }
|
||||
void IRAM_ATTR delay(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); }
|
||||
uint32_t IRAM_ATTR micros() { return (uint32_t) esp_timer_get_time(); }
|
||||
|
||||
void delay_microseconds_safe(uint32_t us) { // avoids CPU locks that could trigger WDT or affect WiFi/BT stability
|
||||
uint32_t start = micros();
|
||||
|
||||
const uint32_t lag = 5000; // microseconds, specifies the maximum time for a CPU busy-loop.
|
||||
// it must be larger than the worst-case duration of a delay(1) call (hardware tasks)
|
||||
// 5ms is conservative, it could be reduced when exact BT/WiFi stack delays are known
|
||||
if (us > lag) {
|
||||
delay((us - lag) / 1000UL); // note: in disabled-interrupt contexts delay() won't actually sleep
|
||||
while (micros() - start < us - lag)
|
||||
delay(1); // in those cases, this loop allows to yield for BT/WiFi stack tasks
|
||||
}
|
||||
while (micros() - start < us) // fine delay the remaining usecs
|
||||
;
|
||||
}
|
||||
|
||||
void delay_us(uint32_t period, void *intfPtr) {
|
||||
delay_microseconds_safe(period);
|
||||
}
|
||||
|
||||
/* Entry point for the example */
|
||||
void setup(void)
|
||||
{
|
||||
/* Desired subscription list of BSEC2 outputs */
|
||||
bsecSensor sensorList[] = {
|
||||
BSEC_OUTPUT_IAQ,
|
||||
BSEC_OUTPUT_RAW_TEMPERATURE,
|
||||
BSEC_OUTPUT_RAW_PRESSURE,
|
||||
BSEC_OUTPUT_RAW_HUMIDITY,
|
||||
BSEC_OUTPUT_RAW_GAS,
|
||||
BSEC_OUTPUT_STABILIZATION_STATUS,
|
||||
BSEC_OUTPUT_RUN_IN_STATUS,
|
||||
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
|
||||
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
|
||||
BSEC_OUTPUT_STATIC_IAQ,
|
||||
BSEC_OUTPUT_CO2_EQUIVALENT,
|
||||
BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
|
||||
BSEC_OUTPUT_GAS_PERCENTAGE,
|
||||
BSEC_OUTPUT_COMPENSATED_GAS
|
||||
};
|
||||
|
||||
/* Initialize the communication interfaces */
|
||||
i2c0.begin(GPIO_NUM_3,GPIO_NUM_0);
|
||||
gpio_set_direction(PANIC_LED, GPIO_MODE_OUTPUT);
|
||||
|
||||
|
||||
/* Initialize the library and interfaces */
|
||||
if (!envSensor.begin(BME68X_I2C_INTF, read_bytes_wrapper, write_bytes_wrapper, delay_us, (void *) &i2c0, millis))
|
||||
{
|
||||
checkBsecStatus(envSensor);
|
||||
}
|
||||
|
||||
/*
|
||||
* The default offset provided has been determined by testing the sensor in LP and ULP mode on application board 3.0
|
||||
* Please update the offset value after testing this on your product
|
||||
*/
|
||||
if (SAMPLE_RATE == BSEC_SAMPLE_RATE_ULP)
|
||||
{
|
||||
envSensor.setTemperatureOffset(TEMP_OFFSET_ULP);
|
||||
}
|
||||
else if (SAMPLE_RATE == BSEC_SAMPLE_RATE_LP)
|
||||
{
|
||||
envSensor.setTemperatureOffset(TEMP_OFFSET_LP);
|
||||
}
|
||||
|
||||
/* Subsribe to the desired BSEC2 outputs */
|
||||
if (!envSensor.updateSubscription(sensorList, ARRAY_LEN(sensorList), SAMPLE_RATE))
|
||||
{
|
||||
checkBsecStatus(envSensor);
|
||||
}
|
||||
|
||||
/* Whenever new data is available call the newDataCallback function */
|
||||
envSensor.attachCallback(newDataCallback);
|
||||
|
||||
ESP_LOGI(TAG, "BSEC library version %u.%u.%u.%u", envSensor.version.major, envSensor.version.minor, envSensor.version.major_bugfix, envSensor.version.minor_bugfix);
|
||||
}
|
||||
|
||||
/* Function that is looped forever */
|
||||
void loop(void)
|
||||
{
|
||||
/* Call the run function often so that the library can
|
||||
* check if it is time to read new data from the sensor
|
||||
* and process it.
|
||||
*/
|
||||
if (!envSensor.run())
|
||||
{
|
||||
checkBsecStatus(envSensor);
|
||||
}
|
||||
}
|
||||
|
||||
void errLeds(void)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
gpio_set_level(PANIC_LED, 1);
|
||||
vTaskDelay(ERROR_DUR / portTICK_PERIOD_MS);
|
||||
gpio_set_level(PANIC_LED, 0);
|
||||
vTaskDelay(ERROR_DUR / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec)
|
||||
{
|
||||
if (!outputs.nOutputs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "BSEC outputs:\n\tTime stamp = %d", (int) (outputs.output[0].time_stamp / INT64_C(1000000)));
|
||||
for (uint8_t i = 0; i < outputs.nOutputs; i++)
|
||||
{
|
||||
const bsecData output = outputs.output[i];
|
||||
switch (output.sensor_id)
|
||||
{
|
||||
case BSEC_OUTPUT_IAQ:
|
||||
ESP_LOGI(TAG, "\tIAQ = %f", output.signal);
|
||||
ESP_LOGI(TAG, "\tIAQ accuracy = %d", (int) output.accuracy);
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_TEMPERATURE:
|
||||
ESP_LOGI(TAG, "\tTemperature = %f", output.signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_PRESSURE:
|
||||
ESP_LOGI(TAG, "\tPressure = %f", output.signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_HUMIDITY:
|
||||
ESP_LOGI(TAG, "\tHumidity = %f", output.signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_GAS:
|
||||
ESP_LOGI(TAG, "\tGas resistance = %f", output.signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_STABILIZATION_STATUS:
|
||||
ESP_LOGI(TAG, "\tStabilization status = %f", output.signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_RUN_IN_STATUS:
|
||||
ESP_LOGI(TAG, "\tRun in status = %f", output.signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
|
||||
ESP_LOGI(TAG, "\tCompensated temperature = %f", output.signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
|
||||
ESP_LOGI(TAG, "\tCompensated humidity = %f", output.signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_STATIC_IAQ:
|
||||
ESP_LOGI(TAG, "\tStatic IAQ = %f", output.signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_CO2_EQUIVALENT:
|
||||
ESP_LOGI(TAG, "\tCO2 Equivalent = %f", output.signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
|
||||
ESP_LOGI(TAG, "\tbVOC equivalent = %f", output.signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_GAS_PERCENTAGE:
|
||||
ESP_LOGI(TAG, "\tGas percentage = %f", output.signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_COMPENSATED_GAS:
|
||||
ESP_LOGI(TAG, "\tCompensated gas = %f", output.signal);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void checkBsecStatus(Bsec2 bsec)
|
||||
{
|
||||
if (bsec.status < BSEC_OK)
|
||||
{
|
||||
ESP_LOGI(TAG, "BSEC error code : %d", bsec.status);
|
||||
errLeds(); /* Halt in case of failure */
|
||||
}
|
||||
else if (bsec.status > BSEC_OK)
|
||||
{
|
||||
ESP_LOGI(TAG, "BSEC warning code : %d", bsec.status);
|
||||
}
|
||||
|
||||
if (bsec.sensor.status < BME68X_OK)
|
||||
{
|
||||
ESP_LOGI(TAG, "BME68X error code : %d", bsec.sensor.status);
|
||||
errLeds(); /* Halt in case of failure */
|
||||
}
|
||||
else if (bsec.sensor.status > BME68X_OK)
|
||||
{
|
||||
ESP_LOGI(TAG, "BME68X warning code : %d", bsec.sensor.status);
|
||||
}
|
||||
}
|
||||
|
||||
void loop_task(void *pv_params) {
|
||||
setup();
|
||||
while (true) {
|
||||
loop();
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void app_main()
|
||||
{
|
||||
ESP_LOGI(TAG, "starting");
|
||||
xTaskCreate(loop_task, "loopTask", 8192, nullptr, 1, NULL);
|
||||
}
|
||||
8
libraries/bsec2/examples/x8_board_examples/Readme.md
Normal file
8
libraries/bsec2/examples/x8_board_examples/Readme.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
basic_config_state.ino example supports regression as well as classification outputs.
|
||||
However BSEC supports only one mode at any given point of time.
|
||||
Ensure to define the output mode required in the example code before compilation.
|
||||
|
||||
Set the OUTPUT_MODE macro to CLASSIFICATION for the classification output of the BSEC algorithm (default).
|
||||
#define OUTPUT_MODE CLASSIFICATION
|
||||
Set the OUTPUT_MODE macro to REGRESSION for the regression output of the BSEC algorithm.
|
||||
#define OUTPUT_MODE REGRESSION
|
||||
242
libraries/bsec2/examples/x8_board_examples/basic/basic.ino
Normal file
242
libraries/bsec2/examples/x8_board_examples/basic/basic.ino
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
/**
|
||||
Copyright (C) 2021 Bosch Sensortec GmbH
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
*/
|
||||
|
||||
/* The new sensor needs to be conditioned before the example can work reliably. You may run this
|
||||
example for 24hrs to let the sensor stabilize.
|
||||
*/
|
||||
|
||||
/**
|
||||
basic.ino sketch :
|
||||
This is an example for illustrating the BSEC virtual outputs using BME688 Development Kit,
|
||||
which has been designed to work with Adafruit ESP32 Feather Board
|
||||
For more information visit :
|
||||
https://www.bosch-sensortec.com/software-tools/software/bme688-software/
|
||||
*/
|
||||
|
||||
#include <bsec2.h>
|
||||
#include <commMux\commMux.h>
|
||||
|
||||
/* Macros used */
|
||||
/* Number of sensors to operate*/
|
||||
#define NUM_OF_SENS 8
|
||||
#define PANIC_LED LED_BUILTIN
|
||||
#define ERROR_DUR 1000
|
||||
|
||||
#define SAMPLE_RATE BSEC_SAMPLE_RATE_ULP
|
||||
|
||||
/* Helper functions declarations */
|
||||
/**
|
||||
@brief : This function toggles the led when a fault was detected
|
||||
*/
|
||||
void errLeds(void);
|
||||
|
||||
/**
|
||||
@brief : This function checks the BSEC status, prints the respective error code. Halts in case of error
|
||||
@param[in] bsec : Bsec2 class object
|
||||
*/
|
||||
void checkBsecStatus(Bsec2 bsec);
|
||||
|
||||
/**
|
||||
@brief : This function is called by the BSEC library when a new output is available
|
||||
@param[in] input : BME68X sensor data before processing
|
||||
@param[in] outputs : Processed BSEC BSEC output data
|
||||
@param[in] bsec : Instance of BSEC2 calling the callback
|
||||
*/
|
||||
void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec);
|
||||
|
||||
/* Create an array of objects of the class Bsec2 */
|
||||
Bsec2 envSensor[NUM_OF_SENS];
|
||||
comm_mux communicationSetup[NUM_OF_SENS];
|
||||
uint8_t bsecMemBlock[NUM_OF_SENS][BSEC_INSTANCE_SIZE];
|
||||
uint8_t sensor = 0;
|
||||
|
||||
/* Entry point for the example */
|
||||
void setup(void)
|
||||
{
|
||||
/* Desired subscription list of BSEC2 outputs */
|
||||
bsecSensor sensorList[] = {
|
||||
BSEC_OUTPUT_IAQ,
|
||||
BSEC_OUTPUT_RAW_TEMPERATURE,
|
||||
BSEC_OUTPUT_RAW_PRESSURE,
|
||||
BSEC_OUTPUT_RAW_HUMIDITY,
|
||||
BSEC_OUTPUT_RAW_GAS,
|
||||
BSEC_OUTPUT_STABILIZATION_STATUS,
|
||||
BSEC_OUTPUT_RUN_IN_STATUS,
|
||||
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
|
||||
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
|
||||
BSEC_OUTPUT_STATIC_IAQ,
|
||||
BSEC_OUTPUT_CO2_EQUIVALENT,
|
||||
BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
|
||||
BSEC_OUTPUT_GAS_PERCENTAGE,
|
||||
BSEC_OUTPUT_COMPENSATED_GAS
|
||||
};
|
||||
|
||||
/* Initialize the communication interfaces */
|
||||
Serial.begin(115200);
|
||||
comm_mux_begin(Wire, SPI);
|
||||
pinMode(PANIC_LED, OUTPUT);
|
||||
delay(100);
|
||||
/* Valid for boards with USB-COM. Wait until the port is open */
|
||||
while (!Serial) delay(10);
|
||||
|
||||
for (uint8_t i = 0; i < NUM_OF_SENS; i++)
|
||||
{
|
||||
/* Sets the Communication interface for the sensors */
|
||||
communicationSetup[i] = comm_mux_set_config(Wire, SPI, i, communicationSetup[i]);
|
||||
|
||||
/* Assigning a chunk of memory block to the bsecInstance */
|
||||
envSensor[i].allocateMemory(bsecMemBlock[i]);
|
||||
|
||||
/* Initialize the library and interfaces */
|
||||
if (!envSensor[i].begin(BME68X_SPI_INTF, comm_mux_read, comm_mux_write, comm_mux_delay, &communicationSetup[i]))
|
||||
{
|
||||
checkBsecStatus (envSensor[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* The default offset provided has been determined by testing the sensor in LP and ULP mode on application board 3.0
|
||||
* Please update the offset value after testing this on your product
|
||||
*/
|
||||
if (SAMPLE_RATE == BSEC_SAMPLE_RATE_ULP)
|
||||
{
|
||||
envSensor[i].setTemperatureOffset(TEMP_OFFSET_ULP);
|
||||
}
|
||||
else if (SAMPLE_RATE == BSEC_SAMPLE_RATE_LP)
|
||||
{
|
||||
envSensor[i].setTemperatureOffset(TEMP_OFFSET_LP);
|
||||
}
|
||||
|
||||
/* Subscribe to the desired BSEC2 outputs */
|
||||
if (!envSensor[i].updateSubscription(sensorList, ARRAY_LEN(sensorList), SAMPLE_RATE))
|
||||
{
|
||||
checkBsecStatus (envSensor[i]);
|
||||
}
|
||||
|
||||
/* Whenever new data is available call the newDataCallback function */
|
||||
envSensor[i].attachCallback(newDataCallback);
|
||||
}
|
||||
|
||||
Serial.println("BSEC library version " + \
|
||||
String(envSensor[0].version.major) + "." \
|
||||
+ String(envSensor[0].version.minor) + "." \
|
||||
+ String(envSensor[0].version.major_bugfix) + "." \
|
||||
+ String(envSensor[0].version.minor_bugfix));
|
||||
}
|
||||
|
||||
/* Function that is looped forever */
|
||||
void loop(void)
|
||||
{
|
||||
/* Call the run function often so that the library can
|
||||
check if it is time to read new data from the sensor
|
||||
and process it.
|
||||
*/
|
||||
for (sensor = 0; sensor < NUM_OF_SENS; sensor++)
|
||||
{
|
||||
if (!envSensor[sensor].run())
|
||||
{
|
||||
checkBsecStatus(envSensor[sensor]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void errLeds(void)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
digitalWrite(PANIC_LED, HIGH);
|
||||
delay(ERROR_DUR);
|
||||
digitalWrite(PANIC_LED, LOW);
|
||||
delay(ERROR_DUR);
|
||||
}
|
||||
}
|
||||
|
||||
void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec)
|
||||
{
|
||||
if (!outputs.nOutputs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("BSEC outputs:\n\tSensor num = " + String(sensor));
|
||||
Serial.println("\tTime stamp = " + String((int) (outputs.output[0].time_stamp / INT64_C(1000000))));
|
||||
for (uint8_t i = 0; i < outputs.nOutputs; i++)
|
||||
{
|
||||
const bsecData output = outputs.output[i];
|
||||
switch (output.sensor_id)
|
||||
{
|
||||
case BSEC_OUTPUT_IAQ:
|
||||
Serial.println("\tIAQ = " + String(output.signal));
|
||||
Serial.println("\tIAQ accuracy = " + String((int) output.accuracy));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_TEMPERATURE:
|
||||
Serial.println("\tTemperature = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_PRESSURE:
|
||||
Serial.println("\tPressure = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_HUMIDITY:
|
||||
Serial.println("\tHumidity = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_GAS:
|
||||
Serial.println("\tGas resistance = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_STABILIZATION_STATUS:
|
||||
Serial.println("\tStabilization status = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RUN_IN_STATUS:
|
||||
Serial.println("\tRun in status = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
|
||||
Serial.println("\tCompensated temperature = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
|
||||
Serial.println("\tCompensated humidity = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_STATIC_IAQ:
|
||||
Serial.println("\tStatic IAQ = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_CO2_EQUIVALENT:
|
||||
Serial.println("\tCO2 Equivalent = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
|
||||
Serial.println("\tbVOC equivalent = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_GAS_PERCENTAGE:
|
||||
Serial.println("\tGas percentage = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_COMPENSATED_GAS:
|
||||
Serial.println("\tCompensated gas = " + String(output.signal));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void checkBsecStatus(Bsec2 bsec)
|
||||
{
|
||||
if (bsec.status < BSEC_OK)
|
||||
{
|
||||
Serial.println("BSEC error code : " + String(bsec.status));
|
||||
errLeds(); /* Halt in case of failure */
|
||||
}
|
||||
else if (bsec.status > BSEC_OK)
|
||||
{
|
||||
Serial.println("BSEC warning code : " + String(bsec.status));
|
||||
}
|
||||
|
||||
if (bsec.sensor.status < BME68X_OK)
|
||||
{
|
||||
Serial.println("BME68X error code : " + String(bsec.sensor.status));
|
||||
errLeds(); /* Halt in case of failure */
|
||||
}
|
||||
else if (bsec.sensor.status > BME68X_OK)
|
||||
{
|
||||
Serial.println("BME68X warning code : " + String(bsec.sensor.status));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,375 @@
|
|||
/**
|
||||
* Copyright (C) 2021 Bosch Sensortec GmbH
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
/* The new sensor needs to be conditioned before the example can work reliably. You may run this
|
||||
* example for 24hrs to let the sensor stabilize.
|
||||
*/
|
||||
|
||||
/**
|
||||
* basic_config_state.ino sketch :
|
||||
* This is an example for integration of BSEC2x library in BME688 development kit,
|
||||
* which has been designed to work with Adafruit ESP32 Feather Board
|
||||
* For more information visit :
|
||||
* https://www.bosch-sensortec.com/software-tools/software/bme688-software/
|
||||
*
|
||||
* For quick integration test, example code can be used with configuration file under folder
|
||||
* Bosch_BSEC2_Library/src/config/FieldAir_HandSanitizer (Configuration file added as simple
|
||||
* code example for integration but not optimized on classification performance)
|
||||
* Config string for H2S and NonH2S target classes is also kept for reference (Suitable for
|
||||
* lab-based characterization of the sensor)
|
||||
*/
|
||||
|
||||
#include <EEPROM.h>
|
||||
#include <bsec2.h>
|
||||
#include <commMux\commMux.h>
|
||||
/* For two class classification use configuration under config/FieldAir_HandSanitizer */
|
||||
#define CLASSIFICATION 1
|
||||
#define REGRESSION 2
|
||||
|
||||
#define COMPLETED 1
|
||||
|
||||
/* Note :
|
||||
For the classification output from BSEC algorithm set OUTPUT_MODE macro to CLASSIFICATION.
|
||||
For the regression output from BSEC algorithm set OUTPUT_MODE macro to REGRESSION.
|
||||
*/
|
||||
#define OUTPUT_MODE CLASSIFICATION
|
||||
|
||||
#if (OUTPUT_MODE == CLASSIFICATION)
|
||||
const uint8_t bsec_config[] = {
|
||||
#include "config/FieldAir_HandSanitizer/bsec_selectivity.txt"
|
||||
};
|
||||
#elif (OUTPUT_MODE == REGRESSION)
|
||||
const uint8_t bsec_config[] = {
|
||||
#include "config/bme688/bme688_reg_18v_300s_4d/bsec_selectivity.txt"
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Macros used */
|
||||
#define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) /* 360 minutes - 4 times a day */
|
||||
/* Number of sensors to operate*/
|
||||
#define NUM_OF_SENS 8
|
||||
#define PANIC_LED LED_BUILTIN
|
||||
#define ERROR_DUR 1000
|
||||
|
||||
/* Helper functions declarations */
|
||||
/**
|
||||
* @brief : This function toggles the led continuously with one second delay
|
||||
*/
|
||||
void errLeds(void);
|
||||
|
||||
/**
|
||||
* @brief : This function checks the BSEC status, prints the respective error code. Halts in case of error
|
||||
* @param[in] bsec : Bsec2 class object
|
||||
*/
|
||||
void checkBsecStatus(Bsec2 bsec);
|
||||
|
||||
/**
|
||||
* @brief : This function updates/saves BSEC state
|
||||
* @param[in] bsec : Bsec2 class object
|
||||
*/
|
||||
void updateBsecState(Bsec2 bsec);
|
||||
|
||||
/**
|
||||
* @brief : This function is called by the BSEC library when a new output is available
|
||||
* @param[in] input : BME68X sensor data before processing
|
||||
* @param[in] outputs : Processed BSEC BSEC output data
|
||||
* @param[in] bsec : Instance of BSEC2 calling the callback
|
||||
*/
|
||||
void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec);
|
||||
|
||||
/**
|
||||
* @brief : This function retrieves the existing state
|
||||
* @param : Bsec2 class object
|
||||
*/
|
||||
bool loadState(Bsec2 bsec);
|
||||
|
||||
/**
|
||||
* @brief : This function writes the state into EEPROM
|
||||
* @param : Bsec2 class object
|
||||
*/
|
||||
bool saveState(Bsec2 bsec);
|
||||
|
||||
/* Create an object of the class Bsec2 */
|
||||
Bsec2 envSensor[NUM_OF_SENS];
|
||||
comm_mux commConfig[NUM_OF_SENS];
|
||||
uint8_t bsecMemBlock[NUM_OF_SENS][BSEC_INSTANCE_SIZE];
|
||||
uint8_t sensor = 0;
|
||||
static uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE];
|
||||
/* Gas estimate names will be according to the configuration classes used */
|
||||
const String gasName[] = { "Field Air", "Hand sanitizer", "Undefined 3", "Undefined 4"};
|
||||
|
||||
/* Entry point for the example */
|
||||
void setup(void)
|
||||
{
|
||||
|
||||
#if (OUTPUT_MODE == CLASSIFICATION)
|
||||
/* Desired subscription list of BSEC2 Classification outputs */
|
||||
bsecSensor sensorList[] = {
|
||||
BSEC_OUTPUT_RAW_TEMPERATURE,
|
||||
BSEC_OUTPUT_RAW_PRESSURE,
|
||||
BSEC_OUTPUT_RAW_HUMIDITY,
|
||||
BSEC_OUTPUT_RAW_GAS,
|
||||
BSEC_OUTPUT_RAW_GAS_INDEX,
|
||||
BSEC_OUTPUT_GAS_ESTIMATE_1,
|
||||
BSEC_OUTPUT_GAS_ESTIMATE_2,
|
||||
BSEC_OUTPUT_GAS_ESTIMATE_3,
|
||||
BSEC_OUTPUT_GAS_ESTIMATE_4
|
||||
};
|
||||
#elif (OUTPUT_MODE == REGRESSION)
|
||||
/* Desired subscription list of BSEC2 Regression outputs */
|
||||
bsecSensor sensorList[] = {
|
||||
BSEC_OUTPUT_RAW_TEMPERATURE,
|
||||
BSEC_OUTPUT_RAW_PRESSURE,
|
||||
BSEC_OUTPUT_RAW_HUMIDITY,
|
||||
BSEC_OUTPUT_RAW_GAS,
|
||||
BSEC_OUTPUT_RAW_GAS_INDEX,
|
||||
BSEC_OUTPUT_REGRESSION_ESTIMATE_1,
|
||||
BSEC_OUTPUT_REGRESSION_ESTIMATE_2,
|
||||
BSEC_OUTPUT_REGRESSION_ESTIMATE_3,
|
||||
BSEC_OUTPUT_REGRESSION_ESTIMATE_4
|
||||
};
|
||||
#endif
|
||||
|
||||
Serial.begin(115200);
|
||||
EEPROM.begin(BSEC_MAX_STATE_BLOB_SIZE + 1);
|
||||
/* Initiate SPI communication */
|
||||
comm_mux_begin(Wire, SPI);
|
||||
pinMode(PANIC_LED, OUTPUT);
|
||||
delay(100);
|
||||
/* Valid for boards with USB-COM. Wait until the port is open */
|
||||
while (!Serial) delay(10);
|
||||
|
||||
uint8_t state_write_otp = 0;
|
||||
|
||||
for (uint8_t i = 0; i < NUM_OF_SENS; i++)
|
||||
{
|
||||
/* Sets the Communication interface for the given sensor */
|
||||
commConfig[i] = comm_mux_set_config(Wire, SPI, i, commConfig[i]);
|
||||
|
||||
/* Assigning a chunk of memory block to the bsecInstance */
|
||||
envSensor[i].allocateMemory(bsecMemBlock[i]);
|
||||
|
||||
/* Initialize the library and interfaces */
|
||||
if (!envSensor[i].begin(BME68X_SPI_INTF, comm_mux_read, comm_mux_write, comm_mux_delay, &commConfig[i]))
|
||||
{
|
||||
checkBsecStatus (envSensor[i]);
|
||||
}
|
||||
|
||||
/* Load the configuration string that stores information on how to classify the detected gas */
|
||||
if (!envSensor[i].setConfig(bsec_config))
|
||||
{
|
||||
checkBsecStatus (envSensor[i]);
|
||||
}
|
||||
|
||||
/* Copy state from the EEPROM to the algorithm */
|
||||
if (state_write_otp == 0)
|
||||
{
|
||||
if (!loadState(envSensor[i]))
|
||||
{
|
||||
checkBsecStatus (envSensor[i]);
|
||||
}
|
||||
state_write_otp = COMPLETED;
|
||||
}
|
||||
|
||||
/* Subscribe for the desired BSEC2 outputs */
|
||||
if (!envSensor[i].updateSubscription(sensorList, ARRAY_LEN(sensorList), BSEC_SAMPLE_RATE_SCAN))
|
||||
{
|
||||
checkBsecStatus (envSensor[i]);
|
||||
}
|
||||
|
||||
/* Whenever new data is available call the newDataCallback function */
|
||||
envSensor[i].attachCallback(newDataCallback);
|
||||
|
||||
updateBsecState(envSensor[i]);
|
||||
}
|
||||
|
||||
Serial.println("\nBSEC library version " + \
|
||||
String(envSensor[0].version.major) + "." \
|
||||
+ String(envSensor[0].version.minor) + "." \
|
||||
+ String(envSensor[0].version.major_bugfix) + "." \
|
||||
+ String(envSensor[0].version.minor_bugfix));
|
||||
}
|
||||
|
||||
/* Function that is looped forever */
|
||||
void loop(void)
|
||||
{
|
||||
/* Call the run function often so that the library can
|
||||
* check if it is time to read new data from the sensor
|
||||
* and process it.
|
||||
*/
|
||||
for (sensor = 0; sensor < NUM_OF_SENS; sensor++)
|
||||
{
|
||||
if (!envSensor[sensor].run())
|
||||
{
|
||||
checkBsecStatus(envSensor[sensor]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void errLeds(void)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
digitalWrite(PANIC_LED, HIGH);
|
||||
delay(ERROR_DUR);
|
||||
digitalWrite(PANIC_LED, LOW);
|
||||
delay(ERROR_DUR);
|
||||
}
|
||||
}
|
||||
|
||||
void updateBsecState(Bsec2 bsec)
|
||||
{
|
||||
static uint16_t stateUpdateCounter = 0;
|
||||
bool update = false;
|
||||
|
||||
if (!stateUpdateCounter || (stateUpdateCounter * STATE_SAVE_PERIOD) < millis())
|
||||
{
|
||||
/* Update every STATE_SAVE_PERIOD minutes */
|
||||
update = true;
|
||||
stateUpdateCounter++;
|
||||
}
|
||||
|
||||
if (update && !saveState(bsec))
|
||||
checkBsecStatus(bsec);
|
||||
}
|
||||
|
||||
void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec)
|
||||
{
|
||||
if (!outputs.nOutputs)
|
||||
return;
|
||||
|
||||
Serial.println("BSEC outputs:\n\tSensor num = " + String(sensor));
|
||||
Serial.println("\tTime stamp = " + String((int) (outputs.output[0].time_stamp / INT64_C(1000000))));
|
||||
|
||||
int index = 0;
|
||||
|
||||
for (uint8_t i = 0; i < outputs.nOutputs; i++)
|
||||
{
|
||||
const bsecData output = outputs.output[i];
|
||||
switch (output.sensor_id)
|
||||
{
|
||||
case BSEC_OUTPUT_RAW_TEMPERATURE:
|
||||
Serial.println("\tTemperature = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_PRESSURE:
|
||||
Serial.println("\tPressure = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_HUMIDITY:
|
||||
Serial.println("\tHumidity = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_GAS:
|
||||
Serial.println("\tGas resistance = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_GAS_INDEX:
|
||||
Serial.println("\tGas index = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
|
||||
Serial.println("\tCompensated temperature = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
|
||||
Serial.println("\tCompensated humidity = " + String(output.signal));
|
||||
break;
|
||||
case BSEC_OUTPUT_GAS_ESTIMATE_1:
|
||||
case BSEC_OUTPUT_GAS_ESTIMATE_2:
|
||||
case BSEC_OUTPUT_GAS_ESTIMATE_3:
|
||||
case BSEC_OUTPUT_GAS_ESTIMATE_4:
|
||||
index = (output.sensor_id - BSEC_OUTPUT_GAS_ESTIMATE_1);
|
||||
if (index == 0) // The four classes are updated from BSEC with same accuracy, thus printing is done just once.
|
||||
{
|
||||
Serial.println("\tAccuracy = " + String((int) output.accuracy));
|
||||
}
|
||||
Serial.println(("\tClass " + String(index + 1) + " probability = ") + String(output.signal * 100) + "%");
|
||||
break;
|
||||
case BSEC_OUTPUT_REGRESSION_ESTIMATE_1:
|
||||
case BSEC_OUTPUT_REGRESSION_ESTIMATE_2:
|
||||
case BSEC_OUTPUT_REGRESSION_ESTIMATE_3:
|
||||
case BSEC_OUTPUT_REGRESSION_ESTIMATE_4:
|
||||
index = (output.sensor_id - BSEC_OUTPUT_REGRESSION_ESTIMATE_1);
|
||||
if (index == 0) // The four targets are updated from BSEC with same accuracy, thus printing is done just once.
|
||||
{
|
||||
Serial.println("\tAccuracy = " + String(output.accuracy));
|
||||
}
|
||||
Serial.println("\tTarget " + String(index + 1) + " = " + String(output.signal * 100));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void checkBsecStatus(Bsec2 bsec)
|
||||
{
|
||||
if (bsec.status < BSEC_OK)
|
||||
{
|
||||
Serial.println("BSEC error code : " + String(bsec.status));
|
||||
errLeds(); /* Halt in case of failure */
|
||||
} else if (bsec.status > BSEC_OK)
|
||||
{
|
||||
Serial.println("BSEC warning code : " + String(bsec.status));
|
||||
}
|
||||
|
||||
if (bsec.sensor.status < BME68X_OK)
|
||||
{
|
||||
Serial.println("BME68X error code : " + String(bsec.sensor.status));
|
||||
errLeds(); /* Halt in case of failure */
|
||||
} else if (bsec.sensor.status > BME68X_OK)
|
||||
{
|
||||
Serial.println("BME68X warning code : " + String(bsec.sensor.status));
|
||||
}
|
||||
}
|
||||
|
||||
bool loadState(Bsec2 bsec)
|
||||
{
|
||||
|
||||
if (EEPROM.read(0) == BSEC_MAX_STATE_BLOB_SIZE)
|
||||
{
|
||||
/* Existing state in EEPROM */
|
||||
Serial.println("Reading state from EEPROM");
|
||||
Serial.print("State file: ");
|
||||
for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE; i++)
|
||||
{
|
||||
bsecState[i] = EEPROM.read(i + 1);
|
||||
Serial.print(String(bsecState[i], HEX) + ", ");
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
if (!bsec.setState(bsecState))
|
||||
return false;
|
||||
} else
|
||||
{
|
||||
/* Erase the EEPROM with zeroes */
|
||||
Serial.println("Erasing EEPROM");
|
||||
|
||||
for (uint8_t i = 0; i <= BSEC_MAX_STATE_BLOB_SIZE; i++)
|
||||
EEPROM.write(i, 0);
|
||||
|
||||
EEPROM.commit();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool saveState(Bsec2 bsec)
|
||||
{
|
||||
if (!bsec.getState(bsecState))
|
||||
return false;
|
||||
|
||||
Serial.println("Writing state to EEPROM");
|
||||
Serial.print("State file: ");
|
||||
|
||||
for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE; i++)
|
||||
{
|
||||
EEPROM.write(i + 1, bsecState[i]);
|
||||
Serial.print(String(bsecState[i], HEX) + ", ");
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
EEPROM.write(0, BSEC_MAX_STATE_BLOB_SIZE);
|
||||
EEPROM.commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue