拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

STM32流体水位传感器实战:从选型到ADC采集与滤波

STM32流体水位传感器实战:从选型到ADC采集与滤波

学习STM32的流体水位传感器

做水位传感器之前,先问自己一个问题:你到底要测什么?是只想知道水箱里“有没有水”,还是想知道“有多少水”?这两种需求对应的传感器方案完全不一样,成本能差出十倍不止。我在做鱼缸水位监测和户外水箱液位远程上报时,把市面上主流的几种方案都试了一圈,踩了不少坑,这篇就把整个学习与实操过程完整记录下来,从传感器选型、硬件接线、STM32端代码实现,到常见的坑和排查方法,一次说清楚。

这套项目非常适合刚入门STM32的同学拿来练手。它不像电机控制那样需要调PID、不像以太网通信那样上来就要啃协议栈,水位传感器涉及的只是GPIO读取、ADC采集、简单的滤波算法和串口输出,全是STM32最基础的外设应用。但你别小看它,里面涉及的传感器原理、信号调理、抗干扰处理,都是工业现场实打实会用到的东西。做完这个项目,你对“传感器数据是怎么一步步变成单片机里能用的数字量”这件事,会有一个完整的认识。

1. 项目整体拆解:先搞清楚你要测的是“有”还是“量”

1.1 水位测量方案的根本差异

市面上的流体水位传感器,说白了分成两大类。第一类是离散量输出,只告诉你“触发了”或者“没触发”,典型代表是浮球开关、光电式液位开关、电极式水位开关。第二类是模拟量或数字量输出,能告诉你“当前液位大概在什么位置”,典型代表是压力式液位变送器、超声波液位计、电容式液位传感器。

这个项目我建议你两类都做一遍。原因很简单:离散量那类电路简单、逻辑清晰,适合第一天就上手,你把GPIO配置、上下拉、中断这些概念全过一遍;模拟量那类需要ADC采样、滤波、标定,直接把你从“点灯选手”拉升到“会处理真实模拟信号”的段位。把这两套都跑通了,市面上90%的传感器你拿到手都知道该怎么接、怎么读了。

1.2 传感器类型的横向对比与选型逻辑

先把市面上常见的几种流体水位传感器拉个表对比,这是我在选型时整理出来的:

传感器类型输出信号精度成本寿命适合场景
浮球开关开关量(通/断)无极低一般,机械活动件易卡死水箱低液位报警、水泵防空转
光电式液位开关开关量无低长,无机械活动件医疗设备、家电水箱
电极式水位传感器开关量/电阻变化无/低极低中,电极易极化腐蚀导电液体、最简单的水位报警
电容式液位传感器模拟电压/数字I2C中中长,非接触式管道内液位、腐蚀性液体
压力式液位变送器4-20mA / 0-3.3V高中高长水塔、深井、工业储罐
超声波液位计串口/PWM/模拟高高长大型容器、开放式水池

选型的核心逻辑就两条:第一,你的液体是什么性质。导电液体可以用电极式的,纯水、盐水都行,但酒精、油这类不导电的液体就别想电极了。第二,你要测的是一个点还是一个连续值。要是只想防止水泵空转,浮球开关几十块钱就解决问题,没必要上超声波。

我最初贪便宜买了不带信号调理的电容式传感器,直接接STM32的ADC引脚,读数飘得像得了帕金森。后来才明白,很多传感器模块和裸传感器完全是两码事,模块上已经帮你把信号调理电路做好了,输出阻抗低、带滤波,接单片机直接就能用。新手做项目,除非你想顺便把模电也学了,否则我建议直接买模块,不要一上来就啃裸传感元件。

1.3 整体系统架构设计

这个项目的系统架构并不复杂,属于典型的单采集节点:

水位传感器 → 信号调理(模块自带或外接) → STM32 引脚(GPIO/ADC) ↓ 数据处理(滤波/标定) ↓ 串口输出 → 上位机/调试助手 ↓ 阈值判断 → 报警/继电器驱动

STM32在这个系统里干的事很纯粹:采集、处理、输出。核心的技术点就三个:GPIO读取开关量、ADC采集模拟量、UART输出结果。再加上一个定时器做软件定时采样,整个项目的代码量控制在四五百行以内,特别适合作为学习STM32外设的综合性练习。

