You are on page 1of 15

BỘ CÔNG THƯƠNG

TRƯỜNG ĐẠI HỌC CÔNG NGHIỆP TP. HỒ CHÍ MINH


KHOA CÔNG NGHỆ ĐIỆN TỬ
--------
TIỂU LUẬN MÔN HỆ THỐNG NHÚNG
NHÓM 4
Đề tài: Sử dụng ESP32 thu thập dữ liệu từ cảm biến khí gas
với cảm biến nhiệt độ và độ ẩm hiển thị lên lcd với app
trên điện thoại sử dụng giao thức TCP/IP với I2C

Giảng viên hướng dẫn : Trương Năng Toàn

Sinh viên thực hiện : Nguyễn Minh Nhựt – 21032541


Nguyễn Thế Khang – 21007341
Lê Tuấn Kiệt – 21139831
Nguyễn Hồng Quang – 21078151
Trần Ngọc Đức – 21016911

TP.HCM, ngày 21 tháng 3 năm 2024

NHÓM 4
Phân công nhiệm vụ cho từng thành viên
Công Thiết Viết Viết Lắp ráp Thuyết
việc/ kế code báo cáo Trình
Tên
Lê Tuấn 10% 0% 50% 10% 20%
Kiệt
Nguyễn 10% 90% 0% 80% 20%
Minh
Nhựt
Nguyễn 10% 0% 50% 20% 20%
Thế
Khang
Nguyễn 50% 0% 20% 15% 20%
Hồng
Quang
Trần 50% 10% 20% 0% 20%
Ngọc
Đức

Đề tài: Sử dụng ESP32 thu thập dữ liệu từ cảm biến khí gas với cảm biến
nhiệt độ và độ ẩm hiển thị lên lcd với app trên điện thoại sử dụng giao
thức TCP/IP với I2C

1. Tổng quát về đề tài

Đề tài mà nhóm em đề cập bao gồm việc sử dụng ESP32 để thu thập dữ liệu từ cảm
biến khí gas, cùng với cảm biến nhiệt độ và độ ẩm, và hiển thị thông tin này lên
màn hình LCD. Bên cạnh đó, là sử dụng một ứng dụng trên điện thoại thông qua
giao thức TCP/IP để truy cập và hiển thị dữ liệu từ ESP32, sử dụng giao tiếp I2C để
kết nối các thành phần.

Đầu tiên, ESP32 được sử dụng như một vi điều khiển chính để thu thập dữ liệu từ
các cảm biến khí gas, cảm biến nhiệt độ và độ ẩm. ESP32 có tích hợp sẵn giao tiếp
I2C, cho phép nó kết nối với các cảm biến thông qua giao thức này. ESP32 có khả
năng đọc dữ liệu từ các cảm biến và lưu trữ chúng để sử dụng sau này.

Tiếp theo, một màn hình LCD được kết nối với ESP32 thông qua giao tiếp I2C.
Màn hình LCD sẽ hiển thị thông tin như dữ liệu khí gas, nhiệt độ và độ ẩm mà
ESP32 thu thập được từ các cảm biến. ESP32 sử dụng các thư viện phù hợp để điều
khiển màn hình LCD và hiển thị thông tin tương ứng lên màn hình.

Để truy cập và hiển thị thông tin từ ESP32 trên điện thoại thông qua giao thức
TCP/IP, bạn cần phát triển một ứng dụng trên điện thoại di động. Ứng dụng này sẽ
kết nối với ESP32 thông qua mạng TCP/IP và gửi yêu cầu để nhận thông tin từ
ESP32. ESP32 sẽ phản hồi bằng cách gửi dữ liệu thu thập được từ các cảm biến.
Điện thoại di động sẽ hiển thị thông tin này lên giao diện ứng dụng.

Giao tiếp I2C được sử dụng để kết nối ESP32 với màn hình LCD, trong khi giao
thức TCP/IP được sử dụng để kết nối ESP32 với ứng dụng trên điện thoại di động.
ESP32 sẽ hoạt động như một máy chủ TCP/IP, và điện thoại di động sẽ hoạt động
như một máy khách TCP/IP. Khi điện thoại di động yêu cầu dữ liệu từ ESP32,
ESP32 sẽ gửi dữ liệu thông qua giao thức TCP/IP để điện thoại di động hiển thị.

2. Tìm hiểu về khái niệm 2 giao thức I2C & TCP/IP:

 Giao thức TCP/IP:


