Water Quality Detection Based on Arduino

<span>TDS</span> (Total Dissolved Solids) is an indicator of the total amount of various inorganic and organic substances dissolved in water, usually expressed in milligrams per liter (mg/L) or parts per million (ppm). These substances include minerals such as calcium, magnesium, sodium, and potassium, as well as trace amounts of organic matter and heavy metals.

This article introduces how to measure the current TDS value of water based on Arduino.

Hardware Preparation

  • Arduino Uno development board (or ESP8266)
  • TDS water quality sensor (around 12 yuan)
  • 0.96 OLED screen

Wiring Connections

Device Name Sensor Arduino
OLED Screen SDA A4
OLED Screen SCL A5
OLED Screen VCC 3.3V/5V
OLED Screen GND GND
TDS Sensor Signal Line A1
TDS Sensor VCC 5V
TDS Sensor GND GND

Sample Code

#include "U8glib.h"

// Sensor configuration
const int TDS_PIN = A1;
const float REF_VOLTAGE = 5.0;    // ADC reference voltage (modify as needed)
const int ADC_RESOLUTION = 1024;  // ADC bit resolution
const float TDS_FACTOR = 0.5;    // Common coefficient according to national standards (needs on-site calibration)

// Display configuration
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0);

// National standard water quality classification thresholds (GB/T 14848-2017)
const int TDS_GOOD = 300;    // Class I water ≤300mg/L
const int TDS_FAIR = 1000;   // Class III water ≤1000mg/L

// Chinese character dot matrix data (unchanged)
static const uint8_t PROGMEM dang[] = {0x01,0x00,0x21,0x08,0x11,0x08,0x09,0x10,0x09,0x20,0x01,0x00,0x7F,0xF8,0x00,0x08,
0x00,0x08,0x00,0x08,0x3F,0xF8,0x00,0x08,0x00,0x08,0x00,0x08,0x7F,0xF8,0x00,0x08,};
static const uint8_t PROGMEM qian[] = {0x10,0x10,0x08,0x10,0x08,0x20,0xFF,0xFE,0x00,0x00,0x3E,0x08,0x22,0x48,0x22,0x48,
0x3E,0x48,0x22,0x48,0x22,0x48,0x3E,0x48,0x22,0x08,0x22,0x08,0x2A,0x28,0x24,0x10,};
static const uint8_t PROGMEM deng[] = {0x20,0x40,0x3F,0x7E,0x48,0x90,0x85,0x08,0x01,0x00,0x3F,0xF8,0x01,0x00,0x01,0x00,
0xFF,0xFE,0x00,0x00,0x00,0x20,0x7F,0xFC,0x08,0x20,0x04,0x20,0x04,0xA0,0x00,0x40,};
static const uint8_t PROGMEM ji[] = {0x10,0x00,0x13,0xFC,0x20,0x84,0x20,0x88,0x48,0x88,0xF8,0x90,0x10,0x9C,0x20,0x84,
0x41,0x44,0xF9,0x44,0x41,0x28,0x01,0x28,0x1A,0x10,0xE2,0x28,0x44,0x44,0x01,0x82,};
static const uint8_t PROGMEM you[] = {0x08,0x90,0x08,0x88,0x08,0x88,0x10,0x80,0x17,0xFE,0x30,0xA0,0x30,0xA0,0x50,0xA0,
0x90,0xA0,0x10,0xA0,0x11,0x20,0x11,0x22,0x11,0x22,0x12,0x22,0x12,0x1E,0x14,0x00,};
static const uint8_t PROGMEM zhong[] = {0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x3F,0xF8,0x21,0x08,0x21,0x08,0x21,0x08,
0x21,0x08,0x21,0x08,0x3F,0xF8,0x21,0x08,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,};
static const uint8_t PROGMEM cha[] = {0x08,0x20,0x04,0x40,0x7F,0xFC,0x01,0x00,0x01,0x00,0x3F,0xF8,0x02,0x00,0x02,0x00,
0xFF,0xFE,0x04,0x00,0x08,0x00,0x17,0xF8,0x20,0x80,0x40,0x80,0x80,0x80,0x1F,0xFC,};

// Improved TDS reading (added average filtering)
float getTDSValue() {
  const int samples = 10;
  float avgVoltage = 0;
  
  for(int i=0; i<samples; i++){
    avgVoltage += analogRead(TDS_PIN) * (REF_VOLTAGE / ADC_RESOLUTION);
    delay(10);
  }
  avgVoltage /= samples;

  // According to the national standard formula: TDS(mg/L) = k * conductivity(μS/cm)
  // Typical k value range 0.55-0.8, default 0.65 needs calibration
  float tdsValue = avgVoltage * TDS_FACTOR * 1000; 

  return tdsValue;
}

// National standard water quality determination
const unsigned char* getWaterLevel(float tds) {
  if (tds <= TDS_GOOD) return you;    // Excellent (Class I)
  else if (tds <= TDS_FAIR) return zhong; // Fair (Class III)
  else return cha;                    // Poor (exceeds Class III)
}

void setup(){
  Serial.begin(115200);
  
  // Display initialization (unchanged)
  if ( u8g.getMode() == U8G_MODE_R3G3B2 ) {
    u8g.setColorIndex(255);
  }
  // ...other display mode settings
}

void loop(){
  static float tds = 0;
  tds = getTDSValue();
  
  u8g.firstPage();  
  do {
    draw(tds);
  } while( u8g.nextPage() );
  
  delay(500); // Extend sampling interval appropriately
}

// Optimize display interface
void draw(float tds) {
  u8g.setFont(u8g_font_unifont);
  
  // Display title
  u8g.drawBitmapP( 0, 0, 2, 16, dang);
  u8g.drawBitmapP( 16, 0, 2, 16, qian);
  u8g.drawStr( 32, 12, "TDS");
  
  // Display value (retain 1 decimal place)
  u8g.setPrintPos(60, 12);
  u8g.print(tds, 1);
  u8g.drawStr( 100, 12, "mg/L"); // National standard unit

  // Display water quality level
  u8g.drawBitmapP( 16, 32, 2, 16, deng);
  u8g.drawBitmapP( 32, 32, 2, 16, ji);
  const unsigned char* level = getWaterLevel(tds);
  u8g.drawBitmapP( 64, 32, 2, 16, level);

  // Serial output for debugging information
  Serial.print("TDS: ");
  Serial.print(tds);
  Serial.println(" mg/L");
}

Note: Due to the limited memory of Arduino, displaying Chinese characters is not as powerful as on ESP8266. Therefore, it can only use font software to extract the required text and place it in the code.

Effect Display

Water Quality Detection Based on Arduino
Tap water at home
Water Quality Detection Based on Arduino
Bottled mineral water

Note: The water quality level is classified according to the national standard water quality classification thresholds (GB/T 14848-2017). Class I water <span>≤300</span> mg/L

Water Quality Classification (Industry Common)

  • 0-9 mg/L: Purified water (e.g., distilled water).
  • 10-60 mg/L: Spring water, mineralized water (contains natural minerals).
  • 60-100 mg/L: Purified water (processed by activated carbon, ultrafiltration).
  • 100-300 mg/L: Ordinary tap water.
  • Above 300 mg/L: Possible contamination, further testing required.

For more exciting articles, please follow us

Leave a Comment