From 682ffb4feeb74055cdd0d3b4380165e93d196842 Mon Sep 17 00:00:00 2001 From: "RZ_MINIX\\rober" Date: Mon, 21 Jul 2025 17:01:48 -0700 Subject: [PATCH] Fixed light noisy reads. Sunchronized with eventula LED light --- main/Led.cpp | 124 +++++++++++++++------- main/Led.h | 12 +++ main/sensors/SensorService.cpp | 186 +++++++++++++++++++++++++-------- main/sensors/SensorService.h | 21 +++- 4 files changed, 263 insertions(+), 80 deletions(-) diff --git a/main/Led.cpp b/main/Led.cpp index d30169b..2f97800 100644 --- a/main/Led.cpp +++ b/main/Led.cpp @@ -38,6 +38,7 @@ void Led::steady(uint8_t r, uint8_t g, uint8_t b, uint8_t d) { int r_s, g_s, b_s; uint32_t val = m_strip->getPixelColor(0); + b_s = val & 0xff; g_s = (val>>8) & 0xff; r_s = (val>>16) & 0xff; @@ -49,6 +50,8 @@ void Led::steady(uint8_t r, uint8_t g, uint8_t b, uint8_t d) int b_ = b_s + ((int)b-b_s)*n/16; m_strip->setPixelColor(0, r_, g_, b_); m_strip->show(); + bool any_led_on = (r_ > 0 || g_ > 0 || b_ > 0); + updateLedPhysicalState(any_led_on); delay(d); } } @@ -86,46 +89,97 @@ void Led::setDirectColor(uint8_t led_index, uint8_t r, uint8_t g, uint8_t b) } } +void Led::updateLedPhysicalState(bool leds_on) +{ + m_leds_physically_on = leds_on; + ESP_LOGD("LED", "LED physical state: %s", leds_on ? "ON" : "OFF"); +} + +void Led::setLightMeasurementSyncMode(bool enable) +{ + m_led_sync_mode = enable; + if (enable) { + // Force LEDs off for light measurement + for(int i = 0; i < 4; i++) { + m_strip->setPixelColor(i, 0, 0, 0); + } + m_strip->show(); + updateLedPhysicalState(false); + ESP_LOGD("LED", "LEDs forced OFF for light measurement"); + } +} + void Led::run() { - struct LED_QUEUE msg; - - while(true) - { - m_queue.receive(&msg, portMAX_DELAY); + struct LED_QUEUE msg; + uint32_t last_light_check = millis(); + updateLedPhysicalState(false); + while(true) + { + // Check if we need to pause for light measurement every 100ms + if (millis() - last_light_check >= 100) { // LIGHT_SYNC_INTERVAL_MS + // Pause LEDs briefly for synchronized light measurement + bool current_led_state = m_led_sync_mode; + setLightMeasurementSyncMode(true); + delay(20); // Brief pause for light measurement + m_led_sync_mode = current_led_state; + last_light_check = millis(); + } + + if (m_queue.receive(&msg, 50)) // Shorter timeout for responsiveness + { + if(msg.program == PROGRAM::STEADY) + { + m_led_sync_mode = false; + steady(msg.r, msg.g, msg.b, 30); + } + else if(msg.program == PROGRAM::PULSATING) + { + // Turn off first + steady(0, 0, 0, 5); + m_led_sync_mode = false; - if(msg.program == PROGRAM::STEADY) - { - steady(msg.r, msg.g, msg.b, 30); - } - else if(msg.program == PROGRAM::PULSATING) - { - // turn off - steady(0, 0, 0, 5); + while(true) + { + for(int n=0; n= 100) { // LIGHT_SYNC_INTERVAL_MS + setLightMeasurementSyncMode(true); + delay(20); + m_led_sync_mode = false; + m_last_light_sync = millis(); + } + + if (!m_led_sync_mode) { + uint8_t m = led_sin_64[n]; + m_strip->setPixelColor(0, msg.r*m/255, msg.g*m/255, msg.b*m/255); + m_strip->show(); - while(true) - { - for(int n=0; nsetPixelColor(0, msg.r*m/255, msg.g*m/255, msg.b*m/255); - m_strip->show(); - delay(30); - } + int r_val = msg.r*m/255; + int g_val = msg.g*m/255; + int b_val = msg.b*m/255; + m_strip->setPixelColor(0, r_val, g_val, b_val); + m_strip->show(); + bool any_led_on = (r_val > 0 || g_val > 0 || b_val > 0); + updateLedPhysicalState(any_led_on); + } + delay(30); + } - if(m_queue.receive(&msg, 0)) - { - if(msg.program == PROGRAM::STEADY) - { - steady(msg.r, msg.g, msg.b, 30); - break; - } - } - - delay(60); - } - } - } + if(m_queue.receive(&msg, 0)) + { + if(msg.program == PROGRAM::STEADY) + { + steady(msg.r, msg.g, msg.b, 30); + break; + } + } + delay(60); + } + } + } + } } Led::Led(uint32_t pin) : m_queue(5) diff --git a/main/Led.h b/main/Led.h index 53dcac6..3d1cb3e 100644 --- a/main/Led.h +++ b/main/Led.h @@ -11,6 +11,7 @@ class Led { + struct LED_QUEUE { uint8_t program; @@ -19,6 +20,17 @@ class Led uint8_t b; }; +private: + bool m_leds_physically_on = false; + bool m_led_sync_mode = false; + uint32_t m_last_light_sync = 0; // Last time we synced with light measurement + + +public: + bool areLedsCurrentlyOff() const { return !m_leds_physically_on; } + void setLightMeasurementSyncMode(bool enable); + void updateLedPhysicalState(bool leds_on); + public: enum PROGRAM diff --git a/main/sensors/SensorService.cpp b/main/sensors/SensorService.cpp index 10a0ed0..d2cdf2c 100644 --- a/main/sensors/SensorService.cpp +++ b/main/sensors/SensorService.cpp @@ -19,7 +19,8 @@ struct BME_DATA m_bme_data; SensorService::SensorService(AppIF & app_if) : m_app_if(app_if) { - + // Initialize light measurement structure + memset(&m_light_measurement, 0, sizeof(m_light_measurement)); } void SensorService::start() @@ -185,64 +186,161 @@ void SensorService::postBme68xData(float pressure, float temp) memset(&m_bme_data, 0, sizeof(m_bme_data)); } +void SensorService::synchronizedLightMeasurement(uint16_t light_value) +{ + uint64_t now = esp_timer_get_time() / 1000; // Convert to milliseconds + + // Initialize measurement window + if (m_light_measurement.window_start == 0) { + m_light_measurement.window_start = now; + m_light_measurement.sample_count = 0; + m_light_measurement.leds_were_controlled = false; + } + + // Add sample to current window (if we have space) + if (m_light_measurement.sample_count < MAX_LIGHT_SAMPLES) { + m_light_measurement.samples[m_light_measurement.sample_count] = light_value; + m_light_measurement.sample_count++; + } + + // Check if LEDs are controlled (for method selection) + bool leds_off = m_app_if.getLed()->areLedsCurrentlyOff(); + if (!leds_off) { + m_light_measurement.leds_were_controlled = true; + } + + // Process window when complete + if (now >= (m_light_measurement.window_start + LIGHT_WINDOW_MS)) { + + uint16_t processed_value; + static uint16_t last_processed_value = 0; + + if (m_light_measurement.leds_were_controlled) { + // LEDs were on during this period - use minimum (original method) + processed_value = findMinimum(m_light_measurement.samples, + m_light_measurement.sample_count); + ESP_LOGI(TAG, "Light (min method): %u, samples: %zu", + processed_value, m_light_measurement.sample_count); + } else { + // LEDs were off - use filtered average + double avg = calculateMovingAverage(m_light_measurement.samples, + m_light_measurement.sample_count); + processed_value = (uint16_t)avg; + ESP_LOGI(TAG, "Light (avg method): %u, samples: %zu", + processed_value, m_light_measurement.sample_count); + } + + // Check for significant change + if (last_processed_value > 0) { + double change = abs((int)processed_value - (int)last_processed_value); + double change_percent = (change / last_processed_value) * 100.0; + + double absolute_threshold = 4096 * 2.0 / 100.0; // 2% of ADC range (~82) + double relative_threshold = 5.0; // 5% relative change + + bool significant_absolute = change > absolute_threshold; + bool significant_relative = change_percent > relative_threshold; + + ESP_LOGI(TAG, "Light change: %0.1f (%0.2f%%) [abs_thresh:%0.1f, rel_thresh:%0.1f%%]", + change, change_percent, absolute_threshold, relative_threshold); + + // Trigger notification for significant changes + // Adjust thresholds based on your requirements + + + if (significant_absolute && significant_relative) { + ESP_LOGI(TAG, "Significant light change detected (both thresholds met)"); + struct MESSAGE_NOTIFY_LIGHT msg; + MQTT_MESSAGE_NOTIFY_LIGHT(&msg); + msg.value = change; + m_app_if.getBuffer()->putBlock((uint8_t*)&msg, sizeof(msg)); + } else { + ESP_LOGD(TAG, "Change not significant: abs=%s, rel=%s", + significant_absolute ? "YES" : "NO", + significant_relative ? "YES" : "NO"); + } + } + + last_processed_value = processed_value; + m_light_value = processed_value; + + // Reset for next window + m_light_measurement.window_start = now; + m_light_measurement.sample_count = 0; + m_light_measurement.leds_were_controlled = false; + } +} + +uint16_t SensorService::findMinimum(const uint16_t* samples, size_t count) +{ + if (count == 0) return 0; + + uint16_t min_val = samples[0]; + for (size_t i = 1; i < count; i++) { + if (samples[i] < min_val) { + min_val = samples[i]; + } + } + return min_val; +} + +double SensorService::calculateMovingAverage(const uint16_t* samples, size_t count, size_t avg_count) +{ + if (count == 0) return 0.0; + + // Use the last 'avg_count' samples or all samples if fewer available + size_t n = (avg_count < count) ? avg_count : count; + size_t start_idx = count - n; + + uint32_t sum = 0; + for (size_t i = start_idx; i < count; i++) { + sum += samples[i]; + } + + return (double)sum / n; +} + /// @brief Actual light value is minimum in 2s window /// @param light_value void SensorService::processLight(int light_value) { - static uint16_t min_light_val = 0xffff; - static int last_light_val = -1; - static uint64_t last_time = esp_timer_get_time(); - uint64_t now; - - // handle light sensor - if(light_value < min_light_val) - min_light_val = light_value; - - now = esp_timer_get_time(); - - if(now - last_time >= 2000000) // >= 2s - { - if(last_light_val >= 0) - { - double change = abs(min_light_val - last_light_val); - ESP_LOGE(TAG, "@L: %0.3f", change); - - if(change > 4096*2/100.0) - { - ESP_LOGI(TAG, "light tripped"); - struct MESSAGE_NOTIFY_LIGHT msg; - MQTT_MESSAGE_NOTIFY_LIGHT(&msg); - msg.value = abs(min_light_val - last_light_val); - m_app_if.getBuffer()->putBlock((uint8_t*)&msg, sizeof(msg)); - } - } - last_light_val = min_light_val; - m_light_value = min_light_val; - min_light_val = 0xffff; - last_time = now; - } + synchronizedLightMeasurement((uint16_t)light_value); } // handles pressure and voc sensor +// Additional noise reduction techniques void SensorService::run_i2c_1() { - while(true) - { - if(m_bmp280->read(m_bmp_data.temp, m_bmp_data.pressure)) - processPressure(m_bmp_data.pressure); + // ADC configuration for better noise performance + analogSetAttenuation(ADC_11db); // For 0-3.3V range + analogSetWidth(12); // 12-bit resolution + + while(true) + { + if(m_bmp280->read(m_bmp_data.temp, m_bmp_data.pressure)) + processPressure(m_bmp_data.pressure); - bool bme_cycle_finished = m_bme68x->read(&m_bme_data); - uint16_t read_light_val = analogRead(LIGHT_SENSOR_PIN); + bool bme_cycle_finished = m_bme68x->read(&m_bme_data); + + // Multiple ADC readings for noise reduction + uint32_t light_sum = 0; + const int num_readings = 4; + for (int i = 0; i < num_readings; i++) { + light_sum += analogRead(LIGHT_SENSOR_PIN); + delayMicroseconds(100); // Small delay between readings + } + uint16_t read_light_val = light_sum / num_readings; - if(bme_cycle_finished) - postBme68xData(m_bmp_data.pressure, m_bmp_data.temp); + if(bme_cycle_finished) + postBme68xData(m_bmp_data.pressure, m_bmp_data.temp); - processLight(read_light_val); + processLight(read_light_val); - delay(10); - } + delay(10); + } } + // handles radar only void SensorService::run_uart() { diff --git a/main/sensors/SensorService.h b/main/sensors/SensorService.h index ec0532d..2d5b8e3 100644 --- a/main/sensors/SensorService.h +++ b/main/sensors/SensorService.h @@ -10,7 +10,7 @@ #include "Bme68x.h" #include "Bmp280.h" #include "LD2410.h" - +#include #include "../AppIF.h" class SensorService @@ -37,6 +37,25 @@ public: SensorService(AppIF & app_if); void start(); + +private: + static const size_t MAX_LIGHT_SAMPLES = 200; + + struct LightMeasurement { + uint16_t samples[MAX_LIGHT_SAMPLES]; + size_t sample_count; + uint64_t window_start; + bool leds_were_controlled; + }; + + LightMeasurement m_light_measurement; + + static const uint64_t LIGHT_WINDOW_MS = 2000; + static const uint32_t LIGHT_SYNC_INTERVAL_MS = 100; // Sync every 100ms + + void synchronizedLightMeasurement(uint16_t light_value); + double calculateMovingAverage(const uint16_t* samples, size_t count, size_t avg_count = 10); + uint16_t findMinimum(const uint16_t* samples, size_t count); }; #endif \ No newline at end of file