Table of Contents
Introduction
This article discusses how to create an IoT based project named Water Quality Tester. We have used different sensors to get the job done. I have also provided the complete schematic diagram and source code of the system so anyone can design it.
Background and Need for the System
In today’s world, water plays an essential role without which we cannot live and it is one of the critical resources as most lives have benefited from its use and can cause harm due to its misuse. Most water-borne diseases are due to the use of degraded quality water which can cause a lot of harm to the population.
The quality of water has deteriorated a lot as can be seen in most of the lakes in the city that have a frothing foam due to impurities released in the water. The water must be treated to make it fit for drinking. Moreover, we should have means to measure water quality in real-time. To ensure safe drinking water, it is necessary to check the quality of the water. Therefore, to monitor the quality of water, we have to design a Water Quality Tester system that allows us to test the quality of water in real-time quickly.
Proposed Solution
The proposed solution is a system that enables users to test the quality of water. We will design an IoT Python-based water monitoring system using Raspberry Pi to identify water impurities, and Potential Hydrogen (pH) variations, detect the presence of bacteria and find out if the water is safe for human consumption. The system consists of the integration of all the sensors namely pH, turbidity, flow, and conductivity sensors, and determines if the water is indeed safe for human consumption.
Before implementing the system, the standard values of all the sensors of a pure water sample are stored in the database which must be considered as a threshold value. The IoT-based core control system is integrated with different sensors such as PH, turbidity, flow, and conductivity sensor. The leads of the sensors are placed in the water sample to be tested.
The sensor values must be processed by Analog to Digital Converter ADC and they must be uploaded by using Raspberry Pi on the cloud. If the sensor value is greater than the threshold value then, it must be communicated to the authorized user via an email or SMS for further action and the same must be displayed on the LCD display of the microcontroller. If the sensor value is less than the threshold value then, the parameters are checked for different water samples. If the water parameters are not within the threshold values, the red LED bulb must glow, else green LED bulb must glow indicating whether the water is safe for drinking or not.

Components Used
We need the following components to create the Water Quality System.
- Raspberry PI 4 Model B 4GB
- Turbidity Sensor
- pH Sensor
- Water Flow Sensor
- TDS Sensor
- Analogue-to-Digital Convertor (ADC 1115)
- LCD (16×2)

Raspberry Pi 4 Model B
We have used Raspberry Pi 4 Model B with 4GB RAM in this project. It has a 1.5 GHz 64-bit quad-core processor, onboard Wi-Fi, Bluetooth and Ethernet, two USB 2.0 ports, two USB 3.0 ports, and dual monitor support with a pair of micro HDMI Type D ports for up to 4K Resolutions.

Turbidity Sensor
The turbidity sensor is used to detect the quality of water by measuring the levels of turbidity or the opaqueness present in water. It uses light to detect suspended particles in water by measuring the light transmittance and scattering rate, which changes with the amount of total suspended solids (TSS) in water. As the TTS increases, the liquid turbidity level increases.

pH Sensor
A pH sensor is used to measure the amount of alkalinity and acidity in water and other solutions. When used correctly, pH sensors are able to ensure the safety and quality of water.

TDS Sensor
TDS (Total Dissolved Solids) indicates how many milligrams of soluble solids are dissolved in one litre of water. In general, the higher the TDS value, the more soluble solids are dissolved in water, and the less clean the water is. Therefore, the TDS value can be used as one reference point for reflecting the cleanliness of the water. This can be applied to domestic water, hydroponic and other fields of water quality testing and monitoring.

Analogue-to-Digital Convertor (ADC 1115)
For microcontrollers without an analogue-to-digital converter such as Raspberry Pi and when you want a higher-precision ADC, the ADS1115 provides 16-bit precision at 860 samples/second over I2C. The chip can be configured as 4 single-ended input channels or two differential channels. This ADC can run from 2V to 5V power/logic, can measure a large range of signals and it is super easy to use. It is a great general-purpose 16-bit converter.

