Arduino TDS 水质检测仪 DIY:测自来水、滤水器和鱼缸

Kartun probe TDS putih dengan hujung pin perak dicelup ke dalam akuarium kaca berisi ikan emas oren, dengan riak teal di permukaan air

家里最有用的水质检测仪,是一支能从水龙头一路提到鱼缸边的。把 TDS 探头经它的测量板接上 Arduino Uno,把 ppm 读数显示在 0.96 寸 OLED 上,自来水、滤水器出水和鱼缸水,各花几秒钟就能比出高下。有一处小小的焊接是绕不开的 —— 我们会指给你看在哪里、为什么。

TDS 传感器测的到底是什么?

TDS(Total Dissolved Solids,总溶解固体)传感器不是一粒一粒地数杂质 —— 它测的是电流穿过水有多容易。溶解的盐分和矿物质在水里分解成离子,正是这些离子在 TDS 探头的两根电极之间搬运电流:离子越多,电导率越高,测量板再把电导率换算成以 ppm(毫克每升)计的溶解固体估算值。纯水几乎完全不导电。

也要明白它看不见什么。细菌、氯和大多数有机物几乎不改变电导率,所以读数低不是水可以放心喝的证明,读数高也不代表有毒 —— 矿泉水本来就特意带着几百 ppm 出售,RO 水则因为膜把离子挡在外面而读数接近零。这就是为什么单独一个数字意义有限,两个读数之间的差才是重点:自来水对比滤水器出水,用数字而不是猜测回答“我的滤水器还管用吗?”这个问题。

你看到的读数 它的意思 它不是这个意思
RO 出水接近 0–50 ppm RO 膜正把几乎所有溶解矿物质挡在外面 —— 滤水器在正常工作 不保证无菌;细菌在这块表上是隐形的
活性炭滤水器出水 ≈ 自来水 正常 —— 活性炭对付的是氯、味道和气味,不是溶解固体 不是你的活性炭滤芯坏了的证据
矿泉水几百 ppm 矿物质本来就是卖点 —— 特意溶进去的 不代表那瓶水脏或有害
鱼缸 TDS 一周比一周高 固体在累积:残饵、排泄物,加上水蒸发时把矿物质留了下来 不能代替氨氮或 pH 测试 —— 它是该换水的信号

我们其他的水类教程测的是水位 —— 比如 JSN-SR04T 水塔监控器 —— 或者像自动浇花系统那样把水抽走。这是第一个给水本身标上数字的项目。

Parts list

ItemPriceQty
Soldering Service for Circuit Boards & Electronics - 5 Pins Soldering ServiceSoldering Service for Circuit Boards & Electronics - 5 Pins Soldering ServiceSOLSE05RM0.80

TDS 板到手时 A0/T1/−/+ 四个焊盘是空的,盒子里也没有排针。加购这项 5 针焊接服务,我们帮你把 4 针排针焊到这些焊盘上 —— 请先在 WhatsApp 上告诉我们,让我们知道是给这块 TDS 板用的。

盒子里有什么 —— 哪一步焊接是必须的?

我们的 TDS Water Quality Sensor 模块到手是两个部分:印着 TDS BOARD V1.0 的蓝色测量板,和一支尾端带两根针状电极的白色圆柱探头,探头的黑色防水线以白色 JST-XH 插头收尾,插进板子左边缘的 2 针插座。它下方的 3 针 JST-XH 是输出插座 —— 但配套的线没有附送,所以我们走的是板子右边缘标着 A0、T1、− 和 + 的四个焊盘,每一个在丝印上都标得清清楚楚。

这四个焊盘出厂就是没有排针的空孔,盒子里也没有排针条 —— 所以焊接排在最前面。从 40 针公排针条上掰下 4 针一段,插进 A0/T1/−/+ 焊盘,从背面焊好;手法我们的排针焊接教程一步一步讲全了。不想碰烙铁?上面材料清单里那行可选项目替你搞定。

