Hotline/Zalo: 0919.890.938 (Mr Hơn)
Bài 1. Đồng hồ thời gian thực dùng ESP32 và màn hình OLED
Đồng hồ thời gian thực (RTC) là một dự án thú vị cho những ai đam mê điện tử và lập trình. Bằng cách kết hợp ESP32 với màn hình OLED, bạn có thể tạo ra một chiếc đồng hồ thời gian thực chính xác và thẩm mỹ. Trong bài viết này, Điện thông minh E-smart sẽ cùng các bạn tìm hiểu cách xây dựng đồng hồ thời gian thực này từ cấu hình phần cứng đến lập trình phần mềm.
RTC là gì?
RTC (Real-Time Clock) là một thành phần quan trọng giúp theo dõi thời gian thực mà không bị ảnh hưởng bởi việc mất điện hoặc khởi động lại hệ thống. RTC thường được sử dụng trong các ứng dụng cần quản lý thời gian như đồng hồ, bộ đếm thời gian, hoặc lịch.
Một số loại RTC phổ biến:
- RTC tích hợp trong vi điều khiển:
- Một số vi điều khiển, như ESP32 hoặc STM32, có tích hợp sẵn RTC trong chip. Loại này thường được dùng khi cần đơn giản hoá thiết kế và giảm chi phí.
- Độ chính xác của RTC tích hợp phụ thuộc vào xung nhịp bên trong và thường kém hơn các RTC chuyên dụng.
- RTC ngoài (RTC độc lập):
- Các mạch RTC ngoài như DS1307, DS3231 là những IC chuyên dụng chỉ làm nhiệm vụ quản lý thời gian. Chúng có thể hoạt động với pin dự phòng, nên khi mất nguồn chính vẫn duy trì được thời gian chính xác.
- DS3231 là một trong những loại phổ biến nhất với độ chính xác cao, còn DS1307 thường kém chính xác hơn.
- RTC trong các thiết bị lưu trữ (như máy tính hoặc hệ thống nhúng lớn):
- Một số hệ thống lưu trữ lớn như máy tính hoặc hệ thống nhúng phức tạp có tích hợp sẵn RTC. Chúng sử dụng pin CMOS để duy trì thời gian khi tắt nguồn.
- RTC trong FPGA (Field-Programmable Gate Array):
- Trong các hệ thống FPGA, đôi khi người ta cần thiết kế riêng module RTC để tích hợp vào ứng dụng. Tuy nhiên, đây là loại ít phổ biến hơn và thường gặp trong các dự án đòi hỏi tuỳ biến cao.
- RTC trong các thiết bị IoT:
- Các thiết bị IoT thường có RTC tích hợp để đồng bộ thời gian từ mạng (NTP – Network Time Protocol) khi kết nối mạng, và sử dụng RTC để giữ thời gian khi mất kết nối.
RTC trên ESP32
Trên ESP32, chức năng RTC có sẵn và có thể được sử dụng để giữ thời gian khi thiết bị đang ở chế độ ngủ sâu hoặc khởi động lại. Để sử dụng RTC trên ESP32, bạn có thể sử dụng thư viện time.h
của Arduino IDE. Thư viện này cung cấp các hàm để thiết lập, lấy và cập nhật thời gian từ các nguồn như NTP (Network Time Protocol) hoặc từ một nguồn bên ngoài khác.
Cách dùng RTC trên ESP32
#include <time.h>
// Hàm thiết lập thời gian bắt đầu
void setupTime() {
struct tm t;
t.tm_year = 2024 - 1900; // Năm 2024 (tính từ 1900)
t.tm_mon = 10; // Tháng 11 (tháng bắt đầu từ 0, nên 10 là tháng 11)
t.tm_mday = 15; // Ngày 15
t.tm_hour = 8; // Giờ 8
t.tm_min = 30; // Phút 30
t.tm_sec = 0; // Giây 0
t.tm_isdst = 0; // Không có giờ mùa hè
time_t epoch = mktime(&t); // Chuyển đổi struct tm thành time_t (giây từ Epoch)
struct timeval tv = {epoch, 0}; // Tạo struct timeval
settimeofday(&tv, NULL); // Thiết lập thời gian hệ thống
}
void setup() {
Serial.begin(115200); // Khởi động Serial với tốc độ truyền 115200 bps
setupTime(); // Gọi hàm thiết lập thời gian ban đầu
}
void loop() {
time_t now;
struct tm timeinfo;
time(&now); // Lấy thời gian hiện tại
localtime_r(&now, &timeinfo); // Chuyển đổi thời gian hiện tại thành struct tm
// In thời gian hiện tại ra Serial Monitor
Serial.println(&timeinfo, "%A, %d-%m-%Y %H:%M:%S");
delay(1000); // Đợi 1 giây
}
Khi sử dụng thư viện time.h, chúng ta có một biến cấu trúc tm được định nghĩa, biến này được dùng để lưu giữ thông tin về thời gian. Các phần tử của biến cấu trúc tm bao gồm:
tm_sec
: Giây (số nguyên từ 0 đến 59).tm_min
: Phút (số nguyên từ 0 đến 59).tm_hour
: Giờ (số nguyên từ 0 đến 23).tm_mday
: Ngày trong tháng (số nguyên từ 1 đến 31).tm_mon
: Tháng trong năm (số nguyên từ 0 đến 11, với 0 là tháng Giêng và 11 là tháng Mười Hai).tm_year
: Năm kể từ 1900 (ví dụ: 120 đại diện cho năm 2020).tm_wday
: Ngày trong tuần (số nguyên từ 0 đến 6, với 0 là Chủ nhật và 6 là Thứ Bảy).tm_yday
: Ngày trong năm (số nguyên từ 0 đến 365, với 0 là ngày 1 tháng 1).tm_isdst
: Cờ xác định thời gian mùa hè (số dương nếu đang trong thời gian mùa hè, số 0 nếu không, và số âm nếu trạng thái này không xác định).
Các ký tự % trong lệnh Serial.println(&timeinfo, “%A, %B %d %Y %H:%M:%S”) đều là phần của chuỗi định dạng thời gian. Chúng thuộc về thư viện time.h và là cách định dạng thời gian trong ngôn ngữ lập trình C. Những ký tự này được dùng để hiển thị các thành phần cụ thể của thời gian. Sau đây là giải thích ý nghĩa của mỗi ký tự:
- %a: Tên viết tắt của ngày trong tuần (ví dụ: “Mon”).
- %A: Tên đầy đủ của ngày trong tuần (ví dụ: “Monday”).
- %b: Tên viết tắt của tháng (ví dụ: “Jan”).
- %B: Tên đầy đủ của tháng (ví dụ: “January”).
- %d: Ngày trong tháng (số có 2 chữ số, ví dụ: “01” đến “31”).
- %H: Giờ trong ngày (24 giờ, số có 2 chữ số, ví dụ: “00” đến “23”).
- %I: Giờ trong ngày (12 giờ, số có 2 chữ số, ví dụ: “01” đến “12”).
- %m: Tháng trong năm (số có 2 chữ số, ví dụ: “01” đến “12”).
- %M: Phút trong giờ (số có 2 chữ số, ví dụ: “00” đến “59”).
- %p: Chu kỳ AM hoặc PM.
- %S: Giây trong phút (số có 2 chữ số, ví dụ: “00” đến “59”).
- %y: Năm dưới dạng số có hai chữ số (ví dụ: “22” cho năm 2022).
- %Y: Năm đầy đủ (ví dụ: “2024”).
Đồng bộ thời gian với NTP
Network Time Protocol (NTP) là một giao thức mạng dùng để đồng bộ hóa thời gian giữa các máy tính và thiết bị trong mạng. NTP kết nối với các máy chủ thời gian trên Internet để lấy thông tin thời gian chuẩn từ các nguồn đáng tin cậy như đồng hồ nguyên tử hoặc GPS. Điều này giúp các thiết bị điều chỉnh thời gian hệ thống để đảm bảo tính chính xác và đồng bộ thời gian giữa tất cả các thiết bị trong mạng, rất quan trọng cho các ứng dụng yêu cầu sự đồng bộ thời gian cao.
Đồng bộ thời gian với NTP cho RTC trên ESP32 mang lại nhiều lợi ích. Nó đảm bảo độ chính xác cao và tự động điều chỉnh sai số của RTC, giúp đơn giản hóa việc quản lý thời gian mà không cần thiết lập thủ công. Ngoài ra, khi ESP32 được đồng bộ hóa với NTP, nó có thể phục vụ như một nguồn thời gian chính xác cho các thiết bị khác trong mạng. Điều này đảm bảo tất cả các thiết bị đều có cùng một thời gian chuẩn, đặc biệt hữu ích cho các ứng dụng yêu cầu sự đồng bộ thời gian chặt chẽ.
Code đồng bộ thời gian với server NTP:
#include <WiFi.h>
#include <time.h>
// Định nghĩa thông tin kết nối WiFi
const char* ssid = "your_SSID"; // Tên mạng WiFi của bạn
const char* password = "your_PASSWORD"; // Mật khẩu WiFi của bạn
// Định nghĩa UTC offset cho múi giờ Việt Nam (UTC+7)
const long gmtOffset_sec = 7 * 3600; // 7 giờ * 3600 giây/giờ
const int daylightOffset_sec = 0; // Không có giờ mùa hè ở Việt Nam
void setup() {
Serial.begin(115200); // Khởi động Serial
WiFi.begin(ssid, password); // Kết nối WiFi
// Chờ kết nối WiFi
while (WiFi.status() != WL_CONNECTED) {
delay(1000); // Đợi 1 giây
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi"); // In thông báo đã kết nối WiFi
// Thiết lập NTP để đồng bộ thời gian với múi giờ Việt Nam
// Tham số đầu là GMT offset (giờ lệch so với UTC),
// Tham số thứ hai là daylight offset (lệch giờ mùa hè),
// Tham số thứ ba là tên server NTP để lấy thời gian
configTime(gmtOffset_sec, daylightOffset_sec, "time.google.com");
// Chờ đồng bộ thời gian từ server NTP
struct tm timeinfo; // Biến để lưu thông tin thời gian
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time");
return; // Thoát nếu không lấy được thời gian
}
// In thời gian hiện tại lên Serial Monitor với thứ tự tùy chỉnh
Serial.println(&timeinfo, "%d-%m-%Y %H:%M:%S");
}
void loop() {
// Cập nhật và in thời gian hiện tại mỗi giây
delay(1000); // Đợi 1 giây
struct tm timeinfo; // Biến để lưu thông tin thời gian
if (getLocalTime(&timeinfo)) {
Serial.println(&timeinfo, "%d-%m-%Y %H:%M:%S");
}
}
Đồng hồ thời gian thực hiển thị OLED
Đấu nối phần cứng
Để tạo đồng hồ thời gian thực này, chúng ta cần chuẩn bị các linh kiện và kết nối chúng theo sơ đồ dưới đây:
Mua linh kiện làm đồng hồ thời gian thực tại đây:
- Kit wifi ESP32: https://shorten.asia/XR9WkVtH
- Màn hình oled 0.96 inch: https://shorten.asia/nrD8U4EC
- Adapter nguồn DC 5V1A: https://shorten.asia/Te9KyG2c
- Jack DC 5.5: https://shorten.asia/pPzWMjUu
- Breadboard test mạch: https://shorten.asia/XqRPNXhN
- Dây cắm breadboard: https://shorten.asia/1ZmdwC6n
- Module realtime DS3231: https://shorten.asia/zNXTqX2V
Code chương trình
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include <time.h>
// Định nghĩa chiều rộng và chiều cao của màn hình OLED
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
// Khởi tạo đối tượng màn hình OLED
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Định nghĩa thông tin kết nối WiFi
const char* ssid = "your_SSID";
const char* password = "your_PASSWORD";
// Định nghĩa UTC offset cho múi giờ Việt Nam (UTC+7)
const long gmtOffset_sec = 7 * 3600;
const int daylightOffset_sec = 0;
// Hàm lấy tên viết tắt của ngày trong tuần
const char* getDayAbbr(int wday) {
switch (wday) {
case 0: return "Sun";
case 1: return "Mon";
case 2: return "Tue";
case 3: return "Wed";
case 4: return "Thu";
case 5: return "Fri";
case 6: return "Sat";
default: return "";
}
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
// Kết nối WiFi
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
// Thiết lập NTP để đồng bộ thời gian
configTime(gmtOffset_sec, daylightOffset_sec, "time.google.com");
// Kiểm tra và khởi động màn hình OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("Real Time Clock");
display.display();
delay(2000); // Hiển thị thông điệp chào mừng trong 2 giây
// Đồng bộ thời gian
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time");
display.setCursor(0, 10);
display.println("Failed to get time");
display.display();
while (1) { delay(1000); }
}
// In thời gian đồng bộ lên Serial Monitor
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}
void loop() {
struct tm timeinfo;
if (getLocalTime(&timeinfo)) {
// Xóa màn hình cũ
display.clearDisplay();
// Hiển thị thời gian (giờ:phút:giây)
display.setTextSize(2);
char timeString[10];
sprintf(timeString, "%02d:%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
Serial.println("Time: " + String(timeString));
int16_t x1, y1;
uint16_t width, height;
display.getTextBounds(timeString, 0, 0, &x1, &y1, &width, &height);
int16_t x = (SCREEN_WIDTH - width) / 2;
display.setCursor(x, 0);
display.print(timeString);
// Hiển thị thứ và ngày (Thứ - ngày-tháng-năm)
display.setTextSize(1);
char dateString[20];
sprintf(dateString, "%s - %02d/%02d/%04d", getDayAbbr(timeinfo.tm_wday), timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900);
display.getTextBounds(dateString, 0, 0, &x1, &y1, &width, &height);
x = (SCREEN_WIDTH - width) / 2;
display.setCursor(x, 40);
display.print(dateString);
display.display();
}
delay(1000); // Cập nhật mỗi giây
}
Để nạp được code đồng hồ thời gian thực trên các bạn cần cài đặt thêm thư viện điều khiển cho màn hình OLED là Adafruit SSD1306
và Adafruit GFX
- Mở Arduino IDE.
- Vào Tools > Manage Libraries….
- Tìm và cài đặt thư viện
Adafruit SSD1306
vàAdafruit GFX
Kết luận
Việc tạo ra một chiếc đồng hồ thời gian thực sử dụng ESP32 và màn hình OLED 0.96 inch không chỉ là một dự án thú vị mà còn rất hữu ích trong thực tế. Với khả năng kết nối WiFi, ESP32 có thể đồng bộ thời gian chính xác từ các máy chủ NTP như Google Public NTP, đảm bảo rằng thời gian luôn được cập nhật và chính xác. Màn hình OLED 0.96 inch với độ phân giải cao và độ tương phản tốt giúp hiển thị thời gian một cách rõ ràng và dễ nhìn. Sự kết hợp giữa ESP32 và màn hình OLED mang lại một giải pháp hiệu quả về chi phí, đồng thời cung cấp một cơ hội học tập tuyệt vời cho những ai muốn tìm hiểu về lập trình vi điều khiển và các giao thức thời gian. Dự án này có thể được mở rộng và ứng dụng trong nhiều lĩnh vực khác nhau, từ hệ thống báo thức thông minh đến các thiết bị IoT yêu cầu đồng bộ thời gian chính xác. Chúc bạn thành công và có những trải nghiệm thú vị khi khám phá thêm các khả năng của ESP32 và màn hình OLED!