Fixed light noisy reads. Sunchronized with eventula LED light
This commit is contained in:
parent
cae6860d9e
commit
682ffb4fee
62
main/Led.cpp
62
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;
|
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,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()
|
void Led::run()
|
||||||
{
|
{
|
||||||
struct LED_QUEUE msg;
|
struct LED_QUEUE msg;
|
||||||
|
uint32_t last_light_check = millis();
|
||||||
|
updateLedPhysicalState(false);
|
||||||
while(true)
|
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)
|
if(msg.program == PROGRAM::STEADY)
|
||||||
{
|
{
|
||||||
|
m_led_sync_mode = false;
|
||||||
steady(msg.r, msg.g, msg.b, 30);
|
steady(msg.r, msg.g, msg.b, 30);
|
||||||
}
|
}
|
||||||
else if(msg.program == PROGRAM::PULSATING)
|
else if(msg.program == PROGRAM::PULSATING)
|
||||||
{
|
{
|
||||||
// turn off
|
// Turn off first
|
||||||
steady(0, 0, 0, 5);
|
steady(0, 0, 0, 5);
|
||||||
|
m_led_sync_mode = false;
|
||||||
|
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
for(int n=0; n<sizeof(led_sin_64); n++)
|
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];
|
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->setPixelColor(0, msg.r*m/255, msg.g*m/255, msg.b*m/255);
|
||||||
m_strip->show();
|
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);
|
delay(30);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,11 +175,11 @@ void Led::run()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delay(60);
|
delay(60);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Led::Led(uint32_t pin) : m_queue(5)
|
Led::Led(uint32_t pin) : m_queue(5)
|
||||||
|
|||||||
12
main/Led.h
12
main/Led.h
@ -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
|
||||||
|
|||||||
@ -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,54 +186,150 @@ 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()
|
||||||
{
|
{
|
||||||
|
// ADC configuration for better noise performance
|
||||||
|
analogSetAttenuation(ADC_11db); // For 0-3.3V range
|
||||||
|
analogSetWidth(12); // 12-bit resolution
|
||||||
|
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
if(m_bmp280->read(m_bmp_data.temp, m_bmp_data.pressure))
|
if(m_bmp280->read(m_bmp_data.temp, m_bmp_data.pressure))
|
||||||
processPressure(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);
|
||||||
@ -243,6 +340,7 @@ void SensorService::run_i2c_1()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// handles radar only
|
// handles radar only
|
||||||
void SensorService::run_uart()
|
void SensorService::run_uart()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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
|
||||||
Loading…
x
Reference in New Issue
Block a user