TDS检测水质
TDS(Total Dissolved Solids),总溶解固体,又称溶解性固体总量,表明1升水中溶有多少毫克溶解性固体。一般来说,TDS值越高,表示水中含有的溶解物越多,水就越不洁净。作为一种可快速检测的参数,TDS目前还是可以作为有效的水质情况反映参数来作为参考。
Qhebot的TDS模块如图:
读取TDS值的主要程序如下:
#define TdsSensorPin A0 //Nano接A6 ESP8266接A0 ESP32接A5或者其他ADC引脚
#define VREF 5.0 // ADC的模拟参考电压(Volt)
#define readCount 40 // 从ADC读取到的模拟值个数
#define SCOUNT 30 // 滤波之后的模拟值个数
int Buffer[SCOUNT]; // 将模拟值存储在数组中,从ADC读取
int BufferTemp[SCOUNT];
int BufferIndex = 0, copyIndex = 0;
float averageVoltage = 0, tdsValue = 0;
float temperature = 25; //未添加温度补偿默认25℃
float compensationVolatge; //修正后的电压用于实现温度补偿
float compensationCoefficient; //温度补偿系数
void setup()
{
Serial.begin(115200);
pinMode(TdsSensorPin, INPUT);
}
void loop()
{
TDS();
delay(500);
}
float TDS() {
static unsigned long analogSampleTimepoint = millis();
if (millis() - analogSampleTimepoint > 40U) //每40毫秒,从ADC读取模拟值
{
analogSampleTimepoint = millis();
for (int BufferIndex = 0; BufferIndex < readCount; BufferIndex++){
Buffer[BufferIndex] = analogRead(TdsSensorPin); //读取传感器输出引脚模拟电压存储到缓冲区
//Serial.printf("Buffer[%d]: %d \n", BufferIndex, Buffer[BufferIndex]);
}
}
static unsigned long printTimepoint = millis();
if (millis() - printTimepoint > 800U) {
printTimepoint = millis();
for (copyIndex = 0; copyIndex < SCOUNT; copyIndex++)
BufferTemp[copyIndex] = Buffer[copyIndex];
//通过中值滤波算法读取更稳定的模拟值,并转换为电压值。getMedianNum见下
averageVoltage = getMedianNum(BufferTemp, SCOUNT) * (float)VREF / 1024.0;
//temperture待测溶液温度 compensationCoefficient温度校正系数
//温度补偿公式: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.02*(fTP-25.0));
compensationCoefficient = 1.0 + 0.02 * (temperature - 25.0);
//温度补偿
compensationVolatge = averageVoltage / compensationCoefficient;
//转换为电压值,根据标准曲线
tdsValue = (133.42 * compensationVolatge * compensationVolatge * compensationVolatge - 255.86 * compensationVolatge * compensationVolatge + 857.39 * compensationVolatge) * 0.5;
}
//串口输出tdsValue
Serial.printf("\n TDS Value:"); Serial.print(tdsValue); Serial.printf("ppm \n");
}
该程序是未添加温度补偿(默认水的温度是25℃),温度补偿只需将DS18B20模块获取的温度赋给全局变量temperature 即可。
上述程序中的中值滤波 getMedianNum 如下,也可以考虑使用其他滤波算法,如:算术平均滤波、滑动平均滤波等。
// 中值滤波 bArray[]数据源 iFilterLen数据个数
int getMedianNum(int bArray[], int iFilterLen)
{
// Serial.printf("\n滤波……");
int bTab[iFilterLen];
for (byte i = 0; i < iFilterLen; i++) {
bTab[i] = bArray[i];
// Serial.printf("\n bArray[%d]: %d ", i, bArray[i]);
}
int i, j, bTemp;
for (j = 0; j < iFilterLen - 1; j++) {
for (i = 0; i < iFilterLen - j - 1; i++) {
if (bTab[i] > bTab[i + 1]) { //使升序排列便于取中值bTemp
bTemp = bTab[i];
bTab[i] = bTab[i + 1];
bTab[i + 1] = bTemp;
}
}
}
if ((iFilterLen & 1) > 0) //和1相与判断奇偶
bTemp = bTab[(iFilterLen - 1) / 2]; //奇数个元素取中值
else
bTemp = (bTab[iFilterLen / 2] + bTab[iFilterLen / 2 - 1]) / 2; //偶数个元素取中值
return bTemp;
}