Added support for proper smell profiles (8 0f them)
This commit is contained in:
parent
0b2fb3feea
commit
58308ef7fb
@ -3,68 +3,365 @@
|
||||
|
||||
#include "Bme68x.h"
|
||||
|
||||
|
||||
static const char *TAG = "Bme68x";
|
||||
|
||||
|
||||
#define MEAS_DUR 140
|
||||
#define NEW_GAS_MEAS (BME68X_GASM_VALID_MSK | BME68X_HEAT_STAB_MSK | BME68X_NEW_DATA_MSK)
|
||||
|
||||
// Define all 8 profiles
|
||||
const Bme68x::HeaterProfile Bme68x::profiles[8] = {
|
||||
/* Profile 0
|
||||
- Index 100 (160°C, 198ms): Light alcohols (methanol, ethanol vapors)
|
||||
- Index 101 (170°C, 297ms): Acetone, light ketones
|
||||
- Index 102 (180°C, 396ms): Isopropanol, cleaning alcohol vapors
|
||||
- Index 103 (190°C, 594ms): Formaldehyde, light aldehydes
|
||||
- Index 104 (200°C, 792ms): Ethyl acetate, light esters
|
||||
- Index 105 (210°C, 990ms): Benzene, light aromatic hydrocarbons
|
||||
- Index 106 (220°C, 1188ms): Toluene, paint solvents
|
||||
- Index 107 (230°C, 1386ms): Xylene, heavier paint thinners
|
||||
- Index 108 (240°C, 1584ms): Styrene, plastic outgassing
|
||||
- Index 109 (200°C, 1980ms): Extended detection of persistent light VOCs
|
||||
*/
|
||||
{
|
||||
.temp_prof = { 160, 170, 180, 190, 200, 210, 220, 230, 240, 200 },
|
||||
.mul_prof = { 2, 3, 4, 6, 8, 10, 12, 14, 16, 20 }
|
||||
},
|
||||
/*
|
||||
## Profile 1 (Indexes 110-119) - Medium-Low Temperature Range
|
||||
**Temperature Range: 220-340°C | Duration Range: 297-2376ms**
|
||||
- Index 110 (220°C, 297ms): Acetic acid, vinegar vapors
|
||||
- Index 111 (240°C, 396ms): Ethylene glycol, antifreeze vapors
|
||||
- Index 112 (260°C, 495ms): Propylene glycol, vaping compounds
|
||||
- Index 113 (280°C, 594ms): Butanol, heavier alcohols
|
||||
- Index 114 (300°C, 792ms): Cooking oils, fatty acid vapors
|
||||
- Index 115 (320°C, 990ms): Ammonia, cleaning product vapors
|
||||
- Index 116 (300°C, 1188ms): Extended cooking oil detection
|
||||
- Index 117 (280°C, 1584ms): Diesel fuel vapors
|
||||
- Index 118 (340°C, 594ms): Naphthalene, mothball compounds
|
||||
- Index 119 (260°C, 2376ms): Extended glycol detection
|
||||
*/
|
||||
{
|
||||
.temp_prof = { 220, 240, 260, 280, 300, 320, 300, 280, 340, 260 },
|
||||
.mul_prof = { 3, 4, 5, 6, 8, 10, 12, 16, 6, 24 }
|
||||
},
|
||||
/*
|
||||
## Profile 2 (Indexes 120-129) - Medium Temperature Range
|
||||
**Temperature Range: 280-360°C | Duration Range: 396-1980ms**
|
||||
- Index 120 (280°C, 396ms): Chloroform, dry cleaning solvents
|
||||
- Index 121 (300°C, 495ms): Gasoline vapors, light hydrocarbons
|
||||
- Index 122 (320°C, 594ms): Methylene chloride, paint strippers
|
||||
- Index 123 (340°C, 693ms): Tetrachloroethylene, dry cleaning
|
||||
- Index 124 (360°C, 792ms): Carbon monoxide, combustion gases
|
||||
- Index 125 (320°C, 1188ms): Natural gas (methane) leaks
|
||||
- Index 126 (340°C, 990ms): Propane, LPG vapors
|
||||
- Index 127 (300°C, 1584ms): Extended gasoline vapor detection
|
||||
- Index 128 (350°C, 594ms): Hydrogen sulfide, sewer gases
|
||||
- Index 129 (330°C, 1980ms): Extended natural gas detection
|
||||
*/
|
||||
{
|
||||
.temp_prof = { 280, 300, 320, 340, 360, 320, 340, 300, 350, 330 },
|
||||
.mul_prof = { 4, 5, 6, 7, 8, 12, 10, 16, 6, 20 }
|
||||
},
|
||||
/*
|
||||
## Profile 3 (Indexes 130-139) - Medium-High Temperature Range
|
||||
**Temperature Range: 340-380°C | Duration Range: 396-2376ms**
|
||||
- Index 130 (340°C, 396ms): Hydrogen gas, battery outgassing
|
||||
- Index 131 (360°C, 495ms): Sulfur dioxide, industrial emissions
|
||||
- Index 132 (350°C, 594ms): Nitrogen oxides, car exhaust
|
||||
- Index 133 (370°C, 693ms): Ozone, electrical arc detection
|
||||
- Index 134 (340°C, 990ms): Extended hydrogen detection
|
||||
- Index 135 (380°C, 495ms): High-temperature combustion products
|
||||
- Index 136 (360°C, 1188ms): Extended SO2 detection
|
||||
- Index 137 (350°C, 1386ms): Extended NOx detection
|
||||
- Index 138 (370°C, 792ms): Chlorine gas, pool chemicals
|
||||
- Index 139 (340°C, 2376ms): Extended battery gas detection
|
||||
*/
|
||||
{
|
||||
.temp_prof = { 340, 360, 350, 370, 340, 380, 360, 350, 370, 340 },
|
||||
.mul_prof = { 4, 5, 6, 7, 10, 5, 12, 14, 8, 24 }
|
||||
},
|
||||
/*
|
||||
## Profile 4 (Indexes 140-149) - Broad Spectrum Detection
|
||||
**Temperature Range: 250-370°C | Duration Range: 594-1980ms**
|
||||
- Index 140 (250°C, 594ms): Ethanol breath detection
|
||||
- Index 141 (280°C, 792ms): Cannabis/marijuana compounds
|
||||
- Index 142 (320°C, 891ms): Tobacco smoke compounds
|
||||
- Index 143 (350°C, 990ms): Burning paper/wood smoke
|
||||
- Index 144 (300°C, 1188ms): Cooking spice vapors
|
||||
- Index 145 (370°C, 693ms): Electrical burning smell
|
||||
- Index 146 (330°C, 1386ms): Mold/mildew compounds
|
||||
- Index 147 (360°C, 891ms): Plastic burning detection
|
||||
- Index 148 (340°C, 1584ms): Food spoilage compounds
|
||||
- Index 149 (320°C, 1980ms): Extended tobacco detection
|
||||
*/
|
||||
{
|
||||
.temp_prof = { 250, 280, 320, 350, 300, 370, 330, 360, 340, 320 },
|
||||
.mul_prof = { 6, 8, 9, 10, 12, 7, 14, 9, 16, 20 }
|
||||
},
|
||||
/*
|
||||
## Profile 5 (Indexes 150-159) - Complex VOC Detection
|
||||
**Temperature Range: 260-380°C | Duration Range: 495-1584ms**
|
||||
- Index 150 (260°C, 495ms): Perfume/cologne compounds
|
||||
- Index 151 (300°C, 594ms): Laundry detergent vapors
|
||||
- Index 152 (340°C, 693ms): Fabric softener chemicals
|
||||
- Index 153 (370°C, 792ms): Bleach/chlorine compounds
|
||||
- Index 154 (320°C, 1188ms): Room deodorizer chemicals
|
||||
- Index 155 (360°C, 990ms): Disinfectant vapors
|
||||
- Index 156 (350°C, 1089ms): Medical alcohol/sanitizer
|
||||
- Index 157 (380°C, 594ms): High-temp disinfection products
|
||||
- Index 158 (330°C, 1584ms): Air freshener compounds
|
||||
- Index 159 (370°C, 1188ms): Extended bleach detection
|
||||
*/
|
||||
{
|
||||
.temp_prof = { 260, 300, 340, 370, 320, 360, 350, 380, 330, 370 },
|
||||
.mul_prof = { 5, 6, 7, 8, 12, 10, 11, 6, 16, 12 }
|
||||
},
|
||||
|
||||
/*
|
||||
## Profile 6 (Indexes 160-169) - High-Temperature VOCs
|
||||
**Temperature Range: 300-380°C | Duration Range: 495-1584ms**
|
||||
- Index 160 (300°C, 495ms): Printer toner compounds
|
||||
- Index 161 (330°C, 594ms): Copier ozone/chemicals
|
||||
- Index 162 (360°C, 693ms): Electronic component outgassing
|
||||
- Index 163 (350°C, 792ms): Flux vapors from soldering
|
||||
- Index 164 (370°C, 891ms): Hot plastic from electronics
|
||||
- Index 165 (340°C, 1188ms): Adhesive/glue vapors
|
||||
- Index 166 (380°C, 594ms): High-temp electronic burning
|
||||
- Index 167 (360°C, 1386ms): Extended electronics detection
|
||||
- Index 168 (350°C, 990ms): Thermal paste vapors
|
||||
- Index 169 (370°C, 1584ms): Extended adhesive detection
|
||||
*/
|
||||
{
|
||||
.temp_prof = { 300, 330, 360, 350, 370, 340, 380, 360, 350, 370 },
|
||||
.mul_prof = { 5, 6, 7, 8, 9, 12, 6, 14, 10, 16 }
|
||||
},
|
||||
/*
|
||||
## Profile 7 (Indexes 170-179) - Combustion & High-Temp Detection
|
||||
**Temperature Range: 340-380°C | Duration Range: 792-1782ms**
|
||||
- Index 170 (350°C, 792ms): Diesel exhaust particles
|
||||
- Index 171 (370°C, 891ms): Gasoline engine exhaust
|
||||
- Index 172 (360°C, 990ms): Motorcycle exhaust compounds
|
||||
- Index 173 (380°C, 693ms): Industrial furnace emissions
|
||||
- Index 174 (340°C, 1584ms): Wood stove/fireplace smoke
|
||||
- Index 175 (370°C, 1188ms): Barbecue/grilling smoke
|
||||
- Index 176 (360°C, 1386ms): Coal burning compounds
|
||||
- Index 177 (350°C, 1782ms): Extended wood smoke detection
|
||||
- Index 178 (380°C, 792ms): Kerosene/jet fuel vapors
|
||||
- Index 179 (370°C, 1584ms): Extended BBQ smoke detection
|
||||
*/
|
||||
{
|
||||
.temp_prof = { 350, 370, 360, 380, 340, 370, 360, 350, 380, 370 },
|
||||
.mul_prof = { 8, 9, 10, 7, 16, 12, 14, 18, 8, 20 }
|
||||
}
|
||||
};
|
||||
|
||||
Bme68x::Bme68x(TwoWire & bus) : m_bus(bus)
|
||||
{
|
||||
m_sensor = new MyLibs::Bme68x();
|
||||
}
|
||||
|
||||
void Bme68x::switchToProfile(uint8_t profile_index)
|
||||
{
|
||||
if (profile_index >= 8) {
|
||||
ESP_LOGE(TAG, "Invalid profile index: %d", profile_index);
|
||||
return;
|
||||
}
|
||||
|
||||
const HeaterProfile& profile = profiles[profile_index];
|
||||
|
||||
ESP_LOGI(TAG, "Switching to profile %d", profile_index);
|
||||
|
||||
// Put sensor to sleep before changing configuration
|
||||
m_sensor->setOpMode(BME68X_SLEEP_MODE);
|
||||
|
||||
// Calculate shared heater duration - use a reasonable base value like original
|
||||
uint32_t tph_dur_us = m_sensor->getMeasDur(BME68X_PARALLEL_MODE);
|
||||
uint16_t tph_dur_ms = tph_dur_us / 1000;
|
||||
|
||||
// Use a fixed reasonable shared heater duration similar to original (around 50-100ms)
|
||||
uint16_t sharedHeatrDur = 140 - tph_dur_ms; // Use original approach
|
||||
|
||||
// Ensure it's positive and reasonable
|
||||
if (sharedHeatrDur > 200 || (int16_t)sharedHeatrDur < 10) {
|
||||
sharedHeatrDur = 99; // Default to original working value
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Profile %d: TPH duration=%u ms, shared heater duration=%u ms",
|
||||
profile_index, tph_dur_ms, sharedHeatrDur);
|
||||
|
||||
// Copy arrays to non-const arrays for the API call
|
||||
uint16_t tempProf[10];
|
||||
uint16_t mulProf[10];
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
tempProf[i] = profile.temp_prof[i];
|
||||
mulProf[i] = profile.mul_prof[i];
|
||||
}
|
||||
|
||||
// Set the heater profile
|
||||
m_sensor->setHeaterProf(tempProf, mulProf, sharedHeatrDur, 10);
|
||||
|
||||
// Check sensor status after configuration
|
||||
//int8_t status = m_sensor->checkStatus();
|
||||
//ESP_LOGI(TAG, "Sensor status after heater config: %d", status);
|
||||
|
||||
// Restart parallel mode
|
||||
m_sensor->setOpMode(BME68X_PARALLEL_MODE);
|
||||
// Clear any leftover data from previous profile
|
||||
delay(100); // Give sensor time to start new profile
|
||||
while(m_sensor->fetchData() > 0) {
|
||||
MyLibs::bme68xData dummy;
|
||||
uint8_t left;
|
||||
do {
|
||||
left = m_sensor->getData(dummy);
|
||||
} while(left > 0);
|
||||
}
|
||||
// Check status after mode change
|
||||
//status = m_sensor->checkStatus();
|
||||
//ESP_LOGI(TAG, "Sensor status after mode change: %d", status);
|
||||
|
||||
m_current_profile = profile_index;
|
||||
}
|
||||
|
||||
void Bme68x::switchToNextProfile()
|
||||
{
|
||||
uint8_t next_profile = (m_current_profile + 1) % 8;
|
||||
switchToProfile(next_profile);
|
||||
ESP_LOGI(TAG, "Cycle completed, switched to profile %d", next_profile);
|
||||
}
|
||||
|
||||
bool Bme68x::init()
|
||||
{
|
||||
m_sensor->begin(0x77, m_bus);
|
||||
|
||||
m_sensor->setTPH();
|
||||
|
||||
uint16_t tempProf[10] = { 320, 100, 100, 100, 200, 200, 200, 320, 320, 320 };
|
||||
/* Multiplier to the shared heater duration */
|
||||
uint16_t mulProf[10] = { 5, 2, 10, 30, 5, 5, 5, 5, 5, 5 };
|
||||
/* Shared heating duration in milliseconds */
|
||||
uint16_t sharedHeatrDur = MEAS_DUR - (m_sensor->getMeasDur(BME68X_PARALLEL_MODE) / 1000);
|
||||
|
||||
m_sensor->setHeaterProf(tempProf, mulProf, sharedHeatrDur, 10);
|
||||
m_sensor->setOpMode(BME68X_PARALLEL_MODE);
|
||||
// Start with profile 0
|
||||
switchToProfile(0);
|
||||
|
||||
m_operational = m_sensor->checkStatus() == BME68X_OK;
|
||||
|
||||
ESP_LOGI(TAG, "BME68X initialized with profile cycling, operational: %s", m_operational ? "true" : "false");
|
||||
|
||||
return m_operational;
|
||||
}
|
||||
|
||||
/// @brief
|
||||
/// @param data
|
||||
/// @return returns true when index 9 is read
|
||||
/// @return returns true when measurement cycle is complete (may take longer than 10s)
|
||||
bool Bme68x::read(struct BME_DATA * data)
|
||||
{
|
||||
static uint32_t read_call_count = 0;
|
||||
static uint32_t last_log_time = 0;
|
||||
static uint64_t cycle_start_time = 0;
|
||||
static bool index_9_detected = false;
|
||||
|
||||
read_call_count++;
|
||||
uint64_t now_ms = esp_timer_get_time() / 1000; // milliseconds
|
||||
uint32_t now_sec = now_ms / 1000; // seconds
|
||||
|
||||
// Log every 10 seconds to show we're being called
|
||||
if (now_sec - last_log_time >= 10) {
|
||||
ESP_LOGI(TAG, "Read called %u times in last 10s, current profile: %d",
|
||||
read_call_count - (last_log_time * 100), m_current_profile);
|
||||
last_log_time = now_sec;
|
||||
}
|
||||
|
||||
// Calculate expected cycle duration for current profile
|
||||
const HeaterProfile& profile = profiles[m_current_profile];
|
||||
uint32_t total_multipliers = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
total_multipliers += profile.mul_prof[i];
|
||||
}
|
||||
uint32_t expected_cycle_duration = total_multipliers * 99; // milliseconds
|
||||
|
||||
// Initialize cycle start time on first call after profile switch
|
||||
if (cycle_start_time == 0) {
|
||||
cycle_start_time = now_ms;
|
||||
index_9_detected = false;
|
||||
ESP_LOGI(TAG, "Starting profile %d cycle, expected duration: %u ms", m_current_profile, expected_cycle_duration);
|
||||
}
|
||||
|
||||
MyLibs::bme68xData bdata;
|
||||
uint8_t left;
|
||||
|
||||
bool ret_val = false;
|
||||
|
||||
if(m_sensor->fetchData() > 0)
|
||||
uint8_t num_fields = m_sensor->fetchData();
|
||||
if(num_fields > 0)
|
||||
{
|
||||
ESP_LOGD(TAG, "fetchData() returned %d fields", num_fields);
|
||||
do
|
||||
{
|
||||
left = m_sensor->getData(bdata);
|
||||
|
||||
if (bdata.status == NEW_GAS_MEAS)
|
||||
if (bdata.status & BME68X_NEW_DATA_MSK)
|
||||
{
|
||||
int ix = _min(bdata.gas_index, 9);
|
||||
|
||||
// Check what status flags we have
|
||||
bool has_new_data = (bdata.status & BME68X_NEW_DATA_MSK) != 0;
|
||||
bool has_heat_stab = (bdata.status & BME68X_HEAT_STAB_MSK) != 0;
|
||||
bool has_gasm_valid = (bdata.status & BME68X_GASM_VALID_MSK) != 0;
|
||||
|
||||
ESP_LOGD(TAG, "Data: profile=%d, index=%d, status=0x%02X (new:%d, heat:%d, gas:%d), resistance=%0.1f",
|
||||
m_current_profile, ix, bdata.status, has_new_data, has_heat_stab, has_gasm_valid, bdata.gas_resistance);
|
||||
|
||||
if (bdata.status == NEW_GAS_MEAS)
|
||||
{
|
||||
// Full valid measurement
|
||||
data->measurement_bitmask |= (1 << ix);
|
||||
data->current_profile = m_current_profile;
|
||||
|
||||
ESP_LOGI(TAG, "VALID measurement: profile=%d, index=%d, resistance=%0.1f",
|
||||
m_current_profile, ix, bdata.gas_resistance);
|
||||
|
||||
if(ix == 0)
|
||||
data->humidity = bdata.humidity;
|
||||
else if(ix == 9)
|
||||
ret_val = true;
|
||||
{
|
||||
ESP_LOGI(TAG, "Index 9 detected for profile %d", m_current_profile);
|
||||
index_9_detected = true;
|
||||
}
|
||||
|
||||
data->measurement[ix].resistance = bdata.gas_resistance;
|
||||
}
|
||||
else if (has_new_data && has_heat_stab)
|
||||
{
|
||||
// Accept measurements with heat stability even if gas measurement isn't fully valid yet
|
||||
//ESP_LOGW(TAG, "Accepting heat-stable measurement: profile=%d, index=%d", m_current_profile, ix);
|
||||
|
||||
data->measurement_bitmask |= (1 << ix);
|
||||
data->current_profile = m_current_profile;
|
||||
|
||||
if(ix == 0)
|
||||
data->humidity = bdata.humidity;
|
||||
else if(ix == 9)
|
||||
{
|
||||
//ESP_LOGI(TAG, "Index 9 detected (heat stable) for profile %d", m_current_profile);
|
||||
index_9_detected = true;
|
||||
}
|
||||
|
||||
data->measurement[ix].resistance = bdata.gas_resistance;
|
||||
}
|
||||
}
|
||||
} while(left > 0);
|
||||
}
|
||||
|
||||
// Check if cycle is complete (Index 9 detected AND enough time has passed)
|
||||
if (index_9_detected) {
|
||||
uint64_t elapsed_time = now_ms - cycle_start_time;
|
||||
|
||||
if (elapsed_time >= expected_cycle_duration) {
|
||||
ESP_LOGI(TAG, "Profile %d cycle complete after %llu ms (expected %u ms)",
|
||||
m_current_profile, elapsed_time, expected_cycle_duration);
|
||||
ret_val = true;
|
||||
cycle_start_time = 0; // Reset for next profile
|
||||
index_9_detected = false;
|
||||
switchToNextProfile();
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Profile %d: waiting for heating completion (%u ms remaining)",
|
||||
m_current_profile, (uint32_t)(expected_cycle_duration - elapsed_time));
|
||||
}
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
@ -16,6 +16,7 @@ struct BME_DATA
|
||||
{
|
||||
float humidity;
|
||||
uint16_t measurement_bitmask;
|
||||
uint8_t current_profile; // Track which profile is active
|
||||
struct GAS_MEASUREMENT measurement[10];
|
||||
};
|
||||
|
||||
@ -26,6 +27,20 @@ protected:
|
||||
TwoWire & m_bus;
|
||||
bool m_operational = false;
|
||||
|
||||
// Profile management
|
||||
uint8_t m_current_profile = 0;
|
||||
|
||||
// Profile definitions
|
||||
struct HeaterProfile {
|
||||
uint16_t temp_prof[10];
|
||||
uint16_t mul_prof[10];
|
||||
};
|
||||
|
||||
static const HeaterProfile profiles[8];
|
||||
|
||||
void switchToProfile(uint8_t profile_index);
|
||||
void switchToNextProfile();
|
||||
|
||||
public:
|
||||
Bme68x(TwoWire & bus);
|
||||
bool init();
|
||||
|
||||
@ -175,7 +175,9 @@ void SensorService::postBme68xData(float pressure, float temp)
|
||||
if(m_bme_data.measurement_bitmask & (1 << n))
|
||||
{
|
||||
p[num_total].resistance = m_bme_data.measurement[n].resistance;
|
||||
p[num_total++].index = n;
|
||||
// Use index 100 + (profile * 10) + measurement_index
|
||||
// Profile 0: indexes 100-109, Profile 1: indexes 110-119, etc.
|
||||
p[num_total++].index = 100 + (m_bme_data.current_profile * 10) + n;
|
||||
}
|
||||
}
|
||||
msg->num_data = num_total;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user