/** * 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_demo_sample.ino * @date 03 Jan 2024 * @version 2.1.5 * * */ /* 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. */ /** * bme68x_demo_sample.ino : * 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 * For more information visit : * https://www.bosch-sensortec.com/software-tools/software/bme688-software/ */ #include "bme68x_datalogger.h" #include "bsec_datalogger.h" #include "label_provider.h" #include "led_controller.h" #include "sensor_manager.h" #include "ble_controller.h" #include #include "utils.h" /* Macros used */ /*! FILE_DATA_READ_SIZE determines the size of the data to be read from the file */ #define FILE_DATA_READ_SIZE UINT16_C(400) /*! * @brief : This function is called by the BSEC library when a new output is available * * @param[in] input : BME68X data * @param[in] outputs : BSEC output data */ void bsecCallBack(const bme68x_data input, const bsecOutputs outputs); /*! * @brief : This function handles BLE message reception * * @param[in] msg : reference to the new BLE message * @param[inout] jsonDoc : reference to the json formatted BLE response */ void bleMessageReceived(const bleController::ble_msg& msg, JsonDocument& jsonDoc); /*! * @brief : This function handles getlabelinfo BLE command reception (get label information) * * @param[in] msg : reference to the new BLE message * @param[inout] jsonDoc : reference to the json formatted BLE response */ void ble_notify_get_label_info(const bleController::ble_msg& msg, JsonDocument& jsonDoc); /*! * @brief : This function handles setlabelinfo BLE command reception (set label information) * * @param[in] msg : reference to the new BLE message * @param[inout] jsonDoc : reference to the json formatted BLE response */ void ble_notify_set_label_info(const bleController::ble_msg& msg, JsonDocument& jsonDoc); /*! * @brief : This function handles setlabel BLE command reception (set class label) * * @param[in] msg : reference to the new BLE message * @param[inout] jsonDoc : reference to the json formatted BLE response */ void ble_notify_set_label(const bleController::ble_msg& msg, JsonDocument& jsonDoc); /*! * @brief : This function handles getrtctime BLE command reception (get RTC timestamp) * * @param[in] msg : reference to the new BLE message * @param[inout] jsonDoc : reference to the json formatted BLE response */ void ble_notify_get_rtc_time(const bleController::ble_msg &msg, JsonDocument& jsonDoc); /*! * @brief : This function handles setrtctime BLE command reception (set RTC timestamp) * * @param[in] msg : reference to the new BLE message * @param[inout] jsonDoc : reference to the json formatted BLE response */ void ble_notify_set_rtc_time(const bleController::ble_msg &msg, JsonDocument& jsonDoc); /*! * @brief : This function handles start BLE command reception (start BLE streaming of BSEC and BME raw data) * * @param[in] msg : reference to the new BLE message * @param[inout] jsonDoc : reference to the json formatted BLE response */ void ble_notify_start_streaming(const bleController::ble_msg &msg, JsonDocument& jsonDoc); /*! * @brief : This function handles stop BLE command reception (stop BLE streaming of BSEC and BME raw data) * * @param[in] msg : reference to the new BLE message * @param[inout] jsonDoc : reference to the json formatted BLE response */ void ble_notify_stop_streaming(const bleController::ble_msg &msg, JsonDocument& jsonDoc); /*! * @brief : This function handles readconfig BLE command reception (start BLE Streaming of config file data) * * @param[in] msg : reference to the new BLE message * @param[inout] jsonDoc : reference to the json formatted BLE response */ void ble_notify_read_config(const bleController::ble_msg &msg, JsonDocument& jsonDoc); /*! * @brief : This function handles BSEC output notification * * @param[in] outputs : reference to the new BSEC output data */ void ble_notify_bsec_output(const bsecOutputs& outputs, const uint8_t sens_num); /*! * @brief : This function handles BME68X data notification * * @param[in] input : reference to the new BME68X data * @param[in] sensNum: sensor number */ void ble_notify_bme68x_data(const bme68x_data& input, const uint8_t sens_num); /*! * @brief : This function handles setappmode BLE command reception (Setting the current App mode) * * @param[in] msg : reference to the new BLE message * @param[inout] jsonDoc : reference to the json formatted BLE response */ void ble_notify_set_appmode(const bleController::ble_msg &msg, JsonDocument& jsonDoc); /*! * @brief : This function handles getappmode BLE command reception (Returns the current App mode) * * @param[in] msg : reference to the new BLE message * @param[inout] jsonDoc : reference to the json formatted BLE response */ void ble_notify_get_appmode(const bleController::ble_msg &msg, JsonDocument& jsonDoc); /*! * @brief : This function handles setgroundtruth BLE command reception(Setting the Groundtruth) * * @param[in] msg : reference to the new BLE message * @param[inout] jsonDoc : reference to the json formatted BLE response */ void ble_notify_set_groundtruth(const bleController::ble_msg &msg, JsonDocument& jsonDoc); /*! * @brief : This function handles getfwversion BLE command reception (Returns the current firmware version) * * @param[in] msg : reference to the new BLE message * @param[inout] jsonDoc : reference to the json formatted BLE response */ void ble_notify_get_fw_version(const bleController::ble_msg &msg, JsonDocument& jsonDoc); /*! * @brief : This function handles sensor manager and BME68X datalogger configuration * * @param[in] bmeExtension : reference to the bmeconfig file extension * * @return Application return code */ demo_ret_code configure_sensor_logging(const String& bme_extension); /*! * @brief : This function handles the read configuration File * * @param[in] configFile : reference to the config File Name * * @return Application return code */ demo_ret_code config_file_read(const String& config_file); /*! * @brief : This function handles BSEC datalogger configuration * * @param[in] aiConfigFile : reference to the AI configuration file extension * * @param[in] bsecConfigFile : reference to the BSEC configuration string file extension * * @param[inout] bsecConfigStr : pointer to the BSEC configuration string * * @param[in] sensorNum : selected sensor number * * @return Application return code */ demo_ret_code configure_bsec_logging(const String& ai_config_file, const String& bsec_config_file, uint8_t bsec_config_str[BSEC_MAX_PROPERTY_BLOB_SIZE], uint8_t sensor_num); /*! * @brief : This function handles collecting, sending sensor raw data via ble and data logging * * @param[in] sensNum : sensor number to collect and log the data * * @return a Bosch return code */ demo_ret_code log_sensor_data(uint8_t sens_num); /*! * @brief : This function receive the system time (in unix epoch time format) from serial port and update into RTC time * * @return void */ void read_sys_time(); uint8_t bsec_config[BSEC_MAX_PROPERTY_BLOB_SIZE]; char config_file_data[FILE_DATA_READ_SIZE]; Bsec2 bsec2[NUM_OF_SENS]; bleController bleCtlr(bleMessageReceived); labelProvider labelPvr; ledController ledCtlr; sensorManager sensorMgr; bme68xDataLogger bme68xDlog; bsecDataLogger bsecDlog; demo_ret_code ret_code; uint8_t selected_sensor; String bme68x_conf_file_name, bsec_conf_file_name, ai_conf_file_name, config_file_name, label_file_name; demo_app_mode app_mode, current_app_mode; gas_label label; bool is_bme68x_conf_available, is_bsec_conf_available, is_ai_conf_available, is_label_info_available; comm_mux comm[NUM_OF_SENS]; uint8_t bsec_mem_block[NUM_OF_SENS][BSEC_INSTANCE_SIZE]; uint8_t sensor = 0; uint8_t sensor_num; uint32_t ground_truth; static uint8_t buff_count = 0; static bsecDataLogger::sensor_io_data buff[NUM_OF_SENS]; uint32_t received_sys_time = 0; void setup() { Serial.begin(115200); /* Setting default mode as idle */ app_mode = DEMO_RECORDING_MODE; current_app_mode = DEMO_RECORDING_MODE; label = BSEC_NO_CLASS; /* Setting default sensor as sensor 0 to collect data */ selected_sensor = NUM_BME68X_UNITS; /* Initializes the label provider module */ labelPvr.begin(); /* Initializes the led controller module */ ledCtlr.begin(); /* initialize the ble controller */ bleCtlr.begin(); /* Initializes the SD and RTC module */ ret_code = utils::begin(); if (ret_code >= EDK_OK) { is_bme68x_conf_available = utils::get_file_with_extension(bme68x_conf_file_name, BME68X_CONFIG_FILE_EXT); /* checks the availability of BME board configuration file */ if (is_bme68x_conf_available) { if (configure_sensor_logging(bme68x_conf_file_name) < EDK_OK) { ret_code = EDK_SENSOR_INITIALIZATION_FAILED; } } else { ret_code = EDK_SENSOR_CONFIG_MISSING_ERROR; } } if (ret_code < EDK_OK) { current_app_mode = DEMO_IDLE_MODE; app_mode = DEMO_IDLE_MODE; } } void loop() { /* Updates the led controller status */ ledCtlr.update(ret_code); read_sys_time(); while (bleCtlr.dequeue_ble_msg()); /*checks the ble connection status, restarts advertising if disconnected */ bleCtlr.check_ble_connection_sts(); if (ret_code >= EDK_OK) { /* Retrieves the current label */ (void) labelPvr.get_label(label); switch (current_app_mode) { /* Gets the bme688 sensors raw data in app and logs the same based on the selected sensors */ case DEMO_RECORDING_MODE: { uint8_t i; /* Flushes the buffered sensor data to the current log file */ ret_code = bme68xDlog.flush(); if (ret_code >= EDK_OK) { if (selected_sensor == NUM_BME68X_UNITS) { /* Schedules the next readable sensor */ while (sensorMgr.schedule_sensor(i)) { /* Gets the given sensor raw data and sends via ble and log into the SD card */ ret_code = log_sensor_data(i); } } else { ret_code = log_sensor_data(selected_sensor); } } } break; /* Example of BSEC library integration: gets the data from one out of 8 sensors (this can be selected through application) and calls BSEC library, get the outputs in app and logs the data */ case DEMO_TEST_ALGORITHM_MODE: { /* Flushes the buffered sensor data to the current log file */ ret_code = bsecDlog.flush_sensor_data(selected_sensor); if (selected_sensor == NUM_OF_SENS) { for (sensor_num = 0; sensor_num < NUM_OF_SENS; sensor_num++) { /* Callback from the user to read data from the BME688 sensors using parallel mode/forced mode, process and store outputs */ (void) bsec2[sensor_num].run(); } } else if (selected_sensor < NUM_OF_SENS) { (void) bsec2[selected_sensor].run(); sensor_num = selected_sensor; } } break; default: break; } } } demo_ret_code log_sensor_data(uint8_t sens_num) { demo_ret_code ret; bme68x_data* sensor_data[3]; /* Returns the selected sensor address */ bme68x_sensor* sensor = sensorMgr.get_sensor(sens_num); /* Retrieves the selected sensor data */ ret = sensorMgr.collect_data(sens_num, sensor_data); if (ret < EDK_OK) { StaticJsonDocument jsonDoc; JsonObject errorObj = jsonDoc.createNestedObject("errorCode"); errorObj["sensor_number"] = sens_num; errorObj["error_code"] = bleController::SENSOR_READ_ERROR; bleCtlr.send_notification(jsonDoc); } else { for (const auto data : sensor_data) { if (data != nullptr) { bme68x_data raw_data; raw_data.temperature = data->temperature; raw_data.pressure = (data->pressure * 0.01f); raw_data.humidity = data->humidity; raw_data.gas_resistance = data->gas_resistance; raw_data.gas_index = data->gas_index; /* sends the sensor raw data via ble */ ble_notify_bme68x_data(raw_data, sens_num); /* Writes the sensor data to the current log file */ ret = bme68xDlog.write_sensor_data(&sens_num, &sensor->id, &sensor->mode, data, &sensor->scan_cycle_index, label, ret); /* Increments scanCycleIndex by one, if cycle completes */ if (raw_data.gas_index == sensor->heater_profile.length - 1) { sensor->scan_cycle_index += 1; if (sensor->scan_cycle_index > sensor->heater_profile.nb_repetitions) { sensor->scan_cycle_index = 1; } } } } } return ret; } void bsecCallBack(const bme68x_data input, const bsecOutputs outputs, Bsec2 bsec) { /* Sending bme raw data via ble */ ble_notify_bme68x_data(input, sensor_num); /* Returns the selected sensor address */ bme68x_sensor *sensor = sensorMgr.get_sensor(sensor_num); if (sensor != nullptr) { bsecDlog.write_sensor_data(&sensor_num, &sensor->id, &input, &sensor->scan_cycle_index, ground_truth, ret_code, buff[sensor_num]); if (input.gas_index == sensor->heater_profile.length - 1) { sensor->scan_cycle_index++; if (sensor->scan_cycle_index > bsecDlog.scanCycles) { sensor->scan_cycle_index = 1; } } if (outputs.nOutputs) { /* Sending BSEC output via ble */ ble_notify_bsec_output(outputs, sensor_num); if (sensor != nullptr) { buff[sensor_num].sensor_num = sensor_num; buff[sensor_num].sensor_id = sensor->id; buff[sensor_num].outputs = outputs; buff[sensor_num].code = ret_code; buff[sensor_num].ground_truth = ground_truth; /* Writing BSEC output into SD card */ ret_code = bsecDlog.write_bsec_output(buff[sensor_num]); } } } } void bleMessageReceived(const bleController::ble_msg &msg, JsonDocument& jsonDoc) { switch (msg.id) { case bleController::GET_LABEL_INFO: ble_notify_get_label_info(msg, jsonDoc); break; case bleController::SET_LABEL_INFO: ble_notify_set_label_info(msg, jsonDoc); break; case bleController::SET_LABEL: ble_notify_set_label(msg, jsonDoc); break; case bleController::GET_RTC_TIME: ble_notify_get_rtc_time(msg, jsonDoc); break; case bleController::SET_RTC_TIME: ble_notify_set_rtc_time(msg, jsonDoc); break; case bleController::START_STREAMING: ble_notify_start_streaming(msg, jsonDoc); break; case bleController::STOP_STREAMING: ble_notify_stop_streaming(msg, jsonDoc); break; case bleController::READ_CONFIG: ble_notify_read_config(msg, jsonDoc); break; case bleController::SET_APPMODE: ble_notify_set_appmode(msg, jsonDoc); break; case bleController::GET_APPMODE: ble_notify_get_appmode(msg, jsonDoc); break; case bleController::SET_GROUNDTRUTH: ble_notify_set_groundtruth(msg, jsonDoc); break; case bleController::GET_FW_VERSION: ble_notify_get_fw_version(msg, jsonDoc); break; default: break; } } void ble_notify_get_label_info(const bleController::ble_msg &msg, JsonDocument& jsonDoc) { demo_ret_code ret; if (current_app_mode != DEMO_IDLE_MODE) { jsonDoc[msg.name] = bleController::APP_ALREADY_IN_STREAMING_MODE; return; } ret = config_file_read(BME68X_LABEL_INFO_FILE_EXT); if (ret == EDK_END_OF_FILE) { jsonDoc.clear(); jsonDoc.add(EOF); } else if (ret == EDK_SD_CARD_INIT_ERROR) { jsonDoc[msg.name] = bleController::SD_CARD_INIT_ERROR; } else if (ret == EDK_EXTENSION_NOT_AVAILABLE) { jsonDoc[msg.name] = bleController::LABEL_INFO_FILE_MISSING; } else { jsonDoc[msg.name] = bleController::FILE_OPEN_ERROR; } } void ble_notify_set_label_info(const bleController::ble_msg &msg, JsonDocument& jsonDoc) { demo_ret_code ret; if (current_app_mode == DEMO_RECORDING_MODE) { if (msg.label_info.label >= LABEL_TAG_MIN_RANGE && msg.label_info.label <= LABEL_TAG_MAX_RANGE) { ret = bme68xDlog.set_label_info(msg.label_info.label, String(msg.label_info.label_name), String(msg.label_info.label_desc)); if (ret == EDK_DATALOGGER_LABEL_INFO_FILE_ERROR) { jsonDoc[msg.name] = bleController::LABEL_FILE_OPEN_FAILED; } else if (ret == EDK_SENSOR_MANAGER_JSON_DESERIAL_ERROR) { jsonDoc[msg.name] = bleController::DESERIALIZATION_FAILED; } else { jsonDoc[msg.name] = bleController::CMD_VALID; } } else { jsonDoc[msg.name] = bleController::LABEL_INVALID; } } else { jsonDoc[msg.name] = bleController::INVALID_APP_MODE; } } void ble_notify_set_label(const bleController::ble_msg &msg, JsonDocument& jsonDoc) { if (msg.label >= LABEL_TAG_MIN_RANGE && msg.label <= LABEL_TAG_MAX_RANGE) { label = static_cast(msg.label); jsonDoc[msg.name] = bleController::CMD_VALID; } else { jsonDoc[msg.name] = bleController::LABEL_INVALID; } } void ble_notify_get_rtc_time(const bleController::ble_msg &msg, JsonDocument& jsonDoc) { jsonDoc[msg.name] = bleController::CMD_VALID; jsonDoc["value"] = utils::get_rtc().now().unixtime(); } void ble_notify_set_rtc_time(const bleController::ble_msg &msg, JsonDocument& jsonDoc) { jsonDoc[msg.name] = bleController::CMD_VALID; utils::get_rtc().adjust(DateTime(msg.rtc_time)); } void ble_notify_start_streaming(const bleController::ble_msg &msg, JsonDocument& jsonDoc) { bsec_virtual_sensor_t bsec_output_list[BSEC_NUMBER_OUTPUTS]; bleController::cmd_status ble_ret_code = bleController::CMD_VALID; const bleController::ble_bsec_msg& bsecMsg = msg.bsec; /* Initializes the SD and RTC module */ ret_code = utils::begin(); if (ret_code >= EDK_OK) { //Flushing the contents of the buffer to the rawdata file generated during the previous run if (current_app_mode == DEMO_TEST_ALGORITHM_MODE) { bsecDlog.flush_sensor_data(selected_sensor); } else if (current_app_mode == DEMO_RECORDING_MODE) { bme68xDlog.flush(); } if (app_mode == DEMO_TEST_ALGORITHM_MODE) { is_bsec_conf_available = utils::get_file_with_extension(bsec_conf_file_name, BSEC_CONFIG_FILE_EXT); is_ai_conf_available = utils::get_file_with_extension(ai_conf_file_name, AI_CONFIG_FILE_EXT); /* checks the availability of BSEC configuration file */ if (is_bsec_conf_available) { if (is_ai_conf_available) { bsec_virtual_sensor_t bsec_output_list[BSEC_NUMBER_OUTPUTS]; float sample_rate = -1; /* Check added to ensure support for multi instance in test algorithm mode */ if (bsecMsg.selected_sensor > NUM_OF_SENS) { ble_ret_code = bleController::BSEC_SELECTED_SENSOR_INVALID; jsonDoc[msg.name] = ble_ret_code; return; } comm_mux_begin(Wire, SPI); for (uint8_t i = 0; i < bsecMsg.len; i++) { bsec_output_list[i] = static_cast(bsecMsg.output_id[i]); } for (uint8_t i = 0; i < NUM_OF_SENS; i++) { bme68x_sensor *sensor = sensorMgr.get_sensor(i); if (sensor == nullptr) { ble_ret_code = bleController::BSEC_SELECTED_SENSOR_INVALID; jsonDoc[msg.name] = ble_ret_code; return; } /* Sets the Communication interface for the sensors */ comm[i] = comm_mux_set_config(Wire, SPI, i, comm[i]); sensor->scan_cycle_index = 1; /* Assigning a chunk of memory block to the bsecInstance */ bsec2[i].allocateMemory(bsec_mem_block[i]); /* Whenever new data is available call the newDataCallback function */ bsec2[i].attachCallback(bsecCallBack); switch (bsecMsg.sample_rate) { case bleController::ULP: sample_rate = BSEC_SAMPLE_RATE_ULP; break; case bleController::LP: sample_rate = BSEC_SAMPLE_RATE_LP; break; case bleController::HP: sample_rate = BSEC_SAMPLE_RATE_SCAN; break; default: break; } if (!bsec2[i].begin(BME68X_SPI_INTF, comm_mux_read, comm_mux_write, comm_mux_delay, &comm[i])) { ble_ret_code = bleController::BSEC_INIT_ERROR; } if (i == 0) { if (configure_bsec_logging(ai_conf_file_name, bsec_conf_file_name, bsec_config, bsecMsg.selected_sensor) < EDK_OK) { ble_ret_code = bleController::BSEC_CONFIG_FILE_ERROR; jsonDoc[msg.name] = ble_ret_code; return; } } /* Checking the aiconfig type and subscription is same (classification/regression) */ if (strcmp(bsecDlog.ai_config_type, "classification") == 0) { for (int i = 0; i < bsecMsg.len; i++) { switch (bsec_output_list[i]) { case BSEC_OUTPUT_REGRESSION_ESTIMATE_1: case BSEC_OUTPUT_REGRESSION_ESTIMATE_2: case BSEC_OUTPUT_REGRESSION_ESTIMATE_3: case BSEC_OUTPUT_REGRESSION_ESTIMATE_4: ble_ret_code = bleController::AI_CONFIG_AND_SUBSCRIPTION_MISSMATCH; jsonDoc[msg.name] = ble_ret_code; return; default: break; } } } else if (strcmp(bsecDlog.ai_config_type, "regression") == 0) { for (int i = 0; i < bsecMsg.len; i++) { switch (bsec_output_list[i]) { case BSEC_OUTPUT_GAS_ESTIMATE_1: case BSEC_OUTPUT_GAS_ESTIMATE_2: case BSEC_OUTPUT_GAS_ESTIMATE_3: case BSEC_OUTPUT_GAS_ESTIMATE_4: ble_ret_code = bleController::AI_CONFIG_AND_SUBSCRIPTION_MISSMATCH; jsonDoc[msg.name] = ble_ret_code; return; default: break; } } } /* Sets the sensor ID when .bmeconfig file is not initilized */ if (sensor->id == 0) { sensor->id = bsec2[i].sensor.getUniqueId(); } if (!bsec2[i].setConfig(bsec_config)) { ble_ret_code = bleController::BSEC_SET_CONFIG_ERROR; } else if (!bsec2[i].updateSubscription(bsec_output_list, bsecMsg.len, sample_rate)) { ble_ret_code = bleController::BSEC_UPDATE_SUBSCRIPTION_ERROR; } else if (!bsec2[i].run()) { ble_ret_code = bleController::BSEC_RUN_ERROR; } else { const bme68x_heatr_conf& heater_conf = bsec2[i].sensor.getHeaterConfiguration(); if (heater_conf.profile_len == 0) { jsonDoc["temperature"] = heater_conf.heatr_temp; jsonDoc["duration"] = ((int32_t)heater_conf.heatr_dur) * GAS_WAIT_SHARED; jsonDoc["mode"] = "forced"; sensor->heater_profile.length = heater_conf.profile_len; sensor->heater_profile.duration[0] = heater_conf.heatr_dur; sensor->heater_profile.temperature[0] = heater_conf.heatr_temp; sensor->mode = BME68X_FORCED_MODE; } else if ((heater_conf.heatr_dur_prof != nullptr) && (heater_conf.heatr_temp_prof != nullptr)) { JsonArray heaterDurationArray = jsonDoc.createNestedArray("duration"); JsonArray heaterTemperatureArray = jsonDoc.createNestedArray("temperature"); for (uint8_t i = 0; i < heater_conf.profile_len; i++) { heaterDurationArray.add(((int32_t)heater_conf.heatr_dur_prof[i]) * GAS_WAIT_SHARED); heaterTemperatureArray.add(heater_conf.heatr_temp_prof[i]); sensor->heater_profile.duration[i] = heater_conf.heatr_dur_prof[i]; sensor->heater_profile.temperature[i] = heater_conf.heatr_temp_prof[i]; } jsonDoc["mode"] = "parallel"; sensor->heater_profile.length = heater_conf.profile_len; sensor->mode = BME68X_PARALLEL_MODE; } selected_sensor = bsecMsg.selected_sensor; current_app_mode = DEMO_TEST_ALGORITHM_MODE; } } } else { ble_ret_code = bleController::AI_CONFIG_FILE_MISSING; } } else { ble_ret_code = bleController::BSEC_CONFIG_FILE_MISSING; } } else { is_bme68x_conf_available = utils::get_file_with_extension(bme68x_conf_file_name, BME68X_CONFIG_FILE_EXT); /* checks the availability of BME board configuration file */ if (is_bme68x_conf_available) { if (configure_sensor_logging(bme68x_conf_file_name) < EDK_OK) { ble_ret_code = bleController::SENSOR_INITIALIZATION_FAILED; ret_code = EDK_SENSOR_INITIALIZATION_FAILED; } else { selected_sensor = bsecMsg.selected_sensor; current_app_mode = DEMO_RECORDING_MODE; } } else { ble_ret_code = bleController::SENSOR_CONFIG_MISSING; ret_code = EDK_SENSOR_CONFIG_MISSING_ERROR; } } jsonDoc[msg.name] = ble_ret_code; } else { jsonDoc[msg.name] = bleController::SD_CARD_INIT_ERROR; ret_code = EDK_SD_CARD_INIT_ERROR; current_app_mode = DEMO_IDLE_MODE; } } void ble_notify_stop_streaming(const bleController::ble_msg &msg, JsonDocument& jsonDoc) { jsonDoc[msg.name] = bleController::CMD_VALID; //Flushing the contents of the buffer to the rawdata file generated during the previous run if (current_app_mode == DEMO_TEST_ALGORITHM_MODE) { bsecDlog.flush_sensor_data(selected_sensor); } else { bme68xDlog.flush(); } selected_sensor = 0; current_app_mode = DEMO_IDLE_MODE; app_mode = DEMO_IDLE_MODE; } void ble_notify_read_config(const bleController::ble_msg &msg, JsonDocument& jsonDoc) { demo_ret_code ret; if (current_app_mode != DEMO_IDLE_MODE) { jsonDoc[msg.name] = bleController::APP_ALREADY_IN_STREAMING_MODE; } else if (msg.file_type >= bleController::BMECONFIG && msg.file_type <= bleController::AICONFIG) { if (msg.file_type == bleController::BMECONFIG) { config_file_name = BME68X_CONFIG_FILE_EXT; } else { config_file_name = AI_CONFIG_FILE_EXT; } ret = config_file_read(config_file_name); if (ret == EDK_END_OF_FILE) { jsonDoc.clear(); jsonDoc.add(EOF); } else if (ret == EDK_SD_CARD_INIT_ERROR) { jsonDoc[msg.name] = bleController::SD_CARD_INIT_ERROR; } else if (ret == EDK_EXTENSION_NOT_AVAILABLE) { if (config_file_name == BME68X_CONFIG_FILE_EXT) { jsonDoc[msg.name] = bleController::SENSOR_CONFIG_MISSING; } else { jsonDoc[msg.name] = bleController::AI_CONFIG_FILE_MISSING; } } else { jsonDoc[msg.name] = bleController::CONFIG_FILE_ERROR; } } else { jsonDoc[msg.name] = bleController::CMD_INVALID; } } void ble_notify_set_appmode(const bleController::ble_msg &msg, JsonDocument& jsonDoc) { if (msg.mode >= DEMO_RECORDING_MODE && msg.mode <= DEMO_TEST_ALGORITHM_MODE) { if (current_app_mode == DEMO_IDLE_MODE) { app_mode = (demo_app_mode)msg.mode; jsonDoc[msg.name] = bleController::CMD_VALID; } else { jsonDoc[msg.name] = bleController::APP_ALREADY_IN_STREAMING_MODE; } } else { jsonDoc[msg.name] = bleController::CMD_INVALID; } } void ble_notify_get_appmode(const bleController::ble_msg &msg, JsonDocument& jsonDoc) { jsonDoc[msg.name] = bleController::CMD_VALID; jsonDoc["appMode"] = (int32_t)current_app_mode; } void ble_notify_set_groundtruth(const bleController::ble_msg &msg, JsonDocument& jsonDoc) { ground_truth = msg.ground_truth; jsonDoc[msg.name] = bleController::CMD_VALID; } void ble_notify_bsec_output(const bsecOutputs& outputs, const uint8_t sens_num) { StaticJsonDocument jsonDoc; JsonArray bsecOutputArray = jsonDoc.createNestedArray("bsec"); for (uint8_t i = 0; i < outputs.nOutputs; i++) { const bsec_output_t& output = outputs.output[i]; JsonObject bsecOutputObj = bsecOutputArray.createNestedObject(); bsecOutputObj["sensor_num"] = sens_num; bsecOutputObj["id"] = output.sensor_id; bsecOutputObj["signal"] = output.signal; bsecOutputObj["accuracy"] = output.accuracy; } bleCtlr.send_notification(jsonDoc); } void ble_notify_bme68x_data(const bme68x_data& data, const uint8_t sens_num) { StaticJsonDocument jsonDoc; JsonObject bme68xObj = jsonDoc.createNestedObject("bme68x"); bme68xObj["sensor_number"] = sens_num; bme68xObj["temperature"] = data.temperature; bme68xObj["pressure"] = data.pressure; bme68xObj["humidity"] = data.humidity; bme68xObj["gas_resistance"] = data.gas_resistance; bme68xObj["gas_index"] = data.gas_index; bleCtlr.send_notification(jsonDoc); } void ble_notify_get_fw_version(const bleController::ble_msg &msg, JsonDocument& jsonDoc) { jsonDoc[msg.name] = bleController::CMD_VALID; jsonDoc["FirmwareVersion"] = FIRMWARE_VERSION; } demo_ret_code configure_sensor_logging(const String& bme_config_file) { demo_ret_code ret = sensorMgr.begin(bme_config_file); if (ret >= EDK_OK) { ret = bme68xDlog.begin(bme_config_file); } return ret; } demo_ret_code configure_bsec_logging(const String& ai_config_file, const String& bsec_config_file, uint8_t bsec_config_str[BSEC_MAX_PROPERTY_BLOB_SIZE], uint8_t sensor_num) { memset(bsec_config_str, 0, BSEC_MAX_PROPERTY_BLOB_SIZE); demo_ret_code ret = bsecDlog.begin(ai_config_file, bsec2[0].version, sensor_num); if (ret >= EDK_OK) { ret = utils::get_bsec_config(bsec_config_file, bsec_config_str); } return ret; } demo_ret_code config_file_read(const String& config_file) { demo_ret_code ret; static bool first_time = true; StaticJsonDocument jsonDoc; while ((ret = utils::read_file(config_file, FILE_DATA_READ_SIZE, config_file_data)) == EDK_OK) { if (first_time) { if (config_file == BME68X_LABEL_INFO_FILE_EXT) { jsonDoc["getlabelinfo"] = bleController::CMD_VALID; } else { jsonDoc["readconfig"] = bleController::CMD_VALID; } bleCtlr.send_notification(jsonDoc); first_time = false; } jsonDoc.clear(); jsonDoc.add(config_file_data); bleCtlr.send_notification(jsonDoc); } first_time = true; return ret; } void read_sys_time() { while (Serial.available()) { String rx_msg; rx_msg = Serial.readStringUntil('\n'); received_sys_time = rx_msg.toInt(); utils::get_rtc().adjust(DateTime(received_sys_time)); } }