2026/10/30 7:29:26

PH、浊度、TDS三合一水质检测传感器代码实战与标定指南

PH、浊度、TDS三合一水质检测传感器代码实战与标定指南 简介面向物联网与环境监测方向的单片机开发者尤其是使用51系列芯片做水质检测入门的初学者这套代码资源围绕pH值、TDS总溶解固体与浊度三项关键指标提供传感器驱动与数据采集的完整实现思路。压缩包共113个文件、约4.1MB以C源码、汇编启动文件、Keil工程配置uvproj、uvopt、m51、HEX烧录文件及PDF传感器说明书为主另含lst列表、obj目标文件与bak备份便于直接编译验证与对照硬件接口。资源已有8553人学习下载说明其在同类项目中具备较高参考价值。读者可从中获得pH电极信号AD转换与温度补偿、电导率换算TDS、光学散射法测浊度的代码框架以及传感器接线、校准与滤波处理的实践参考适合快速搭建水质检测原型并理解各模块的数据处理流程。1. 水质检测三件套PH、浊度、TDS 传感器代码到底能跑出什么去年帮一个做水产养殖的朋友看塘口监测他买回来三个探头——PH、浊度和 TDS卖家只丢了一份引脚定义代码全靠自己写。这类场景其实很典型传感器硬件便宜、资料零散真正卡住人的不是接线而是三路模拟量怎么采样、怎么换算成有意义的物理量、怎么在同一个循环里不互相干扰。这份代码资源就是围绕这三个探头展开的把 PH 的电压-酸碱度映射、浊度的电压-浑浊度换算、TDS 的电导率补偿都落到了可运行的函数里。适合谁做毕业设计的学生、搞农业物联网原型的开发者、想给鱼缸或净水设备加监测的硬件爱好者。它不追求工业级精度但把「能读、能算、能打印」这条链路走通了拿过来改改就能接进自己的采集系统。2. 三路传感器代码结构拆解从引脚定义到物理量输出2.1 目录与文件组织这类传感器代码通常不会做成庞大的工程常见做法是一个主程序文件加若干传感器驱动文件或者干脆全部塞进一个.ino/.c文件里。我拿到的版本是 Arduino 风格的单文件结构核心就三块全局的引脚与标定常量、三个独立的读取函数、一个setuploop的调度骨架。文件顶部先定义引脚// 传感器引脚定义按实际接线修改 #define PH_PIN A0 // PH 模拟输出 #define TURB_PIN A1 // 浊度模拟输出 #define TDS_PIN A2 // TDS 模拟输出 // 标定参数不同探头出厂值不同必须实测修正 #define PH_OFFSET 0.0 // PH 零点偏移 #define PH_SLOPE 3.5 // 每单位 PH 对应的电压斜率 #define TURB_CLEAR 4.2 // 清水中浊度传感器电压 #define TDS_K 0.5 // TDS 换算系数引脚用宏定义而不是硬编码在函数里好处是换板子时只改这一处。标定参数单独拎出来是因为这三个探头几乎不存在「开箱即准」的情况PH 探头尤其明显同一批货零点能差出 0.3 个 PH。把标定值集中放在文件头改起来不用满文件找魔数。2.2 PH 读取电压到酸碱度的线性映射PH 探头的模拟输出和酸碱度是近似线性关系代码里做的就是一次线性变换加中值滤波。直接读一次模拟量会有抖动所以常见做法是连续采多次取中位数float readPH() { int samples[10]; // 连续采样 10 次排序后取中位数滤掉偶发尖峰 for (int i 0; i 10; i) { samples[i] analogRead(PH_PIN); delay(10); } // 简单冒泡排序 for (int i 0; i 9; i) for (int j i 1; j 10; j) if (samples[i] samples[j]) { int t samples[i]; samples[i] samples[j]; samples[j] t; } int median samples[5]; // 12 位 ADC、5V 参考换算成电压 float voltage median * 5.0 / 1023.0; // 线性映射到 PH 值再叠加零点偏移 float ph PH_SLOPE * voltage PH_OFFSET; return ph; }analogRead返回 0~1023 的整数5.0 / 1023.0是 10 位 ADC 在 5V 参考下的分辨率。PH_SLOPE和PH_OFFSET需要拿标准缓冲液实测把探头放进 PH 4.00 和 PH 6.86 的缓冲液里各读一次电压两点确定一条直线斜率就是(6.86-4.00)/(V2-V1)偏移量反推。代码里给的 3.5 和 0.0 只是占位直接烧进去读数一定偏。中位数滤波比平均值更抗单点干扰探头附近有电机启停时尤其明显。2.3 浊度读取电压反比关系的处理浊度传感器的特性和 PH 相反——水越浑输出电压越低所以换算时要做反向处理。代码里用清水电压作为基准按比例折算float readTurbidity() { int raw analogRead(TURB_PIN); float voltage raw * 5.0 / 1023.0; // 电压低于清水基准时按比例换算浊度单位 NTU float ntu; if (voltage TURB_CLEAR) { ntu 0.0; // 比清水还清视为 0 } else { // 经验公式系数需按探头手册或实测修正 ntu (TURB_CLEAR - voltage) * 100.0; } return ntu; }TURB_CLEAR是探头在纯净水里的输出电压实测时把探头洗净擦干、放进静置过的清水里读一次即可。(TURB_CLEAR - voltage) * 100.0里的 100 是经验系数不同型号差异很大有的探头手册会给标准曲线那就按曲线拟合别硬套这个线性式。浊度这块最容易翻车的地方是气泡探头表面附着气泡时电压会突然掉一截读数虚高所以采样前最好让探头在水里静置几秒或者加个简单的连续采样剔除突变值。2.4 TDS 读取温度补偿不能省TDS 本质是测电导率再换算而电导率对温度敏感每升高 1℃ 大约变化 2%。代码里如果只做电压换算不做温度补偿夏天和冬天读数能差出一大截float readTDS(float temperature) { int raw analogRead(TDS_PIN); float voltage raw * 5.0 / 1023.0; // 先按 25℃ 基准做温度补偿 float compVoltage voltage / (1.0 0.02 * (temperature - 25.0)); // 电导率换算再乘系数得到 TDSppm float ec (compVoltage - 0.0) * TDS_K; float tds ec * 500.0; return tds; }temperature由外部传入可以是 DS18B20 这类温度传感器读到的值没有温度探头时传 25.0 就退化成不补偿。0.02是电导率的温度系数常见取值 0.02/℃。TDS_K是电导率到 TDS 的转换系数一般取 0.5 左右但不同水质河水、自来水、纯净水的离子组成不同这个系数会有偏差做对比监测时固定一个值就行做绝对值测量就得用标准液标定。ec * 500.0是把电导率单位换算成 ppm 的常用写法具体量纲要看探头手册。2.5 主循环调度三路采样怎么不打架三个探头共用 ADC如果在一个循环里连续读前一路的采样保持电容还没放完电就读下一路会出现串扰。代码里的处理是每路之间留间隔并且把结果统一打印void loop() { float ph readPH(); delay(50); // 给 ADC 切换留出稳定时间 float ntu readTurbidity(); delay(50); float tds readTDS(25.0); // 无温度探头时固定 25℃ Serial.print(PH: ); Serial.print(ph, 2); Serial.print( NTU: ); Serial.print(ntu, 1); Serial.print( TDS: ); Serial.println(tds, 0); delay(1000); // 采样周期 1 秒 }delay(50)看着不起眼但省掉它之后读数会飘这是血泪经验。Serial.print的第二参数是小数位数PH 保留两位、浊度一位、TDS 取整够看又不刷屏。采样周期 1 秒对水质监测足够水质变化本来就慢没必要高频采。如果后面要接显示屏或上传云端把打印换成对应的输出函数即可三个读取函数不用动。3. 把代码跑起来接线、烧录与标定流程3.1 硬件接线与供电注意三个探头都是模拟输出接线逻辑一致VCC 接 5V部分浊度探头要 3.3~5V看手册、GND 共地、信号线接对应的模拟引脚。PH 探头模块通常带一个运放板输出阻抗低直接接 ADC 没问题浊度探头有的型号输出是脉冲式那就不能直接analogRead得用中断计数这点买之前一定要确认输出类型。TDS 探头同理模拟输出的才能用这份代码。供电上有个坑三个探头同时工作电流不大但如果和电机、继电器共用一路 5VADC 读数会跟着抖。常见做法是给传感器单独一路 LDO或者至少加个 100μF 电容在电源入口。接线尽量短模拟线远离电源线这些是老生常谈但真出事的时候都是这些细节。3.2 烧录与串口验证Arduino 环境下把代码贴进 IDE选对板子和端口点上传。烧录成功后打开串口监视器波特率设成 9600和Serial.begin(9600)对应。正常应该看到每秒一行读数。如果全是 0 或者全是 1023先查接线和供电再看引脚宏定义有没有写错——A0写成0在某些板子上是数字引脚读出来永远是 0。验证顺序建议是先只接 PH 探头确认读数随探头在空气和水中变化再接浊度看清水和浑水的差值最后接 TDS。一次接一个出问题好定位。三个一起接上再调试读数不对时根本分不清是哪一路的锅。3.3 标定决定读数准不准的关键一步标定是这份代码能不能用的分水岭。PH 用两点标定准备 PH 4.00 和 PH 6.86 的缓冲液网上能买到粉剂自己配探头洗净后分别泡进去等读数稳定一般 30 秒到 1 分钟记下两次的电压值代入PH_SLOPE (6.86-4.00)/(V2-V1)、PH_OFFSET 4.00 - PH_SLOPE*V1把算出来的值填回宏定义重新烧录。浊度用清水基准探头放纯净水里读电压填进TURB_CLEAR。TDS 如果有标准液比如 707ppm 的校准液泡进去读一次反推TDS_K。标定完别急着收工拿自来水测一下做交叉验证。自来水 PH 一般在 7 左右、TDS 在 100~300ppm 之间读数落在这个区间说明标定方向对了。偏差大就回头查缓冲液有没有污染、探头有没有洗干净。4. 避坑与排查三个探头最容易翻车的地方4.1 读数一直不变或卡在极值现象串口打印的 PH 永远是 7.00 或者 0浊度永远是 0。原因多半是引脚定义和实际接线对不上或者探头没供电。先拿万用表量探头信号脚对地电压正常应该在 0~5V 之间随水质变化如果恒定 0V 或 5V查供电和线序。还有一种情况是analogRead的引脚号写成了数字口编号读的是数字电平自然只有 0 和 1023 两种结果。4.2 PH 读数漂移、越测越偏现象刚标定完是准的放水里半小时后读数慢慢往上爬。原因是 PH 探头需要浸泡活化新探头或久置的探头直接干放会失准用前要在 3M KCl 溶液里泡 24 小时。另外探头表面结垢也会导致漂移尤其测硬水时定期用软布蘸稀盐酸轻擦别用力刮玻璃球泡。代码层面可以在readPH里加个滑动平均但治标不治本探头维护才是根子。4.3 浊度读数忽高忽低现象同一杯水读数在 20 和 200 之间跳。先排除气泡探头入水后静置几秒再采。再查电源浊度探头对供电纹波敏感和继电器共用电源时继电器吸合瞬间读数会飙。解决方法是给探头单独供电或加大滤波电容代码里把中位数滤波的采样次数从 10 提到 20 也能压一部分抖动。如果探头是脉冲输出型那这份模拟读取代码根本不适用得换中断计数方案。4.4 TDS 夏天冬天差一大截现象同一桶水中午测和晚上测能差几十 ppm。这就是没做温度补偿的典型表现。确认readTDS的temperature参数传的是真实水温而不是固定 25.0有 DS18B20 就接上没有的话至少用个热敏电阻估个大概。补偿系数 0.02 是通用值如果测的是高纯度水系数要调小具体查探头手册。4.5 三路同时读互相干扰现象单独接一路时读数正常三路一起接就全飘。原因是 ADC 多路切换时采样保持电容残留上一路的电荷。代码里每路之间的delay(50)就是干这个的如果还不行把间隔加到 100ms或者在每次analogRead前先空读一次丢弃。硬件上给每个信号脚加个 0.1μF 对地电容也有帮助但别加太大会影响响应速度。5. 二次开发从串口打印到可用监测系统的改造路径这份代码的定位是「能读能算」离「能用」还差一层。我一般会按三步改先加数据落盘再接显示或上传最后做异常判断。第一步把loop里的Serial.print换成结构化输出。比如输出一行 CSV方便后面用表格或脚本分析// 输出 CSV 格式方便导入表格或上传 Serial.print(millis()); Serial.print(,); Serial.print(ph, 2); Serial.print(,); Serial.print(ntu, 1); Serial.print(,); Serial.println(tds, 0);时间戳用millis()接 RTC 模块后换成真实时间。CSV 的好处是不用改解析逻辑Excel 直接能开。第二步接显示或通信。本地显示用 OLED 或 LCD把三个值分行刷上去要上云就接 ESP8266/ESP32 的 WiFi把 CSV 行通过 MQTT 发出去。这里有个改造技巧把三个读取函数和输出逻辑解耦读取函数只管返回float输出单独写一个publish()函数换通信方式时只动publish传感器代码一行不改。第三步加阈值判断和告警。水质监测的价值在于「异常时告诉你」所以要在主循环里加判断// 阈值判断超限时置标志位 bool phAlarm (ph 6.5 || ph 8.5); bool turbAlarm (ntu 50.0); bool tdsAlarm (tds 1000.0); if (phAlarm || turbAlarm || tdsAlarm) { // 触发蜂鸣器或上报告警 }阈值按养殖品种或用途定养鱼和饮用的标准完全不同别照抄。告警要做迟滞比如 PH 超过 8.5 触发、回落到 8.3 才解除否则在阈值附近会反复报警。还有一个容易被忽略的改造点标定参数不该写死在代码里。做成运行时可通过串口命令修改或者存进 EEPROM换探头时不用重新烧录。我吃过这个亏——现场换了个 PH 探头手边没电脑只能干等。从那以后我每次做传感器项目标定值一律走 EEPROM 或配置命令烧录只负责逻辑。希望这份拆解帮到你把三个探头真正跑成一套能用的监测链路。本文还有配套的精品资源点击获取