print("Smart Water Quality Monitoring System using IoT")
print("Created by: MUIZ SALLEH")
print("25/4/2025")
print("Mini Project")
#import libraries/modules
from machine import Pin, PWM, SoftI2C, ADC
import ULTRASONIC_LIBRARY
import oled_library
from neopixel import NeoPixel
from time import sleep
import math
#Pin declaration
# ADC Inputs
# Potentiometer for pH level (GPIO27)
ph_sensor = ADC(Pin(27))
ph_sensor.atten(ADC.ATTN_11DB)
ph_sensor.width(ADC.WIDTH_10BIT)
# LDR (AO to GPIO13)
ldr = ADC(Pin(13))
ldr.atten(ADC.ATTN_11DB)
ldr.width(ADC.WIDTH_10BIT)
# Temperature sensor (NTC on GPIO26)
ntc = ADC(Pin(26)) # NTC Thermistor
ntc.atten(ADC.ATTN_11DB)
ntc.width(ADC.WIDTH_12BIT)
# Outputs
red_led = Pin(14, Pin.OUT) # indicator water level
green_led = Pin(12, Pin.OUT) # normal pH LED
yellow_led = Pin(32, Pin.OUT) # Low pH LED
blue_led = Pin(25, Pin.OUT) # High pH LED
buzzer = PWM(Pin(33), freq=1000)
np = NeoPixel(Pin(16), 16)
#Object declaration
# OLED Setup
i2c = SoftI2C(scl=Pin(22), sda=Pin(21))
screen = oled_library.SSD1306_I2C(width=128, height=64, i2c=i2c)
# Ultrasonic Sensor
TRIG = Pin(5, Pin.OUT)
ECHO = Pin(17, Pin.IN)
water_sensor = ULTRASONIC_LIBRARY.HCSR04(trigger_pin=TRIG, echo_pin=ECHO, timeout_us=30000)
#parameter declaration
# Servo Setup
servo = PWM(Pin(18), freq=50)
# Function: Move Servo to angle
def move_servo(angle):
duty = int(((angle / 180) * 2 + 0.5) / 20 * 1023)
servo.duty(duty)
#Function: read temperature using the Beta formula
def read_temperature_celsius(samples=10):
BETA = 3950
total = 0
for _ in range(samples):
analog_value = ntc.read()
total += analog_value
sleep(0.01) # Short delay between samples
avg_value = total / samples
resistance = 4095.0 / avg_value - 1
temp_kelvin = 1 / (math.log(resistance) / BETA + 1.0 / 298.15)
temp_celsius = temp_kelvin - 273.15
return round(temp_celsius, 1)
# Main Program
while True:
# Water level
measure_distance = water_sensor.distance_cm()
print("Water level =", measure_distance, "cm")
# LDR light level
light_value = ldr.read()
lux = (1023 - light_value) * (1000 / 1023)
print("Lux:", lux)
# Potentiometer for pH level and LED
pot_value = ph_sensor.read()
ph_value = round((pot_value / 1023) * 14, 1) # Scale to pH 0–14
print("pH value:", ph_value)
if ph_value < 15:
ph_status = "Low"
yellow_led.on() # Turn on yellow LED for low pH
green_led.off()
blue_led.off()
elif 15 <= ph_value <= 35:
ph_status = "OK"
yellow_led.off() # Turn off yellow LED for normal pH
green_led.on() # Turn on green LED for normal pH
blue_led.off()
else:
ph_status = "High"
yellow_led.off() # Turn off yellow LED for high pH
green_led.off()
blue_led.on() # Turn on blue LED for high pH
#Read Temperature (NTC Sensor)
temp_c = read_temperature_celsius()
# Display on OLED
screen.fill(0)
screen.text("Monitor 1", 0, 0)
screen.text("Water: " + str(measure_distance) + "cm", 0, 10)
screen.text("Lux: " + str(int(lux)), 0, 20)
screen.text("pH: " + str(ph_value), 0, 30)
screen.text("pH Status: " + ph_status, 0, 40)
screen.text("Temp: {}C".format(temp_c), 0, 50)
screen.show()
# Buzzer if water level too high
if measure_distance > 300:
red_led.on()
buzzer.duty(1023)
elif measure_distance < 150:
red_led.on()
buzzer.duty(1023)
else:
red_led.off()
buzzer.duty(0)
# Neopixel reacts to darkness
if lux < 500:
for i in range(16):
np[i] = (255, 255, 255)
np.write()
else:
for i in range(16):
np[i] = (0, 0, 0)
np.write()
# Servo Temperature Control
if temp_c >= 30:
move_servo(90)
else:
move_servo(0)
sleep(0.5)