from machine import Pin, PWM
from umqtt.simple import MQTTClient
import neopixel
import network
import ujson
import time
# ======================================
# WiFi & MQTT Configuration
# ======================================
SSID = "Wokwi-GUEST"
PASSWORD = ""
BROKER = "mqtt-dashboard.com"
CLIENT_ID = "do7amqtt_re"
TOPIC_SENSORS = b"do7a_d7d7"
TOPIC_BUZZER = b"esp32/actuators/buzzer"
# ======================================
# GPIO Pin Assignment
# ======================================
RING_PIN = 18
RING_PIXELS = 60
BUZZER_PRESSURE_PIN = 22 # Buzzer الأصلي الخاص بالضغط (PWM)
BUZZER_ACTUATOR_PIN = 19 # Buzzer الجديد الخاص بالـ Actuators (GPIO 19)
TEMP_LED_PIN = 13
HUM_LED_PIN = 27
MOTION_LED_PIN = 32
LIGHT_SERVO_PIN = 21
WATER_SERVO_PIN = 15
# ======================================
# Outputs Setup
# ======================================
ring = neopixel.NeoPixel(Pin(RING_PIN), RING_PIXELS)
temp_led = Pin(TEMP_LED_PIN, Pin.OUT)
hum_led = Pin(HUM_LED_PIN, Pin.OUT)
motion_led = Pin(MOTION_LED_PIN, Pin.OUT)
light_servo = PWM(Pin(LIGHT_SERVO_PIN), freq=50)
water_servo = PWM(Pin(WATER_SERVO_PIN), freq=50)
# 1. Buzzer 1 (Pressure PWM)
buzzer_pressure = PWM(Pin(BUZZER_PRESSURE_PIN))
buzzer_pressure.duty(0)
# 2. Buzzer 2 (Actuator Digital - Pin 19)
buzzer_actuator = Pin(BUZZER_ACTUATOR_PIN, Pin.OUT)
buzzer_actuator.value(0)
# ======================================
# Thresholds
# ======================================
TEMP_THRESHOLD = 30
HUM_THRESHOLD = 70
LIGHT_THRESHOLD = 2000
SOIL_THRESHOLD = 40
MIN_DISTANCE = 5
MAX_DISTANCE = 395
WATER_WAIT = 3000 # 3 seconds between each stop
positions = [0, 45, 90, 135, 0]
# ======================================
# Ring Light Functions
# ======================================
colors = [
(0, 0, 255), # Blue
(0, 255, 255), # Cyan
(0, 255, 0), # Green
(255, 255, 0), # Yellow
(255, 128, 0), # Orange
(255, 0, 0) # Red
]
def interpolate(c1, c2, t):
return (
int(c1[0] + (c2[0] - c1[0]) * t),
int(c1[1] + (c2[1] - c1[1]) * t),
int(c1[2] + (c2[2] - c1[2]) * t)
)
def gradient(percent):
sections = len(colors) - 1
x = percent * sections
i = int(x)
if i >= sections:
return colors[-1]
return interpolate(colors[i], colors[i + 1], x - i)
def update_ring(distance):
if distance is None:
distance = MAX_DISTANCE
if distance < MIN_DISTANCE:
distance = MIN_DISTANCE
if distance > MAX_DISTANCE:
distance = MAX_DISTANCE
percent = (MAX_DISTANCE - distance) / (MAX_DISTANCE - MIN_DISTANCE)
leds = int(percent * RING_PIXELS)
color = gradient(percent)
for i in range(RING_PIXELS):
if i < leds:
ring[i] = color
else:
ring[i] = (0, 0, 0)
ring.write()
# ======================================
# Servo Helper
# ======================================
def servo_angle(servo, angle):
duty = int(25 + (angle / 180) * 100)
servo.duty(duty)
# ======================================
# Actuators Logic
# ======================================
light_open = False
def update_light(light_value):
global light_open
if light_value is None:
return
if light_value < LIGHT_THRESHOLD:
if not light_open:
servo_angle(light_servo, 90)
light_open = True
else:
if light_open:
servo_angle(light_servo, 0)
light_open = False
def update_temp(temp):
if temp is None: return
temp_led.value(1 if temp >= TEMP_THRESHOLD else 0)
def update_humidity(humidity):
if humidity is None: return
hum_led.value(1 if humidity >= HUM_THRESHOLD else 0)
def update_motion(motion):
if motion is None: return
motion_led.value(1 if motion else 0)
previous_pressure = None
def update_pressure(pressure):
global previous_pressure
if pressure is None: return
if previous_pressure is None:
previous_pressure = pressure
return
if pressure < previous_pressure:
buzzer_pressure.freq(2500)
buzzer_pressure.duty(512)
time.sleep_ms(150)
buzzer_pressure.duty(0)
previous_pressure = pressure
# ======================================
# Water Servo Logic
# ======================================
watering = False
watering_step = 0
last_move_time = 0
servo_angle(water_servo, 0)
def update_water(soil):
global watering, watering_step, last_move_time
now = time.ticks_ms()
if watering and soil >= SOIL_THRESHOLD:
servo_angle(water_servo, 0)
watering = False
watering_step = 0
print("Soil OK - Stop")
return
if (soil < SOIL_THRESHOLD) and (not watering):
watering = True
watering_step = 0
servo_angle(water_servo, positions[0])
last_move_time = now
print("Watering Started")
return
if not watering:
return
if time.ticks_diff(now, last_move_time) < WATER_WAIT:
return
watering_step += 1
if watering_step >= len(positions):
servo_angle(water_servo, 0)
watering = False
watering_step = 0
print("Watering Finished")
return
servo_angle(water_servo, positions[watering_step])
last_move_time = now
print("Water Position:", positions[watering_step])
# ======================================
# MQTT Callback
# ======================================
latest_soil = None
def callback(topic, msg):
global latest_soil
# التحكم المباشر بالـ Buzzer الجديد من Node-RED
if topic == TOPIC_BUZZER:
if msg == b'1':
buzzer_actuator.value(1)
print("Buzzer Actuator: ON")
elif msg == b'0':
buzzer_actuator.value(0)
print("Buzzer Actuator: OFF")
return
# قراءة الحساسات عند استقبال رسائل الحساسات
if topic == TOPIC_SENSORS:
try:
sensor_data = ujson.loads(msg)
update_ring(sensor_data.get("distance"))
update_temp(sensor_data.get("temp"))
update_humidity(sensor_data.get("humidity"))
update_motion(sensor_data.get("motion"))
update_pressure(sensor_data.get("pressure"))
update_light(sensor_data.get("light"))
latest_soil = sensor_data.get("soil")
except ValueError:
print("Ignored non-JSON message")
except Exception as e:
print("Callback error:", e)
# ======================================
# WiFi Connect
# ======================================
wifi = network.WLAN(network.STA_IF)
wifi.active(True)
wifi.connect(SSID, PASSWORD)
while not wifi.isconnected():
time.sleep(0.2)
print("WiFi Connected")
# ======================================
# MQTT Connect
# ======================================
client = MQTTClient(CLIENT_ID, BROKER)
client.set_callback(callback)
client.connect()
# الاشتراك في الـ Topics الاثنين
client.subscribe(TOPIC_SENSORS)
client.subscribe(TOPIC_BUZZER)
print("MQTT Connected & Subscribed to Topics")
# ======================================
# Main Loop
# ======================================
while True:
client.check_msg()
if latest_soil is not None:
update_water(latest_soil)
time.sleep_ms(50)Loading
esp32-devkit-c-v4
esp32-devkit-c-v4