diff --git a/main/sensors/Bme68x.cpp b/main/sensors/Bme68x.cpp index 3da97f1..76ad898 100644 --- a/main/sensors/Bme68x.cpp +++ b/main/sensors/Bme68x.cpp @@ -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); - - data->measurement_bitmask |= (1 << ix); + + // 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(ix == 0) - data->humidity = bdata.humidity; - else if(ix == 9) - ret_val = true; + if (bdata.status == NEW_GAS_MEAS) + { + // Full valid measurement + data->measurement_bitmask |= (1 << ix); + data->current_profile = m_current_profile; - data->measurement[ix].resistance = bdata.gas_resistance; + 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) + { + 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; } \ No newline at end of file diff --git a/main/sensors/Bme68x.h b/main/sensors/Bme68x.h index 3db7ea1..94e1802 100644 --- a/main/sensors/Bme68x.h +++ b/main/sensors/Bme68x.h @@ -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]; }; @@ -25,6 +26,20 @@ protected: MyLibs::Bme68x * m_sensor = nullptr; 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); diff --git a/main/sensors/SensorService.cpp b/main/sensors/SensorService.cpp index d2cdf2c..cbecc66 100644 --- a/main/sensors/SensorService.cpp +++ b/main/sensors/SensorService.cpp @@ -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;