-Giao thức TCP/IP (Transmission Control Protocol/Internet Protocol) là một bộ
giao thức mạng được sử dụng rộng rãi trong việc truyền thông và truyền dữ liệu
trên Internet. Nó được xem là cơ sở của mô hình Internet và cung cấp các quy tắc
và quy trình để truyền thông tin giữa các thiết bị mạng khác nhau.
-TCP/IP bao gồm hai phần chính: TCP (Transmission Control Protocol) và IP
(Internet Protocol). IP là giao thức mạng chịu trách nhiệm định tuyến và chuyển
tiếp gói tin giữa các thiết bị trên mạng. TCP là giao thức truyền tải đảm bảo tính
toàn vẹn và đáng tin cậy của dữ liệu. Nó chia dữ liệu thành các gói tin, xác định địa
chỉ nguồn và đích, và đảm bảo việc truyền tải dữ liệu một cách tin cậy và theo đúng
thứ tự.
-Giao thức TCP/IP cung cấp một kiến trúc mạng phân tầng, trong đó các tầng thấp
hơn đảm nhận việc chuyển tiếp gói tin qua mạng vật lý, trong khi các tầng cao hơn
đảm nhận xử lý các giao thức ứng dụng như HTTP (Hypertext Transfer Protocol),
FTP (File Transfer Protocol), và SMTP (Simple Mail Transfer Protocol).

 Giao thức I2C:


-Giao thức I2C (Inter-Integrated Circuit) là một giao thức giao tiếp đường dây hai
dây được sử dụng để kết nối và truyền dữ liệu giữa các thiết bị điện tử trong một hệ
thống. Nó được phát triển bởi Philips Semiconductor (hiện là NXP
Semiconductors) và được sử dụng phổ biến trong việc kết nối các linh kiện như
cảm biến, bộ nhớ, vi điều khiển, và các phần tử điện tử khác trên một mạch in.
-Giao thức I2C sử dụng hai dây truyền thông: dây SDA (Serial Data Line) và dây
SCL (Serial Clock Line). Dây SDA dùng để truyền dữ liệu giữa các thiết bị, trong
khi dây SCL được sử dụng để đồng bộ hóa truyền thông giữa các thiết bị. Mỗi thiết
bị trong mạng I2C được gán một địa chỉ duy nhất để có thể nhận hoặc gửi dữ liệu.
-Giao thức I2C cho phép truyền dữ liệu đồng thời giữa một thiết bị điều khiển
(master) và nhiều thiết bị nô lệ (slave), đồng thời hỗ trợ tính năng nâng cao như
chia sẻ đồng thời bus I2C cho nhiều thiết bị master và slave. Việc truyền dữ liệu
trên giao thức I2C được điều khiển bởi master, và mỗi thiết bị nô lệ sẽ phản hồi với
dữ liệu tương ứng khi được yêu cầu.
-Được sử dụng rộng rãi trong việc kết nối các thành phần điện tử trong các thiết bị
nhúng, hệ thống điều khiển và các ứng dụng IoT, nơi việc truyền thông dữ liệu giữa
các thành phần là cần thiết.

3. Sơ đồ các khối
4. Lưu đồ giải thuật
5. Mã nguồn của chương trình

#include <LiquidCrystal_I2C.h> # Câu lệnh này bao gồm thư viện


<LiquidCrystal_I2C.h>. Thư viện này cung cấp các chức năng để điều khiển màn
hình LCD I2C.
#include <Wire.h> # Câu lệnh này bao gồm thư viện <Wire.h>. Thư viện này cung
cấp các chức năng để giao tiếp I2C.
LiquidCrystal_I2C lcd(0x27, 16, 2); # Câu lệnh này tạo ra một đối tượng lcd của
lớp <LiquidCrystal_I2C>.

/*----------------------------------------------------------*/

