Add Zone.Identifier files for wifi-connect and partitions

- Created new Zone.Identifier file in components/wifi-connect with ZoneId=3.
- Created new Zone.Identifier file in partitions.csv with ZoneId=3.
This commit is contained in:
Wang Beihong
2026-03-28 16:12:37 +08:00
parent a9be1dd6b9
commit 8c33fe7411
47 changed files with 2090 additions and 3132 deletions

View File

@@ -1,6 +1,6 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "."
PRIV_REQUIRES esp_wifi cjson nvs_flash lvgl_st7735s_use esp_driver_i2c esp_type_utils esp_timer espressif__servo esp_event esp_netif serial_mcu mqtt
PRIV_REQUIRES wifi-connect cjson nvs_flash lvgl_st7735s_use esp_driver_i2c esp_type_utils esp_timer espressif__servo esp_event esp_netif serial_mcu mqtt
WHOLE_ARCHIVE
)

View File

@@ -15,10 +15,7 @@ dependencies:
# # All dependencies of `main` are public by default.
# public: true
protocol_examples_common:
path: ../components/protocol_examples_common
espressif/esp_lvgl_port: ^2.7.0
k0i05/esp_ahtxx: ^1.2.7
@@ -27,5 +24,4 @@ dependencies:
espressif/mqtt: ^1.0.0
espressif/cjson: ^1.7.19
chiehmin/sgp30: ^1.0.0

View File

