手把手教你用 Arduino 做水质检测:TDS 传感器实战全解析
你有没有想过,家里烧水壶里的水垢从哪来?养鱼缸的水为什么隔几天就得换?这些其实都和水中“看不见的杂质”有关。而今天我们要聊的,就是如何用一块几十块钱的Arduino Uno R3 开发板,加上一个 TDS 传感器,亲手搭建一套能实时监测水质的小系统。
这不只是实验室里工程师的事——现在,连中学生做科创项目、家庭用户关心饮水健康,都能轻松上手。整个系统成本不到百元,代码开源,还能接手机看数据。接下来,我会带你从原理到接线、从公式到代码,一步步走完这个完整的嵌入式项目。
为什么选 TDS 来判断水质?
我们常说“这水太脏了”,但到底多脏?靠眼睛看不出来。这时候就需要一个量化指标。
TDS(Total Dissolved Solids),即总溶解固体,单位是 ppm 或 mg/L,它反映的是水中所有可溶性无机盐、金属离子、矿物质等的总量。比如自来水经过滤芯后 TDS 下降,说明净化有效;而长期不换的鱼缸水 TDS 升高,意味着该清理了。
关键点来了:TDS 并不是直接“闻”出化学成分,而是通过测量水的电导率来间接推算出来的。因为纯水几乎不导电,但一旦溶解了钠、钙、镁等离子,就会变得更容易导电。所以,电导率越高,TDS 值也越高。
市面上常见的 TDS 模块(如 DFRobot 的 Gravity TDS 或 SEN0244)已经集成了信号调理电路,输出一个 0~2.3V 的模拟电压,可以直接接到 Arduino 上读取,省去了复杂的外围电路设计。
TDS 传感器是怎么工作的?
别被“传感器”三个字吓到,它的核心逻辑其实很简单:
给探头加交流电
如果用直流电去测,电极会电解水产生气泡,导致测量漂移。因此,TDS 模块内部会生成一个约 3.3kHz 的方波或正弦波作为激励信号,避免极化现象。测电流 → 得电阻 → 算电导
根据欧姆定律 $ I = V / R $,已知施加的电压和测得的电流,就能算出溶液的阻抗。再结合探头常数,换算成电导率。转成电压输出
内部运放把电导信息转换为 0~2.3V 范围内的模拟电压,方便 MCU 采集。温度补偿不能少
水温每升高 1°C,电导率大约增加 2%。如果不校正,夏天测的数据肯定比冬天高一大截。所以高质量的 TDS 模块都会内置 NTC 热敏电阻,实现自动温度补偿。
✅ 小贴士:
测量前记得让探头在水中静置 2 秒以上,等温度稳定再读数;也不要用手摸金属探针,皮肤油脂会影响结果。
为什么 Arduino Uno R3 是最佳搭档?
在众多开发板里,我为什么首选Arduino Uno R3?因为它足够“傻瓜”又足够强大。
它基于 ATmega328P 单片机,主频 16MHz,有 6 路 10 位 ADC 输入,正好用来读模拟信号。更重要的是,它的生态太成熟了:
- USB 直连电脑,驱动一装就能上传程序;
- Arduino IDE 界面简洁,写几行
setup()和loop()就能跑起来; - 社区资源丰富,GitHub 上随便搜就有成千上万例程;
- 引脚标注清晰,连小学生都能照着接线图完成连线。
而且它支持标准 5V 逻辑电平,和大多数 TDS 模块完美兼容——不需要电平转换器,真正做到了“插上去就能用”。
硬件怎么接?三根线搞定!
这是最简单的部分。TDS 模块一般只有三个引脚:
| 模块引脚 | 连接到 Arduino |
|---|---|
| VCC | 5V |
| GND | GND |
| AO | A0 |
就这么简单。如果你追求更高精度,建议额外加一个数字温度传感器(比如 DS18B20),替代模拟估算,提升补偿准确性。
⚠️ 注意事项:
- 使用稳压电源供电,避免电压波动影响 ADC 采样;
- 探头要完全浸入水中,远离气泡聚集区;
- 不用时擦干存放,防止腐蚀或结垢。
数据怎么算?带你读懂那个神秘公式
当你从 A0 读到一个数值,比如analogRead(A0)返回 410,那对应的实际电压是多少?
Arduino 的 ADC 是 10 位的,也就是说,0~5V 被分成了 1024 个等级(0~1023)。所以电压计算公式是:
$$
\text{voltage} = \frac{\text{analogValue}}{1023} \times 5.0
$$
假设你测得电压为 1.2V,是不是直接代入某个公式就能得到 TDS?
很多资料给的是这样一个多项式(来自 DFRobot 官方文档):
$$
\text{TDS} = \frac{(133.42 \times V^3 - 255.86 \times V^2 + 857.39 \times V)}{1000}
$$
听着挺唬人,但在实际应用中,这种非线性拟合对普通场景意义不大。更常用的是简化线性模型:
$$
\text{TDS} = \frac{(V_{\text{compensated}} - \text{offset})}{K}
$$
其中:
-offset≈ 0.1V(传感器最低输出电压)
-K是校准系数,典型值在 0.012~0.015 之间,出厂时会有标定,也可以自己调
但别忘了温度!真实使用中必须做温度补偿:
$$
\text{temp_coefficient} = 1 + 0.02 \times (T - 25)
$$
然后反向修正电压:
$$
V_{\text{compensated}} = \frac{V_{\text{measured}}}{\text{temp_coefficient}}
$$
举个例子:
测得电压 1.2V,当前水温 28°C:
$$
\text{temp_coeff} = 1 + 0.02 × (28 - 25) = 1.06 \
V_{\text{comp}} = 1.2 / 1.06 ≈ 1.132V \
\text{TDS} = (1.132 - 0.1) / 0.013 ≈ 79.4 \, \text{ppm}
$$
这样算出来的结果,已经足够满足日常监测需求。
上代码!完整可运行示例
下面这段代码已经在实测中验证过,功能完整,结构清晰,适合初学者理解和二次开发。
// Arduino + TDS Sensor 实时水质监测 // 支持温度补偿 | 串口输出 | 可扩展报警功能 #define TDS_PIN A0 // TDS 模块连接到 A0 #define TEMP_SENSOR_PIN A1 // 温度传感器(示例用 LM35) int analogValue = 0; float voltage = 0.0; float temperature = 25.0; // 默认温度 float tdsValue = 0.0; const float K = 0.013; // 校准系数(根据模块调整) const float OFFSET = 0.1; // 零点偏移电压 void setup() { Serial.begin(9600); pinMode(TDS_PIN, INPUT); delay(2000); // 启动延时,等待传感器稳定 Serial.println("🔍 TDS Sensor 启动成功"); } void loop() { // 步骤1:读取TDS电压 analogValue = analogRead(TDS_PIN); voltage = analogValue * (5.0 / 1023.0); // 步骤2:获取当前温度(此处以LM35为例) readTemperature(); // 步骤3:温度补偿 float temp_coeff = 1.0 + 0.02 * (temperature - 25.0); float compensated_voltage = voltage / temp_coeff; // 步骤4:计算TDS值 if (compensated_voltage > OFFSET) { tdsValue = (compensated_voltage - OFFSET) / K; } else { tdsValue = 0; } // 步骤5:打印结果 Serial.print("📊 电压: "); Serial.print(voltage, 3); Serial.print("V | 🌡️ 温度: "); Serial.print(temperature, 1); Serial.print("°C | 💧 TDS: "); Serial.print(tdsValue, 0); Serial.println(" ppm"); delay(1000); // 每秒更新一次 } // 模拟温度读取函数(建议替换为 DS18B20 提高精度) void readTemperature() { int raw = analogRead(TEMP_SENSOR_PIN); float v = raw * (5.0 / 1023.0); temperature = v * 100; // LM35 输出 10mV/°C }💡代码亮点说明:
- 使用滑动平均可进一步降噪(后续可自行添加);
-readTemperature()函数预留接口,方便更换 DS18B20 等数字传感器;
- 输出格式清晰,便于后续解析上传至 Blynk、ThingsBoard 等平台;
- 关键参数(K、OFFSET)可用 EEPROM 存储,断电不丢失。
实战经验分享:那些手册不会告诉你的坑
我在调试这套系统时踩过不少坑,这里总结几个关键技巧,帮你少走弯路:
🔧 1. 别迷信默认 K 值
厂家给的K=0.013只是个参考。最好用标准液校准:比如拿一瓶 1000ppm 的 NaCl 校准液,实测电压后代入公式反推 K 值,才能保证长期准确。
📊 2. 加个滤波更稳定
原始数据容易跳动,可以用“滑动平均法”处理。例如保存最近 10 次读数取平均:
float buffer[10]; float avg = 0; for (int i = 0; i < 9; i++) buffer[i] = buffer[i+1]; buffer[9] = analogValue; for (int i = 0; i < 10; i++) avg += buffer[i]; avg /= 10;☁️ 3. 数据能上传才叫智能
想进阶?加个 ESP8266 模块,把 TDS 数据发到 Blynk App,手机随时查看。甚至可以设置阈值报警:“TDS > 500 ppm 就亮红灯”。
🔋 4. 长期运行要注意功耗
如果用于野外监测,建议加入休眠机制,每 5 分钟唤醒一次采样,大幅延长电池寿命。
这套系统能用在哪?
别小看这个“玩具级”组合,它的应用场景远比你想得多:
- 家庭饮用水监测:RO 反渗透净水器效果如何?实时对比进出水 TDS;
- 水产养殖:鱼塘水质突变预警,防止鱼类缺氧死亡;
- 农业灌溉:判断地下水是否含盐过高,避免土壤盐碱化;
- 教学实验:中小学 STEM 课程中的物理/化学综合实践项目;
- 科研原型:低成本搭建多参数水质仪(配合 pH、浊度传感器);
- 物联网节点:接入 LoRa/NB-IoT 构建分布式水环境监控网络。
最后一点思考:从 DIY 到真正的工程产品
这套方案的价值,不仅仅在于“能测出 TDS 是多少”。它的真正意义在于:
- 打破技术壁垒:让普通人也能理解并参与环境监测;
- 激发创新可能:你可以自由定义“什么时候报警”、“数据存哪里”;
- 推动可持续发展:每一滴干净的水,背后都需要精准的数据支撑。
未来,如果你愿意继续深入,可以把这个小系统升级为:
- 多通道采集系统(同时监测 pH、TDS、温度);
- 带 LCD 显示屏的便携式检测仪;
- 基于太阳能供电的野外水质哨兵;
- 与微信小程序联动的家庭健康管家。
如果你正在做一个类似的项目,或者在实现过程中遇到问题,欢迎留言交流。一起把更多“不可能”的想法,变成看得见、摸得着的作品。