#define BLYNK_TEMPLATE_ID "TMPL6Rq3G5x8N"
#define BLYNK_TEMPLATE_NAME "Project1"
#define BLYNK_AUTH_TOKEN "Hij5jQrsI2n1E2Gim9Mpg17iY_uoKjp_" // Auth Token cho Blynk
// Thư viện cần cho dựa án
#include <DHT.h> // Thêm thư viện DHT
#include <BlynkSimpleEsp32.h>
#include <Wire.h> // Thư viện cho giao diện I2C
#include <Adafruit_GFX.h> // Thư viện đồ họa cho màn hình OLED
#include <Adafruit_SSD1306.h> // Thư viện cho màn hình OLED SSD1306
#include <ESP32Servo.h> // Thư viện Servo
#include <WiFi.h>
#include <NTPClient.h>
// Thiết lập các chân
//#define SOILMOISTURE_PIN 35 // Chân độ ẩm đất
#define OLED_SDA 21
#define OLED_SCL 22
#define DHTPIN 15 // Chân cảm biến DHT22
#define DHTTYPE DHT22 // Định nghĩa loại cảm biến DHT (DHT22)
#define LED_PIN 12 // Chân GPIO cho LED
#define RELAY_PIN 14 // Chân GPIO cho relay
#define SWITCH_VIRTUAL_PIN 4 // Chân ảo nhận tín hiệu từ Blynk
#define BLYNK_LED_VIRTUAL_PIN 3 // Chân ảo của LED trong Blynk
#define SERVO_PIN 32 // Chân GPIO điều khiển servo
#define CHECK_INTERVAL 10000 // 15 phút gọi hàm 1 lần
#define THRESHOLD_TEMP 35 // Ngưỡng nhiệt độ
//Kết nối Blynk và wifi
char auth[] = BLYNK_AUTH_TOKEN;
char ssid[] = "Wokwi-GUEST"; // SSID của Wi-Fi
char pass[] = ""; // Mật khẩu Wi-Fi
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 7 * 3600; // Offset từ GMT (Việt Nam là GMT+7)
const int daylightOffset_sec = 0; // Không có ánh sáng ban ngày (daylight saving time)
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, ntpServer, gmtOffset_sec, daylightOffset_sec);
BlynkTimer timer;
// Khởi tạo cảm biến nhiệt độ độ ẩm
Servo myServo; // Tạo đối tượng Servo
DHT dht(DHTPIN, DHTTYPE); // Khởi tạo đối tượng DHT với chân kết nối và loại cảm biến
int hour, minute;
int dayOfWeek, month;
float t, h;
// Cấu hình màn hình OLED
#define SCREEN_WIDTH 128 // Chiều rộng màn hình (128 pixels)
#define SCREEN_HEIGHT 64 // Chiều cao màn hình (64 pixels)
#define OLED_RESET -1 // Không sử dụng chân reset
#define OLED_I2C_ADDRESS 0x3C // Địa chỉ I2C mặc định của OLED
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET, OLED_SDA, OLED_SCL); // Khởi tạo đối tượng OLED
bool switchOfBlynk = false; // Biến cờ xác định trạng thái của switch Blynk
bool autoMode = true;
void turnOn(){
digitalWrite(RELAY_PIN, HIGH); // Bật relay
digitalWrite(LED_PIN, HIGH); // Bật LED của hệ thống
Blynk.virtualWrite(V3, 255); // Bật LED của Blynk(với giá trị 255)
myServo.write(90);
}
void turnOff(){
digitalWrite(RELAY_PIN, LOW); // Tắt relay
digitalWrite(LED_PIN, LOW); // Tắt LED của hệ thống
Blynk.virtualWrite(V3, 0); // Tắt LED của Blynk(với giá trị 0)
myServo.write(0);
}
void setup() {
Serial.begin(115200);
analogReadResolution(12); // Đặt độ phân giải ADC là 12 bit (0-4095)
dht.begin(); // Khởi động cảm biến DHT
Blynk.begin(auth, ssid, pass); // Kết nối đến Blynk bằng wifi đã thiết lập ở trên
timer.setInterval(2000L, sendData); // Thiết lập thời gian gửi dữ liệu (timer.run()), 2giây/lần
Wire.begin(OLED_SDA, OLED_SCL);
if (!oled.begin(SSD1306_SWITCHCAPVCC, OLED_I2C_ADDRESS)) {
while (true); // Dừng lại nếu không thể khởi động màn hình OLED
}
timeClient.begin();
pinMode(LED_PIN, OUTPUT); // Thiết lập LED là đầu ra
pinMode(RELAY_PIN, OUTPUT); // Thiết lập chân relay là đầu ra
myServo.attach(SERVO_PIN); // Kết nối servo với chân GPIO
turnOff();
}
void readData(){
h = dht.readHumidity(); // Đọc độ ẩm từ cảm biến DHT
t = dht.readTemperature(); // Đọc nhiệt độ từ cảm biến DHT
// Thông báo lỗi nếu đọc dữ liệu không thành công
if (isnan(h) || isnan(t)) {
return;
}
Serial.println("Nhiet do:" + String(t));
Serial.println("do am:" + String(h));
}
void sendData(){
//Đọc xong thì gửi dữ liệu vào các chân ảo tương ứng trong Blynk
Blynk.virtualWrite(V0, t); // Gửi dữ liệu nhiệt độ từ biến t đến Blynk
Blynk.virtualWrite(V1, h); // Gửi dữ liệu độ ẩm từ biến h đến Blynk
}
void displayOLED(){
// Xóa màn hình trước khi hiển thị thông tin mới
oled.clearDisplay();
// Hiển thị nhiệt độ và độ ẩm trên màn hình OLED
oled.setTextSize(0.5); // Chọn kích thước chữ
oled.setTextColor(SSD1306_WHITE); // Chọn màu chữ (trắng)
oled.setCursor(0, 0); // Đặt con trỏ để viết văn bản
oled.println("Nhom 20 - Do an 1");
oled.setCursor(0, 12); // Chuyển con trỏ xuống
oled.println("Nhiet do: " + String(t) + "C"); // Hiển thị nhiệt độ
oled.setCursor(0, 24); // Chuyển con trỏ xuống
oled.println("Do am: " + String(h) + "%"); // Hiển thị độ ẩm
oled.display(); // Cập nhật màn hình với thông tin mới
}
// Hàm đọc tín hiệu từ Blynk
BLYNK_WRITE(SWITCH_VIRTUAL_PIN) { // Chân ảo nhận tín hiệu từ Blynk
int state = param.asInt(); // Lấy trạng thái từ Blynk (0 hoặc 1)
if(state == 1) {
switchOfBlynk = true;
autoMode = false;
}
else{
switchOfBlynk = false;
}
}
static unsigned long startTime = 0; // Biến lưu thời điểm bắt đầu quá trình tưới
const unsigned long duration = 60000; // Thời gian tưới (ms), ví dụ 1 phút
static unsigned long temp = 0;
static bool watering = false;
//Cài đặt chế độ tưới, tưới tự động và tưới thủ công thông qua điều khiển trên Blynk
void mode(){
// Lấy thời gian hiện tại (theo múi giờ của Việt Nam)
String formattedTime = timeClient.getFormattedTime();
// Lấy ngày hiện tại (theo múi giờ của Việt Nam)
time_t epochTime = timeClient.getEpochTime();
struct tm *timeInfo;
timeInfo = localtime(&epochTime);
// Lấy giờ và phút
hour = timeInfo->tm_hour;
minute = timeInfo->tm_min;
// Lấy ngày trong tuần (0: Chủ nhật, 1: Thứ 2, ..., 6: Thứ 7)
dayOfWeek = timeInfo->tm_wday;
// Lấy tháng (0: Tháng 1, 1: Tháng 2, ..., 11: Tháng 12)
month = timeInfo->tm_mon + 1; // Thêm 1 vì tháng trong cấu trúc tm là từ 0 đến 11
// Kiểm tra điều kiện, nếu swithOn == false tức là nút tưới thủ công trên Blynk chưa được bấm
if(autoMode){ // Chế độ tưới tự động
if((month == 6) && (dayOfWeek == 1 || dayOfWeek == 6) && (hour == 21) && (minute <= 41)){
if(!watering){
startTime = millis();
watering = true;
turnOn();
}
}
temp = millis() - startTime;
if ((watering) && (temp >= duration)) {
Serial.println("Off");
turnOff(); // Tắt thiết bị
watering = false;
temp = 0;
}
}
else{ // Chế độ tưới thủ công
if(switchOfBlynk) turnOn();
else{
turnOff();
autoMode = true;
}
}
}
static unsigned long lastCheckTime = 0; // Biến lưu thời gian lần cuối cùng hàm checkTemperature() được gọi
static unsigned long currentTime = 0;
unsigned int count = 0; // Biến đếm
void checkTemperature() {
if (t > THRESHOLD_TEMP) { // Nếu nhiệt độ vượt quá ngưỡng
count++; // Tăng biến đếm
Serial.println(count);
Serial.println("\n");
}
}
void irrigate(){
if(count >= 5){
if(!watering){
startTime = millis();
watering = true;
turnOn();
count = 0;
}
}
temp = millis() - startTime;
Serial.println("temp: " + String(temp));
if ((watering) && (temp >= 15000)) {
Serial.println("Off");
turnOff(); // Tắt thiết bị
watering = false;
temp = 0;
}
}
void loop() {
currentTime = millis(); // Lấy thời gian hiện tại
timeClient.update();
readData();
displayOLED();
mode();
if ((currentTime - lastCheckTime) >= CHECK_INTERVAL) {
if (hour >= 23 && hour <= 24){
checkTemperature(); // Gọi hàm kiểm tra nhiệt độ
lastCheckTime = currentTime; // Cập nhật thời gian cuối cùng
}
}
irrigate();
Blynk.run(); // Vòng lặp chính của Blynk (Duy trì kết nối giữa hệ thống với Blynk)
timer.run(); // Thực hiện nhiệm vụ mỗi giây 1 lần
}