2. 硬件连接与原理:从传感器到STM32引脚

2.1 开关量传感器的接线方式

先讲最简单的,开关量水位传感器。浮球开关内部就是一个干簧管或者微动开关,串在两根线之间。你只需要把一端接STM32的GND,另一端通过一个10kΩ上拉电阻接到3.3V,同时引到STM32的一个GPIO引脚上。

这里有个关键细节:必须加上拉电阻。不加上拉电阻,开关断开时GPIO引脚就是悬空的,读到的电平完全不确定,有时候是高有时候是低,能把人逼疯。加了上拉电阻之后,开关断开时引脚被电阻拉到高电平,开关闭合时引脚被拉到GND,逻辑就干净了。

STM32的GPIO配置代码,我用的标准库写法是这样:

GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);

用HAL库的话更简单,直接在CubeMX里把PB0配置成GPIO_Input,Pull-up/Pull-down选Pull-up,生成代码之后读引脚状态就行。注意一个容易错的地方:STM32的GPIO输入模式,配置成上拉输入之后,引脚默认被拉到高电平。所以浮球开关的逻辑我建议做成:读到低电平表示“水位到达”,读到高电平表示“水位未到达”。因为开关闭合(水位到达)会强行把引脚拉低,这个信号比高电平可靠得多。

2.2 模拟量传感器的核心原理

模拟量水位传感器就高级一些了,输出的电压值随液位变化。以最常见的压力式液位变送器为例,它的原理是:传感器内部有一个感压膜片,液体的静压作用在膜片上,膜片变形导致内部的电阻应变片阻值变化,经过电桥电路转成微弱的电压信号,再经过放大器放大,最后通过电压-电流转换电路输出标准的4-20mA电流信号,或者0-3.3V/0-5V的电压信号。

这里面有一个必须理解的概念:4-20mA对应0-100%液位。4mA表示空,20mA表示满。为什么不用0-20mA?因为4mA这个下限可以用来判断断线,如果电流变成0mA,说明传感器坏了或者线断了,而不是液位为零。工业现场这个设计非常巧妙。

如果拿到的是4-20mA输出的传感器,STM32的ADC引脚不能直接接,因为你测的是电流不是电压,需要一个250Ω的精密电阻把电流转换成1-5V的电压。但注意,STM32的ADC输入范围是0-3.3V,1-5V直接接进去会把ADC烧掉。解决办法是用电阻分压,把5V按比例降到3.3V以下,或者干脆买那种0-3.3V输出的小量程变送器,更省事。

2.3 电容式与光电式传感器的补充说明

顺手说一下另外两种很多人会碰到的传感器。

电容式液位传感器,我用来测鱼缸水位的那种,原理是利用水和空气介电常数不同,导致传感器周围的电容值变化,内部电路把这个电容变化转化成电压或数字信号输出。我用的那款是1-Wire总线的,和DS18B20温度传感器用同样的通信协议,占用一个GPIO就能读数据。这种传感器最大的好处是非接触测量,探头防腐蚀,放在鱼缸外侧就能感知水位。坏处是容易受温度影响,夏天的漂移比冬天明显,需要做温度补偿。

光电式液位开关内部有一个红外发光管和一个光敏接收管,光线通过棱镜反射回接收管。有液体时,光线被液体散射掉,接收管收不到信号,输出状态翻转。这种传感器没有机械活动部件,寿命比浮球长得多,而且响应极快,适合需要频繁检测的场合,就是价格稍贵。

关于1-Wire总线的驱动,网上现成代码很多,我不建议从零写时序,把别人调通的延时版本改一改能用就行。水位的实时性要求没那么高,驱动代码别死磕性能,稳定就行。

3. STM32端软件实现:从ADC采集到数据滤波

3.1 开发环境与项目初始化

我做这个项目用的开发板是市面上最常见的STM32F103C8T6最小系统板,就是所谓“蓝色药丸”那块板子。开发环境用的是Keil MDK5,配合STM32CubeMX做初始化代码生成。我知道很多人刚开始都用标准外设库,但我的建议是尽早切到HAL库。理由很简单:标准外设库官方已经停止维护很久了,HAL库虽然代码啰嗦一点,但是CubeMX可以帮你生成底层配置,尤其是引脚复用和时钟树,手写太容易出错。