卡通特写:蓝色 TDS 板上四个空焊盘孔,镊子在上方夹着一段 4 针公排针,烙铁从右边靠近
这个项目唯一的焊接活:从公排针条掰下的 4 针一段,装进出厂时空着的 A0/T1/−/+ 焊盘。

TDS 板为什么要用 AC「摇晃」探头?

板上的 CD4060BM 芯片是一个振荡器 —— 它用交变电压(AC)而不是 DC 驱动探头,而这正是这支探头不能用两根线直接接模拟引脚来读的原因。DC 电流流过水时会把离子拉向电极,在电极表面堆出一层带电的膜 —— 极化 —— 这层膜会越来越掐住电流,读数就一路往下漂个不停。AC 激励换向太快,这层膜根本来不及成形。旁边的 LMV324A 芯片再把探头信号转换回平滑的 DC 电压,让 A0 引脚能够读取。

它的工作参数:这块 TDS 板在 3.3–5.5 V 供电下运行,消耗 3–6 mA,输出 0–2.3 V 模拟电压,测量范围 0–1000 ppm,25 °C 下精度为满量程的 ±10%。电压由厂商的多项式换算成 ppm —— (133.42V³ − 255.86V² + 857.39V) × 0.5 —— 公式里的 V 应该是做过温度补偿的电压,因为离子在更暖的水里跑得更欢 —— 水的电导率每升高一摄氏度上升约 2%。T1 焊盘是温度探头的输入;我们让它空着,在代码里固定 25 °C,所以更冷的水会读得略低、更暖的水读得略高。对于比较类的用法 —— 自来水对滤水器、这周对上周 —— 只要在相近的温度下比较,这个局限几乎察觉不到。

TDS 板和 OLED 怎么接到 Uno?

刚焊好的那 4 针排针,加上 0.96 寸 OLED 出厂就装好的公排针,让整个接线变成纯粹的公对母杜邦线活 —— 连面包板都不用:母头插模块引脚,公头插 Uno 的母排座。如果你手上只有公对公杜邦线,一块面包板就能补上这个公母缺口。

模块引脚 Uno 引脚 原因
TDS + 5V 板子供电 —— 范围 3.3–5.5 V;5 V 正好对上 sketch 里的 VREF
TDS − GND POWER 排座上两个 GND 孔任选其一
TDS A0 A0 sketch 读取的 0–2.3 V 模拟输出
TDS T1 温度探头输入 —— 留空;补偿在代码里按 25 °C 处理
OLED GND GND 这块模块的排针 GND 在前:GND VCC SCL SDA
OLED VCC 3.3V 这块 SSD1306 模块在 3.3–5 V 都跑得很顺
OLED SCL A5 Uno 的 I2C 时钟(或 R3 数字排座末端的 SCL 孔)
OLED SDA A4 Uno 的 I2C 数据(或 SDA 孔)

留意 OLED 的排针 GND 在前 —— GND VCC SCL SDA,和大多数模块 VCC 在前的习惯正好相反,照习惯接线正是烧掉它的经典方式。还有一条贯穿整个项目的安全规则:这里全是低压 DC,但水和电子元件终究不是朋友 —— 每次要在这套装置附近移水、加水或倒水,先把 USB 线拔掉。

这个 TDS 检测仪的 sketch 长什么样?

在 Library Manager 安装 Adafruit SSD1306,让 IDE 顺手把 Adafruit GFX 一起拉进来。代码里有一个值得弄懂的决定:原始读数用中位数过滤,而不是平均值。AC 激励和晃动的水面会在 ADC 读数里制造偶发的尖峰 —— 平均值会让一个尖峰拖歪整个读数,中位数则直接把它扔掉。

// 便携 TDS 检测仪:TDS Board V1.0 接 A0,SSD1306 OLED 挂 I2C 总线。
// ppm 读数大约每秒在屏幕上刷新一次。
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 oled(128, 64, &Wire, -1);

const uint8_t PIN_TDS = A0;     // TDS 板的 A0 焊盘 -> Uno 的 A0 引脚
const float   VREF    = 5.0;    // 板子由 Uno 的 5V 引脚供电
const uint8_t N       = 30;     // 中位数缓冲区大小

