Fixed light noisy reads. Sunchronized with eventula LED light

This commit is contained in:
RZ_MINIX\rober 2025-07-21 17:01:48 -07:00
parent cae6860d9e
commit 682ffb4fee
4 changed files with 263 additions and 80 deletions

View File

@ -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,30 +89,81 @@ 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;
uint32_t last_light_check = millis();
updateLedPhysicalState(false);
while(true)
{
m_queue.receive(&msg, portMAX_DELAY);
// 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
// Turn off first
steady(0, 0, 0, 5);
m_led_sync_mode = false;
while(true)
{
for(int n=0; n<sizeof(led_sin_64); n++)
{
// Check for light measurement sync
if (millis() - m_last_light_sync >= 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();
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);
}
@ -121,12 +175,12 @@ void Led::run()
break;
}
}
delay(60);
}
}
}
}
}
Led::Led(uint32_t pin) : m_queue(5)
{

View File

@ -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

View File

@ -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,54 +186,150 @@ 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()
{
// 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);
// 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);
@ -243,6 +340,7 @@ void SensorService::run_i2c_1()
}
}
// handles radar only
void SensorService::run_uart()
{

View File

@ -10,7 +10,7 @@
#include "Bme68x.h"
#include "Bmp280.h"
#include "LD2410.h"
#include <algorithm>
#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