CubeMX里的配置流程是这样的:选好芯片型号STM32F103C8Tx,在Pinout视图里把PA0配置为ADC1_IN0,PA9和PA10配置为USART1的TX和RX,然后RCC里把HSE设成Crystal/Ceramic Resonator,SYS里Debug选Serial Wire,不然你会发现下载完程序之后第二次就无法下载了,这是新手最常见的坑之一。时钟树直接选最大72MHz,生成的代码就能跑。

3.2 ADC采集代码实现

用HAL库读取ADC的值,代码非常简洁:

// 在main.c中 uint16_t adc_value = 0; float voltage = 0.0f; float water_level = 0.0f; while (1) { // 软件触发ADC转换,等待转换完成 HAL_ADC_Start(&hadc1); if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK) { adc_value = HAL_ADC_GetValue(&hadc1); } HAL_ADC_Stop(&hadc1); // 将ADC原始值转换为电压值,STM32的ADC是12位的,参考电压3.3V voltage = (float)adc_value / 4095.0f * 3.3f; // 假设传感器输出0.5V对应空,2.5V对应满 water_level = (voltage - 0.5f) / (2.5f - 0.5f) * 100.0f; if (water_level < 0.0f) water_level = 0.0f; if (water_level > 100.0f) water_level = 100.0f; printf("ADC Value: %d, Voltage: %.2fV, Water Level: %.1f%%\r\n", adc_value, voltage, water_level); HAL_Delay(500); }

注意我用了printf,这需要重定向。在Keil里用微库的话,重定向函数写成:

#include <stdio.h> int fputc(int ch, FILE *f) { while (USART1->SR & (1 << 7) == 0); // 等待发送寄存器空 USART1->DR = ch; return ch; }

这是在标准库模式下重定向串口输出的常见写法,配合串口助手就能看到打印数据了。

3.3 数据滤波:真实采集数据的痛点

如果你照着上面的代码做完,大概率会遇到一个让你头大的问题:ADC读到的数据来回跳,液位明明没变,数值却在上下波动。这是所有做模拟采集的人都要过的第一关,叫噪声。

噪声的来源有三个主要路径。一是电源纹波,STM32的3.3V供电如果不干净,参考电压就不稳定,ADC自然跟着跳。二是传感器本身的信号波动,压力传感器的膜片受到液体扰动,输出有小幅抖动是正常的。三是电磁干扰,导线靠近电机、继电器这些感性负载时,会把干扰耦合进来。

解决方案从软到硬分三层。硬件上在传感器电源和地之间加100nF去耦电容,靠近STM32的VREF引脚也要加。软件上就要上滤波算法。

我实测下来最稳的是中位值滤波加滑动平均的复合滤波法。思路也很朴素:先连续采5个值,取中间的那个,去掉最大最小值,避免突发干扰。然后把有效值放进一个长度为10的队列,每次取平均。这样既滤掉了随机噪声,又能对真实的液位变化保持一定的响应速度。

核心代码实现:

#define FILTER_BUFFER_SIZE 10 uint16_t filter_buffer[FILTER_BUFFER_SIZE] = {0}; uint8_t filter_index = 0; uint16_t sensor_filtered_read(void) { // 中位值滤波:取5次采样的中间值 uint16_t samples[5]; for (int i = 0; i < 5; i++) { HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, 100); samples[i] = HAL_ADC_GetValue(&hadc1); HAL_ADC_Stop(&hadc1); HAL_Delay(10); } // 简单的排序找中间值 for (int i = 0; i < 4; i++) { for (int j = i + 1; j < 5; j++) { if (samples[j] < samples[i]) { uint16_t temp = samples[i]; samples[i] = samples[j]; samples[j] = temp; } } } uint16_t median = samples[2]; // 滑动平均滤波 filter_buffer[filter_index] = median; filter_index = (filter_index + 1) % FILTER_BUFFER_SIZE; uint32_t sum = 0; for (int i = 0; i < FILTER_BUFFER_SIZE; i++) { sum += filter_buffer[i]; } return (uint16_t)(sum / FILTER_BUFFER_SIZE); }