int   sampel[N];                // 原始 ADC 读数
uint8_t idx = 0;

// 水温假定固定为 25 摄氏度 —— T1 焊盘没有接线,
// 所以温度补偿在代码里按这个固定值进行。
const float SUHU_AIR = 25.0;

// 中位数滤掉板子 AC 激励带来的偶发尖峰,
// 比普通平均值好得多。
float median(int *d, uint8_t n) {
  int t[N];
  for (uint8_t i = 0; i < n; i++) t[i] = d[i];
  for (uint8_t i = 0; i < n - 1; i++)          // 简单插入排序
    for (uint8_t j = i + 1; j < n; j++)
      if (t[j] < t[i]) { int s = t[i]; t[i] = t[j]; t[j] = s; }
  if (n & 1) return t[n / 2];
  return (t[n / 2 - 1] + t[n / 2]) / 2.0;
}

void setup() {
  Serial.begin(9600);
  if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // 这块模块应答 0x3C
    Serial.println("SSD1306 not found at 0x3C - check wiring");
    while (true) delay(100);
  }
  oled.setTextColor(SSD1306_WHITE);
  for (uint8_t i = 0; i < N; i++) sampel[i] = analogRead(PIN_TDS);
}

void loop() {
  static unsigned long tSampel = 0, tPapar = 0;

  // 每 40 ms 取一个读数,放进环形缓冲区
  if (millis() - tSampel >= 40) {
    tSampel = millis();
    sampel[idx] = analogRead(PIN_TDS);
    idx = (idx + 1) % N;
  }

  // 每秒计算并显示一次
  if (millis() - tPapar >= 1000) {
    tPapar = millis();

    float v = median(sampel, N) * VREF / 1024.0;   // ADC -> 伏特

    // 温度补偿:水的电导率每摄氏度上升约 2%。
    // SUHU_AIR = 25.0 时这个系数 = 1.0(即不做修正)。
    float pekali = 1.0 + 0.02 * (SUHU_AIR - 25.0);
    float vKompensasi = v / pekali;

    // 板子厂商给的电压 -> ppm 转换多项式
    float ppm = (133.42 * vKompensasi * vKompensasi * vKompensasi
               - 255.86 * vKompensasi * vKompensasi
               + 857.39 * vKompensasi) * 0.5;
    if (ppm < 0) ppm = 0;

    Serial.print("V=");
    Serial.print(v, 3);
    Serial.print("  TDS=");
    Serial.print(ppm, 0);
    Serial.println(" ppm");

    oled.clearDisplay();
    oled.setTextSize(1);
    oled.setCursor(0, 0);
    oled.print("TDS");
    oled.setCursor(98, 0);
    oled.print(v, 2);
    oled.print("V");
    oled.setTextSize(3);                 // 大号数字,隔远也看得清
    oled.setCursor(10, 22);
    oled.print(ppm, 0);
    oled.setTextSize(1);
    oled.setCursor(104, 36);
    oled.print("ppm");
    oled.display();
  }
}

编译出来是 16,746 字节 —— Uno sketch 空间的一半。SUHU_AIR 那一行故意写成变量而不是埋进公式的数字:25 °C 时系数正好是 1.0,日后谁想在 T1 焊盘加温度探头,只要把这一个值换成真实读数就行。

ppm 读数在家里怎么用?

TDS 检测仪最有力的用法是比较,所以先立一条基准线:把探头尖端浸进一杯自来水,等数字稳住,记下来。然后第二杯接滤水器出水 —— 橱下 RO 应该比自来水断崖式下跌,屋外的活性炭滤水器则读得几乎一样,两种结果都正常,就像上面那张解读表说的。至于鱼缸,每周记一次的读数就是一条靠得住的趋势线:水蒸发时把矿物质留下,残饵不断累积,一条爬升的线就是该换掉部分水的早期信号。

