TDS的全名是Total Dissolved Solids,指的是水中溶解的所有固體物質的總量。這些溶解固體包括無機鹽類(如鈉、鈣、鎂、鉀、碳酸氫鹽、硫酸鹽和氯化物)和有機物質。TDS 的測量可以用百萬分率(ppm)作為單位來描述水中的礦物質或污染物濃度,來評估水質的純淨度。1 ppm 表示每升水中含有 1 毫克的溶解固體物質,飲用水的 TDS 通常在 300 ppm 以下,而RO水的 TDS 值則通常低於 10 ppm。
我的看法是TDS可以拿來做魚缸水質的監控,不同的生物有不同的需求,並不是愈低愈好。但它可以是一個飼主行為的參考,比如說TDS突然從穩定的區間迅速攀升,那也許飼主可以進行換水的動作。
TDS有筆型的檢測器可以使用,但既然ESP32都玩上癮了,沒有道理不把它做成即時監控。需要的材料以下,成本大約在500元內
- ESP32開發板一片(我買的是DOIT ESP32 Devkit V1開發板)
- MicroUSB線一條
- Keyestudio TDS感應器總成一組(含探棒與訊號處理電路板)
Keyestudio TDS感應器輸出腳位有三個
- 紅色是VCC,接ESP32的3.3V腳位。
- 黃色是DATA,接ESP32 ADC1所屬的GPIO較佳(GPIO32~GPIO39)。
- 黑色是GND,接ESP32的GND腳位。
ESP32上的GPIO總共有二組ADC,其中ADC2與Wi-Fi模組共用,當Wi-Fi啟用時會有訊號的干擾。一開始我將TDS的DATA接到ADC2的GPIO腳位,發現不管怎樣TDS都是0ppm,不斷修改程式做debug後才發現跟Wi-Fi模組有關,後來換去ADC1的GPIO腳位就沒事了。
我手上的ESP32,從賣家提供的圖片上來看,ADC1 GPIO引腳有32~36/39總共6個,其中GPIO36也稱為VP,GPIO39也稱為VN。這些GPIO在Wi-Fi模組啟用時不會與Wi-Fi模組衝突。ESP32的ADC2 GPIO引腳有2/4/12~15/25~27總共9個,如果要啟用Wi-Fi模組的話就盡量避開。
TDS探頭的保護套記得拆掉,露出探針後即可丟入水中。
Keyestudio原廠文件裡有給Arduino開發板的程式碼,但我用的是ESP32,GPIO定義、輸入電壓與ADC分辨率不同,需要修改一下。
在void loop()中主要分二個部分,TDS取樣以每40毫秒取樣一次並將值記錄在analogBuffer[]裡的第analogBufferIndex,當analogBufferIndex等於取樣數30後會重置。這個意思是analogBuffer[]會記錄最新30筆的TDS讀值。在TDS計算的部分,每800毫秒計算一次,計算時會取當下analogBuffer[]裡的30個值做平均。這樣做是為了避免單次取樣異常。
#define TDS_SENSOR_PIN 36 // 定義GPIO腳位為GPIO36
#define VREF 3.3 // ESP32輸入電壓為3.3V
#define SCOUNT 30 // 取樣數
int analogBuffer[SCOUNT];
int analogBufferIndex = 0;
float averageVoltage = 0;
float TDSValue = 0;
float temperature = 25; // 因沒有溫度計讀值,設定固定溫度為25做溫度補償用。
void setup()
{
Serial.begin(115200);
pinMode(TDS_SENSOR_PIN, INPUT);
}
float lastTDSValue = -1; // 定義最後的TDS值為無效(-1)
void loop()
{
// TDS取樣
static unsigned long analogSampleTimepoint = 0;
if(millis() - analogSampleTimepoint > 40)
{
analogSampleTimepoint = millis();
analogBuffer[analogBufferIndex] = analogRead(TDS_SENSOR_PIN);
analogBufferIndex++;
if(analogBufferIndex == SCOUNT)
{
analogBufferIndex = 0;
}
}
// TDS計算
static unsigned long printTimepoint = 0;
if(millis() - printTimepoint > 800)
{
printTimepoint = millis();
float sum = 0;
for(int i = 0; i < SCOUNT; i++)
{
sum += analogBuffer[i];
}
averageVoltage = (sum / SCOUNT) * (float)VREF / 4095; // 轉換為電壓值,ESP32的ADC解析度是12位(4095)。
float compensationCoefficient = 1.0 + 0.02 * (temperature - 25.0); // 溫度補償公式,溫度設定在25度即無補償。
float compensationVoltage = averageVoltage / compensationCoefficient; // 溫度補償後的電壓值
TDSValue = (133.42 * compensationVoltage * compensationVoltage * compensationVoltage - 255.86 * compensationVoltage * compensationVoltage + 857.39 * compensationVoltage) * 0.5; // 將電壓值轉換為TDS值
TDSValue = (int)TDSValue;
if (TDSValue != lastTDSValue) // 如果TDS數值有更新,才會將結果Print出來。
{
lastTDSValue = TDSValue;
Serial.print("TDS Value: ");
Serial.print(TDSValue, 0);
Serial.println(" ppm");
}
}
}
從Arduino的Serial Monitor可以看到TDS的值