sTodo-m5paper-client/libraries/bsec2/examples/bme68x_demo_sample/bme68x_datalogger.cpp
2025-06-30 20:47:33 +02:00

438 lines
15 KiB
C++

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