浸测手法有三条简单规则。只浸探头尖端 —— 水位保持在带凹槽的护圈以下,线缆接头绝不入水。拿稳探头别晃,避开滤水器的气泡和水泵的水流,因为流动的水会摇乱读数。每测完一个样本,用清水冲洗探头并擦干,免得上一杯的盐分残留抬高下一杯的读数。

卡通图:两杯水中间夹着奶油色滤水器,青色箭头从带杂点的一杯流向干净的一杯,白色 TDS 探头浸在第二杯里
前后对比测试:同一路水在水龙头和滤水器出口各测一次 —— 答案在两个读数的差里,不在任何单独一个数字上。

我们常见的错误

探头不经板子直接接模拟引脚。两根线插进水里确实会出读数 —— 一个在 DC 极化掐住电极表面电流时不停往下漂的读数。让读数稳定的正是板子的 AC 激励;没有板子的探头不是检测仪。

OLED 按习惯当成 VCC 在前来接。这块屏的排针是 GND VCC SCL SDA —— 地在最前。每次上电前先把丝印读一遍,因为供电接反可以直接送走这块模块。

整支探头连护圈一起沉进水里。需要入水的只有带电极的尖端。护圈和线缆接头始终留在水面以上 —— 它的防水设计是为浸测准备的,不是为整支泡水。

冰箱里的冷水拿来和室温自来水比。电导率每摄氏度上升约 2%,而代码把温度固定在 25 °C,跨温度比较会给出误导性的结论。先让冷的那杯回到室温再测。

把读数当成水质的最终判决。这块表是精度为满量程 ±10% 的业余估算工具 —— 意思是这个百分比按 1000 ppm 的量程顶端计算,不是按当前读数,所以任何读数的偏差都可能达到 ±100 ppm。这个偏差在同一支探头上大多是系统性的,这正是趋势和比较依然可靠的原因 —— 但它不是饮用水认证仪器。

常见问题

TDS 读数低是不是代表水可以放心喝?

不是。TDS 只通过电导率估算溶解固体 —— 细菌、病毒和氯几乎不改变它,所以受污染的水可以读得很低,优质矿泉水反而读得很高。用读数来监测滤水器和鱼缸趋势,不要当成安全判决。

为什么我的 TDS 读数不稳定?

常见原因:探头在晃、气泡粘在电极上,或者样本正好取在水泵或滤水器的水流里。把探头拿稳、放进静止的水里等几秒 —— sketch 里的中位数能滤掉偶发尖峰,但救不了一直翻腾的水。

这块表能用在海水缸吗?

不合适。海水在 35,000 ppm 上下,远远超出这块板 0–1000 ppm 的测量范围,读数会饱和、失去意义。淡水鱼缸、鱼池和家用滤水器则正好落在这个量程的正中央。

这个 TDS 传感器需要校准吗?

比较类的用法 —— 自来水对滤水器、这周对上周 —— 不需要:sketch 里厂商的多项式已经够用,而且满量程 ±10% 的精度对比较的两边一视同仁。只有当你需要能和其他仪表对得上的绝对数字时,用标准液校准才值得。

能让探头一直泡在鱼缸里吗?

不要。这支探头是为短暂浸测设计的:测完、拿出来、冲洗、擦干。长期浸泡会加速电极腐蚀,让读数随时间漂移 —— 而每周一次的读数就已经足够画出有用的鱼缸趋势了。

卡通完工图:蓝色 Arduino Uno 用线接着 TDS 板和显示数字的 OLED,白色探头浸在一杯水里
完工的检测仪:Uno 给两块模块供电,TDS 板把电压送进 A0,OLED 把 ppm 显示得隔远也看得清。

当水质变成你亲手记下的数字,它就不再是猜谜。焊好那四根针,浸下探头尖端,下次再有人问家里的滤水器还管不管用,你的答案是两个读数 —— 一个滤前,一个滤后。

最后更新:2026年8月。有问题?WhatsApp 联系我们

Leave a Reply

Your email address will not be published. Required fields are marked *