// Khai báo thư viện
#include "DHTesp.h" // Điều khiển DHT22
#include <WiFi.h> // Kết nối wifi
#include <ThingSpeak.h> // Lưu dữ liệu lên cloud ThingSpeak
#include <PubSubClient.h> // Kết nối MQTTServer
#include <ArduinoJson.h> // Đóng gói dữ liệu thành Json
#include <Wire.h> // Giao tiếp với các thiết bị I2C
#include <LiquidCrystal_I2C.h> // Điều khiển LCD 16x2
#include <TM1637.h> // Điều khiển module hiển thị 7 đoạn (4 chữ số)
#include "RTClib.h" // Điều khiển RTC DS1307
//===============================================================================
// Khai báo chân thiết bị
const int pinRedButton = 25;
const int pinGreenButton = 16;
const int pinBuzzer = 4;
const int pinGreenLed = 2;
const int pinDHT22 = 26;
const int pinPhotoSensor = 33;
const int pinSoundSensor = 34;
const int pinPIR = 27;
const int pinTRIG = 32;
const int pinECHO = 35;
const int pinRelayForLed = 5;
const int pinRelayForFan = 23;
const int CLK = 18;
const int DIO = 19;
//===============================================================================
// Khai báo biến toàn cục
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* MQTTServer = "broker.hivemq.com";
const int port = 1883;
const unsigned long myChannelId = 2788701;
const char* myWriteAPIKey = "UX62EIZAAZ2GTPDC";
WiFiClient ThingSpeakClient;
WiFiClient MQTTClient; // Được dùng để kết nối tới MQTT
PubSubClient client(MQTTClient); /*
Vẫn sử dụng wifiClient để kết nối đến MQTTServer nhưng đã được thư viện hỗ trợ */
bool isWorking = false;
bool isPresent = false;
bool stop = false;
bool colon = false;
bool isLedAuto = true;
bool isLedSend = false;
bool isFanAuto = true;
bool isFanSend = false;
unsigned long lastBlinkTime = 0;
unsigned long lastPubEnvDataTime = 0;
unsigned long lastStoreEnvDataTime = 0;
unsigned long lastCountdownTime = 0;
unsigned long lastToneTime = 0;
unsigned long lastMillis = 0;
unsigned long lastDistance = 0;
unsigned long lastPresent = 0;
int lastRedState = LOW;
int lastGreenState = LOW;
int ledState = LOW;
const float GAMMA = 0.7;
const float RL10 = 50;
DHTesp DHT22;
float tempVal = 0;
float humidVal = 0;
float lightVal = 500;
int soundVal = 0;
LiquidCrystal_I2C lcd(0x27, 16, 2);
TM1637 tm1637;
RTC_DS1307 rtc;
bool PomodoroTechnique = false;
bool TimeBlocking = false;
bool TimeBoxing = false;
unsigned int step = 0;
unsigned int nowHour = 0;
unsigned int nowMinute = 0;
unsigned int hour = 0;
unsigned int minute = 0;
unsigned long second = 0;
unsigned int startHour = 0;
unsigned int startMinute = 0;
unsigned int endHour = 0;
unsigned int endMinute = 0;
//===============================================================================
// Các hàm của Lê Thành Lợi
void ledSV22() {
unsigned long currentTime = millis();
if(currentTime >= lastBlinkTime + 1000) {
ledState = !ledState;
digitalWrite(pinGreenLed, ledState ? HIGH : LOW);
lastBlinkTime = currentTime;
}
}
void getEnvData() {
TempAndHumidity data = DHT22.getTempAndHumidity();
tempVal = data.temperature;
humidVal = data.humidity;
float analogValue = analogRead(pinPhotoSensor);
float voltage = analogValue / 4096.0 * 5.0;
float resistance = 2000.0 * voltage / (1.0 - voltage / 5.0);
lightVal = pow(RL10 * 1e3 * pow(10.0, GAMMA) / resistance, (1.0 / GAMMA));
soundVal = analogRead(pinSoundSensor);
}
void publishEnvData() {
unsigned long currentTime = millis();
if(currentTime >= lastPubEnvDataTime + 5000) {
StaticJsonDocument<200> jsonDoc;
jsonDoc["temperature"] = tempVal;
jsonDoc["humidity"] = humidVal;
jsonDoc["light"] = lightVal;
jsonDoc["sound"] = soundVal;
char buffer[200];
serializeJson(jsonDoc, buffer);
client.publish("/SV22/in/EnvData", buffer);
lastPubEnvDataTime = currentTime;
}
}
void storeEnvData() {
unsigned long currentTime = millis();
if(currentTime >= lastStoreEnvDataTime + 30000) {
float ran1 = random(-4000, 8001) / 100.0;
float ran2 = random(0, 10001) / 100.0;
float ran3 = random(1, 100001) / 100.0;
int ran4 = random(0, 181);
ThingSpeak.setField(1, ran1);
ThingSpeak.setField(2, ran2);
ThingSpeak.setField(3, ran3);
ThingSpeak.setField(4, ran4);
ThingSpeak.writeFields(myChannelId, myWriteAPIKey);
lastStoreEnvDataTime = currentTime;
}
}
long getDistance() {
digitalWrite(pinTRIG, LOW);
delayMicroseconds(2);
digitalWrite(pinTRIG, HIGH);
delayMicroseconds(10);
digitalWrite(pinTRIG, LOW);
long duration = pulseIn(pinECHO, HIGH);
long distance = duration * 0.034 / 2.0;
return distance;
}
void checkPresence() {
int PIRVal = digitalRead(pinPIR);
if(PIRVal == 1) {
lastDistance = getDistance();
isPresent = true;
}
if(isPresent) {
unsigned long currentDistance = getDistance();
if(currentDistance > lastDistance + 50) {
unsigned long currentTime = millis();
if(currentTime > lastPresent + 10000) {
digitalWrite(pinGreenLed, LOW);
tone(pinBuzzer, 200, 560);
if(currentTime > lastPresent + 20000) {
client.publish("/SV22/in/Notification","");
noTone(pinBuzzer);
isPresent = false;
stop = true;
}
}
}
else {
lastPresent = millis();
int buttonState = digitalRead(pinGreenButton);
if(buttonState == HIGH) {
noTone(pinBuzzer);
lastDistance = getDistance();
}
}
}
}
void wifiConnect() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println(" Connected to Wifi!");
}
void mqttConnect() {
while(!client.connect("SV22-22127151-22127238"))
delay(5000);
Serial.println("Connected to MQTT Server!");
// Subscribe topic ở đây (thiết bị nhận tín hiệu từ web)
client.subscribe("/SV22/out/turnOn");
client.subscribe("/SV22/out/turnOff");
client.subscribe("/SV22/out/mode/led");
client.subscribe("/SV22/out/mode/fan");
client.subscribe("/SV22/out/mode/man/led");
client.subscribe("/SV22/out/mode/man/fan");
client.subscribe("/SV22/out/PomodoroTechnique");
client.subscribe("/SV22/out/PomodoroTechnique/stop");
client.subscribe("/SV22/out/TimeBlocking/setUp");
client.subscribe("/SV22/out/TimeBlocking");
client.subscribe("/SV22/out/TimeBlocking/stop");
}
// MQTT Receiver
void callback(char* topic, byte* message, unsigned int length) {
Serial.println(topic);
String payload;
for(int i = 0; i < length; i++)
payload += (char)message[i];
Serial.println(payload);
// Xử lý tín hiệu nhận ở đây
if(strcmp(topic, "/SV22/out/turnOn") == 0)
isWorking = true;
if(strcmp(topic, "/SV22/out/turnOff") == 0)
isWorking = false;
if(strcmp(topic, "/SV22/out/mode/led") == 0) {
if(payload == "AUTO")
isLedAuto = true;
else {
isLedAuto = false;
digitalWrite(pinRelayForLed, LOW);
}
}
if(strcmp(topic, "/SV22/out/mode/fan") == 0) {
if(payload == "AUTO")
isFanAuto = true;
else {
isFanAuto = false;
digitalWrite(pinRelayForFan, LOW);
}
}
if(strcmp(topic, "/SV22/out/mode/man/led") == 0) {
if(payload == "true")
digitalWrite(pinRelayForLed, HIGH);
else digitalWrite(pinRelayForLed, LOW);
}
if(strcmp(topic, "/SV22/out/mode/man/fan") == 0) {
if(payload == "true")
digitalWrite(pinRelayForFan, HIGH);
else digitalWrite(pinRelayForFan, LOW);
}
if(strcmp(topic, "/SV22/out/PomodoroTechnique") == 0) {
lastDistance = getDistance();
isPresent = true;
tm1637.displayClear();
lcd.clear();
lcd.setCursor(4, 0);
lcd.print("POMODORO");
lcd.setCursor(3, 1);
lcd.print("TECHNIQUE!");
tone(pinBuzzer, 2000, 200);
PomodoroTechnique = true;
digitalWrite(pinGreenLed, HIGH);
client.publish("/SV22/in/PomodoroTechnique/prepare", "prepare");
minute = 5;
second = 0;
step = 1;
}
if(strcmp(topic, "/SV22/out/PomodoroTechnique/stop") == 0)
if(PomodoroTechnique)
stop = true;
if(strcmp(topic, "/SV22/out/TimeBlocking/setUp") == 0)
sscanf(payload.c_str(), "%d:%d:%d:%d", &startHour, &startMinute, &endHour, &endMinute);
if(strcmp(topic, "/SV22/out/TimeBlocking") == 0) {
digitalWrite(pinGreenLed, LOW);
lcd.clear();
lcd.setCursor(6, 0);
lcd.print("TIME");
lcd.setCursor(4, 1);
lcd.print("BLOCKING");
tone(pinBuzzer, 2000, 200);
TimeBlocking = true;
step = 1;
}
if(strcmp(topic, "/SV22/out/TimeBlocking/stop") == 0)
if(TimeBlocking)
stop = true;
}
bool countdown() {
unsigned long currentTime = millis();
if(currentTime >= lastCountdownTime + 1000) {
tm1637.displayTime(minute, second, colon);
colon = !colon;
second--;
if(second == -1) {
second = 59;
minute--;
if(minute == -1)
return true;
}
lastCountdownTime = currentTime;
}
return false;
}
void checkStop() {
int greenButtonState = digitalRead(pinGreenButton);
if(greenButtonState != lastGreenState) {
lastMillis = millis();
lastGreenState = greenButtonState;
}
while(digitalRead(pinGreenButton) == HIGH) {
if(millis() > lastMillis + 5000) {
lastMillis = millis();
stop = true;
break;
}
}
}
bool confirm() {
if(digitalRead(pinGreenButton) == HIGH) {
digitalWrite(pinGreenLed, LOW);
return true;
}
unsigned long currentTime = millis();
if(currentTime > lastToneTime + 1000) {
lastToneTime = currentTime;
tone(pinBuzzer, 500, 500);
ledState = !ledState;
digitalWrite(pinGreenLed, ledState ? HIGH : LOW);
}
return false;
}
void pomodoroTechnique() {
if(!PomodoroTechnique)
return;
checkPresence();
if(stop) {
PomodoroTechnique = false;
client.publish("/SV22/in/PomodoroTechnique/stop", "stop");
tm1637.displayClear();
lcd.clear();
lcd.setCursor(4, 0);
lcd.print("STOPPED!");
isPresent = false;
step = 0;
stop = false;
return;
}
if(step == 1) {
// Prepare
checkStop();
if(stop)
return;
if(digitalRead(pinGreenButton) == HIGH || countdown()) {
tone(pinBuzzer, 2000, 200);
digitalWrite(pinGreenLed, LOW);
client.publish("/SV22/in/PomodoroTechnique/start", "start1");
minute = 0;
second = 10;
step = 2;
}
}
else if(step == 2) {
// Cycle 1
checkStop();
if(stop)
return;
if(countdown()) {
tone(pinBuzzer, 2000, 200);
delay(400);
tone(pinBuzzer, 2000, 200);
delay(400);
tone(pinBuzzer, 2000, 200);
client.publish("/SV22/in/PomodoroTechnique/break", "break");
minute = 0;
second = 10;
step = 3;
}
}
else if(step == 3) {
// Break 1
checkStop();
if(stop)
return;
ledState = !ledState;
digitalWrite(pinGreenLed, ledState ? HIGH : LOW);
if(countdown())
step = 4;
}
else if(step == 4 && confirm()) {
client.publish("/SV22/in/PomodoroTechnique/start", "start2");
minute = 0;
second = 10;
step = 5;
}
else if(step == 5) {
// Cycle 2
checkStop();
if(stop)
return;
if(countdown()) {
tone(pinBuzzer, 2000, 200);
delay(400);
tone(pinBuzzer, 2000, 200);
delay(400);
tone(pinBuzzer, 2000, 200);
client.publish("/SV22/in/PomodoroTechnique/break", "break");
minute = 0;
second = 10;
step = 6;
}
}
else if(step == 6) {
// Break 2
checkStop();
if(stop)
return;
ledState = !ledState;
digitalWrite(pinGreenLed, ledState ? HIGH : LOW);
if(countdown())
step = 7;
}
else if(step == 7 && confirm()) {
client.publish("/SV22/in/PomodoroTechnique/start", "start3");
minute = 0;
second = 10;
step = 8;
}
else if(step == 8) {
// Cycle 3
checkStop();
if(stop)
return;
if(countdown()) {
tone(pinBuzzer, 2000, 200);
delay(400);
tone(pinBuzzer, 2000, 200);
delay(400);
tone(pinBuzzer, 2000, 200);
client.publish("/SV22/in/PomodoroTechnique/break", "break");
minute = 0;
second = 10;
step = 9;
}
}
else if(step == 9) {
// Break 3
checkStop();
if(stop)
return;
ledState = !ledState;
digitalWrite(pinGreenLed, ledState ? HIGH : LOW);
if(countdown())
step = 10;
}
else if(step == 10 && confirm()) {
client.publish("/SV22/in/PomodoroTechnique/start", "start4");
minute = 0;
second = 10;
step = 11;
}
else if(step == 11) {
// Cycle 4
checkStop();
if(stop)
return;
if(countdown()) {
client.publish("/SV22/in/PomodoroTechnique/end", "end");
tone(pinBuzzer, 500, 2000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("CONGRATULATIONS!");
isPresent = false;
step = 0;
PomodoroTechnique = false;
}
}
}
//===============================================================================
// Các hàm của Lâm Tiến Huy
void timeSV22() {
DateTime now = rtc.now();
nowHour = now.hour();
nowMinute = now.minute();
tm1637.displayTime(nowHour, nowMinute, colon);
colon = !colon;
}
void ledControl() {
if(isLedAuto) {
if(lightVal < 300) {
digitalWrite(pinRelayForLed, HIGH);
if(!isLedSend) {
client.publish("/SV22/in/mode/auto/led", "on");
isLedSend = true;
}
}
else {
digitalWrite(pinRelayForLed, LOW);
if(isLedSend) {
client.publish("/SV22/in/mode/auto/led", "off");
isLedSend = false;
}
}
}
}
void fanControl() {
if(isFanAuto) {
if(tempVal > 25 || humidVal > 40) {
digitalWrite(pinRelayForFan, HIGH);
if(!isFanSend) {
client.publish("/SV22/in/mode/auto/fan", "on");
isFanSend = true;
}
}
else {
digitalWrite(pinRelayForFan, LOW);
if(isFanSend) {
client.publish("/SV22/in/mode/auto/fan", "off");
isFanSend = false;
}
}
}
}
void timeBlocking() {
if(!TimeBlocking)
return;
if(stop) {
TimeBlocking = false;
client.publish("/SV22/in/TimeBlocking/stop", "stop");
tm1637.displayClear();
lcd.clear();
lcd.setCursor(4, 0);
lcd.print("STOPPED!");
isPresent = false;
step = 0;
stop = false;
return;
}
timeSV22();
if(step == 1) {
if(startHour == endHour && startMinute == endMinute) {
client.publish("/SV22/in/TimeBlocking/makeError", "error");
TimeBlocking = false;
lcd.clear();
step = 0;
return;
}
step = 2;
}
else if(step == 2) {
checkStop();
if(stop)
return;
hour = nowHour;
minute = nowMinute + 1;
if(minute == 60) {
minute = 0;
hour++;
if(hour == 24)
hour = 0;
}
if(hour == startHour && minute == startMinute) {
client.publish("/SV22/in/TimeBlocking/remind", "remind");
client.publish("/SV22/in/TimeBlocking/Notification", "");
step = 3;
}
}
else if(step == 3) {
checkStop();
if(stop)
return;
if(nowHour == startHour && nowMinute == startMinute) {
client.publish("/SV22/in/TimeBlocking/confirmStart", "confirmStart");
step = 4;
}
}
else if(step == 4) {
checkStop();
if(stop)
return;
if(confirm()) {
client.publish("/SV22/in/TimeBlocking/start", "start");
isPresent = true;
lastDistance = getDistance();
step = 5;
}
}
else if(step == 5) {
checkPresence();
checkStop();
if(stop)
return;
if(nowHour == endHour && nowMinute == endMinute) {
client.publish("/SV22/in/TimeBlocking/confirmEnd", "confirmEnd");
step = 6;
}
}
else if(step == 6) {
checkStop();
if(stop)
return;
if(confirm()) {
client.publish("/SV22/in/TimeBlocking/end", "end");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("CONGRATULATIONS!");
isPresent = false;
step = 0;
TimeBlocking = false;
}
}
}
//===============================================================================
void setup() {
Serial.begin(9600);
Serial.print("Connecting to WiFi: ");
wifiConnect();
ThingSpeak.begin(ThingSpeakClient);
client.setServer(MQTTServer, port);
client.setCallback(callback);
client.setKeepAlive(90);
pinMode(pinRedButton, INPUT);
pinMode(pinGreenButton, INPUT);
pinMode(pinBuzzer, OUTPUT);
pinMode(pinGreenLed, OUTPUT);
DHT22.setup(pinDHT22, DHTesp::DHT22);
pinMode(pinPhotoSensor, INPUT);
pinMode(pinSoundSensor, INPUT);
pinMode(pinPIR, INPUT);
pinMode(pinTRIG, OUTPUT);
pinMode(pinECHO, INPUT);
pinMode(pinRelayForLed, OUTPUT);
pinMode(pinRelayForFan, OUTPUT);
lcd.init();
lcd.backlight();
lcd.print("Welcome to SV22!");
tm1637.begin(CLK, DIO, 4);
tm1637.setBrightness(7);
if (! rtc.begin())
Serial.println("Couldn't find RTC");
randomSeed(analogRead(0));
}
void loop() {
if(!client.connected()) {
Serial.print("Connecting to MQTT: ");
mqttConnect();
}
client.loop();
int redButtonState = digitalRead(pinRedButton);
if(redButtonState != lastRedState) {
if(redButtonState == HIGH) {
isWorking = !isWorking;
if(isWorking)
client.publish("/SV22/in/turnOn", "on");
else client.publish("/SV22/in/turnOff", "off");
}
lastRedState = redButtonState;
}
if(isWorking) {
if(step == 0) {
ledSV22();
timeSV22();
}
getEnvData();
storeEnvData();
publishEnvData();
ledControl();
fanControl();
pomodoroTechnique();
timeBlocking();
}
else {
lcd.clear();
tm1637.displayClear();
digitalWrite(pinGreenLed, LOW);
digitalWrite(pinBuzzer, LOW);
digitalWrite(pinRelayForLed, LOW);
digitalWrite(pinRelayForFan, LOW);
PomodoroTechnique = false;
TimeBlocking = false;
TimeBoxing = false;
step = 0;
}
lastMillis = millis();
delay(200);
}