#include <DHT.h> # Câu lệnh này bao gồm thư viện <DHT.h>. Thư viện này cung
cấp các chức năng để đọc dữ liệu từ cảm biến <DHT1> hoặc <DHT22>.
//#include <DHT_U.h>
#define Gas 34 # Định nghĩa hằng số Gas với giá trị 34. Hằng số này được sử dụng
để xác định chân GPIO được kết nối với cảm biến khí gas.
#define Led 2 # Định nghĩa hằng số Led với giá trị 2
#define Gas2 0 # Định nghĩa hằng số Gas2 với giá trị 0
#define Fan 3 # Định nghĩa hằng số Fan với giá trị 3
/*----------------------------------------------------------*/
#define BLYNK_TEMPLATE_ID "TMPL60Lo-ESlj"
#define BLYNK_TEMPLATE_NAME "Esp32S NodeMcuCopy"
#define BLYNK_AUTH_TOKEN "TZczixmGaWVX3ZVoIE8aBI986Wmy7688"
//#define BLYNK_FIRMWARE_VERSION "0.1.0"
#define BLYNK_PRINT Serial
#include <WiFi.h> # Khai báo thư viện <Wifi.h>
#include <WiFiClient.h> # Khai báo thư viện <WifiClient.h>
#include <BlynkSimpleEsp32.h> # Khai báo thư viện <BlynkSimpleEsp32.h>

BlynkTimer timer; # Tạo ra một đối tượng timer của lớp BlynkTimer. Lớp
BlynkTimer được sử dụng để tạo các bộ đếm thời gian trong Blynk.
int flag = 0; # Khai báo biến flag kiểu int và khởi tạo giá trị ban đầu là 0
char ssid[] = "Nguyen Nhut"; # Khai báo mảng ký tự ssid và khởi tạo giá trị là
chuỗi "Nguyen Nhut". Mảng ssid được sử dụng để lưu trữ tên mạng WiFi.
char pass[] = "0987654321"; # Khai báo mảng ký tự pass và khởi tạo giá trị là
chuỗi "0987654321". Mảng pass được sử dụng để lưu trữ mật khẩu mạng WiFi.
/*----------------------------------------------------------*/

#define DHTPIN 4 # Định nghĩa hằng số <DHTPIN> với giá trị 4


