#define BLYNK_TEMPLATE_ID "TMPL6kKq1mKbe"
#define BLYNK_TEMPLATE_NAME "LVTN"
#define BLYNK_AUTH_TOKEN "BB79xKB7QHgMaOjgH24rJtk2XWD875R-"
#include <BlynkSimpleEsp32.h>
#include <Wire.h>
// #include <PCF8574.h>
// #include <DHT.h>
// #include <LiquidCrystal_I2C.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// WiFi
char ssid[] = "Wokwi-GUEST";
char pass[] = "";
// --- Cảm biến ---
#define DHTPIN D4
#define DHTTYPE DHT11
// DHT dht(DHTPIN, DHTTYPE);
#define WATER_LEVEL_PIN 12
#define WATER_TEMP_PIN 11
OneWire oneWire(WATER_TEMP_PIN);
DallasTemperature waterSensor(&oneWire);
// --- LCD + I2C ---
// LiquidCrystal_I2C lcd(0x27, 16, 2);
// PCF8574 pcf8574(0x20);
// --- Encoder ---
#define ENCODER_CLK 4
#define ENCODER_DT 5
#define ENCODER_SW 6
volatile int8_t encoderDelta = 0;
// --- Thiết bị điều khiển ---
enum Device {
PUMP1 = 0,
PUMP2,
VALVE_PERCENT,
PUMP3,
FAN,
LIGHT
};
#define RELAY_COUNT 6
bool deviceStates[RELAY_COUNT] = {false, false, false, false, false, false};
int valvePercent = 0;
int currentMenu = 0;
bool inControlMenu = false;
unsigned long lastInteractionTime = 0;
// --- Auto Control ---
unsigned long lastPump2Time = 0;
const unsigned long PUMP2_INTERVAL = 48UL * 60UL * 60UL * 1000UL; // 48 giờ
bool pump2Running = false;
unsigned long pump2StartTime = 0;
// --- Encoder ngắt ---
void IRAM_ATTR encoderISR() {
static bool lastCLK = HIGH;
bool clk = digitalRead(ENCODER_CLK);
bool dt = digitalRead(ENCODER_DT);
if (clk != lastCLK) {
encoderDelta += (clk == dt) ? -1 : 1;
lastInteractionTime = millis();
inControlMenu = true;
}
lastCLK = clk;
}
void setup() {
Serial.begin(115200);
Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
Wire.begin(8, 9); // I2C
// lcd.init();
// lcd.backlight();
// dht.begin();
waterSensor.begin();
// --- Kiểm tra thiết bị ---
Serial.println(F("=== Khởi động hệ thống ==="));
Serial.print("✅ WiFi: ");
Serial.println(WiFi.status() == WL_CONNECTED ? "OK" : "❌");
Serial.print("✅ Blynk: ");
Serial.println(Blynk.connected() ? "OK" : "❌");
Serial.print("✅ PCF8574: ");
// Serial.println(pcf8574.begin() ? "OK" : "❌");
Serial.print("✅ DHT11: ");
// Serial.println(isnan(dht.readTemperature()) ? "❌ Lỗi" : "OK");
Serial.print("✅ DS18B20: ");
waterSensor.requestTemperatures();
float t = waterSensor.getTempCByIndex(0);
Serial.println(t == DEVICE_DISCONNECTED_C ? "❌ Lỗi" : "OK");
// Tắt toàn bộ thiết bị
for (int i = 0; i < RELAY_COUNT; i++) {
// pcf8574.write(i, HIGH);
}
// Encoder
pinMode(ENCODER_CLK, INPUT_PULLUP);
pinMode(ENCODER_DT, INPUT_PULLUP);
pinMode(ENCODER_SW, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), encoderISR, CHANGE);
lastInteractionTime = millis();
}
void loop() {
Blynk.run();
unsigned long now = millis();
autoControl(now);
if (!inControlMenu && now - lastInteractionTime > 2000) {
displaySensor();
lastInteractionTime = now;
}
if (inControlMenu && now - lastInteractionTime > 5000) {
inControlMenu = false;
// lcd.clear();
}
// --- Xử lý encoder xoay ---
if (encoderDelta != 0) {
int delta = encoderDelta;
encoderDelta = 0;
if (currentMenu == VALVE_PERCENT) {
valvePercent += delta * 5;
valvePercent = constrain(valvePercent, 0, 100);
Serial.println ("BlynkWrite V7");
//Blynk.virtualWrite(V7, valvePercent);
} else {
deviceStates[currentMenu] = delta > 0;
// pcf8574.write(currentMenu, deviceStates[currentMenu] ? LOW : HIGH);
updateBlynkState(currentMenu);
}
displayControl();
}
// --- Xử lý nút encoder ---
static bool lastButtonState = HIGH;
bool buttonState = digitalRead(ENCODER_SW);
if (lastButtonState == HIGH && buttonState == LOW) {
lastInteractionTime = now;
if (!inControlMenu) {
inControlMenu = true;
currentMenu = 0;
} else {
currentMenu = (currentMenu + 1) % RELAY_COUNT;
}
displayControl();
}
lastButtonState = buttonState;
// --- Tắt bơm 2 sau 5 giây ---
if (pump2Running && now - pump2StartTime >= 5000) {
// pcf8574.write(PUMP2, HIGH);
pump2Running = false;
deviceStates[PUMP2] = false;
updateBlynkState(PUMP2);
Serial.println("⏹️ Tắt bơm 2 sau 5s");
}
}
// ------------------ HÀM PHỤ --------------------
void autoControl(unsigned long now) {
float tempAir = 24.0;//dht.readTemperature();
int waterLevel = analogRead(WATER_LEVEL_PIN);
// Quạt & Sò lạnh - Đèn + tản nhiệt
if (tempAir > 27) {
Serial.println ("BlynkWrite V8");
// pcf8574.write(FAN, LOW); deviceStates[FAN] = true; //Blynk.virtualWrite(V8, true);
// pcf8574.write(LIGHT, HIGH); deviceStates[LIGHT] = false; //Blynk.virtualWrite(V9, false);
} else if (tempAir < 23) {
Serial.println ("BlynkWrite V9");
// pcf8574.write(FAN, HIGH); deviceStates[FAN] = false; //Blynk.virtualWrite(V8, false);
// pcf8574.write(LIGHT, LOW); deviceStates[LIGHT] = true; //Blynk.virtualWrite(V9, true);
} else {
Serial.println ("BlynkWrite V9, V8");
// pcf8574.write(FAN, HIGH); deviceStates[FAN] = false; //Blynk.virtualWrite(V8, false);
// pcf8574.write(LIGHT, HIGH); deviceStates[LIGHT] = false; //Blynk.virtualWrite(V9, false);
}
// Bơm 1 theo mực nước
if (waterLevel < 300) {
Serial.println ("BlynkWrite V5");
// pcf8574.write(PUMP1, LOW); deviceStates[PUMP1] = true; //Blynk.virtualWrite(V5, true);
} else if (waterLevel > 550) {
// pcf8574.write(PUMP1, HIGH); deviceStates[PUMP1] = false; //Blynk.virtualWrite(V5, false);
}
// Bơm 2 mỗi 48h
if (now - lastPump2Time >= PUMP2_INTERVAL) {
// pcf8574.write(PUMP2, LOW);
pump2Running = true;
pump2StartTime = now;
deviceStates[PUMP2] = true;
Serial.println ("BlynkWrite V6");
// Blynk.virtualWrite(V6, true);
lastPump2Time = now;
Serial.println("🟢 Bật bơm 2 theo chu kỳ 48h");
}
}
void displayControl() {
// lcd.clear();
if (currentMenu != VALVE_PERCENT) {
// pcf8574.write(currentMenu, deviceStates[currentMenu] ? LOW : HIGH);
}
// lcd.setCursor(0, 0);
switch (currentMenu) {
// case PUMP1: lcd.print("Bom 1: "); lcd.print(deviceStates[PUMP1] ? "Bat" : "Tat"); break;
// case PUMP2: lcd.print("Bom 2: "); lcd.print(deviceStates[PUMP2] ? "Bat" : "Tat"); break;
// case VALVE_PERCENT: lcd.print("Van: "); lcd.print(valvePercent); lcd.print("%"); break;
// case PUMP3: lcd.print("Bom 3: "); lcd.print(deviceStates[PUMP3] ? "Bat" : "Tat"); break;
// case FAN: lcd.print("Quat + So: "); lcd.print(deviceStates[FAN] ? "Bat" : "Tat"); break;
// case LIGHT: lcd.print("Den + Tan: "); lcd.print(deviceStates[LIGHT] ? "Bat" : "Tat"); break;
}
// lcd.setCursor(0, 1);
// lcd.print("Nhay/Nhan de doi");
}
void displaySensor() {
float tempAir = 24.0;//dht.readTemperature();
float humid = 50.0;//dht.readHumidity();
int level = map(analogRead(WATER_LEVEL_PIN), 0, 1023, 0, 100);
waterSensor.requestTemperatures();
float tempWater = waterSensor.getTempCByIndex(0);
// lcd.setCursor(0, 0);
// lcd.print("T:"); lcd.print(tempAir); lcd.print((char)223); lcd.print(" H:"); lcd.print(humid); lcd.print("%");
// lcd.setCursor(0, 1);
// lcd.print("Nuoc:"); lcd.print(level); lcd.print("% ");
// lcd.print("N:"); lcd.print(tempWater); lcd.print((char)223);
Serial.println ("BlynkWrite V1");
Serial.println ("BlynkWrite V2");
Serial.println ("BlynkWrite V3");
Serial.println ("BlynkWrite V4");
// Blynk.virtualWrite(V1, tempAir);
// Blynk.virtualWrite(V2, humid);
// Blynk.virtualWrite(V3, tempWater);
// Blynk.virtualWrite(V4, level);
}
void updateBlynkState(int index) {
int vpin[] = {V5, V6, V7, V12, V8, V9};
Serial.println ("BlynkWrite V5, V6, V7, V12, V8, V9");
if (index >= 0 && index < 6) {
//Blynk.virtualWrite(vpin[index], deviceStates[index]);
}
}