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; int r_s, g_s, b_s;
uint32_t val = m_strip->getPixelColor(0); uint32_t val = m_strip->getPixelColor(0);
b_s = val & 0xff; b_s = val & 0xff;
g_s = (val>>8) & 0xff; g_s = (val>>8) & 0xff;
r_s = (val>>16) & 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; int b_ = b_s + ((int)b-b_s)*n/16;
m_strip->setPixelColor(0, r_, g_, b_); m_strip->setPixelColor(0, r_, g_, b_);
m_strip->show(); m_strip->show();
bool any_led_on = (r_ > 0 || g_ > 0 || b_ > 0);
updateLedPhysicalState(any_led_on);
delay(d); 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() void Led::run()
{ {
struct LED_QUEUE msg; struct LED_QUEUE msg;
uint32_t last_light_check = millis();
while(true) 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 first
steady(0, 0, 0, 5);
m_led_sync_mode = false;
if(msg.program == PROGRAM::STEADY) while(true)
{ {
steady(msg.r, msg.g, msg.b, 30); for(int n=0; n<sizeof(led_sin_64); n++)
} {
else if(msg.program == PROGRAM::PULSATING) // Check for light measurement sync
{ if (millis() - m_last_light_sync >= 100) { // LIGHT_SYNC_INTERVAL_MS
// turn off setLightMeasurementSyncMode(true);
steady(0, 0, 0, 5); 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) int r_val = msg.r*m/255;
{ int g_val = msg.g*m/255;
for(int n=0; n<sizeof(led_sin_64); n++) int b_val = msg.b*m/255;
{ m_strip->setPixelColor(0, r_val, g_val, b_val);
uint8_t m = led_sin_64[n]; m_strip->show();
m_strip->setPixelColor(0, msg.r*m/255, msg.g*m/255, msg.b*m/255); bool any_led_on = (r_val > 0 || g_val > 0 || b_val > 0);
m_strip->show(); updateLedPhysicalState(any_led_on);
delay(30); }
} delay(30);
}
if(m_queue.receive(&msg, 0)) if(m_queue.receive(&msg, 0))
{ {
if(msg.program == PROGRAM::STEADY) if(msg.program == PROGRAM::STEADY)
{ {
steady(msg.r, msg.g, msg.b, 30); steady(msg.r, msg.g, msg.b, 30);
break; break;
} }
} }
delay(60);
delay(60); }
} }
} }
} }
} }
Led::Led(uint32_t pin) : m_queue(5) Led::Led(uint32_t pin) : m_queue(5)

View File

@ -11,6 +11,7 @@
class Led class Led
{ {
struct LED_QUEUE struct LED_QUEUE
{ {
uint8_t program; uint8_t program;
@ -19,6 +20,17 @@ class Led
uint8_t b; 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: public:
enum PROGRAM enum PROGRAM

View File

@ -19,7 +19,8 @@ struct BME_DATA m_bme_data;
SensorService::SensorService(AppIF & app_if) : m_app_if(app_if) 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() void SensorService::start()
@ -185,64 +186,161 @@ void SensorService::postBme68xData(float pressure, float temp)
memset(&m_bme_data, 0, sizeof(m_bme_data)); 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 /// @brief Actual light value is minimum in 2s window
/// @param light_value /// @param light_value
void SensorService::processLight(int light_value) void SensorService::processLight(int light_value)
{ {
static uint16_t min_light_val = 0xffff; synchronizedLightMeasurement((uint16_t)light_value);
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;
}
} }
// handles pressure and voc sensor // handles pressure and voc sensor
// Additional noise reduction techniques
void SensorService::run_i2c_1() void SensorService::run_i2c_1()
{ {
while(true) // ADC configuration for better noise performance
{ analogSetAttenuation(ADC_11db); // For 0-3.3V range
if(m_bmp280->read(m_bmp_data.temp, m_bmp_data.pressure)) analogSetWidth(12); // 12-bit resolution
processPressure(m_bmp_data.pressure);
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); 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) if(bme_cycle_finished)
postBme68xData(m_bmp_data.pressure, m_bmp_data.temp); postBme68xData(m_bmp_data.pressure, m_bmp_data.temp);
processLight(read_light_val); processLight(read_light_val);
delay(10); delay(10);
} }
} }
// handles radar only // handles radar only
void SensorService::run_uart() void SensorService::run_uart()
{ {

View File

@ -10,7 +10,7 @@
#include "Bme68x.h" #include "Bme68x.h"
#include "Bmp280.h" #include "Bmp280.h"
#include "LD2410.h" #include "LD2410.h"
#include <algorithm>
#include "../AppIF.h" #include "../AppIF.h"
class SensorService class SensorService
@ -37,6 +37,25 @@ public:
SensorService(AppIF & app_if); SensorService(AppIF & app_if);
void start(); 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 #endif