#include <WiFi.h>
#include "PubSubClient.h"
#include "DHTesp.h"
#include <LiquidCrystal_I2C.h>
#include <ESP32Servo.h>
//Kênh gửi
const char * MQTTServer = "broker.emqx.io";
const char* data_ldr_Topic = "IoT/Blinds"; //LDR
const char* data_gas_Topic = "IoT/Gasensor"; //GAS
const char* data_pir_Topic = "IoT/PIRSensor"; //PIR
const char* data_ultra_Topic = "IoT/Ultrasonic"; //Ultrasonic
const char* data_rain_Topic = "IoT/RAINSensor"; //Rain
const char* data_dht_Topic = "IoT/DHT22"; //TEMP & HUMI
const char* dhtCharTopic = "IoT/CharData";
const char* alertTopic = "IoT/Alert"; // nhiệt độ high/low/normal
// Kênh nhận
const char* doorControlTopic = "IoT/DoorControl";
const char* doorControlTopicResponse = "IoT/DoorControl/Response";
const char* AirControlTopic = "IoT/AirControl";
const char* AirControlTopicResponse = "IoT/AirControlTopic/Response";
const char* homeControlTopic = "IoT/HomeControl";
const char* homeControlTopicResponse = "IoT/HomeControl/Response";
// Tạo ID ngẫu nhiên tại: https://www.guidgen.com/
const char * MQTT_ID = "c5afa5ae-d3a0-48b7-b850-92015a281909";
int Port = 1883;
WiFiClient espClient;
PubSubClient client(espClient);
//............................................................................................................
LiquidCrystal_I2C lcd(0x27, 20, 4);
//Các đèn led báo hiệu
const int ledAir = 16; //led xanh lam : máy điều hòa
const int ledHome = 17; //led trắng : thể hiện đèn trong nhà, cửa ngoài, sân
const int ledWar = 4; //led đỏ : thể hiện cảnh báo nhấp nháy hoặc sáng liên tục
const int ledPir = 32; //led cam : đèn ở cửa trong nhà, nhà vệ sinh
//DHT22
const int DHT_PIN = 23;
DHTesp dhtSensor;
//Buzzer
const int buzzer = 14;
//Gas Sensor
const int gasPin = 12;
//PIR
const int pir = 25;
//LDR
const int LDR_PIN = 34;
//Rain Sensor
const int RAIN_ANALOG = 18;
const int RAIN_DIGITAL = 19;
//Ultrasonic
const int TRIG_PIN = 27;
const int ECHO_PIN = 26;
float duration_us, distance_cm;
//Servo
const int servoBlindsPin = 15;
Servo servoBlinds; //Rèm cửa
const int servoWindowPin = 13;
Servo servoWindow; //Cửa sổ
const int servoDoorPin = 2;
Servo servoDoor; //Cửa nhà
const int servoDryingRackPin = 5;
Servo servoDryingRack; //Giàn phơi đồ
void WIFIConnect() {
Serial.println("Connecting to SSID: Wokwi-GUEST");
WiFi.begin("Wokwi-GUEST", "");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("WiFi connected");
Serial.print(", IP address: ");
Serial.println(WiFi.localIP());
}
void MQTT_Reconnect() {
while (!client.connected()) {
if (client.connect(MQTT_ID)) {
Serial.print("MQTT Topic: ");
Serial.print(data_ldr_Topic);
Serial.println(" connected");
Serial.println("");
Serial.print("MQTT Topic: ");
Serial.print(data_gas_Topic);
Serial.println(" connected");
Serial.println("");
Serial.print("MQTT Topic: ");
Serial.print(data_pir_Topic);
Serial.println(" connected");
Serial.println("");
Serial.print("MQTT Topic: ");
Serial.print(data_ultra_Topic);
Serial.println(" connected");
Serial.println("");
Serial.print("MQTT Topic: ");
Serial.print(data_rain_Topic);
Serial.println(" connected");
Serial.println("");
Serial.print("MQTT Topic: ");
Serial.print(data_dht_Topic);
Serial.println(" connected");
Serial.println("");
Serial.print("MQTT Topic: ");
Serial.print(alertTopic);
Serial.println(" connected");
Serial.println("");
Serial.print("MQTT Topic: ");
Serial.print(dhtCharTopic);
Serial.println(" connected");
Serial.println("");
} else {
Serial.print("failed, rc = ");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void callback(char* topic, byte* message, unsigned int length) {
Serial.print("Message arrived on topic: ");
Serial.println(topic);
Serial.print("Message: ");
String receivedMessage;
for (int i = 0; i < length; i++) {
Serial.print((char)message[i]);
receivedMessage += (char)message[i];
}
//Mở cửa nhà
Serial.println();
if (String(topic) == doorControlTopic) {
if (receivedMessage == "Open The Door") {
servoDoor.write(180); // Mở cửa nhà
client.publish(doorControlTopicResponse, "Cửa đã mở");
} else if (receivedMessage == "Closed The Door") {
servoDoor.write(0); // Đóng cửa nhà
client.publish(doorControlTopicResponse, "Cửa đã đóng");
}
}
if (String(topic) == AirControlTopic) {
if (receivedMessage == "Turn on Air") {
digitalWrite(ledAir, HIGH); // Bật lò sưởi
client.publish(AirControlTopicResponse, "Điều hòa đã bật");
} else if (receivedMessage == "Turn off Air") {
digitalWrite(ledAir, LOW); // Tắt lò sưởi
client.publish(AirControlTopicResponse, "Điều hòa đã tắt");
}
}
if (String(topic) == homeControlTopic) {
if (receivedMessage == "Turn on Led Home") {
digitalWrite(ledHome, HIGH); // Bật ledHome
client.publish(homeControlTopicResponse, "Đèn đã bật");
} else if (receivedMessage == "Turn on Led Home") {
digitalWrite(ledHome, LOW); // Tắt ledHome
client.publish(homeControlTopicResponse, "Đèn đã tắt");
}
}
}
void setup() {
Serial.begin(115200);
//LCD
lcd.init();
lcd.backlight();
noTone(buzzer);
// Print something
lcd.setCursor(2, 0);
lcd.print("Welcome IOT!");
//Rain Sensor
pinMode(RAIN_ANALOG, INPUT);
pinMode(RAIN_DIGITAL, INPUT);
analogReadResolution(10);
//Ultrasonic
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
//DHT22
dhtSensor.setup(DHT_PIN, DHTesp::DHT22);
//Led và buzzer
pinMode(ledAir, OUTPUT); //led xanh lam: máy điều hòa
pinMode(ledHome, OUTPUT); // led trắng: đèn ở khu vực luôn sáng khi trời tối như cửa ngoài, cổng...
pinMode(ledWar, OUTPUT); //led đỏ: cảnh báo có khí gas
pinMode(ledPir, OUTPUT); //led cam: đèn cửa trong nhà, nhà vệ sinh, khu ít người qua lại
digitalWrite(ledAir, LOW);
digitalWrite(ledHome, LOW);
digitalWrite(ledWar, LOW);
digitalWrite(ledPir, LOW);
//Servo
servoBlinds.attach(servoBlindsPin, 500, 2400);
servoBlinds.write(0);
servoWindow.attach(servoWindowPin, 500, 2400);
servoWindow.write(0);
servoDryingRack.attach(servoDryingRackPin, 500, 2400);
servoDryingRack.write(0);
servoDoor.attach(servoDoorPin, 500, 2400);
servoDoor.write(0);
//Buzzer
pinMode(buzzer, OUTPUT);
//Gas Sensor
pinMode(gasPin, INPUT);
//LDR
pinMode(LDR_PIN, INPUT);
//PIR
pinMode(pir, INPUT);
WIFIConnect();
client.setServer(MQTTServer, Port);
client.setCallback(callback);
//Đăng ký kênh nhận
client.subscribe(doorControlTopic);
client.subscribe(AirControlTopic);
client.subscribe(homeControlTopic);
}
//LDR: điều khiển mở/đóng rèm cửa theo cường độ ánh sáng, map giá trị từ 1 đến 100 lux................................................................
void controlBlinds() {
int rawLight = analogRead(LDR_PIN);
int lightValue = map(rawLight, 0, 1024, 100, 0);
Serial.print("Lux: ");
Serial.println(lightValue);
if (lightValue > 50) {
Serial.println("Mở rèm cửa");
servoBlinds.write(180); // mở rèm
delay(500);
//Gửi
client.publish(data_ldr_Topic, "Open the blinds");
} else {
Serial.println("Đóng rèm cửa");
servoBlinds.write(0);
delay(500);
//Gửi
client.publish(data_ldr_Topic, "Close the blinds");
}
}
//GAS: khi có khí gas thì đồng thời bật còi báo động, led cảnh báo và mở cửa sổ...................................................................
unsigned long previousMillis = 0;
const long interval = 250;
void checkGasSensor() {
unsigned long currentMillis = millis();
if (digitalRead(gasPin) == HIGH) {
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
Serial.println("Cảnh báo: Có khí GAS rò gỉ");
digitalWrite(ledWar, HIGH); // Nhấp nháy đèn LED
tone(buzzer, 2000); //Bật còi báo động
servoWindow.write(180); //Mở cửa sổ
delay(500);
lcd.setCursor(0, 2); // Đặt con trỏ tại dòng thứ ba để hiển thị lên lcd
lcd.print("CANH BAO: Co khi GAS");
// Gửi tin nhắn đến MQTT
client.publish(data_gas_Topic, "Gas leak");
}
} else {
Serial.println("Không có khí GAS");
digitalWrite(ledWar, LOW); // Đảm bảo đèn LED tắt
noTone(buzzer); //Tắt còi báo động
servoWindow.write(0); //Đóng cửa sổ
delay(500);
lcd.setCursor(0, 2); // Đặt con trỏ tại dòng thứ ba
lcd.print(" "); // Xóa dòng thứ ba
// Gửi tin nhắn đến MQTT
client.publish(data_gas_Topic, "No gas detected");
}
}
//PIR: khi phát hiện chuyển động thì bật đèn ở cửa, nhà vệ sinh..................................................................................
bool pirState = LOW;
void checkPIRSensor() {
bool currentPIRState = digitalRead(pir);
if (currentPIRState != pirState) {
pirState = currentPIRState;
if (pirState == HIGH) {
Serial.println("Phát hiện chuyển động");
digitalWrite(ledPir, HIGH);// Bật đèn
lcd.setCursor(0, 3);
lcd.print("Motion Detected");
// Gửi thông điệp MQTT
client.publish(data_pir_Topic, "Motion Detected");
} else {
Serial.println("Không phát hiện chuyển động");
digitalWrite(ledPir, LOW);// tắt đèn
lcd.setCursor(0, 3);
lcd.print(" ");
// Gửi thông điệp MQTT
client.publish(data_pir_Topic, "No Motion Detected");
}
}
}
//ULTRA: khi mực nước hơn 5 cm thì bật còi và đèn...................................................................................................
const float height = 350; //độ cao nơi gắn cảm biến 350 cm
const float height_War = 50; // độ cao mực nước cảnh báo
void checkUltrasonic() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
duration_us = pulseIn(ECHO_PIN, HIGH);
distance_cm = height - duration_us * 0.034 / 2;
Serial.print("Độ cao mực nước: ");
Serial.println(distance_cm);
if (distance_cm > height_War) {
digitalWrite(ledWar, HIGH); // Bật đèn cảnh báo
tone(buzzer, 1000); // Bật còi báo động
//Gửi
client.publish(data_ultra_Topic, "Flooding");
} else {
digitalWrite(ledWar, LOW); // Tắt đèn cảnh báo
noTone(buzzer); // Tắt còi báo động
}
//Gửi
client.publish(data_ultra_Topic, "No Flooding");
}
//RAIN: khi trời mưa thì kéo dàn phơi đồ và ngược lại..........................................................................................
void checkRainSensor() {
int rainAnalogValue = analogRead(RAIN_ANALOG);
bool isRaining = digitalRead(RAIN_DIGITAL) == HIGH;
// Serial.print("Rain amount: ");
// Serial.println(rainAnalogValue);
if (isRaining) {
Serial.println("It's raining! Kéo giàn phơi vào.");
servoDryingRack.write(0); // Kéo giàn phơi vào
delay(500);
//Gửi
client.publish(data_rain_Topic, "Rain Detected");
} else {
Serial.println("No rain detected. Kéo giàn phơi ra.");
servoDryingRack.write(180); // Kéo giàn phơi ra
delay(500);
//Gửi
client.publish(data_rain_Topic, "No Rain Detected");
}
}
//DHT: Gửi nhiệt độ và độ ẩm, bật điều hòa khi nhiệt độ > 40 && < 20.......................................................................................................................................
const int NUM_READINGS = 5;
float tempReadings[NUM_READINGS] = {0.0};
float humReadings[NUM_READINGS] = {0.0};
int readIndex = 0;
float totalTemp = 0.0;
float totalHum = 0.0;
int numActualReadings = 0; // Biến theo dõi số lượng đọc giá trị thực tế
void readAndDisplayDHT() {
// Đọc giá trị nhiệt độ và độ ẩm
float temperature = dhtSensor.getTemperature();
float humidity = dhtSensor.getHumidity();
// Kiểm tra xem giá trị đọc được có hợp lệ
if (isnan(temperature) || isnan(humidity)) {
lcd.clear();
lcd.print("Error reading DHT");
return;
}
// Định dạng dữ liệu để gửi
String payload = String(temperature) + "," + String(humidity);
// Gửi dữ liệu đến topic MQTT
client.publish(dhtCharTopic, payload.c_str());
// Cập nhật mảng trượt của giá trị nhiệt độ và độ ẩm
totalTemp -= tempReadings[readIndex];
totalHum -= humReadings[readIndex];
tempReadings[readIndex] = temperature;
humReadings[readIndex] = humidity;
totalTemp += temperature;
totalHum += humidity;
if (numActualReadings < NUM_READINGS) {
numActualReadings++;
}
readIndex = (readIndex + 1) % NUM_READINGS;
// Tính trung bình
float avgTemp = (numActualReadings == NUM_READINGS) ? (totalTemp / NUM_READINGS) : (totalTemp / numActualReadings);
float avgHum = (numActualReadings == NUM_READINGS) ? (totalHum / NUM_READINGS) : (totalHum / numActualReadings);
// Gửi dữ liệu nhiệt độ và độ ẩm
char tempHumString[20];
sprintf(tempHumString, "%0.2f,%0.2f", avgTemp, avgHum);
client.publish(data_dht_Topic, tempHumString);
// Kiểm tra ngưỡng và hiển thị giá trị hoặc cảnh báo
if (avgTemp > 40) {
Serial.println("Nhiệt độ trên 40°C");
lcd.setCursor(0, 0);
// Đặt con trỏ vào vị trí đầu của dòng 1
lcd.print(" "); // Ghi đè dòng 1 bằng khoảng trắng
lcd.setCursor(0, 1); // Đặt con trỏ vào vị trí đầu của dòng 2
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print("WARNING: Temp HIGH");
digitalWrite(ledAir, HIGH);// bật điều hòa
//Gửi
client.publish(alertTopic, "WARNING: HIGH");
} else if (avgTemp < 20) {
lcd.setCursor(0, 0); // Đặt con trỏ vào vị trí đầu của dòng 1
lcd.print(" "); // Ghi đè dòng 1 bằng khoảng trắng
lcd.setCursor(0, 1); // Đặt con trỏ vào vị trí đầu của dòng 2
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print("WARNING: Temp LOW");
digitalWrite(ledAir, HIGH); //bật điều hòa
//Gửi
client.publish(alertTopic, "WARNING: LOW");
} else {
// Hiển thị thông thường nếu không vượt ngưỡng
lcd.setCursor(0, 0); // Đặt con trỏ vào vị trí đầu của dòng 1
lcd.print(" "); // Ghi đè dòng 1 bằng khoảng trắng
lcd.setCursor(0, 1); // Đặt con trỏ vào vị trí đầu của dòng 2
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print("NhietDo: ");
lcd.print(temperature);
lcd.print(" C");
lcd.setCursor(0, 1);
lcd.print("DoAm: ");
lcd.print(humidity);
lcd.print("%");
digitalWrite(ledAir, LOW);// tắt điều hòa
//Gửi
client.publish(alertTopic, "WARNING: NORMAL");
}
}
//--------------------------------------------------------
void process() {
checkPIRSensor();
controlBlinds();
checkGasSensor();
readAndDisplayDHT();
checkUltrasonic();
checkRainSensor();
Serial.println("......................");
}
void loop() {
if (!client.connected()) {
MQTT_Reconnect();
client.subscribe(doorControlTopic);
client.subscribe(doorControlTopicResponse);
client.subscribe(AirControlTopic);
client.subscribe(AirControlTopicResponse);
client.subscribe(homeControlTopic);
client.subscribe(homeControlTopicResponse);
}
client.loop();
process();
delay(1000);
}