ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

硬件部分|01基于ESP32的宠物饮水系统

硬件部分|01基于ESP32的宠物饮水系统 一、硬件部分ESP32×1tb6612×1TDS 水质传感器x1水位传感器×1小水泵x1按键x2LED灯x1跳线/杜邦线 若干接线部分水泵控制 | GPIO4 | 输出 | 经 S8050 三极管驱动高电平启动水泵 || 水位传感器 | GPIO34 | 模拟输入 | ADC1_CH60~3.3V仅输入 || TDS 传感器 | GPIO35 | 模拟输入 | ADC1_CH70~3.3V仅输入 || 紧急补水键 | GPIO13 | 输入上拉 | 按下为 LOW需 80ms 消抖 || 参数重置键 | GPIO14 | 输入上拉 | 按下为 LOW清除故障标志 || 状态 LED | GPIO2 | 输出 | 经 220Ω 限流高电平亮二、代码部分使用arduino开发watersystem.ino代码#include config.h #include pump_control.h #include sensor_task.h #include blinker_comm.h #include alarm_task.h #include Arduino.h #include freertos/FreeRTOS.h #include freertos/task.h /* loop 任务栈增至 16KB供 Blinker/WiFi/MQTT 使用避免栈溢出导致 Guru Meditation */ #if defined(ESP_LOOP_TASK_STACK_SIZE) ESP_LOOP_TASK_STACK_SIZE(16384); #elif defined(SET_LOOP_TASK_STACK_SIZE) SET_LOOP_TASK_STACK_SIZE(16 * 1024); #endif #define BTN_DEBOUNCE_MS 80 #define BLINKER_REPORT_INTERVAL_MS 500 #define DEBUG_STATUS_INTERVAL_MS 10000 /* 每 10 秒打印一次运行状态 */ static uint32_t s_lastBlinkerReport 0; static uint32_t s_lastDebugStatus 0; static bool s_firstLoop true; static uint32_t s_btnEmergencyLast 0; static uint32_t s_btnResetLast 0; void setup() { #if defined(DEBUG_SERIAL) Serial.begin(115200); delay(500); DBG_PRINTLN(F([系统] PetWaterSystem 启动...)); #endif pinMode(PIN_BTN_EMERGENCY, INPUT_PULLUP); pinMode(PIN_BTN_RESET, INPUT_PULLUP); pump_init(); #if defined(DEBUG_SERIAL) DBG_PRINTLN(F([系统] 水泵初始化完成)); #endif xTaskCreate(pump_task, pump, TASK_PUMP_STACK, NULL, TASK_PUMP_PRIO, NULL); xTaskCreate(sensor_task, sensor, TASK_SENSOR_STACK, NULL, TASK_SENSOR_PRIO, NULL); xTaskCreate(alarm_task, alarm, TASK_ALARM_STACK, NULL, TASK_ALARM_PRIO, NULL); #if defined(DEBUG_SERIAL) DBG_PRINTLN(F([系统] 任务创建完成: pump, sensor, alarm)); #endif /* 延时使 WiFi 子系统就绪后再初始化 Blinker */ #if defined(DEBUG_SERIAL) DBG_PRINTLN(F([系统] 延时 2 秒等待 WiFi 子系统就绪...)); #endif delay(2000); #if defined(DEBUG_SERIAL) DBG_PRINTLN(F([Blinker] 正在连接 WiFi 与 Blinker 云可能需 5~15 秒...)); #endif blinker_init(); #if defined(DEBUG_SERIAL) DBG_PRINTLN(F([Blinker] 连接成功设备已上线)); DBG_PRINTLN(F([系统] 进入主循环)); #endif } void loop() { uint32_t now millis(); #if defined(DEBUG_SERIAL) if (s_firstLoop) { s_firstLoop false; DBG_PRINTLN(F([loop] 主循环已启动)); } #endif /* Blinker 驱动需频繁调用以维持 MQTT 心跳与响应 */ blinker_run(); /* 处理待推送告警由 alarm_task 请求在此执行避免 alarm_task 栈溢出 */ blinker_processNotify(); /* 周期上报水位、TDS 到 Blinker APP */ if ((now - s_lastBlinkerReport) BLINKER_REPORT_INTERVAL_MS) { s_lastBlinkerReport now; blinker_reportData(); } #if defined(DEBUG_SERIAL) /* 每 10 秒打印运行状态 */ if ((now - s_lastDebugStatus) DEBUG_STATUS_INTERVAL_MS) { s_lastDebugStatus now; DBG_PRINTF([状态] 水位%.1f%% TDS%d ppm 水泵%s\n, sensor_getWaterLevel(), sensor_getTDS(), pump_isRunning() ? 运行 : 停止); } #endif /* 按键扫描 */ if (digitalRead(PIN_BTN_EMERGENCY) LOW) { if ((now - s_btnEmergencyLast) BTN_DEBOUNCE_MS) { s_btnEmergencyLast now; pump_requestManualFill(0); } } if (digitalRead(PIN_BTN_RESET) LOW) { if ((now - s_btnResetLast) BTN_DEBOUNCE_MS) { s_btnResetLast now; pump_clearFault(); } } vTaskDelay(pdMS_TO_TICKS(20)); }alarm_task.cpp代码/** * file alarm_task.cpp * brief 告警与 LED 状态指示正常慢闪、异常快闪/常亮协同 Blinker 告警推送 */ #include config.h #include alarm_task.h #include sensor_task.h #include blinker_comm.h #include pump_control.h #include Arduino.h #include freertos/FreeRTOS.h #include freertos/task.h #define LED_ON_MS 200 #define LED_OFF_SLOW_MS 800 /* 正常慢闪 */ #define LED_OFF_FAST_MS 100 /* 异常快闪 */ #define NOTIFY_INTERVAL_MS 30000 /* 告警推送间隔避免刷屏 */ static uint32_t s_lastNotifyMs 0; static AlarmType s_lastAlarm ALARM_NONE; void alarm_task(void* pvParameters) { (void)pvParameters; pinMode(PIN_LED, OUTPUT); digitalWrite(PIN_LED, LOW); TickType_t lastWake xTaskGetTickCount(); const TickType_t period pdMS_TO_TICKS(TASK_ALARM_PERIOD_MS); bool ledOn false; uint32_t ledToggleMs 0; uint32_t offMs LED_OFF_SLOW_MS; for (;;) { vTaskDelayUntil(lastWake, period); uint32_t now millis(); AlarmType alarm sensor_getAlarm(); PumpState pumpState pump_getState(); /* 根据状态选择 LED 闪烁模式 */ if (alarm ALARM_LOW_WATER || alarm ALARM_OVERFLOW || pumpState PUMP_DRY_RUN_FAULT || pumpState PUMP_OVERFLOW_FAULT) { offMs LED_OFF_FAST_MS; /* 严重异常快闪 */ } else if (alarm ALARM_TDS_HIGH) { offMs LED_OFF_FAST_MS; /* TDS 超标快闪 */ } else { offMs LED_OFF_SLOW_MS; /* 正常慢闪 */ } /* LED 闪烁 */ if (ledOn) { if ((now - ledToggleMs) LED_ON_MS) { digitalWrite(PIN_LED, LOW); ledOn false; ledToggleMs now; } } else { if ((now - ledToggleMs) offMs) { digitalWrite(PIN_LED, HIGH); ledOn true; ledToggleMs now; } } /* 告警推送间隔发送避免刷屏 */ if (alarm ! ALARM_NONE || pumpState PUMP_DRY_RUN_FAULT || pumpState PUMP_OVERFLOW_FAULT) { if ((now - s_lastNotifyMs) NOTIFY_INTERVAL_MS alarm ! s_lastAlarm) { s_lastNotifyMs now; s_lastAlarm alarm; if (alarm ALARM_LOW_WATER) { blinker_requestNotify(BLINKER_NOTIFY_LOW_WATER); } else if (alarm ALARM_OVERFLOW || pumpState PUMP_OVERFLOW_FAULT) { blinker_requestNotify(BLINKER_NOTIFY_OVERFLOW); } else if (alarm ALARM_TDS_HIGH) { blinker_requestNotify(BLINKER_NOTIFY_TDS); } else if (pumpState PUMP_DRY_RUN_FAULT) { blinker_requestNotify(BLINKER_NOTIFY_DRY_RUN); } } } else { s_lastAlarm ALARM_NONE; } } }alarm_task.h代码/** * file alarm_task.h * brief 告警与状态指示任务 - LED 闪烁、异常告警 */ #ifndef ALARM_TASK_H #define ALARM_TASK_H #ifdef __cplusplus extern C { #endif /** * brief 告警与 LED 状态指示任务入口FreeRTOS 任务函数 */ void alarm_task(void* pvParameters); #ifdef __cplusplus } #endif #endif /* ALARM_TASK_H */blinker.cpp代码/** * file blinker_comm.cpp * brief Blinker 物联网通信数据上报、远程控制、告警推送 * note 使用前需在 config.h 配置 BLINKER_AUTH、WIFI_SSID、WIFI_PASSWORD */ #define BLINKER_WIFI #include config.h #include blinker_comm.h /* 须在 Blinker.h 之前包含本模块头文件避免库内宏与 BLINKER_NOTIFY_* 枚举常量名冲突 */ #include Blinker.h #include sensor_task.h #include pump_control.h #include Arduino.h #include freertos/FreeRTOS.h #include freertos/task.h /* Blinker 组件键名需与 APP 中配置一致 */ #define BLINKER_KEY_WATER num-level /* 水位数据 */ #define BLINKER_KEY_TDS num-tds /* TDS 数据 */ #define BLINKER_KEY_PUMP btn-pump /* 远程补水按钮 */ #define BLINKER_KEY_TIMER max-timer /* 定时补水可选 */ static BlinkerNumber BlinkerWater(BLINKER_KEY_WATER); static BlinkerNumber BlinkerTDS(BLINKER_KEY_TDS); static BlinkerButton BlinkerPump(BLINKER_KEY_PUMP); static void button_pump_cb(const String state) { (void)state; #if defined(DEBUG_SERIAL) DBG_PRINTLN(F([Blinker] 远程补水按钮已触发)); #endif pump_requestManualFill(0); /* 0 自动补到水位达标 */ } void blinker_init(void) { #if defined(DEBUG_SERIAL) /* Blinker.begin 为阻塞调用内部会连接 WiFi 和 MQTT */ DBG_PRINTLN(F([Blinker] 调用 Blinker.begin请稍候...)); #endif Blinker.begin(BLINKER_AUTH, WIFI_SSID, WIFI_PASSWORD); BlinkerPump.attach(button_pump_cb); #if defined(DEBUG_SERIAL) DBG_PRINTLN(F([Blinker] Blinker.begin 返回初始化完成)); #endif } void blinker_reportData(void) { float level sensor_getWaterLevel(); int tds sensor_getTDS(); BlinkerWater.print((float)((int)(level * 10) / 10.0f)); /* 保留 1 位小数 */ BlinkerTDS.print(tds); } void blinker_notify(const char* msg) { Blinker.notify(msg); } /* 待推送告警alarm_task 设置loop 执行避免在 alarm_task 栈上调用 Blinker.notify 导致崩溃 */ static volatile BlinkerNotifyType s_pendingNotify BLINKER_NOTIFY_NONE; void blinker_requestNotify(BlinkerNotifyType type) { s_pendingNotify type; } void blinker_processNotify(void) { BlinkerNotifyType type s_pendingNotify; if (type BLINKER_NOTIFY_NONE) return; s_pendingNotify BLINKER_NOTIFY_NONE; const char* msg ; switch (type) { case BLINKER_NOTIFY_LOW_WATER: msg 缺水告警水位过低请及时加水; break; case BLINKER_NOTIFY_OVERFLOW: msg 溢水告警水位过高水泵已停止; break; case BLINKER_NOTIFY_TDS: msg 水质告警TDS 超标建议更换水源; break; case BLINKER_NOTIFY_DRY_RUN: msg 水泵空转保护请检查进水管; break; default: return; } #if defined(DEBUG_SERIAL) DBG_PRINTLN(F([Blinker] 告警已推送到 APP)); #endif Blinker.notify(msg); } /** * brief 驱动 Blinker 通信需在主循环中频繁调用 * note 原 blinker_task 已移除改为在 loop 中调用避免独立任务栈溢出导致 Double exception */ void blinker_run(void) { Blinker.run(); }blink.h代码/** * file blinker_comm.h * brief Blinker 物联网通信 - 数据上报、远程控制、告警推送 * note 需在 Arduino IDE 中安装 Blinker 库并在 config.h 配置 WiFi 与密钥 */ #ifndef BLINKER_COMM_H #define BLINKER_COMM_H #ifdef __cplusplus extern C { #endif /** 告警推送类型由 alarm_task 请求loop 中 blinker_processNotify 执行减轻任务栈压力 */ typedef enum { BLINKER_NOTIFY_NONE 0, BLINKER_NOTIFY_LOW_WATER, BLINKER_NOTIFY_OVERFLOW, BLINKER_NOTIFY_TDS, BLINKER_NOTIFY_DRY_RUN } BlinkerNotifyType; /** * brief 初始化 BlinkerWiFi 连接、回调注册 */ void blinker_init(void); /** * brief 数据上报水位、TDS、水泵状态 * note 由 loop 周期性调用 */ void blinker_reportData(void); /** * brief 推送告警到 APP直接调用 Blinker.notify栈较大时慎用 * param msg 告警消息 */ void blinker_notify(const char* msg); /** * brief 请求推送告警可在任意任务中调用实际 notify 在 loop 的 blinker_processNotify 中执行 */ void blinker_requestNotify(BlinkerNotifyType type); /** * brief 处理待推送告警须在 loop 中调用 */ void blinker_processNotify(void); /** * brief 驱动 Blinker 通信须在 loop 中频繁调用维持 MQTT 心跳 */ void blinker_run(void); #ifdef __cplusplus } #endif #endif /* BLINKER_COMM_H */config.h代码/** * file config.h * brief 智能宠物饮水系统 - 配置文件引脚、阈值、WiFi、Blinker * note 使用前请修改 BLINKER_AUTH、WIFI_SSID、WIFI_PASSWORD */ #ifndef CONFIG_H #define CONFIG_H /* 启用则开启 Serial 调试会增加约 20KB 固件编译超限时请注释掉 */ #define DEBUG_SERIAL /* WiFi 与 Blinker 配置必改 */ #define BLINKER_AUTH 2b2af3758d43 // 点灯科技设备密钥 #define WIFI_SSID ADC // WiFi 名称 #define WIFI_PASSWORD cp135971 // WiFi 密码 /* 硬件引脚定义ESP32-WROOM-DA */ /* TB6612 电机驱动模块引脚定义驱动水泵- 根据实际接线修改 */ #define PIN_PUMP_PWM 4 // TB6612 PWMA 输入速度控制 #define PIN_PUMP_AIN1 15 // TB6612 AIN1 输入方向控制 1 #define PIN_PUMP_AIN2 16 // TB6612 AIN2 输入方向控制 2 /* STBY 引脚直接接 3.3V无需 GPIO 控制 */ #define PIN_WATER_LEVEL 34 // 雨水传感器 ADCADC1_CH6仅输入 #define PIN_TDS 35 // TDS 传感器 ADCADC1_CH7仅输入 #define PIN_BTN_EMERGENCY 13 // 紧急补水键内部上拉按下 LOW #define PIN_BTN_RESET 14 // 参数重置键 #define PIN_LED 2 // 状态 LED经 220Ω 限流高电平亮部分板载 LED /* ADC 参数ESP32 12 位 */ #define ADC_BITS 12 #define ADC_MAX ((1 ADC_BITS) - 1) // 4095 /* 水位阈值0~100 百分比 */ #define WATER_LEVEL_LOW 20 // 低于此值自动补水 #define WATER_LEVEL_HIGH 90 // 达到此值停泵溢水保护 #define WATER_LEVEL_CRITICAL 5 // 低于此值缺水告警 /* TDS 阈值ppm */ #define TDS_WARN 500 // 超过此值水质告警 /* 水泵保护参数 */ #define PUMP_DRY_RUN_SEC 15 // 空转保护启动后此秒数内水位无上升则停机 #define PUMP_MIN_RUN_MS 3000 // 水泵最短运行时间避免频繁启停 /* 滑动平均滤波窗口 */ #define FILTER_WINDOW_WATER 12 #define FILTER_WINDOW_TDS 8 /* FreeRTOS 任务参数 */ #define TASK_PUMP_STACK 2048 #define TASK_PUMP_PRIO 3 #define TASK_PUMP_PERIOD_MS 10 #define TASK_SENSOR_STACK 2048 #define TASK_SENSOR_PRIO 2 #define TASK_SENSOR_PERIOD_MS 100 /* Blinker 已移至 loop 运行以下参数已废弃 */ #define TASK_BLINKER_STACK 8192 #define TASK_BLINKER_PRIO 1 #define TASK_BLINKER_PERIOD_MS 500 #define TASK_ALARM_STACK 2048 #define TASK_ALARM_PRIO 0 #define TASK_ALARM_PERIOD_MS 50 /* 串口调试宏仅当 DEBUG_SERIAL 启用时生效 */ #if defined(DEBUG_SERIAL) #define DBG_PRINT(x) Serial.print(x) #define DBG_PRINTLN(x) Serial.println(x) #define DBG_PRINTF(...) Serial.printf(__VA_ARGS__) #else #define DBG_PRINT(x) #define DBG_PRINTLN(x) #define DBG_PRINTF(...) #endif #endif /* CONFIG_H */filter.cpp代码/** * file filters.cpp * brief 滑动平均滤波器实现 */ #include filters.h MovingAverageFilter::MovingAverageFilter(int size) { _size (size 1) ? size : 2; _buf new float[_size]; _head 0; _count 0; _sum 0; _out 0; for (int i 0; i _size; i) _buf[i] 0; } MovingAverageFilter::~MovingAverageFilter() { delete[] _buf; } float MovingAverageFilter::update(float value) { if (_count _size) { _buf[_count] value; _sum value; _count; } else { _sum - _buf[_head]; _buf[_head] value; _sum value; _head (_head 1) % _size; } _out _sum / _count; return _out; } void MovingAverageFilter::reset() { _head 0; _count 0; _sum 0; _out 0; for (int i 0; i _size; i) _buf[i] 0; }filters.h代码/** * file filters.h * brief 滑动平均滤波器 - 用于水位、TDS 等模拟量去噪 */ #ifndef FILTERS_H #define FILTERS_H /** * brief 滑动平均滤波器类 */ class MovingAverageFilter { public: /** * param size 窗口大小建议 8~16 */ MovingAverageFilter(int size); ~MovingAverageFilter(); /** * brief 输入新采样值返回滤波后的结果 */ float update(float value); /** * brief 重置滤波器清空历史数据 */ void reset(); /** * brief 获取当前滤波输出不更新 */ float getValue() const { return _out; } /** * brief 是否已填满窗口滤波稳定 */ bool isReady() const { return _count _size; } private: float* _buf; int _size; int _head; int _count; float _sum; float _out; }; #endif /* FILTERS_H */pump_control.cpp代码/** * file pump_control.cpp * brief 水泵控制与空转/溢水保护实现TB6612电机驱动模块 */ #include config.h #include pump_control.h #include Arduino.h #include freertos/FreeRTOS.h #include freertos/task.h /* 共享变量由 sensor_task 更新 */ static volatile float s_waterLevel 0; /* 控制状态 */ static volatile bool s_pumpOn false; static volatile PumpState s_state PUMP_OFF; static volatile bool s_manualRequest false; static volatile uint32_t s_manualDurationMs 0; static volatile bool s_timedFillActive false; static volatile uint32_t s_timedRemainMs 0; static uint32_t s_pumpStartMs 0; static float s_waterLevelAtStart 0; void pump_init(void) { /* TB6612 引脚初始化 */ pinMode(PIN_PUMP_PWM, OUTPUT); pinMode(PIN_PUMP_AIN1, OUTPUT); pinMode(PIN_PUMP_AIN2, OUTPUT); /* 初始状态TB6612 已使能STBY 接 3.3V电机停止 */ digitalWrite(PIN_PUMP_AIN1, LOW); // AIN1 低电平 digitalWrite(PIN_PUMP_AIN2, LOW); // AIN2 低电平 digitalWrite(PIN_PUMP_PWM, LOW); // PWM 低电平电机停止 s_pumpOn false; s_state PUMP_OFF; s_manualRequest false; s_timedFillActive false; #if defined(DEBUG_SERIAL) DBG_PRINTLN(F([水泵] TB6612初始化完成)); #endif } void pump_setWaterLevel(float level) { s_waterLevel level; } bool pump_isRunning(void) { return s_pumpOn; } PumpState pump_getState(void) { return s_state; } void pump_requestManualFill(uint32_t duration_ms) { s_manualRequest true; s_manualDurationMs duration_ms; } void pump_requestTimedFill(bool enable, uint32_t duration_ms) { s_timedFillActive enable; if (enable) { s_timedRemainMs duration_ms; } } void pump_clearFault(void) { if (s_state PUMP_DRY_RUN_FAULT || s_state PUMP_OVERFLOW_FAULT) { s_state PUMP_OFF; } } void pump_task(void* pvParameters) { (void)pvParameters; TickType_t lastWake xTaskGetTickCount(); const TickType_t period pdMS_TO_TICKS(TASK_PUMP_PERIOD_MS); for (;;) { vTaskDelayUntil(lastWake, period); float level s_waterLevel; uint32_t now millis(); /* 故障时仅响应清除除非有手动补水请求 */ if (s_state PUMP_DRY_RUN_FAULT || s_state PUMP_OVERFLOW_FAULT) { if (!s_manualRequest) { digitalWrite(PIN_PUMP_PWM, LOW); // TB6612停止输出 s_pumpOn false; continue; } else { // 手动补水请求覆盖故障状态 s_state PUMP_OFF; } } /* 溢水保护 */ if (level WATER_LEVEL_HIGH) { digitalWrite(PIN_PUMP_PWM, LOW); // TB6612停止输出 s_pumpOn false; s_state PUMP_OVERFLOW_FAULT; #if defined(DEBUG_SERIAL) DBG_PRINTLN(F([水泵] 溢水保护: 水位过高已停机)); #endif s_manualRequest false; s_timedFillActive false; continue; } /* 判断是否需要补水 */ bool needFill false; if (s_manualRequest) { needFill true; if (s_manualDurationMs 0) { uint32_t elapsed now - s_pumpStartMs; if (elapsed s_manualDurationMs) { s_manualRequest false; needFill false; } } else { // 移除水位检查允许用户在任何水位下手动补水 // 仅在达到溢水保护水位时停止 if (level WATER_LEVEL_HIGH) { s_manualRequest false; needFill false; } } } if (s_timedFillActive s_timedRemainMs 0) { needFill true; s_timedRemainMs - TASK_PUMP_PERIOD_MS; if (s_timedRemainMs 0) s_timedRemainMs 1; /* 本周期内仍运行 */ if (s_timedRemainMs TASK_PUMP_PERIOD_MS) { s_timedRemainMs 0; s_timedFillActive false; } } if (!needFill level WATER_LEVEL_LOW) { needFill true; /* 自动补水 */ } /* 执行启停 */ if (needFill) { if (!s_pumpOn) { s_pumpStartMs now; s_waterLevelAtStart level; } /* TB6612 启动电机AIN1 高电平AIN2 低电平PWM 高电平全速 */ digitalWrite(PIN_PUMP_AIN1, HIGH); // AIN1 高电平 digitalWrite(PIN_PUMP_AIN2, LOW); // AIN2 低电平 digitalWrite(PIN_PUMP_PWM, HIGH); // PWM 高电平全速运行 s_pumpOn true; s_state PUMP_RUNNING; /* 空转保护 */ uint32_t runTime (now - s_pumpStartMs) / 1000; if (runTime PUMP_DRY_RUN_SEC) { float rise level - s_waterLevelAtStart; if (rise 2.0f) { digitalWrite(PIN_PUMP_PWM, LOW); // TB6612停止输出 s_pumpOn false; s_state PUMP_DRY_RUN_FAULT; #if defined(DEBUG_SERIAL) DBG_PRINTLN(F([水泵] 空转保护: 运行超时水位无上升已停机)); #endif s_manualRequest false; s_timedFillActive false; } } } else { /* 最短运行时间保护运行不足则继续 */ if (s_pumpOn) { uint32_t runMs now - s_pumpStartMs; if (runMs PUMP_MIN_RUN_MS) { digitalWrite(PIN_PUMP_PWM, LOW); // TB6612停止输出 s_pumpOn false; s_state PUMP_OFF; } } else { s_state PUMP_OFF; } } } }pump_control.h代码/** * file pump_control.h * brief 水泵控制与保护空转/溢水/手动/自动/定时 */ #ifndef PUMP_CONTROL_H #define PUMP_CONTROL_H #include stdint.h #include stdbool.h /* C 下可用bool 为基本类型 */ #ifdef __cplusplus extern C { #endif /** 水泵运行状态 */ typedef enum { PUMP_OFF, PUMP_RUNNING, PUMP_DRY_RUN_FAULT, PUMP_OVERFLOW_FAULT } PumpState; /** * brief 初始化水泵控制GPIO、状态 */ void pump_init(void); /** * brief 设置水位百分比由 sensor_task 调用0~100 */ void pump_setWaterLevel(float level); /** * brief 获取水泵是否正在运行 */ bool pump_isRunning(void); /** * brief 获取水泵状态 */ PumpState pump_getState(void); /** * brief 请求手动补水本地按键或 Blinker 远程 * param duration_ms 0 表示自动补水直到水位达标 */ void pump_requestManualFill(uint32_t duration_ms); /** * brief 请求定时补水Blinker 定时组件 * param enable 1 启动0 停止 * param duration_ms 本次补水时长 */ void pump_requestTimedFill(bool enable, uint32_t duration_ms); /** * brief 清除故障标志参数重置键 */ void pump_clearFault(void); /** * brief 水泵控制任务入口FreeRTOS 任务函数 */ void pump_task(void* pvParameters); #ifdef __cplusplus } #endif #endif /* PUMP_CONTROL_H */sensor_task.cpp代码/** * file sensor_task.cpp * brief 水位与 TDS 采集、滑动平均滤波、告警检测 */ #include config.h #include sensor_task.h #include pump_control.h #include filters.h #include Arduino.h #include freertos/FreeRTOS.h #include freertos/task.h /* TDS 标定系数根据实际模块调整 */ #define TDS_K (0.5f) /* 经验系数 */ static MovingAverageFilter* s_waterFilter nullptr; static MovingAverageFilter* s_tdsFilter nullptr; static volatile float s_waterLevel 0; static volatile int s_tdsValue 0; static volatile AlarmType s_alarm ALARM_NONE; float sensor_getWaterLevel(void) { return s_waterLevel; } int sensor_getTDS(void) { return s_tdsValue; } AlarmType sensor_getAlarm(void) { return s_alarm; } void sensor_task(void* pvParameters) { (void)pvParameters; s_waterFilter new MovingAverageFilter(FILTER_WINDOW_WATER); s_tdsFilter new MovingAverageFilter(FILTER_WINDOW_TDS); /* ESP32 ADC 衰减 11dB量程约 0~3.3VWiFi 开启时仅 ADC1 可用 */ analogReadResolution(ADC_BITS); analogSetAttenuation(ADC_11db); TickType_t lastWake xTaskGetTickCount(); const TickType_t period pdMS_TO_TICKS(TASK_SENSOR_PERIOD_MS); for (;;) { vTaskDelayUntil(lastWake, period); /* 读取原始 ADC水位空低水位低 ADC满高 ADC */ int rawWater analogRead(PIN_WATER_LEVEL); int rawTDS analogRead(PIN_TDS); /* 滑动平均滤波 */ float waterFiltered s_waterFilter-update((float)rawWater); float tdsFiltered s_tdsFilter-update((float)rawTDS); /* 水位百分比ADC 越高水位越低干燥时ADC高映射到 0~100 */ float level 100.0f - (waterFiltered / ADC_MAX) * 100.0f; if (level 0) level 0; if (level 100) level 100; s_waterLevel level; /* TDSADC 电压 * 系数 得到 ppm 近似值需根据实际传感器标定 */ float voltage (tdsFiltered / ADC_MAX) * 3.3f; int tds (int)(voltage * TDS_K * 1000); if (tds 0) tds 0; s_tdsValue tds; /* 同步给水泵控制 */ pump_setWaterLevel(s_waterLevel); /* 告警检测 */ if (level WATER_LEVEL_CRITICAL) { s_alarm ALARM_LOW_WATER; } else if (level WATER_LEVEL_HIGH) { s_alarm ALARM_OVERFLOW; } else if (tds TDS_WARN) { s_alarm ALARM_TDS_HIGH; } else { /* 溢水故障由 pump 单独处理此处仅根据水位 */ if (pump_getState() PUMP_OVERFLOW_FAULT) { s_alarm ALARM_OVERFLOW; } else if (s_alarm ! ALARM_OVERFLOW) { s_alarm ALARM_NONE; } } } }sensor_task.h代码/** * file sensor_task.h * brief 传感器采集任务 - 水位、TDS 采集与滤波 */ #ifndef SENSOR_TASK_H #define SENSOR_TASK_H #ifdef __cplusplus extern C { #endif /** 告警类型供 alarm_task 使用 */ typedef enum { ALARM_NONE 0, ALARM_LOW_WATER, /* 缺水 */ ALARM_OVERFLOW, /* 溢水由 pump 触发 */ ALARM_TDS_HIGH /* TDS 超标 */ } AlarmType; /** * brief 获取当前水位百分比 0~100 */ float sensor_getWaterLevel(void); /** * brief 获取当前 TDS 值 ppm */ int sensor_getTDS(void); /** * brief 获取当前告警类型 */ AlarmType sensor_getAlarm(void); /** * brief 传感器采集任务入口FreeRTOS 任务函数 */ void sensor_task(void* pvParameters); #ifdef __cplusplus } #endif #endif /* SENSOR_TASK_H */三、成果展示实物部分blinker 部分
返回列表