/// © MiroZ 2024 #include "app_config.h" #include "TaskMgr.h" #include "SensorService.h" #include "Buffers.h" #include "SensorData.h" #include "Settings.h" #include "Led.h" static const char *TAG = "sensors"; #define ms_to_us(ms) ((ms)*1000) #define LIGHT_SENSOR_PIN 36 struct BMP_DATA m_bmp_data; struct BME_DATA m_bme_data; SensorService::SensorService(AppIF & app_if) : m_app_if(app_if) { } void SensorService::start() { ESP_LOGW(TAG, "Starting sensor service..."); esp_log_level_set("gpio", ESP_LOG_WARN); memset(&m_bmp_data, 0, sizeof(m_bmp_data)); memset(&m_bme_data, 0, sizeof(m_bme_data)); m_bmp280 = new Bmp280(Wire); m_bme68x = new Bme68x(Wire); m_ld2410 = new LD2410(); bool hw_fault = false; if(!m_bmp280->init()) { hw_fault = true; ESP_LOGE(TAG, "bmp280 sensor error"); } if(!m_bme68x->init()) { hw_fault = true; ESP_LOGE(TAG, "bme68x sensor error"); } if(!m_ld2410->init()) { hw_fault = true; ESP_LOGE(TAG, "ld2410 sensor error"); } if(hw_fault) m_app_if.getLed()->setColor(255, 0, 0); assert(m_i2c1_task = TaskMgr::getInstance().createTask(std::bind(&SensorService::run_i2c_1, this), I2C1_TASK_NAME, I2C1_TASK_STACK_SIZE, I2C1_TASK_PRIORITY, I2C1_TASK_CORE)); assert(m_i2c2_task = TaskMgr::getInstance().createTask(std::bind(&SensorService::run_uart, this), UART_TASK_NAME, UART_TASK_STACK_SIZE, UART_TASK_PRIORITY, UART_TASK_CORE)); pinMode(LIGHT_SENSOR_PIN, INPUT); } void::SensorService::processPressure(float pressure) { static float filtered = 0; static uint64_t previous = esp_timer_get_time(); if(filtered == 0 && pressure != 0) filtered = pressure; uint64_t now = esp_timer_get_time(); // ESP_LOGI(TAG, "delta T: %d", (uint32_t)((now-previous)/1000)); filtered = 0.95 * filtered + 0.05 * pressure; previous = now; // ESP_LOGI(TAG, "%0.3f \t%0.3f", pressure, filtered); } void SensorService::postBme68xData(float pressure, float temp) { uint8_t msg_buffer[sizeof(struct MESSAGE_SENSORS_BLOCK) + 10*sizeof(struct GAS_DATA)]; struct MESSAGE_SENSORS_BLOCK * msg = (struct MESSAGE_SENSORS_BLOCK *)msg_buffer; MQTT_MESSAGE_BLOCK_SENSOR(msg); msg->humidity = m_bme_data.humidity; msg->light = m_light_value; msg->pressure = pressure; msg->temperature = temp + SETTINGS.sensors.temperature.temp_offset; int num_total = 0; struct GAS_DATA * p = msg->data; for(int n = 0; n < 10; n++) { if(m_bme_data.measurement_bitmask & (1 << n)) { p[num_total].resistance = m_bme_data.measurement[n].resistance; p[num_total++].index = n; } } msg->num_data = num_total; m_app_if.getBuffer()->putBlock((uint8_t*)msg, sizeof(*msg) + num_total * sizeof(struct GAS_DATA)); // clear the blackboard memset(&m_bme_data, 0, sizeof(m_bme_data)); } /// @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) { if(abs(min_light_val - last_light_val) > 4096*5/100) { ESP_LOGI(TAG, "light tripped"); struct MESSAGE_NOTIFY_LIGHT msg; MQTT_MESSAGE_NOTIFY_LIGHT(&msg); msg.value = min_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 void SensorService::run_i2c_1() { 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); if(bme_cycle_finished) postBme68xData(m_bmp_data.pressure, m_bmp_data.temp); processLight(read_light_val); delay(10); } } // handles radar only void SensorService::run_uart() { int64_t last_read = esp_timer_get_time(); while(true) { bool has_read = m_ld2410->read(); if(has_read && esp_timer_get_time() - last_read >= ms_to_us(10000-50)) { int64_t now = esp_timer_get_time(); // ESP_LOGI(TAG, "count %d", (int)m_ld2410->stationary_energy[0]); if(m_ld2410->stationary_energy[0] != 0) { struct MESSAGE_RADAR_BLOCK msg; MQTT_MESSAGE_BLOCK_RADAR(&msg); for(int n = 0; n < 24; n++) { if(n < 14) msg.vals[n] = m_ld2410->motion_energy[n] > 0xffff ? 0xffff : (uint16_t)m_ld2410->motion_energy[n]; else msg.vals[n] = m_ld2410->stationary_energy[n-14] > 0xffff ? 0xffff : (uint16_t)m_ld2410->stationary_energy[n-14]; } m_app_if.getBuffer()->putBlock((uint8_t*)&msg, sizeof(msg)); // ESP_LOGI(TAG, "delta t: %lld", (now - last_read)/1000); last_read = now; #if 0 ESP_LOGI("stationary energy", "%0.0f %0.0f %0.0f %0.0f %0.0f %0.0f %0.0f", m_ld2410->stationary_energy[3]/m_ld2410->stationary_energy[0], m_ld2410->stationary_energy[4]/m_ld2410->stationary_energy[0], m_ld2410->stationary_energy[5]/m_ld2410->stationary_energy[0], m_ld2410->stationary_energy[6]/m_ld2410->stationary_energy[0], m_ld2410->stationary_energy[7]/m_ld2410->stationary_energy[0], m_ld2410->stationary_energy[8]/m_ld2410->stationary_energy[0], m_ld2410->stationary_energy[9]/m_ld2410->stationary_energy[0]); ESP_LOGW("motion energy", "%0.0f %0.0f %0.0f %0.0f %0.0f %0.0f %0.0f %0.0f %0.0f", m_ld2410->motion_energy[5]/m_ld2410->motion_energy[0], m_ld2410->motion_energy[6]/m_ld2410->motion_energy[0], m_ld2410->motion_energy[7]/m_ld2410->motion_energy[0], m_ld2410->motion_energy[8]/m_ld2410->motion_energy[0], m_ld2410->motion_energy[9]/m_ld2410->motion_energy[0], m_ld2410->motion_energy[10]/m_ld2410->motion_energy[0], m_ld2410->motion_energy[11]/m_ld2410->motion_energy[0], m_ld2410->motion_energy[12]/m_ld2410->motion_energy[0], m_ld2410->motion_energy[13]/m_ld2410->motion_energy[0]); #endif m_ld2410->resetGates(); } } if(has_read) // next read will happen in 100ms. sleep untill just before then. delay(95); } }