Water Flow Sensor
The water flow sensor consists of a copper body, a water rotor, and a hall-effect sensor. When water flows through the rotor, the rotor rolls, and its speed changes with different rates of flow. And the hall-effect sensor outputs the corresponding pulse signal. We have used this sensor to measure the water flow rate and total quantity of water in litres.

Final Circuit Diagram

Flow Chart

Source Code
from itertools import count
import RPi.GPIO as GPIO
from ast import Try
from datetime import date, datetime
from gpiozero import LED
import sys,time
import board
import busio
import adafruit_ads1x15.ads1115 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
import lcd
import requests
from requests.structures import CaseInsensitiveDict
import os
from time import sleep
import lcd
from pymongo import MongoClient
import pymongo
import urllib
MONGODB_USER="pi4"
MONGODB_PASS=os.getenv("MONGODB_PASS")
MONGODB_DB = 'water_quality_db'
MONGODB_COLLECTION = 'sensor_logs'
def mongo_db_connection():
CONNECTION_STRING = "mongodb+srv://pi4:"+ urllib.parse.quote(MONGODB_PASS) +"@cluster0.wbrzjmu.mongodb.net/?retryWrites=true&w=majority"
try:
client = MongoClient(CONNECTION_STRING)
dbname = client[MONGODB_DB]
collection_name = dbname[MONGODB_COLLECTION]
print("Database connected successfully!")
return collection_name
except:
print("Database could not be connected!")
PH_STANDARD_RANGE = (5.5,8.5)
TUR_STANDARD_RANGE = (1,1800)
TDS_STANDARD_RANGE = (1000,2500)
WF_STANDARD_RANGE = (500,1000)
# Constants for SMS_API
SMS = {
"ID":"*****",
"NUMBER": "923131115563" ,
"BRAND": "GULRAIZ GULSHAN"
}
# Constants for WHATSAPP_API
WHATSAPP = {
"URL" : "https://graph.facebook.com/v13.0/109450551816278/messages",
"NUMBER": "923486812804"
}
# Func to send SMS
def sendSMS(MSG_BODY):
url = "http://api.m4sms.com/api/#sendSMS?id="+SMS["ID"]+"&pass="+os.getenv("SMS_API_PASSWORD")+"&mobile="+SMS["NUMBER"]+"&brandname="+SMS["BRAND"]+"&msg="+MSG_BODY+"&language=English&network=1;"
try:
r = requests.get(url)
print("SMS SENT TO "+SMS["NUMBER"])
except:
print("SMS FAILED TO SENT")
# Func to send WHATSAPP
def sendWhatsApp(TDS,PH,TS,WFS,STATUS):
headers = CaseInsensitiveDict()
headers = {
'Authorization': 'Bearer '+os.getenv('WHATSAPP_TOKEN'),
'Content-Type': 'application/json'
}
data = '{ "messaging_product": "whatsapp", "recipient_type": "individual", "to": "'+ WHATSAPP["NUMBER"] +'", "type": "template", "template": { "name": "techwiz_sensor_values", "language": { "code": "en" }, "components": [ { "type": "body", "parameters": [ { "type": "text", "text": "'+str(TDS)+'" }, { "type": "text", "text": "'+str(PH)+'" }, { "type": "text", "text": "'+str(TS)+'" }, { "type": "text", "text": "'+str(WFS)+'" }, { "type": "text", "text": "'+str(STATUS)+'" } ] } ] } }'
try:
resp = requests.post(WHATSAPP["URL"], headers=headers, data=data)
#print(resp.status_code)
print("WhatsApp SENT TO "+WHATSAPP["NUMBER"])
except:
print("WhatsApp FAILED TO SENT, Error Code: ", resp.status_code)
# Func for LCD display
def displayLCD(LINE_,VALUE,ALIGN):
LINE_NUMBER = {1: lcd.LCD_LINE_1,2: lcd.LCD_LINE_2}
lcd.lcd_init()
lcd.lcd_byte(LINE_NUMBER[LINE_], lcd.LCD_CMD)
lcd.lcd_string(str(VALUE),ALIGN)
#Calling SCL & SDA ports from Pi Board
i2c = busio.I2C(board.SCL, board.SDA)
ads = ADS.ADS1115(i2c)
ads.gain = 1
#Assigning Channel of ADS1115 to sensors
channel0 = AnalogIn(ads, ADS.P0) # For pH sensor
channel1 = AnalogIn(ads, ADS.P1) # For Turbidity sensor
channel2 = AnalogIn(ads, ADS.P2) # For TDS sensor
channel3 = AnalogIn(ads, ADS.P3) # For WaterFlow sensor
run = True
while run:
# Calculating pH
PH_VOL = channel0.voltage
PH_VAL = round(-5.70*PH_VOL+21.34,2)
# Calculating Turbidity
TUR_VOL = channel1.voltage
TUR_VAL = round(-1120.4*(TUR_VOL**2)+(5742.3*TUR_VOL)-4353.8)
#Calculating TDS
TDS_VOL = channel2.voltage
TDS_VAL = round((133.42/TDS_VOL**3-255.86*TDS_VOL**2+857.39*TDS_VOL)*0.5)
WF_VAL = round(channel3.value)
logs = str(datetime.now()),": pH: ",str(PH_VAL),", Tur: ",str(TUR_VAL),", TDS: ",str(TDS_VAL),", WF: ",str(WF_VAL) + "\n"
print(logs)
file = open("logs.txt","a")
file.writelines(logs)
file.close()
IS_PH_OK = PH_VAL >= PH_STANDARD_RANGE[0] and PH_VAL <= PH_STANDARD_RANGE[1]
IS_TUR_OK = TUR_VAL >= TUR_STANDARD_RANGE[0] and TUR_VAL <= TUR_STANDARD_RANGE[1]
IS_TDS_OK = TDS_VAL >= TDS_STANDARD_RANGE[0] and TDS_VAL <= TDS_STANDARD_RANGE[1]
#IS_WF_OK = WF_VAL >= WF_STANDARD_RANGE[0] and WF_VAL <= WF_STANDARD_RANGE[1]
IS_DRINKABLE = (IS_PH_OK and IS_TUR_OK and IS_TDS_OK)
STATUS = "CLEAN" if IS_DRINKABLE else "POLLUTED"
sensor_log = {
"ph_sensor_value" : PH_VAL,
"tds_sensor_value" : TDS_VAL,
"tur_sensor_value" : TUR_VAL,
"wf_sensor_value" : WF_VAL,
"status" : STATUS,
"date_added": datetime.now()
}
LINE1="P:" + str(round(PH_VAL,2)) + ", T:" + str(round(TUR_VAL,2))
LINE2="C:" + str(round(TDS_VAL,2)) + ", W:" + str(round(WF_VAL,2))
db = mongo_db_connection()
SMS_MSG_BODY = "Water Quality Tester\n" + "pH: " + str(round(PH_VAL,2)) + "\nTurbidity: " + str(round(TUR_VAL,2)) + "\nConductivity: " + str(round(TDS_VAL,2)) + "\nWaterFlow: " + str(round(WF_VAL,2)) + "\n\nThe water is: " + STATUS
if IS_DRINKABLE:
print("Water is ", STATUS)
displayLCD(1,"Water: " + STATUS,1)
sleep(5)
displayLCD(1,LINE1,1)
displayLCD(2,LINE2,2)
try:
db.insert_one(sensor_log)
print("Data logged successfully in db")
except:
print("Data not inserted in db")
sendWhatsApp(str(round(TDS_VAL)),str(round(PH_VAL,2)),str(round(TUR_VAL,2)),str(round(WF_VAL,2)),STATUS)
sendSMS(SMS_MSG_BODY)
else:
print("Water is ", STATUS)
displayLCD(1,"Water: " + STATUS,1)
sleep(5)
displayLCD(1,LINE1,1)
displayLCD(2,LINE2,2)
try:
db.insert_one(sensor_log)
print("Data logged successfully in db")
except:
print("Data not inserted in db")
sendWhatsApp(str(round(TDS_VAL)),str(round(PH_VAL,2)),str(round(TUR_VAL,2)),str(round(WF_VAL,2)),STATUS)
sendSMS(SMS_MSG_BODY)
sleep(10)