import network
import machine
import time
import ntptime
import dht
import gc
import BlynkLib
Wokwi_Sim = True
Wokwi_SSID = 'Wokwi-GUEST'
Wokwi_PASS = ''
# --- Configuration ---
WIFI_SSID = 'Anh Duc'
WIFI_PASS = '12092002'
BLYNK_AUTH = ''
TIMEZONE_OFFSET= 7 * 3600 #GMT+7
last_dht_read = time.ticks_ms()
DHT_INTERVAL = 2500
manual = 1
pump_vled = 0
soil_pump_threshold = 30
fan_temp_threshold = 35
# PrevVar to cut down data uploads
last_hum = 0
last_temp = 0
last_soilmoist = -15
last_pump_vled = -1
wifi_attempt = 20
# Funcs
def sync_all():
global temp
global last_temp
global hum
global last_hum
global soil_percentage
blynk.virtual_write(4, hum + 2)
last_hum = hum
blynk.virtual_write(5, temp - 1)
last_temp = temp
blynk.virtual_write(6, round(soil_percentage))
last_soilmoist = soil_percentage
upload_report("Synced temp, soil, hum)")
def wifi_sim_check():
if Wokwi_Sim == True:
wifi.connect(Wokwi_SSID, Wokwi_PASS)
print('>>Running Wokwi simulation mode<<')
else:
wifi.connect(WIFI_SSID, WIFI_PASS)
def sync_time():
attempt = 2
success = 0
while attempt > 0 and success == 0:
try:
print("Syncing time with NTP...")
ntptime.settime() # This gets UTC time from pool.ntp.org
print("Time synced!")
attempt = 0
success = 1
break
except:
attempt = attempt - 1
print("Error syncing time, retrying in 1s")
time.sleep(1)
print("Retrying..., attempts left: ", attempt)
else:
print('Sync failed, skipping...')
bled.value(1)
time.sleep(0.5)
bled.value(0)
bled.value(1)
time.sleep(0.5)
bled.value(0)
bled.value(1)
def update_time():
# 1. Get current time from the ESP32 internal clock
# We add the offset to convert UTC to your local time
current_sec = time.time() + TIMEZONE_OFFSET
t = time.localtime(current_sec)
# 2. Format the time and date into a nice string
# Format: YYYY-MM-DD HH:MM:SS
time_str = "{:04}-{:02}-{:02} {:02}:{:02}".format(
t[0], t[1], t[2], t[3], t[4]
)
# 3. Send it to the Blynk Display widget (V1)
blynk.virtual_write(1, time_str)
upload_report("Send time")
print("Button pressed! Sent time:", time_str)
def measure():
global manual
global last_temp
global last_hum
global last_soilmoist
global last_pump_vled
global pump_vled
global soil_value
global light_value
global temp
global hum
global soil_percentage
global min_soil_pump
global fan_temp_threshold
try:
# Read sensors
light_value = light_sen.read()
soil_value = soil_sen.read()
#print(f"{soil_value}")
sensor.measure()
#Temperature measurement
temp = sensor.temperature()
temp_diff = abs(temp - last_temp)
#Humidity measurement
hum = sensor.humidity()
hum_diff = abs(hum - last_hum)
#Soil moisture measurement
soil_percentage = (soil_value - soil_dry) * 100 / (soil_wet - soil_dry)
soil_percentage = max(0, min(100, soil_percentage))
soil_diff = abs(soil_percentage - last_soilmoist)
#Light measurement (Sun: )
light = ()
#Sensor report
print(f"Hum: {hum}%, Temp: {temp}C, Soil: {soil_percentage}%")
print(f"Last hum {last_hum}, HumDiff {hum_diff}, LastTemp {last_temp}, tempdiff {temp_diff}, lastSoil {last_soilmoist}, soilDiff {soil_diff}, BlynkState {blynk.state}")
print(f"Min soil pump threshold: {soil_pump_threshold}")
print(f"Fan threshold: {fan_temp_threshold}")
# 2. AUTO PUMP
if manual == 0:
if soil_percentage <= soil_pump_threshold:
soil_led.value(1)
pump_relay.value(1)
print("Auto-Pumping: ON")
pump_vled = 255
elif soil_percentage >= 70:
soil_led.value(0)
pump_relay.value(0)
pump_vled = 0
print("Auto-Pumping: OFF")
# 3. Network Updates: ONLY try to send if we have a connection
if wifi.isconnected():
if hum_diff >= 3:
blynk.virtual_write(4, hum + 2)
last_hum = hum
print("Hum Data sent")
if temp_diff >= 1:
blynk.virtual_write(5, temp - 1)
last_temp = temp
print("Temp Data sent")
if soil_diff >= 5:
blynk.virtual_write(6, round(soil_percentage))
last_soilmoist = soil_percentage
print("Soil Data sent")
except OSError as e:
blynk.state = 0
print(f"Connection lost, Manual mode is disabled {e}")
manual = 0
except Exception as e:
print(f"Sensor Error: {e}")
machine.reset()
def wifi_reconnect():
global wifi_attempt
global manual
while not wifi.isconnected():
if manual != 0:
manual = 0 #Force AUTO mode
time.sleep(0.5)
print(".", end="")
wifi_attempt = wifi_attempt - 1
if wifi_attempt == 0:
print("Connection Failed, retrying later")
measure()
time.sleep(2)
wifi_attempt = 10
def upload_report(data):
print(f"Data sent {data}")
def fan_control():
global temp
global fan_temp_threshold
if temp >= fan_temp_threshold:
fan_relay.value(1)
elif temp <= 10:
fan_relay.value(0)
# Hardware Setup
# LEDs
bled = machine.Pin(2, machine.Pin.OUT)
soil_led = machine.Pin(27, machine.Pin.OUT)
#Temp aaand moisur Sensor
if Wokwi_Sim == True:
sensor = dht.DHT22(machine.Pin(5))
else:
sensor = dht.DHT11(machine.Pin(5))
#Soil_sensor (Dry: 2496, WET 850)
soil_sen = machine.ADC(machine.Pin(34))
soil_sen.atten(machine.ADC.ATTN_11DB)
soil_wet = 810
soil_dry = 2550
#Pump control
pump_relay = machine.Pin(27, machine.Pin.OUT)
#fan control
fan_relay = machine.Pin(4, machine.Pin.OUT)
#Light sensor
light_sen = machine.ADC(machine.Pin(35))
light_sen.atten(machine.ADC.ATTN_11DB)
# --- Wi-Fi Connection ---
print("Connecting to Wi-Fi...")
wifi = network.WLAN(network.STA_IF)
wifi.active(False)
time.sleep(0.5)
wifi.active(True)
time.sleep(1)
wifi.config(pm=0)
wifi_sim_check()
while not wifi.isconnected():
time.sleep(0.5)
print(".", end="")
print("\nConnected to Wi-Fi!")
print("IP Address:", wifi.ifconfig()[0])
# --- Blynk Setup ---
print("Connecting to Blynk...")
# Initialize Blynk
blynk = BlynkLib.Blynk(BLYNK_AUTH, insecure=True)
# --- Virtual Pin Handler ---
@blynk.on("V0")
def v0_write_handler(value):
# Blynk sends values as a list of strings (e.g., ['1'] or ['0'])
button_state = int(value[0])
if button_state == 1:
bled.value(1) # Turn LED ON
print("Blynk Switch: ON")
blynk.virtual_write(3, 'ON')
upload_report("Button On")
update_time()
blynk.sync_virtual(2, 4, 6, 5)
upload_report("Sync all")
else:
bled.value(0) # Turn LED OFF
print("Blynk Switch: OFF")
blynk.virtual_write(3, 'OFF')
upload_report("Button oFF")
@blynk.on("V2")
def v1_write_handler(value):
if int(value[0]) == 1:
update_time()
@blynk.on("V7")
def manual_mode(value):
global manual
manual = int(value[0])
blynk.sync_virtual(8)
upload_report("Manual pump check")
if manual == 1:
print('Manual ON')
else:
print("Manual OFF")
@blynk.on("V8")
def pump_manual(value):
global manual
global pump_vled
m_pump = int(value[0])
if manual == 1:
if m_pump == 1:
soil_led.value(1)
pump_vled = 255
pump_relay.value(1)
else:
soil_led.value(0)
pump_vled = 0
pump_relay.value(0)
else:
print("Manual not Enabled")
@blynk.on("V9")
def pump_threshold(value):
global soil_pump_threshold
soil_pump_threshold = int(value[0])
@blynk.on("V10")
def fan_threshold(value):
global fan_temp_threshold
fan_temp_threshold = int(value[0])
# --- Main Loop ---
sync_time()
update_time()
blynk.run()
print("Blynk is ready! Try toggling the switch in your Blynk app.")
bled.value(0)
bled.value(1)
time.sleep(1)
bled.value(0)
soil_led.value(0)
pump_relay.value(0)
#blynk.virtual_write(2, 0)
#upload_report("Check pump led")
blynk.sync_virtual(9)
upload_report("Moist slider check")
blynk.sync_virtual(10)
upload_report("Temp slider check")
blynk.sync_virtual(7)
upload_report("Manual MODE check")
while True:
try:
# 1. Wi-Fi Check
if not wifi.isconnected():
print("Wi-Fi connection lost! Attempting to reconnect...")
wifi_sim_check()
wifi_reconnect()
print("\nWi-Fi Reconnected!")
time.sleep(3)
blynk.connect()
blynk.sync_virtual(7)
upload_report("Reconnect pump sync")
sync_all()
time.sleep(1)
elif blynk.state == 0:
try:
print("Reconnecting...")
blynk.connect()
blynk.sync_virtual(7)
upload_report("Reconnect pump sync")
sync_all()
except Exception as e:
print(f"Reconnect failed: {e}")
time.sleep(1)
blynk.run()
# 3. Non-blocking Sensor Reads
current_time = time.ticks_ms()
if time.ticks_diff(current_time, last_dht_read) > DHT_INTERVAL:
measure()
fan_control()
gc.collect()
print(f"{light_value}")
last_dht_read = current_time
if last_pump_vled != pump_vled:
blynk.virtual_write(2, pump_vled)
print(f"{last_pump_vled}, {pump_vled}")
last_pump_vled = pump_vled
upload_report("Pump LED state")
# 4. Catch the ECONNRESET and other network errors gracefully
except OSError as e:
manual = 0
print(f"Network error: {e}")
print("Reset wifi...")
wifi.active(False)
time.sleep(0.5)
wifi.active(True)
time.sleep(1)
wifi_sim_check()
wifi_reconnect()
print("\nWi-Fi Reconnected!")
time.sleep(3)
# Re-establish connection to the Blynk server
blynk.connect()
blynk.sync_virtual(7)
upload_report("Reconnect pump sync")
sync_all()
time.sleep(1)
# 5. Catch any other random crashes so the loop never dies
except AttributeError as e:
blynk.state = 0
time.sleep(1)
except Exception as e:
print(f"Unexpected error in main loop: {e}")
time.sleep(1)
machine.reset()
SUPER FAS FANS
pump