你说这个算法是必要的吗?如果只是Demo演示,确实是锦上添花。但如果你要把这套东西用在控制水泵上,一天到晚因为噪声导致误动作,不是水泵频繁启停损坏电机,就是该报警时没报警水漫金山。真实项目里,软件的抗干扰能力比硬件的性能参数更值钱。

3.4 多点监测与阈值控制

只测一个水位点还是有点浪费,我后面扩展成了三路监测:一路在鱼缸底部(防漏水干烧)、一路在中间(正常水位范围)、一路在顶部(防止溢流)。每一路接一个独立的浮球开关,分别接到STM32的三个GPIO引脚。

三路监测的代码逻辑如下:

#define SENSOR_BOTTOM_PIN GPIO_PIN_0 // PB0 #define SENSOR_MIDDLE_PIN GPIO_PIN_1 // PB1 #define SENSOR_TOP_PIN GPIO_PIN_2 // PB2 typedef enum { LEVEL_LOW, // 低液位,需要补水 LEVEL_OK, // 正常液位 LEVEL_HIGH // 高液位,需要停止补水/报警 } WaterLevelStatus; WaterLevelStatus check_water_level(void) { uint8_t bottom = GPIO_ReadInputDataBit(GPIOB, SENSOR_BOTTOM_PIN); uint8_t middle = GPIO_ReadInputDataBit(GPIOB, SENSOR_MIDDLE_PIN); uint8_t top = GPIO_ReadInputDataBit(GPIOB, SENSOR_TOP_PIN); // 注意:开关闭合时引脚为低电平(0),表示该位置已接触水 if (top == 0) return LEVEL_HIGH; if (middle == 0) return LEVEL_OK; if (bottom == 0) return LEVEL_LOW; return LEVEL_LOW; }

有了这个状态机之后,你在主循环里定期检查,然后根据状态决定是否需要打开补水泵或者触发声光报警器,这套逻辑和工业上水塔全自动液位控制是同一个思路,只不过工业上会用PLC来做,可靠性要求更高。

4. 调试过程与问题排查:我踩过的那些坑

4.1 串口输出乱码的真相

做调试第一步肯定是把数据打印出来看,但我第一次上电,串口助手输出的是满屏乱码。排查过程倒是很快,因为常识告诉我:这类问题要么是波特率不对,要么是时钟频率不对。

我用的是USART1,CubeMX里配置的波特率是115200,串口助手也选的115200,这没问题。问题出在时钟上——外部晶振没焊或者虚焊的时候,系统会默认用内部8MHz时钟,而CubeMX代码是按72MHz生成的所有外设时序配置,实际跑8MHz,算出来的波特率自然就是错的。

判断方法很简单:看串口的实际波特率。如果预期115200实际499200左右,多半就是这个问题。解决办法是检查开发板上的8MHz晶振是否焊好,或者直接把CubeMX里的时钟源改成内部时钟,配合重新计算外设参数。

4.2 ADC所有引脚都读到4095的排查

还有一个特别坑的现象:ADC读出来永远是4095,满量程。不管怎么改变量液位,数值纹丝不动。排查过程比较曲折,首先怀疑传感器坏了,单独测传感器发现有输出,排除。然后怀疑ADC配置问题,重新检查CubeMX配置也没错。

最后发现问题出在供电上。我用的那块运放信号调理模块需要12V供电,我用的是一个劣质DC-DC模块,纹波巨大,导致整个系统的GND电平不稳。ADC的参考电压虽然还是3.3V,但相对于传感器信号的地已经被抬高了不少,读出来自然就是满量程。

这是个常见到不能再常见的问题:共地。传感器、信号调理模块、STM32开发板、电源模块,所有设备的GND必须可靠地连接在一起。如果有任何一个设备的GND是浮空的,模拟信号采集就会出错。我在后来做一个温湿度计项目时也碰到过一模一样的现象,排查步骤刻在脑子里了。

4.3 水位数据的温度漂移问题

电容式传感器在夏天使用时会遇到另一个麻烦:温度漂移。气温从25℃升到35℃,即使水位一点没变,ADC读数也会自己往下掉。这是因为水的介电常数受温度影响,温度越高介电常数越低,传感器感知到的电容变化随之变小。

