"""
ShrimpMate - IoT Module day du chuc nang (MicroPython, ESP32)
Chuc nang:
1) Doc cam bien: nhiet do (DHT22) + pH gia lap (bien tro)
2) Dieu khien mo-to: nut nhan vat ly + lenh tu xa qua MQTT
3) Canh bao an toan khi nhiet do / pH vuot nguong
4) Lich cho an tu dong dinh ky
5) Edge Buffer: luu tam du lieu khi mat mang, dong bo lai sau
6) Gui du lieu + canh bao len MQTT broker (mo phong Backend/App)
Xem du lieu MQTT tai: http://www.hivemq.com/demos/websocket-client/
- Connect -> Subscribe topic "shrimpmate/pond1/data" va "shrimpmate/pond1/alert"
- Publish lenh dieu khien: topic "shrimpmate/pond1/cmd", payload "ON" hoac "OFF"
"""
import network
import time
import ujson
from machine import Pin, ADC
import dht
from umqtt.simple import MQTTClient
# ---------------- Cau hinh chan ----------------
DHT_PIN = 15
PH_PIN = 34
RELAY_PIN = 26
LED_STATUS_PIN = 2
LED_ALERT_PIN = 4
BUTTON_PIN = 27
sensor = dht.DHT22(Pin(DHT_PIN))
ph_adc = ADC(Pin(PH_PIN))
ph_adc.atten(ADC.ATTN_11DB) # doc full range 0-3.3V
relay = Pin(RELAY_PIN, Pin.OUT)
led_status = Pin(LED_STATUS_PIN, Pin.OUT)
led_alert = Pin(LED_ALERT_PIN, Pin.OUT)
button = Pin(BUTTON_PIN, Pin.IN, Pin.PULL_UP)
# ---------------- Cau hinh MQTT ----------------
MQTT_CLIENT_ID = "shrimpmate-esp32"
MQTT_BROKER = "broker.mqttdashboard.com"
TOPIC_DATA = b"shrimpmate/pond1/data"
TOPIC_ALERT = b"shrimpmate/pond1/alert"
TOPIC_CMD = b"shrimpmate/pond1/cmd"
# ---------------- Nguong an toan (safety rule) ----------------
TEMP_MIN, TEMP_MAX = 26.0, 32.0
PH_MIN, PH_MAX = 7.0, 8.5
# ---------------- Trang thai he thong ----------------
motor_on = False
last_button_state = 1
edge_buffer = [] # luu tam khi mat mang
MAX_BUFFER = 20
READ_INTERVAL = 5 # doc cam bien moi 5s
FEED_INTERVAL = 30 # lich cho an tu dong moi 30s (demo)
last_read = 0
last_feed = 0
client = None
wifi_ok = False
def connect_wifi():
global wifi_ok
print("Dang ket noi WiFi", end="")
sta = network.WLAN(network.STA_IF)
sta.active(True)
sta.connect("Wokwi-GUEST", "")
tries = 0
while not sta.isconnected() and tries < 30:
print(".", end="")
time.sleep(0.3)
tries += 1
wifi_ok = sta.isconnected()
print(" Da ket noi!" if wifi_ok else " Khong ket noi duoc, dung Edge Buffer.")
return wifi_ok
def on_mqtt_message(topic, msg):
"""Nhan lenh dieu khien tu xa (mo phong lenh tu App/Web)."""
cmd = msg.decode().strip().upper()
print("[MQTT] Nhan lenh: {} -> {}".format(topic.decode(), cmd))
if cmd == "ON":
set_motor(True)
elif cmd == "OFF":
set_motor(False)
def connect_mqtt():
global client
try:
client = MQTTClient(MQTT_CLIENT_ID, MQTT_BROKER)
client.set_callback(on_mqtt_message)
client.connect()
client.subscribe(TOPIC_CMD)
print("[MQTT] Da ket noi va subscribe lenh dieu khien.")
flush_buffer()
return True
except Exception as e:
print("[MQTT] Loi ket noi:", e)
return False
def set_motor(state):
global motor_on
motor_on = state
relay.value(1 if state else 0)
led_status.value(1 if state else 0)
msg = ">> MO-TO: DA BAT (dang cho an...)" if state else ">> MO-TO: DA TAT."
print(msg)
def handle_button():
global last_button_state
current = button.value()
if last_button_state == 1 and current == 0:
set_motor(not motor_on)
time.sleep(0.3)
last_button_state = current
def read_ph():
raw = ph_adc.read() # 0 - 4095
return round(6.0 + (raw / 4095.0) * 3.0, 2) # gia lap pH 6.0 - 9.0
def publish_or_buffer(payload):
if client and wifi_ok:
try:
client.publish(TOPIC_DATA, payload)
print("[MQTT] Da gui:", payload)
return
except Exception as e:
print("[MQTT] Gui that bai:", e)
buffer_reading(payload)
def buffer_reading(payload):
if len(edge_buffer) < MAX_BUFFER:
edge_buffer.append(payload)
print("[Edge Buffer] Da luu tam ({} ban ghi).".format(len(edge_buffer)))
def flush_buffer():
global edge_buffer
if not edge_buffer:
return
for payload in edge_buffer:
try:
client.publish(TOPIC_DATA, payload)
except Exception:
return
print("[Edge Buffer] Da dong bo {} ban ghi len server.".format(len(edge_buffer)))
edge_buffer = []
def check_anomaly(temp, ph):
alert = False
reason = ""
if temp is not None and (temp < TEMP_MIN or temp > TEMP_MAX):
alert = True
reason += "Nhiet do bat thuong! "
if ph < PH_MIN or ph > PH_MAX:
alert = True
reason += "pH bat thuong! "
led_alert.value(1 if alert else 0)
if alert:
print("CANH BAO:", reason)
if client and wifi_ok:
try:
client.publish(TOPIC_ALERT, reason)
except Exception as e:
print("[MQTT] Gui canh bao that bai:", e)
def read_and_process():
try:
sensor.measure()
temp = sensor.temperature()
except Exception:
temp = None
print("Loi doc cam bien DHT22!")
ph = read_ph()
print("Nhiet do: {} C | pH: {}".format(temp, ph))
payload = ujson.dumps({"temp": temp, "ph": ph, "ts": time.ticks_ms()})
publish_or_buffer(payload)
check_anomaly(temp, ph)
def auto_feed():
print(">> Lich cho an tu dong: kich hoat mo-to...")
set_motor(True)
time.sleep(2)
set_motor(False)
# ---------------- Chuong trinh chinh ----------------
set_motor(False)
print("=== ShrimpMate IoT Module - Full Feature Demo ===")
if connect_wifi():
connect_mqtt()
last_read = time.time()
last_feed = time.time()
while True:
handle_button()
if client and wifi_ok:
try:
client.check_msg() # kiem tra lenh dieu khien tu xa (non-blocking)
except Exception as e:
print("[MQTT] Mat ket noi:", e)
wifi_ok = False
now = time.time()
if now - last_read >= READ_INTERVAL:
last_read = now
read_and_process()
if now - last_feed >= FEED_INTERVAL:
last_feed = now
auto_feed()
time.sleep(0.05)