#define DHTTYPE DHT11 # Định nghĩa hằng số <DHTTYPE> với giá trị
<DHT11>
DHT dht(DHTPIN, DHTTYPE); # Tạo ra một đối tượng dht của lớp <DHT>. Lớp
DHT được sử dụng để đọc dữ liệu từ cảm biến <DHT11> hoặc <DHT22>.
int f, g; # Khai báo hai biến f và g kiểu int.
int fanState = 1; # Khai báo biến fanState kiểu int và khởi tạo giá trị ban đầu là 1.
Biến fanState được sử dụng để lưu trữ trạng thái của quạt (bật hoặc tắt).
void setup() {
// put your setup code here, to run once:
pinMode(Gas, INPUT); # Thiết lập chân Gas là ngõ vào
pinMode(Gas2, INPUT); # Thiết lập chân Gas2 là ngõ vào
pinMode(Led, OUTPUT); # Thiết lập chân Led là ngõ ra
pinMode(Fan, OUTPUT); # Thiết lập chân Fan là ngõ ra
Serial.begin(115200); # Khởi tạo giao tiếp serial với tốc độ truyền baud rate là
115200 bps (bits per second - bít trên giây) thông qua cổng Serial.
Serial.println(F("DHT11 test!")); # Gửi một dòng dữ liệu (kết thúc bằng ký tự
xuống dòng) qua cổng serial.
dht.begin(); # Khởi tạo đối tượng dht được sử dụng để giao tiếp với cảm biến
<DHT11>
Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass); # Khởi tạo kết nối Blynk
//Wire.begin(D2, D1);
lcd.begin(); # Khởi tạo màn hình LCD.
lcd.backlight(); # Bật đèn nền màn hình LCD.
lcd.home(); # Di chuyển con trỏ về vị trí đầu tiên trên màn hình LCD.
lcd.clear(); # Xóa tất cả nội dung trên màn hình LCD
timer.setInterval(1000L, notification); # Hàm này thiết lập khoảng thời gian giữa
các lần thực thi hàm notification.
}
void getdata()
{
g = analogRead(Gas); # Đọc giá trị tín hiệu analog từ chân Gas của vi điều khiển
Arduino. Giá trị trả về sẽ được lưu trữ trong biến g. Biến g thường là kiểu int và có
giá trị trong khoảng từ 0 đến 1023 (đối với Arduino Uno).
Blynk.virtualWrite(V2,g); # Hàm của thư viện Blynk dùng để gửi dữ liệu lên các
virtual pin. Gửi giá trị của biến g lên virtual pin V2 trên Blynk.
float h = dht.readHumidity(); # Đọc giá trị độ ẩm từ cảm biến <DHT11> và lưu
trữ trong biến h.
Blynk.virtualWrite(V1,h); # Gửi giá trị độ ẩm (h) lên virtual pin V1 trên Blynk.
float t = dht.readTemperature(); # Đọc giá trị nhiệt độ từ cảm biến <DHT11> và
lưu trữ trong biến t.
Blynk.virtualWrite(V0,t); # Gửi giá trị nhiệt độ (t) lên virtual pin V0 trên Blynk.
if (isnan(h) || isnan(t) || isnan(f)) # Kiểm tra xem giá trị trong biến h có phải là
Not-a-Number (NaN) hay không. Hàm isnan trả về true nếu h là NaN, và false nếu
ngược lại.
{
Serial.println(F("Failed to read from DHT sensor!")); # Hàm in một dòng dữ liệu
ra màn hình nối tiếp.
return;
}

Serial.print(F("Humidity: ")); # Hàm để gửi dữ liệu ra cổng serial.


Serial.print(h);
Serial.print(F("% Temperature: "));
Serial.print(t);
Serial.print(F("°C"));
Serial.print(F(" Gas: "));
Serial.print(g);
Serial.println("PPM");
Serial.print(F(" Fan: "));
if (f==1){
Serial.println("On");
}
else {
Serial.println("Off");
}
# Khối lệnh if kiểm tra điều kiện f == 1. Biến f chưa được giải thích trước đó.
# Nếu f bằng 1, in ra "On" (Bật) trên dòng mới.
# Nếu f khác 1, in ra "Off" (Tắt) trên dòng mới.
lcd.clear(); # Xóa màn hình LCD (lcd.clear())
lcd.setCursor(0,0); # Di chuyển con trỏ đến vị trí đầu tiên, dòng đầu tiên
(lcd.setCursor(0,0))
lcd.print("Nhiet do: "); # In ra "Nhiet do: " (lcd.print("Nhiet do: "))
lcd.print(t);
lcd.print("*C");
lcd.setCursor(0,1);
lcd.print("Do am: ");
lcd.print(h);
lcd.print("%");

delay(2000); # Dừng chương trình 2 giây

lcd.clear();
lcd.setCursor(0,0);
lcd.print("Gas: ");
lcd.print(g);
lcd.print("PPM");
lcd.setCursor(0,1);
lcd.print("Fan: ");
if (f==1){
lcd.print("On");
}
else {
lcd.print("Off");
}
# if (f==1): Kiểm tra xem f có bằng 1 hay không.
# Nếu f == 1: In ra "On" (Bật) trên màn hình LCD.
# else: Nếu f không bằng 1.
# In ra "Off" (Tắt) trên màn hình LCD.
delay(2000);
}
void notification() # Hàm notification() này có chức năng kiểm tra cảm biến khí gas
thứ hai (Gas2) và thực hiện các hành động cảnh báo khi phát hiện rò rỉ khí gas.
{
if (digitalRead(Gas2)==1 && flag==0)
# Kiểm tra xem chân Gas2 có đang ở mức cao (HIGH) hay không. Mức cao thường
biểu thị khí gas được phát hiện.
# Kiểm tra xem biến flag có bằng 0 hay không. Biến flag được sử dụng để đánh dấu
trạng thái cảnh báo đã được kích hoạt.
# Điều kiện thỏa mãn: Cảm biến Gas2 báo gas ( mức cao) và flag bằng 0 (chưa
cảnh báo).
{
digitalWrite(Led,0); # Bật đèn LED (mức thấp thường dùng để bật đèn).
Serial.println(); # In xuống dòng trên màn hình nối tiếp.
Serial.println("Attention! Gas leak detected!"); # In ra thông báo "Attention! Gas
leak detected!" (Chú ý! Phát hiện rò rỉ khí gas!).
//Blynk.email("nguyennhut09102003@gmail.com","Gas", "Attention! Gas leak
detected!");
Blynk.logEvent("alarm","Gas Leakage Detected"); # Gửi log sự kiện "alarm" với
nội dung "Gas Leakage Detected" (Phát hiện rò rỉ khí gas) lên Blynk.
flag = 1; # Thiết lập flag thành 1 để đánh dấu trạng thái cảnh báo đã được kích
hoạt.
if(digitalRead(Fan) == 0)
# Kiểm tra trạng thái quạt hiện tại: digitalRead(Fan) == 0 - Kiểm tra xem chân Fan
có đang ở mức thấp (LOW) hay không (nguyên tắc điều khiển quạt có thể khác
nhau).
{
if(fanState == 1){
digitalWrite(Fan,1);
Blynk.virtualWrite(V4,digitalRead(Fan));
delay(200);
fanState=0;
}
}else{
fanState=1;
}
# Kiểm tra trạng thái bật/tắt quạt mong muốn (fanState): if(fanState == 1): Nếu
fanState bằng 1 (mong muốn bật quạt).
# digitalWrite(Fan, 1);: Bật quạt (mức cao thường dùng để bật quạt).
# Blynk.virtualWrite(V4, digitalRead(Fan));: Gửi trạng thái bật/tắt quạt (đọc từ
chân Fan) lên virtual pin V4 trên Blynk (để hiển thị hoặc xử lý trên giao diện
Blynk).
# delay(200);: Dừng chương trình 200 mili giây.
# fanState = 0;: Thiết lập fanState thành 0 (để lần sau không bật quạt lại trừ khi có
thay đổi).
# Nếu quạt đang bật (LOW): else - Nếu chân Fan đang ở mức thấp (quạt đang bật).
# fanState = 1;: Thiết lập fanState thành 1 (để lần sau bật quạt nếu cần).
Blynk.virtualWrite(V3,1); # Gửi giá trị 1 lên virtual pin V3 trên Blynk.
}
else
{
flag = 0; # Thiết lập lại biến flag bằng 0.
digitalWrite(Led,1); # Tắt đèn LED (mức cao thường dùng để tắt đèn).
if(digitalRead(Fan) == 0){
if(fanState == 1){
digitalWrite(Fan,1);
Blynk.virtualWrite(V4,digitalRead(Fan));
delay(200);
fanState=0;
}
}else{
fanState=1;
}
# Kiểm tra và điều khiển quạt (tương tự như trong khối if):
# Các câu lệnh digitalRead(Fan), fanState, digitalWrite(Fan),
Blynk.virtualWrite(V4), delay(200), và fanState hoạt động tương tự như trong khối
if.
# Kiểm tra trạng thái quạt hiện tại và bật/tắt quạt theo fanState (trạng thái mong
muốn) nhưng lần này là để tắt quạt (nếu đang bật).

