Example Code for Arduino-Water Quality Data Monitoring

This project demonstrates how to connect the RS485 Water Quality Electrical Conductivity Sensor to an Arduino board to read real-time electrical conductivity (EC), temperature, salinity, and Total Dissolved Solids (TDS) data. You will learn how to implement ModBus-RTU protocol communication over RS485, process sensor data, and display results on the serial monitor.

Hardware Preparation

Software Preparation

  • Arduino IDE (latest version recommended)
  • No additional libraries are required; the SoftwareSerial library used in the code is built into the Arduino IDE.

Wiring Diagram

If the power of the RS485 device is small and the required current is less than 12V-160mA, the RS485 to UART signal conversion module does not require a 12V external power supply, making wiring more convenient.

Other Preparation Work

  1. Ensure the sensor is properly calibrated (factory-calibrated, ready to use out of the box; re-calibration can be done later if needed).
  2. Verify RS485 wiring connections: match 485-A to A, 485-B to B, VCC and GND to correct voltage levels.
  3. Confirm the sensor's default baud rate is 4800 (matches the sample code configuration).

Sample Code

#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3);                                         //TX,RX
uint8_t Com[8] = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xC4, 0x0B };   //EC、Temperature
uint8_t Com1[8] = { 0x01, 0x03, 0x00, 0x02, 0x00, 0x02, 0x65, 0xCB };  //Salinity、TDS
float ec, tem;
int tds, sal;

void setup() {
  Serial.begin(9600);
  mySerial.begin(4800);
}
void loop() {
  EC_Temperature();
  Serial.print("EC = ");
  Serial.print(ec);
  Serial.print(" us/cm  ");
  Serial.print(" Temperature = ");
  Serial.print(tem, 1);
  Serial.println("°C");
  Salinity_TDS();
  Serial.print(" Salinity = ");
  Serial.print(sal);
  Serial.print(" PPM  ");
  Serial.print(" TDS = ");
  Serial.print(tds);
  Serial.println(" PPM");
  Serial.println(" ");
  delay(1000);
}

void EC_Temperature(void) {
  uint8_t Data[12] = { 0 };
  uint8_t ch = 0;
  bool flag = 1;
  long timeStart = millis();
  long timeStart1 = 0;
  while (flag) {

    if ((millis() - timeStart1) > 100) {
      while (mySerial.available() > 0) {
        mySerial.read();
      }
      mySerial.write(Com, 8);
      timeStart1 = millis();
    }

    if ((millis() - timeStart) > 1000) {
      Serial.println("Time out");
      return -1;
    }

    if (readN(&ch, 1) == 1) {
      if (ch == 0x01) {
        Data[0] = ch;
        if (readN(&ch, 1) == 1) {
          if (ch == 0x03) {
            Data[1] = ch;
            if (readN(&ch, 1) == 1) {
              if (ch == 0x04) {
                Data[2] = ch;
                if (readN(&Data[3], 6) == 6) {
                  if (CRC16_2(Data, 7) == (Data[7] * 256 + Data[8])) {
                    ec = Data[3] * 256 + Data[4];
                    tem = (Data[5] * 256 + Data[6]) / 10.0;
                    flag = 0;
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}


void Salinity_TDS(void) {
  uint8_t Data[12] = { 0 };
  uint8_t ch = 0;
  bool flag = 1;
  long timeStart = millis();
  long timeStart1 = 0;
  while (flag) {

    if ((millis() - timeStart1) > 100) {
      while (mySerial.available() > 0) {
        mySerial.read();
      }
      mySerial.write(Com1, 8);
      timeStart1 = millis();
    }

    if ((millis() - timeStart) > 1000) {
      Serial.println("Time out1");
      return -1;
    }

    if (readN(&ch, 1) == 1) {
      if (ch == 0x01) {
        Data[0] = ch;
        if (readN(&ch, 1) == 1) {
          if (ch == 0x03) {
            Data[1] = ch;
            if (readN(&ch, 1) == 1) {
              if (ch == 0x04) {
                Data[2] = ch;
                if (readN(&Data[3], 6) == 6) {
                  if (CRC16_2(Data, 7) == (Data[7] * 256 + Data[8])) {
                    sal = Data[3] * 256 + Data[4];
                    tds = Data[5] * 256 + Data[6];
                    flag = 0;
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}


uint8_t readN(uint8_t *buf, size_t len) {
  size_t offset = 0, left = len;
  int16_t Tineout = 500;
  uint8_t *buffer = buf;
  long curr = millis();
  while (left) {
    if (mySerial.available()) {
      buffer[offset] = mySerial.read();
      offset++;
      left--;
    }
    if (millis() - curr > Tineout) {
      break;
    }
  }
  return offset;
}

unsigned int CRC16_2(unsigned char *buf, int len) {
  unsigned int crc = 0xFFFF;
  for (int pos = 0; pos < len; pos++) {
    crc ^= (unsigned int)buf[pos];
    for (int i = 8; i != 0; i--) {
      if ((crc & 0x0001) != 0) {
        crc >>= 1;
        crc ^= 0xA001;
      } else {
        crc >>= 1;
      }
    }
  }

  crc = ((crc & 0x00ff) << 8) | ((crc & 0xff00) >> 8);
  return crc;
}

Result

The serial monitor displays the EC value, temperature, salinity, and TDS data collected by the sensor.

Additional Information

  • The sensor includes automatic temperature compensation to ensure accurate readings under varying environmental conditions.
  • For long-term use, you can re-calibrate the sensor using the provided calibration solution and ModBus register settings (refer to the Communication Protocol section for details).
  • The sensor's IP68 waterproof rating allows it to be submerged in water for extended periods, suitable for various aquatic monitoring applications.

Was this article helpful?

TOP