@@ -37,14 +37,14 @@
#include "ui_display.h"
#include "iot_servo.h"
#include "serial_mcu.h"
#include "protocol_examples_common.h"
#include "wifi-connect.h" // WiFi连接管理
/* ========================= 2. 宏定义 ========================= */
// 日志标签
#define TAG "main"
#define AHT30_TAG "i2c0_ahtxx_task"
#define BH1750_TAG "i2c0_bh1750_task"
#define MQ135_TAG "mq135_task"
#define MQ135_TAG "mq135_task" // (removed) MQ135-related functionality disabled
#define LVGL_TAG "lvgl_task"
#define SERVO_TAG "servo_task"
#define SNTP_TAG "sntp_task"
@@ -123,10 +123,9 @@ typedef struct
float temperature;
float humidity;
float lux;
float air_quality;
bool ahtxx_valid;
bool bh1750_valid;
bool mq135_valid;
// MQ135 fields removed: air_quality, mq135_valid
} sensor_data_t;
// 设备状态结构体
@@ -145,7 +144,7 @@ typedef struct
float temperature;
float humidity;
float light_intensity;
uint16_t air_quality;
// air_quality removed (MQ135)
char curtain_state[10];
char led_state[10];
uint8_t led_power;
@@ -181,9 +180,10 @@ bool g_high_temp_alerted = false; // 高温提醒是否已发送(避免重复
// 自动通风控制模式配置
#define VENTILATION_MODE_TAG "ventilation_mode"
#define AIR_QUALITY_THRESHOLD 50.0f // 空气质量阈值
bool g_ventilation_mode_enabled = true; // 通风模式是否启用
bool g_air_quality_alerted = false; // 空气质量提醒是否已发送(避免重复提醒)
// AIR quality and ventilation functionality removed
// #define AIR_QUALITY_THRESHOLD 50.0f
// bool g_ventilation_mode_enabled = true;
// bool g_air_quality_alerted = false;
// 时间段配置
time_period_config_t g_day_period = {
@@ -276,7 +276,7 @@ void init_gpio_output(void);
// 传感器任务
void i2c0_ahtxx_task(void *pvParameters);
void i2c0_bh1750_task(void *pvParameters);
void mq135_task(void *pvParameters);
// MQ135 task removed: void mq135_task(void *pvParameters);
// 其他任务
static void rx_task(void *arg);
@@ -1194,7 +1194,7 @@ void mqtt_publish_task(void *pvParameters)
g_device_message.telemetry.temperature = 0;
g_device_message.telemetry.humidity = 0;
g_device_message.telemetry.light_intensity = 0;
g_device_message.telemetry.air_quality = 0;
// air_quality removed from telemetry
strcpy(g_device_message.telemetry.curtain_state, "close");
strcpy(g_device_message.telemetry.led_state, "close");
g_device_message.telemetry.led_power = 0;
@@ -1224,11 +1224,7 @@ void mqtt_publish_task(void *pvParameters)
g_device_message.telemetry.light_intensity = g_sensor_data.lux;
}
// 更新空气质量数据
if (g_sensor_data.mq135_valid)
{
g_device_message.telemetry.air_quality = g_sensor_data.air_quality;
}
// MQ135 data removed: air_quality not populated
xSemaphoreGive(xMqttMessageMutex);
}
@@ -1269,7 +1265,7 @@ void mqtt_publish_task(void *pvParameters)
cJSON_AddNumberToObject(telemetry_obj, "humidity", roundf(g_device_message.telemetry.humidity * 100) / 100);
cJSON_AddNumberToObject(telemetry_obj, "light_intensity", roundf(g_device_message.telemetry.light_intensity * 100) / 100);
cJSON_AddNumberToObject(telemetry_obj, "air_quality", g_device_message.telemetry.air_quality);
// air_quality removed from telemetry
cJSON_AddStringToObject(telemetry_obj, "curtain_state", g_device_message.telemetry.curtain_state);
cJSON_AddStringToObject(telemetry_obj, "led_state", g_device_message.telemetry.led_state);
cJSON_AddNumberToObject(telemetry_obj, "led_power", g_device_message.telemetry.led_power);
@@ -2073,12 +2069,7 @@ void i2c0_ahtxx_task(void *pvParameters)
ui_update_sensor_data(
g_sensor_data.ahtxx_valid ? g_sensor_data.temperature : -1.0f,
g_sensor_data.ahtxx_valid ? g_sensor_data.humidity : -1.0f,
g_sensor_data.bh1750_valid ? g_sensor_data.lux : -1.0f,
g_sensor_data.mq135_valid ? g_sensor_data.air_quality : -1.0f,
g_sensor_data.mq135_valid ? (g_sensor_data.air_quality <= 20 ? "Excellent" : g_sensor_data.air_quality <= 100 ? "Good"
: g_sensor_data.air_quality <= 300 ? "Moderate"
: "High")
: "N/A");
g_sensor_data.bh1750_valid ? g_sensor_data.lux : -1.0f);
xSemaphoreGive(xSensorDataMutex);
}
@@ -2182,12 +2173,7 @@ void i2c0_bh1750_task(void *pvParameters)
ui_update_sensor_data(
g_sensor_data.ahtxx_valid ? g_sensor_data.temperature : -1.0f,
g_sensor_data.ahtxx_valid ? g_sensor_data.humidity : -1.0f,
g_sensor_data.bh1750_valid ? g_sensor_data.lux : -1.0f,
g_sensor_data.mq135_valid ? g_sensor_data.air_quality : -1.0f,
g_sensor_data.mq135_valid ? (g_sensor_data.air_quality <= 20 ? "Excellent" : g_sensor_data.air_quality <= 100 ? "Good"
: g_sensor_data.air_quality <= 300 ? "Moderate"
: "High")
: "N/A");
g_sensor_data.bh1750_valid ? g_sensor_data.lux : -1.0f);
xSemaphoreGive(xSensorDataMutex);
}
@@ -2213,36 +2199,7 @@ void print_sensor_data(void)
{
if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
ESP_LOGI(TAG, "======= Sensor Data ========");
if (g_sensor_data.ahtxx_valid)
{
ESP_LOGI(TAG, "Temperature: %.2f°C", g_sensor_data.temperature);
ESP_LOGI(TAG, "Humidity: %.2f%%", g_sensor_data.humidity);
}
else
{
ESP_LOGI(TAG, "Temperature: Invalid");
ESP_LOGI(TAG, "Humidity: Invalid");
}
if (g_sensor_data.bh1750_valid)
{
ESP_LOGI(TAG, "Light Intensity: %.2flx", g_sensor_data.lux);
}
else
{
ESP_LOGI(TAG, "Light Intensity: Invalid");
}
if (g_sensor_data.mq135_valid)
{
ESP_LOGI(TAG, "Air Quality: %.2f Index", g_sensor_data.air_quality);
}
else
{
ESP_LOGI(TAG, "Air Quality: Invalid");
}
ESP_LOGI(TAG, "==========================");
// 已移除所有传感器数据日志打印
xSemaphoreGive(xSensorDataMutex);
}
}
@@ -2585,34 +2542,34 @@ static void alarm_clock_main_task(void *arg)
void app_main(void)
{
esp_log_level_set("*", ESP_LOG_INFO);
esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE);
esp_log_level_set("mqtt_example", ESP_LOG_VERBOSE);
esp_log_level_set("transport_base", ESP_LOG_VERBOSE);
esp_log_level_set("esp-tls", ESP_LOG_VERBOSE);
esp_log_level_set("transport", ESP_LOG_VERBOSE);
esp_log_level_set("outbox", ESP_LOG_VERBOSE);
esp_log_level_set("dhcps", ESP_LOG_DEBUG);
esp_log_level_set("esp_netif", ESP_LOG_DEBUG);
esp_log_level_set("esp_netif_lwip", ESP_LOG_DEBUG);
esp_log_level_set("wifi", ESP_LOG_DEBUG);
initialize_nvs(); // 初始化NVS
// esp_log_level_set("*", ESP_LOG_INFO);
// esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE);
// esp_log_level_set("mqtt_example", ESP_LOG_VERBOSE);
// esp_log_level_set("transport_base", ESP_LOG_VERBOSE);
// esp_log_level_set("esp-tls", ESP_LOG_VERBOSE);
// esp_log_level_set("transport", ESP_LOG_VERBOSE);
// esp_log_level_set("outbox", ESP_LOG_VERBOSE);
// esp_log_level_set("dhcps", ESP_LOG_DEBUG);
// esp_log_level_set("esp_netif", ESP_LOG_DEBUG);
// esp_log_level_set("esp_netif_lwip", ESP_LOG_DEBUG);
// esp_log_level_set("wifi", ESP_LOG_DEBUG);
// 初始化 Wi-Fi 配网组件,支持长按按键进入配网
ESP_ERROR_CHECK(wifi_connect_init());
printf("设备启动完成:进入配网模式,手机连接 ESP32-* 后访问 http://192.168.4.1\n");
ESP_ERROR_CHECK(esp_netif_init()); // Initialize ESP-NETIF
// 创建默认事件循环
ESP_ERROR_CHECK(esp_event_loop_create_default());
// 连接WIFI
ESP_ERROR_CHECK(example_connect());
// ESP_ERROR_CHECK(example_connect());
// Print out Access Point Information
wifi_ap_record_t ap_info;
ESP_ERROR_CHECK(esp_wifi_sta_get_ap_info(&ap_info));
ESP_LOGI(TAG, "--- Access Point Information ---");
ESP_LOG_BUFFER_HEX("MAC Address", ap_info.bssid, sizeof(ap_info.bssid));
ESP_LOG_BUFFER_CHAR("SSID", ap_info.ssid, sizeof(ap_info.ssid));
ESP_LOGI(TAG, "Primary Channel: %d", ap_info.primary);
ESP_LOGI(TAG, "RSSI: %d", ap_info.rssi);
// // Print out Access Point Information
// wifi_ap_record_t ap_info;
// ESP_ERROR_CHECK(esp_wifi_sta_get_ap_info(&ap_info));
// ESP_LOGI(TAG, "--- Access Point Information ---");
// ESP_LOG_BUFFER_HEX("MAC Address", ap_info.bssid, sizeof(ap_info.bssid));
// ESP_LOG_BUFFER_CHAR("SSID", ap_info.ssid, sizeof(ap_info.ssid));
// ESP_LOGI(TAG, "Primary Channel: %d", ap_info.primary);
// ESP_LOGI(TAG, "RSSI: %d", ap_info.rssi);
// 初始化I2C总线
ESP_ERROR_CHECK(i2c_master_init());
@@ -2663,10 +2620,9 @@ void app_main(void)
xTaskCreate(time_period_check_task, "time_period_task", 4096, NULL, 5, NULL);
// 创建降温模式任务
xTaskCreate(cooling_mode_task, "cooling_mode_task", 4096, NULL, 5, NULL);
// 创建自动通风控制模式任务
xTaskCreate(ventilation_mode_task, "ventilation_mode_task", 4096, NULL, 5, NULL);
// 创建MQ135传感器任务
xTaskCreate(mq135_task, "mq135_task", 4096, NULL, 5, NULL);
// 自动通风与 MQ135 相关任务已移除
// xTaskCreate(ventilation_mode_task, "ventilation_mode_task", 4096, NULL, 5, NULL);
// xTaskCreate(mq135_task, "mq135_task", 4096, NULL, 5, NULL);
// 创建外设控制任务
xTaskCreate(peripheral_control_task, "peripheral_control_task", 4096, NULL, 5, NULL);
@@ -2679,7 +2635,7 @@ void app_main(void)
while (1)
{
// 定期打印传感器数据
// print_sensor_data();
print_sensor_data();
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
}
@@ -2697,7 +2653,7 @@ static void cooling_mode_task(void *pvParameters)
cooling_mode_load_from_nvs();
// 高温阈值
const float HIGH_TEMP_THRESHOLD = 35.0f;
const float HIGH_TEMP_THRESHOLD = 48.0f;
while (1)
{
@@ -2761,7 +2717,7 @@ static void cooling_mode_task(void *pvParameters)
xSemaphoreGive(xControlFlagMutex);
}
ESP_LOGW(COOLING_MODE_TAG, "High temperature alert: %.1f°C (>35°C)", current_temp);
ESP_LOGW(COOLING_MODE_TAG, "High temperature alert: %.1f°C (>40°C)", current_temp);
// 发送MQTT提醒消息
if (g_mqtt_client != NULL)
@@ -2819,309 +2775,15 @@ static void cooling_mode_task(void *pvParameters)
*/
static void ventilation_mode_task(void *pvParameters)
{
ESP_LOGI(VENTILATION_MODE_TAG, "Ventilation mode task started");
while (1)
{
if (g_ventilation_mode_enabled)
{
float current_air_quality = 0;
bool air_quality_valid = false;
// 获取当前空气质量
if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
current_air_quality = g_sensor_data.air_quality;
air_quality_valid = g_sensor_data.mq135_valid;
xSemaphoreGive(xSensorDataMutex);
}
if (air_quality_valid)
{
// 检测空气质量是否超过阈值
if (current_air_quality > AIR_QUALITY_THRESHOLD)
{
// 需要通风,确保风扇开启
if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE)
{
if (!fan_control_flag)
{
fan_control_flag = true;
ESP_LOGW(VENTILATION_MODE_TAG, "Air quality %.2f > %.1f, Fan ON",
current_air_quality, AIR_QUALITY_THRESHOLD);
update_telemetry_and_report();
}
xSemaphoreGive(xControlFlagMutex);
}
// 发送提醒(只发送一次)
if (!g_air_quality_alerted)
{
g_air_quality_alerted = true;
// 蜂鸣器发出轻微提示音(短促)
sendControlFrame(0x02, 1);
vTaskDelay(pdMS_TO_TICKS(200)); // 持续200ms
sendControlFrame(0x02, 0);
// 发送MQTT提醒消息
if (g_mqtt_client != NULL)
{
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "type", "device_message");
cJSON_AddStringToObject(root, "device_id", g_device_message.device_id);
cJSON_AddStringToObject(root, "device_type", g_device_message.device_type);
cJSON_AddStringToObject(root, "message_type", "air_quality_alert");
cJSON *data_obj = cJSON_CreateObject();
cJSON_AddStringToObject(data_obj, "alert", "卧室需要通风");
cJSON_AddNumberToObject(data_obj, "air_quality", roundf(current_air_quality * 10) / 10);
cJSON_AddItemToObject(root, "data", data_obj);
char *json_message = cJSON_Print(root);
if (json_message)
{
esp_mqtt_client_publish(g_mqtt_client, MQTT_PUBLISH_TOPIC_QOS0, json_message, 0, 0, 0);
ESP_LOGI(VENTILATION_MODE_TAG, "Air quality alert sent: %s", json_message);
free(json_message);
}
cJSON_Delete(root);
}
}
}
else if (current_air_quality < (AIR_QUALITY_THRESHOLD - 10)) // 添加滞后,防止频繁切换
{
// 空气质量恢复良好,关闭风扇
if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE)
{
if (fan_control_flag)
{
fan_control_flag = false;
ESP_LOGI(VENTILATION_MODE_TAG, "Air quality %.2f < %.1f, Fan OFF",
current_air_quality, AIR_QUALITY_THRESHOLD - 10);
update_telemetry_and_report();
}
xSemaphoreGive(xControlFlagMutex);
}
// 重置提醒标志
if (g_air_quality_alerted)
{
g_air_quality_alerted = false;
ESP_LOGI(VENTILATION_MODE_TAG, "Air quality normalized: %.2f, reset alert", current_air_quality);
}
}
}
}
// 每5秒检查一次
vTaskDelay(pdMS_TO_TICKS(5000));
}
ESP_LOGI("ventilation_mode_task", "ventilation mode removed");
vTaskDelete(NULL);
}
// MQ135传感器任务
// MQ135 task removed; provide a short stub to avoid undefined references
void mq135_task(void *pvParameters)
{
// 初始化ADC
adc1_config_width(ADC_WIDTH_BIT_12); // ADC1 分辨率 12 位
adc1_config_channel_atten(ADC_CHANNEL_0, ADC_ATTEN_DB_12); // GPIO34, 12dB
// 动态校准变量
static bool r0_calibrated = false;
static float R0 = 10.0f; // 初始 R0更合理的经验值10kΩ左右
static int calibrate_count = 0; // 校准采样计数器
static float rs_sum = 0.0f; // Rs累加值
// 采样平滑滤波窗口
#define SAMPLE_WINDOW_SIZE 20 // 滑动平均窗口大小
static float voltage_buffer[SAMPLE_WINDOW_SIZE] = {0};
static int buffer_index = 0;
static bool buffer_filled = false;
// 预热阶段
#define WARMUP_SAMPLES 100 // 预热采样次数
static int warmup_count = 0;
ESP_LOGI(MQ135_TAG, "MQ135 sensor task started - Preheating for %d samples...", WARMUP_SAMPLES);
while (1)
{
int adc_value = adc1_get_raw(ADC_CHANNEL_0);
// ADC -> 电压(工程近似)
float voltage = (adc_value * 3.3f) / 4095.0f;
// 电压保护
if (voltage >= 3.3f)
voltage = 3.29f;
if (voltage <= 0.0f)
voltage = 0.01f;
// === 采样平滑滤波 ===
// 存入滑动窗口
voltage_buffer[buffer_index] = voltage;
buffer_index = (buffer_index + 1) % SAMPLE_WINDOW_SIZE;
// 检查窗口是否填满
if (buffer_index == 0)
{
buffer_filled = true;
}
// 计算滑动平均电压
float smoothed_voltage = 0.0f;
int window_size = buffer_filled ? SAMPLE_WINDOW_SIZE : buffer_index;
for (int i = 0; i < window_size; i++)
{
smoothed_voltage += voltage_buffer[i];
}
smoothed_voltage /= window_size;
// 计算 RsRL = 10kΩ
float rs = ((3.3f - smoothed_voltage) / smoothed_voltage) * 10.0f;
// === 预热阶段 ===
if (warmup_count < WARMUP_SAMPLES)
{
warmup_count++;
if (warmup_count % 20 == 0) // 每20个采样打印一次进度
{
ESP_LOGI(MQ135_TAG, "MQ135 preheating... %d/%d", warmup_count, WARMUP_SAMPLES);
}
vTaskDelay(5000 / portTICK_PERIOD_MS);
continue;
}
else if (warmup_count == WARMUP_SAMPLES)
{
warmup_count++;
ESP_LOGI(MQ135_TAG, "MQ135 preheating completed, starting calibration");
vTaskDelay(100 / portTICK_PERIOD_MS); // 短暂延迟
continue;
}
// === 温湿度补偿 ===
// MQ135传感器对温度和湿度敏感需要进行补偿
float temperature = 20.0f; // 默认20°C
float humidity = 65.0f; // 默认65%RH
// 获取实际温湿度
if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
if (g_sensor_data.ahtxx_valid)
{
temperature = g_sensor_data.temperature;
humidity = g_sensor_data.humidity;
}
xSemaphoreGive(xSensorDataMutex);
}
// 温度补偿公式 (参考MQ135数据手册)
// R0(T) = R0(T0) * exp(A * (1/T - 1/T0))
// A为温度系数约为0.00025
float temp_compensation = 1.0f + 0.00025f * (temperature - 20.0f);
// 湿度补偿 (经验公式)
float hum_compensation = 1.0f + 0.0005f * (humidity - 65.0f);
// 应用补偿后的Rs
float rs_compensated = rs / (temp_compensation * hum_compensation);
// === R0校准阶段 ===
if (!r0_calibrated && calibrate_count < 100)
{
rs_sum += rs_compensated;
calibrate_count++;
if (calibrate_count == 100)
{
// 使用100次采样的平均值作为R0假设在干净空气中启动
R0 = rs_sum / 100.0f;
ESP_LOGI(MQ135_TAG, "MQ135 R0 calibrated to: %.2f (from 100 samples, temp=%.1f°C, hum=%.1f%%)",
R0, temperature, humidity);
r0_calibrated = true;
}
else if (calibrate_count % 20 == 0)
{
ESP_LOGI(MQ135_TAG, "Calibrating R0... %d/100", calibrate_count);
}
}
else if (!r0_calibrated)
{
// 如果校准未完成使用初始R0值
ESP_LOGI(MQ135_TAG, "Using initial R0: %.2f", R0);
}
// Rs/R0 比值
float ratio = rs_compensated / R0;
// 防止ratio异常传感器故障或校准错误
if (ratio <= 0.1f || ratio > 100.0f)
{
ESP_LOGW(MQ135_TAG, "Abnormal ratio detected: %.3f, skipping", ratio);
vTaskDelay(5000 / portTICK_PERIOD_MS);
continue;
}
/* =====================================================
* MQ135 传感器方程
* Rs/R0 = a * (ppm)^b
* ppm = (Rs/R0 / a)^(1/b)
* 对于 MQ135: a=116.602, b=-2.769
* ===================================================== */
float mq135_concentration = 116.602f * powf(ratio, -2.769f);
// 工程修正:不使用放大系数
mq135_concentration = mq135_concentration * 1.0f;
// 下限保护,避免负值或过小值
if (mq135_concentration < 1.0f)
mq135_concentration = 1.0f;
// 上限保护
if (mq135_concentration > 1000.0f)
{
ESP_LOGW(MQ135_TAG, "Concentration too high: %.2f, clamped to 1000", mq135_concentration);
mq135_concentration = 1000.0f;
}
// 保留两位小数
mq135_concentration = roundf(mq135_concentration * 100.0f) / 100.0f;
// 更新全局数据
if (xSensorDataMutex != NULL &&
xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
g_sensor_data.air_quality = mq135_concentration;
g_sensor_data.mq135_valid = true;
xSemaphoreGive(xSensorDataMutex);
}
// UI 更新
if (xSensorDataMutex != NULL &&
xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
ui_update_sensor_data(
g_sensor_data.ahtxx_valid ? g_sensor_data.temperature : -1.0f,
g_sensor_data.ahtxx_valid ? g_sensor_data.humidity : -1.0f,
g_sensor_data.bh1750_valid ? g_sensor_data.lux : -1.0f,
mq135_concentration,
mq135_concentration <= 20 ? "Excellent" : mq135_concentration <= 100 ? "Good"
: mq135_concentration <= 300 ? "Moderate"
: "High");
xSemaphoreGive(xSensorDataMutex);
}
// 每10次采样打印一次详细日志
static int log_count = 0;
log_count++;
if (log_count >= 10)
{
log_count = 0;
ESP_LOGI(MQ135_TAG, "ADC:%d, Volt:%.3fV->%.3fV, Rs:%.2f, Ratio:%.3f, PPM:%.2f (T:%.1f°C, H:%.1f%%)",
adc_value, voltage, smoothed_voltage, rs, ratio, mq135_concentration, temperature, humidity);
}
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
ESP_LOGI("mq135_task", "mq135 task removed");
vTaskDelete(NULL);
}
// Remaining MQ135 implementation removed