解决办法有两个方向。硬件方向是在传感器信号输出端加一个高通滤波器,只让液位变化的交流分量通过,把缓慢的温度漂移滤掉,但这样对需要静态精确测量的场景不友好。软件方向是加温度传感器做补偿,实测出温度和水位读数的对应关系,做一个线性拟合,在代码里边查边补。

如果只是做学习项目,我不建议在温度补偿上花太多时间。你只要知道这个现象存在,并且在系统设计的时候预留一个温度传感器的引脚就够了。

4.4 常见故障速查表

整理一个表格,拿到手直接对着排查:

故障现象可能原因排查顺序与解决措施
串口输出乱码时钟配置异常、波特率不匹配先核对CubeMX时钟树是否72MHz,再看串口助手波特率与代码一致
ADC读到固定满量程/零传感器未接、共地不良、参考电压不稳先万用表量传感器输出,再检查所有设备的GND是否共地
读数跳动大电源纹波、引脚悬空、软件未滤波加去耦电容,确认引脚不悬空,上中位值+滑动平均滤波
浮球开关不触发上拉电阻缺失、引脚配置错误万用表测开关通断,检查GPIO是否配置成上拉输入
传感器接上后系统复位引脚灌入大电流、电源供电不足检查信号电平是否超过3.3V,提高电源功率
下载程序后第二次无法下载SWD引脚被禁用或占用在CubeMX SYS里选Serial Wire,按住复位键尝试下载

4.5 抗干扰处理的关键细节

这部分是我自己反复吃过亏才总结出来的经验,价值要比前面所有代码加起来都高。

第一,传感器的信号线尽量用双绞线或者屏蔽线,屏蔽层单端接地。我最初图省事,用普通的杜邦线把传感器连接到开发板,走线还和继电器控制线绑在一起,结果只要继电器一动作,ADC读数就剧烈跳动。

第二,STM32的电源脚要加两个电容,一个大容量电解电容(100μF)和一个高频去耦电容(100nF)并联。电解电容保证瞬态电流需求,去耦电容滤掉高频噪声。别觉得这是玄学,我见过太多项目最后卡在电源不稳定上。

第三,如果你要用继电器驱动水泵,注意继电器的线圈是一个大电感,断电瞬间会产生反向电动势,这个电压可以达到上百伏。必须在继电器线圈两端反向并联一个1N4007二极管,把反向电动势泄放掉。不然轻则导致系统复位,重则击穿STM32的GPIO引脚。

5. 拓展玩法:从单点监测到智能控制系统

5.1 联动补水泵和声光报警器

做完基本的液位检测之后,我很自然就想让它干点实际的活,于是给它接上了继电器模块,驱动一个12V的小水泵做自动补水。控制逻辑是这样的:当检测到LEVEL_LOW时,打开继电器补水;当检测到LEVEL_OK时,关闭继电器停止补水;当检测到LEVEL_HIGH时,除了关闭继电器,还要拉响蜂鸣器报警,提示有溢流风险。

继电器模块的控制端是一个低电平有效的输入引脚,用三极管或光耦隔离驱动。接到STM32之后,代码逻辑非常直接:

// 假设继电器控制引脚是PB12 #define RELAY_PIN GPIO_PIN_12 void control_pump(WaterLevelStatus level) { switch(level) { case LEVEL_LOW: GPIO_SetBits(GPIOB, RELAY_PIN); // 高电平 = 继电器断开 = 水泵停止 break; case LEVEL_OK: GPIO_ResetBits(GPIOB, RELAY_PIN); // 低电平 = 继电器吸合 = 水泵启动 break; case LEVEL_HIGH: GPIO_SetBits(GPIOB, RELAY_PIN); // 同时触发蜂鸣器报警 break; } }

这里有个很关键的防抖问题:浮球开关在水面波动的时候,会反复通断,如果直接控制继电器,继电器会跟着不停地吸合释放,几天就烧坏了。必须做软件防抖——连续多次读取到相同状态,才认为状态真的变了。最简单的实现是:连续采样10次,如果全部是低电平才判定为水位到达,中间有任何一次是高电平就重置计数。

5.2 增加远程监控与数据记录