Blynk.virtualWrite(V3,0);
# Gửi giá trị 0 lên virtual pin V3 trên Blynk.
# Có thể virtual pin V3 được dùng để điều khiển hiển thị (ví dụ như tắt đèn cảnh
báo) trên giao diện Blynk khi không còn rò rỉ khí gas.
};
}
void loop() {

Blynk.run(); # Thực hiện các tác vụ cần thiết để giao tiếp với Blynk server.
getdata();
# Gọi đến một hàm khác có tên getdata().
# Chức năng của hàm này chưa được cung cấp nhưng có thể liên quan đến việc đọc
dữ liệu từ cảm biến hoặc thực hiện các tính toán cần thiết.
timer.run(); # Gọi đến một hàm khác có tên timer.run().

// put your main code here, to run repeatedly:


// Wait a few seconds between measurements.
}
BLYNK_WRITE(V4)
{
f = param.asInt();
digitalWrite(Fan,f);
}
# Đây là một hàm xử lý sự kiện nhận dữ liệu từ virtual pin V4 trên Blynk.
# BLYNK_WRITE(V4): Khai báo đây là hàm xử lý sự kiện nhận dữ liệu (WRITE)
từ virtual pin V4.
# f = param.asInt();: Gán giá trị nhận được từ virtual pin V4 vào biến f. Hàm asInt()
chuyển đổi dữ liệu sang kiểu số nguyên.
# digitalWrite(Fan, f);: Điều khiển quạt dựa trên giá trị f.
# Nếu f bằng 1, có thể bật quạt (tùy thuộc vào cách điều khiển phần cứng).
# Nếu f bằng 0, có thể tắt quạt.
6. Kết quả
 Nếu khí gas tăng cao thì tự động gửi về gmail của người sử dụng:

7. Nhận
xét
Đề tài giúp
chúng em
hiểu thêm
về lập trình
vi điều
khiển, ứng
dụng các
giao thức
truyền
thông.
Ứng dụng
kiến thức
hệ thống
nhúng vào
các dự án
thực tiễn.

You might also like