#
# Smart Irrigation System (ESP32 + MicroPython)
# Includes LCD API + I2C driver
from machine import Pin, ADC, PWM, I2C
from time import sleep, sleep_ms
import dht
# LCD API (merged)
class LcdApi:
def __init__(self, num_lines, num_columns):
self.num_lines = num_lines
self.num_columns = num_columns
self.cursor_x = 0
self.cursor_y = 0
def clear(self):
self.move_to(0, 0)
self.putstr(" " * (self.num_lines * self.num_columns))
self.move_to(0, 0)
def move_to(self, cursor_x, cursor_y):
self.cursor_x = cursor_x
self.cursor_y = cursor_y
def putchar(self, char):
raise NotImplementedError
def putstr(self, string):
for char in string:
if char == '\n':
self.cursor_x = 0
self.cursor_y += 1
if self.cursor_y >= self.num_lines:
self.cursor_y = 0
else:
self.putchar(char)
self.cursor_x += 1
if self.cursor_x >= self.num_columns:
self.cursor_x = 0
self.cursor_y += 1
if self.cursor_y >= self.num_lines:
self.cursor_y = 0
# I2C LCD Driver (merged)
LCD_CLR = 0x01
LCD_HOME = 0x02
LCD_ENTRY_MODE = 0x04
LCD_ENTRY_INC = 0x02
LCD_ON_CTRL = 0x08
LCD_ON_DISPLAY = 0x04
LCD_FUNCTION = 0x20
LCD_FUNCTION_2LINES = 0x08
LCD_DDRAM = 0x80
LCD_BACKLIGHT = 0x08
LCD_ENABLE = 0x04
class I2cLcd(LcdApi):
def __init__(self, i2c, i2c_addr, num_lines, num_columns):
self.i2c = i2c
self.i2c_addr = i2c_addr
self.num_lines = num_lines
self.num_columns = num_columns
self.backlight = LCD_BACKLIGHT
self.buffer = bytearray(1)
self.init_lcd()
super().__init__(num_lines, num_columns)
def init_lcd(self):
sleep_ms(20)
self.hal_write_init_nibble(0x03)
sleep_ms(5)
self.hal_write_init_nibble(0x03)
sleep_ms(1)
self.hal_write_init_nibble(0x03)
self.hal_write_init_nibble(0x02)
self.hal_write_command(LCD_FUNCTION | LCD_FUNCTION_2LINES)
self.hal_write_command(LCD_ON_CTRL | LCD_ON_DISPLAY)
self.hal_write_command(LCD_CLR)
self.hal_write_command(LCD_ENTRY_MODE | LCD_ENTRY_INC)
def hal_write_command(self, cmd):
self.hal_write_byte(cmd, 0)
def hal_write_data(self, data):
self.hal_write_byte(data, 1)
def hal_backlight_on(self):
self.backlight = LCD_BACKLIGHT
def hal_backlight_off(self):
self.backlight = 0
def hal_write_init_nibble(self, nibble):
self.hal_write_byte(nibble << 4, 0)
def hal_write_byte(self, data, mode):
high = data & 0xF0
low = (data << 4) & 0xF0
self.hal_write(high | mode)
self.hal_write(low | mode)
def hal_write(self, data):
self.buffer[0] = data | self.backlight
self.i2c.writeto(self.i2c_addr, self.buffer)
self.pulse_enable(data)
def pulse_enable(self, data):
self.buffer[0] = data | LCD_ENABLE | self.backlight
self.i2c.writeto(self.i2c_addr, self.buffer)
sleep_ms(1)
self.buffer[0] = (data & ~LCD_ENABLE) | self.backlight
self.i2c.writeto(self.i2c_addr, self.buffer)
sleep_ms(1)
def putchar(self, char):
self.hal_write_data(ord(char))
# Pin definitions
DHTPIN = Pin(4)
SOIL_MOISTURE_PIN = ADC(Pin(34))
SOIL_MOISTURE_PIN.atten(ADC.ATTN_11DB) # full range 0-3.3V
RELAY_PIN = Pin(18, Pin.OUT)
LED_PIN = Pin(2, Pin.OUT)
SERVO_PIN = Pin(5)
moistureThreshold = 2000
# DHT Sensor
dht_sensor = dht.DHT22(DHTPIN)
# Servo setup
servo = PWM(SERVO_PIN, freq=50) # 50Hz
# I2C LCD setup
i2c = I2C(scl=Pin(22), sda=Pin(21), freq=400000)
lcd = I2cLcd(i2c, 0x27, 2, 16)
# Helper Functions
def set_servo(angle):
duty = int(((angle / 180) * 102) + 26) # Map angle 0-180 to duty cycle
servo.duty(duty)
def start_watering():
set_servo(90)
lcd.move_to(0, 1)
lcd.putstr("Watering ON... ")
def stop_watering():
set_servo(0)
lcd.move_to(0, 1)
lcd.putstr("Watering OFF ")
# Main program loop
lcd.putstr("Smart Irrigation")
while True:
try:
# Read sensors
soilMoistureValue = SOIL_MOISTURE_PIN.read()
dht_sensor.measure()
temperature = dht_sensor.temperature()
humidity = dht_sensor.humidity()
# Show readings
lcd.move_to(0, 0)
lcd.putstr("T:{}C H:{}% ".format(temperature, humidity))
# Auto watering logic
if soilMoistureValue < moistureThreshold:
RELAY_PIN.value(0) # Relay ON
LED_PIN.value(1) # LED ON
start_watering()
else:
RELAY_PIN.value(1) # Relay OFF
LED_PIN.value(0) # LED OFF
stop_watering()
sleep(2)
except Exception as e:
print("Error:", e)
sleep(2)