串口打印数据只在开发调试时有用,真正使用环境里人不可能一直盯着串口助手。我在后期把USART1的数据通过一个TTL转WiFi模块(ESP8266)发到了局域网内,手机浏览器打开一个网页就能看到当前水位和温度数据。其实就是在ESP8266里做了一个极简的HTTP服务器,STM32通过串口把封装好的数据帧发给ESP8266,ESP8266解析之后渲染成网页。

这里不展开细说,但把通信协议设计一下:

// 协议帧格式:帧头 + 设备ID + 数据长度 + 水位值 + 温度值 + 状态字 + 校验和 // 帧头: 0xAA 0x55 // 示例: AA 55 01 04 00 64 1E 01 93

STM32端每隔500ms就发一帧,ESP8266收齐一帧之后校验通过,更新页面上的数据。这套协议的写法虽然简单,但涵盖了通信协议设计的核心要素:帧同步、数据校验、状态上报。后面如果你想接云平台或者用MQTT上报数据,思维模型是一样的。

5.3 学习路径建议:做完这个项目下一步该学什么

如果你顺着这篇博客把代码都跑通了,我建议你按下面几个方向继续深入。

第一,把模拟量的压力式液位传感器换成MODBUS协议输出的工业传感器,学习RS485通信和MODBUS-RTU协议。STM32的USART支持方向控制,接一个MAX485芯片就能搞定。这个方向对应的是工业现场的真实应用场景,找工作面试能聊的东西就多了。

第二,用定时器输入捕获功能做超声波液位计。超声波模块输出一个与距离成正比的脉宽信号,定时器捕获脉宽就能算出距离,联动物位高度。这个项目能帮你把STM32定时器这个核心外设吃透,比干看视频学定时器有意思多了。

第三,增加一块OLED屏幕做本地显示,把水位、电量、报警状态全部可视化。STM32驱动I2C接口的OLED几乎是必学技能,代码量不大,但能锻炼你读时序图和调试软件I2C的能力。

6. 实操心得:从这套项目里总结出的经验

整个项目做下来,我最深的体会是三件事。

第一,传感器选型比代码重要。代码写得再漂亮,传感器选错了一切白搭。接触被测量之前,先搞清楚液体的性质、温度范围、容器形状、量程精度,再决定用哪种传感器。我见过不少人花几百块买超声波液位计,结果用在一个要测0.5米小水箱的场景里,超声波盲区就有0.2米,完全测不了。选型这事,一定是先算需求再掏钱。

第二,做嵌入式开发一定要先把手上的开发环境搞稳定。Keil MDK5加STM32CubeMX的组合,我用了很多年,中间踩过的坑包括:Keil5安装STM32芯片包时要选对应版本,否则找不到芯片型号;CubeMX生成的工程默认编译优化级别可能导致局部变量乱优化,调成-O0能省掉很多莫名其妙的bug;下载器用ST-Link V2时,如果提示固件升级千万别手贱取消,不然下一次连接就会报错。开发环境不顺,项目连开始都开始不了。

第三,数据滤波不是简单调参,而是理解信号本身。刚开始调滤波参数的时候,我总想找一组“万能参数”,结果发现水位变化快的时候要求响应速度快,水位稳定时要求滤波力度大,根本不可能两全。后来我把采样频率和滤波窗口分开考虑:采样频率提高之后,滑动平均窗口对真实变化的延迟缩小了,滤波效果也能保持。这个过程中,你对十进制转二进制、浮点运算精度、CPU周期开销的理解都会被逼着加深,这些才是嵌入式开发最有价值的部分。

我在做这个项目的过程中翻阅了大量网上资料,其中江科大(B站UP主)的STM32入门视频对我的帮助很大,尤其是对GPIO和定时器的讲解,通俗易懂、实操性强。虽然那套视频是基于标准外设库的,但里面的外设原理部分完全通用,配合现在的HAL库学习,效果很好。

最后再分享一个实用小技巧:做液位监测的时候,别只盯着传感器数据本身。你去观察水箱的水位变化曲线,其实能判断很多系统状态——水位掉了又涨是有人在用水,水位直线下降是漏水了,水位长时间不动可能是传感器卡住了。学会了看数据、判断状态,你的代码才真正转化为解决问题的能力。

返回列表