
!"#
$$$
!!
%&'(
)*
+(,
! -!.&/#
01234052678239:2* GV Nguyễn Trung Dũng
6;<* Điện tử và Kỹ thuật Máy tính
=>8?* DHKM16A1HN
6@AB6CD60E2* Nhóm 2
Hà Nội, ngày 21 tháng 10 năm 2025

F -G%
STT Họ và tên Mã sinh viên Ghi chú
1 Trần Thanh Nhàn 22174800055 Nhóm trưởng
2 Nguyễn Thị Hùng Anh 22174800049 Thành viên
3 Phạm Công Bằng 22174800059 Thành viên

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
&H
Nhóm sinh viên xin gửi lời cảm ơn chân thành tới thầy Vũ Trung Dũng – giảng
viên môn Thực hành Lập trình di động đã tận tình giảng dạy, hướng dẫn và hỗ trợ
chúng em trong suốt quá trình học tập và thực hiện đề tài.
Xin chân thành cảm ơn Khoa Điện tử và Kỹ thuật máy tính, Trường Đại học
Kinh tế – Kỹ thuật Công nghiệp (UNETI) đã tạo điều kiện về cơ sở vật chất, trang
thiết bị và môi trường học tập thuận lợi để nhóm có thể hoàn thành tốt đề tài này.
Cuối cùng, nhóm xin gửi lời cảm ơn đến các thành viên trong nhóm đã luôn tích
cực, hỗ trợ và cùng nhau hoàn thành từng phần của đề tài đúng tiến độ.
Nhóm em xin chân thành cảm ơn!
1

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
&FI
Trong thời đại công nghiệp hóa, đô thị hóa phát triển mạnh mẽ, vấn đề ô nhiễm
không khí ngày càng trở nên nghiêm trọng và ảnh hưởng trực tiếp đến sức khỏe con
người.mViệc xây dựng các hệ thống giám sát môi trường thông minh, có khả năng đo
và hiển thị chất lượng không khí theo thời gian thực, đang trở thành một hướng nghiên
cứu thiết thực và có ý nghĩa.
Nhằm vận dụng các kiến thức đã học về lập trình Android, mạng máy tính và hệ
thống IoT, nhóm chúng em thực hiện đề tài J60KBLKM239N2330OAPOBD6QB>7R23
L6S23L6TU. Đề tài tập trung vào việc xây dựng một ứng dụng di động có thể nhận dữ
liệu từ vi điều khiển ESP32 thông qua mạng WiFi, hiển thị thông số môi trường trực
quan, đồng thời cảnh báo khi vượt ngưỡng cho phép.
Thông qua quá trình thực hiện, nhóm đã có cơ hội củng cố kiến thức lập trình,
kỹ năng làm việc nhóm, tư duy logic và khả năng kết hợp giữa phần cứng và phần
mềm trong một hệ thống IoT hoàn chỉnh. Mặc dù đã rất cố gắng, nhưng do thời gian
và kiến thức còn hạn chế, báo cáo không tránh khỏi thiếu sót. Nhóm rất mong nhận
được ý kiến đóng góp của thầy cô để đề tài được hoàn thiện hơn.
2

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
,&,
&H VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVW
&FIVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVX
,&,VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVY
( ,'HVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVZ
W*[\))VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV]
WVWV&^9;D6_2`aBb0VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV]
WVXV NDB05c4b?6dA40De<`aBb0VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV]
1.2.1. Mục tiêu chung........................................................................................6
1.2.2. Mục tiêu cụ thể.........................................................................................6
1.2.3. Phạm vi thực hiện....................................................................................6
WVYVf236g<De<`aBb0VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVh
1.3.1. Ý nghĩa thực tiễn......................................................................................7
1.3.2. Ý nghĩa học thuật.....................................................................................7
WViVjD`0kA2l0mnBDe<6EB6o23VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVp
X*qr"(%sVVVVVVVVVVVVVVVVVVVVVVVVVVVWt
XVWVQcBuvDBl23B6kDe<6EB6o23VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWt
XVXV6w2DM236EB6o23VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWt
2.2.1. Danh sách linh kiện...............................................................................10
2.2.2. Sơ đồ nguyên lý (mô tả kết nối).............................................................10
XVYV6MD2x23De<By23L6o0?6w2DM23VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWW
XViV6w2AaA`0acL60k2z-YXVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWW
2.4.1. Mục tiêu..................................................................................................11
2.4.2. Thư viện sử dụng...................................................................................11
2.4.3. Cấu hình WiFi và server........................................................................11
2.4.4. Đọc dữ liệu cảm biến..............................................................................12
2.4.5. Gửi dữ liệu lên server.............................................................................12
3

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
2.4.6. Lấy ngưỡng cảnh báo từ server.............................................................12
2.4.7. Xử lý hiển thị cảnh báo..........................................................................13
2.4.8. Chu kỳ cập nhật.....................................................................................13
2.4.9. Kết quả hoạt động của phần cứng.........................................................13
Y*+(,! -!.&/
#VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWi
YVWV080B60EcD6c234aM239N23VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWi
YVXVQcBuvD6EB6o23?6w2AaA29u;09VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWi
YVYV b26{26D6T26| <02DB040B}VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWi
YViV0kc`~B6o23L5|6<uBDB040B}VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVW]
YVZVb0`jB237•23D126mO;|-€BB023PDB040B}VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWh
YV]V{AL0KA4b•cQB9‚>0Ec|-€<uD6DB040B}VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWƒ
YVhVqcQB9‚>0Ecu<„0>€qVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWp
YVƒVKB…c16;dB`†23M239N23VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVXW
i*\H‡!!ˆ!‰VVVVVVVVVXX
iVWVKB…c1B6CD2360EAVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVXX
iVXVO2630O6EB6o23VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVXX
4.2.1. Ưu điểm..................................................................................................22
4.2.2. Hạn chế..................................................................................................22
4.2.3. Hướng phát triển....................................................................................22
iVYVa•cQBM239N23B6CDBKVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVXY
4

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
( ,'H
5

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
W*[\))
WVWV&^9;D6_2`aBb0
Trong bối cảnh hiện nay, ô nhiễm không khí đang trở thành một trong những vấn
đề môi trường nghiêm trọng nhất, đặc biệt tại các khu đô thị lớn và khu công nghiệp.
Nồng độ bụi mịn PM2.5, PM10 cùng các khí độc hại như CO, NH , SO … ngày càng₃ ₂
tăng, gây ảnh hưởng trực tiếp đến sức khỏe con người và chất lượng cuộc sống.
Tuy nhiên, phần lớn các thiết bị giám sát không khí thương mại hiện nay có D60
?6TD<;‡L6@Bu0k2L6<0‡4bB60KcBT26>0266;dB. Vì vậy, nhóm quyết định thực hiện
đề tài JEB6o2330OAPOBD6QB>7R23L6S23L6TB6S23A026U nhằm mục tiêu thiết
kế một hệ thống ;|29u;09D60?6TB6Q?, dễ cài đặt, cho phép …c<2POB4bD126
mO;D6QB>7R23L6S23L6TB6€;B6Š030<2B6CD, phù hợp với nội dung học phần Thực
hành Lập trình di động.
WVXV NDB05c4b?6dA40De<`aBb0
1.2.1. Mục tiêu chung
Xây dựng một hệ thống có khả năng:
Thu thập dữ liệu về bụi mịn, nhiệt độ, độ ẩm và nồng độ khí độc trong môi
trường.
Tự động truyền dữ liệu qua mạng WiFi để lưu trữ và hiển thị trên ứng dụng
Android.
Cảnh báo màu và âm thanh khi chỉ số vượt ngưỡng an toàn.
Cho phép người dùng xem lại dữ liệu, tra cứu theo thời gian và xuất báo cáo.
1.2.2. Mục tiêu cụ thể
6w2DM23* sử dụng z-YX(€40BW kết nối với cảm biến SDS011 (bụi
mịn), SHT31 (nhiệt độ – độ ẩm), MQ-2 và MQ-135 (khí gas và khí độc), kèm
LED và buzzer cảnh báo.
6w2AaA* xây dựng M239N2329u;09 bằng Java trên Android Studio,
hiển thị dữ liệu realtime, lưu trữ và thống kê qua cơ sở dữ liệu }-\&|
q .
KB2o0* truyền dữ liệu thông qua giao thức 4b‹- giữa ESP32 và
server, sau đó ứng dụng Android lấy dữ liệu qua .
0k2B6Œ* giao diện Android trực quan, sử dụng thư viện 29u;096<uB để
biểu diễn dữ liệu dạng biểu đồ.
1.2.3. Phạm vi thực hiện
Hệ thống hoạt động trong Ad23•0Ž02†0m†•&•.
Dữ liệu được lưu trữ trên >;D<>P€u4€u•q •, chưa triển khai lên cloud.
Chỉ giám sát các chỉ số cơ bản: PM2.5, PM10, nhiệt độ, độ ẩm, MQ2, MQ135.
6

Cảm biến ESP32
Server
(PHP + MySQL)
Ứng dụng
Android
THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
Ứng dụng tập trung vào 60k2B6Œ4bD126mO;, chưa mở rộng AI dự đoán.
WVYVf236g<De<`aBb0
1.3.1. Ý nghĩa thực tiễn
Giúp người dùng 26n2m0KBB{26Bud23S260‘AL6S23L6TBd0L6c4CD>’?
`jB theo thời gian thực.
Ứng dụng được triển khai dễ dàng cho 6†30<`{26‡Bu7Š236_D6;jD9;<26
2360E?26“, nâng cao ý thức bảo vệ môi trường.
Cung cấp cơ sở để ?6”2BTD6‡B6o23L54b`7<u<D126mO;P8A khi chất
lượng không khí vượt ngưỡng cho phép.
1.3.2. Ý nghĩa học thuật
Giúp sinh viên 4n29N23L0K2B6MD>05223b26 gồm: lập trình vi điều khiển,
xử lý dữ liệu cảm biến, lập trình Android, cơ sở dữ liệu và giao tiếp IoT.
Hiểu rõ …c}Bu{26?6OBBu0k2M239N23;6;b2D6•26, từ phần cứng –
truyền dữ liệu – đến giao diện người dùng.
Củng cố kỹ năng >n?Bu{26P;DL€B‡‡4bB60KBLKD–P—9‚>0Ec.
WViV S6{26Bl23…c<26EB6o23
Hệ thống được chia thành bốn thành phần chính, hoạt động B6€;D6c˜0Buc}a2
9‚>0EcBcw2BC qua mạng WiFi nội bộ (LAN):
3c}52>^6;dB`†23*
1. z-YX thu thập dữ liệu từ các cảm biến:
o -(-tWW: đo bụi mịn PM2.5, PM10.
o -YW: đo nhiệt độ và độ ẩm.
o \X‡ \WYZ: phát hiện khí gas, khói và khí độc hại.
2. Dữ liệu sau khi được xử lý và xác định ngưỡng sẽ được z-YX3™0>52P€u4€u
q thông qua -•„0>€<99š9<B<V?6?• để lưu vào D–P—
9‚>0Ec }-\&.
3. +239N2329u;09 sau đó truy xuất dữ liệu từ MySQL qua •„0>€
3€Bš9<B<V?6?‡3€BšB6u€P6;>9PV?6?‡›• để hiển thị, thống kê và cảnh báo.
4. Dựa trên dữ liệu nhận được, app 60k2B6Œu€<>B0A€, đổi màu giao diện (xanh –
vàng – đỏ) và phát âm thanh cảnh báo nếu vượt ngưỡng.Người dùng có thể
•€Am0kc`~B6o23L5‡B{AL0KA9‚>0EcB6€;B6Š030<2‡6;jD•cQB„0>€-
để phân tích.
7
Hình 1. 1. Sơ đồ tổng quan

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
8

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
WViVjD`0kA2l0mnBDe<6EB6o23
uc}a29‚>0EcBC`†23* Toàn bộ dữ liệu cảm biến được gửi và lưu trữ hoàn
toàn tự động qua HTTP.
0OAPOBB6Š030<2B6CD* Ứng dụng Android cập nhật liên tục các chỉ số từ cơ
sở dữ liệu.
126mO;B6S23A026* LED và buzzer kích hoạt khi vượt ngưỡng, giao diện
đổi màu trực quan.
&7cBu‚œ?6”2BTD6* Dữ liệu được lưu trong MySQL, có thể xem lại, thống
kê theo ngày hoặc giờ.
—u†239‘9b23* Có thể nâng cấp kết nối cloud (Firebase/Thingspeak) hoặc
bổ sung AI dự đoán xu hướng ô nhiễm.
9

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
X*qr"(%s
XVWVQcBuvDBl23B6kDe<6EB6o23
Hệ thống gồm hai khối chính:
6o0?6w2DM23•B60KBmŒ;•* sử dụng vi điều khiển ESP32 DevKit V1 làm
trung tâm, kết nối các cảm biến đo môi trường (bụi mịn, khí gas, nhiệt độ – độ
ẩm).
6o0?6w2AaA* bao gồm chương trình trên ESP32, ứng dụng Android, cơ sở
dữ liệu MySQL và các API PHP trung gian.
Dữ liệu được thu thập từ cảm biến, gửi lên server XAMPP để lưu trữ, và được
ứng dụng Android truy xuất, hiển thị theo thời gian thực.
XVXV6w2DM236EB6o23
2.2.1. Danh sách linh kiện
-
52>026L0E2 6MD2x23D6T26 60D6v
1 z-YX
(€40BW
Vi điều khiển trung tâm, thu thập và
truyền dữ liệu qua WiFi
32-bit, hỗ trợ WiFi
& BLE
2 1Am0K2
-(-tWW
Đo nồng độ bụi mịn PM2.5 và PM10 Giao tiếp UART
3 1Am0K2
-YW
Đo nhiệt độ và độ ẩm không khí Giao tiếp I2C
4 1Am0K2
\X
Phát hiện khí gas, khói, LPG Ngõ ra analog
5 1Am0K2
\WYZ
Phát hiện khí độc, CO , NH₂ ₃ Ngõ ra analog
6 &z( Hiển thị màu cảnh báo theo mức ô
nhiễm
Kết nối GPIO 25-
27
7 c••€u Báo hiệu âm thanh khi vượt ngưỡng GPIO 18
8 3c~2Z|
X
Cung cấp điện cho ESP32 và cảm biến —
2.2.2. Sơ đồ nguyên lý (mô tả kết nối)
10

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
Hình 2. 1. Sơ đồ kết nối phần cứng
XVYV6MD2x23De<By23L6o0?6w2DM23
6o0D1Am0K2* đo đạc liên tục thông số môi trường (bụi, khí, nhiệt độ, độ
ẩm).
6o0•™>^Buc23B”A•z-YX•* thu thập, lọc và xử lý dữ liệu, xác định
ngưỡng ô nhiễm, điều khiển cảnh báo và gửi dữ liệu lên server.
6o0D126mO;* hiển thị bằng LED RGB (xanh – vàng – đỏ) và phát âm thanh
qua buzzer khi vượt ngưỡng.
6o023c~2* cung cấp ổn định 5V/2A cho toàn hệ thống.
XViV6w2AaA`0acL60k2z-YX
2.4.1. Mục tiêu
Thu thập dữ liệu cảm biến SDS011, MQ-2, MQ-135, SHT31.
Gửi dữ liệu lên server MySQL thông qua •<99š9<B<V?6?•V
Lấy ngưỡng cảnh báo từ server qua •3€BšB6u€P6;>9PV?6?•.
Hiển thị trạng thái đèn cảnh báo và buzzer tương ứng.
2.4.2. Thư viện sử dụng
#include <WiFi.h> // quản lý kết nối WiFi.
#include <HTTPClient.h> // gửi và nhận dữ liệu qua HTTP.
#include <Adafruit_SHT31.h> // điều khiển cảm biến nhiệt độ – độ ẩm.
#include <ArduinoJson.h> // mã hóa dữ liệu thành định dạng JSON để gửi lên
server.
2.4.3. Cấu hình WiFi và server
const char* ssid = "Nha 19 Vinh Hung";
const char* password = "0343729584";
11

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
const char* serverAddData = "http://192.168.1.80/giamsatkk/api/add_data.php";
const char* serverThreshold =http://192.168.1.80/giamsatkk/api/get_thresholds.php ;
ESP32 kết nối WiFi nội bộ và sử dụng API PHP để giao tiếp với MySQL.
2.4.4. Đọc dữ liệu cảm biến
float temp = sht31.readTemperature();
float hum = sht31.readHumidity();
float mq2 = analogRead(MQ2_PIN);
float mq135 = analogRead(MQ135_PIN);
Đọc giá trị nhiệt độ, độ ẩm, và khí gas từ các cảm biến tương ứng.
2.4.5. Gửi dữ liệu lên server
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(serverAddData);
http.addHeader("Content-Type", "application/json");
DynamicJsonDocument doc(256);
doc["temp"] = temp;
doc["hum"] = hum;
doc["mq2"] = mq2;
doc["mq135"] = mq135;
String json;
serializeJson(doc, json);
http.POST(json);
http.end();
}
010B6TD6* ESP32 tạo một đối tượng JSON chứa dữ liệu cảm biến, gửi POST
request tới add_data.php, nơi dữ liệu được lưu vào MySQL. Quá trình này
diễn ra lặp lại sau mỗi 3–5 giây.
2.4.6. Lấy ngưỡng cảnh báo từ server
HTTPClient http;
http.begin(serverThreshold);
int httpCode = http.GET();
if (httpCode == 200) {
String payload = http.getString();
DynamicJsonDocument doc(256);
deserializeJson(doc, payload);
float limit_pm25 = doc["pm25"];
12

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
float limit_mq2 = doc["mq2"];
}
ESP32 đọc giá trị ngưỡng do người dùng cài đặt trên app Android, từ đó điều
khiển đèn cảnh báo.
2.4.7. Xử lý hiển thị cảnh báo
if (mq2 > limit_mq2 || mq135 > 3000) {
digitalWrite(RED_LED, HIGH);
tone(BUZZER, 2000);
} else {
digitalWrite(GREEN_LED, HIGH);
noTone(BUZZER);
}
Nếu giá trị cảm biến vượt ngưỡng → đèn đỏ + còi kêu.
Nếu trong ngưỡng an toàn → đèn xanh + tắt còi.
2.4.8. Chu kỳ cập nhật
delay(5000);
Mỗi 5 giây, ESP32 đọc dữ liệu mới, gửi lên server, và cập nhật lại trạng thái
cảnh báo.
2.4.9. Kết quả hoạt động của phần cứng
Dữ liệu được ghi nhận chính xác và cập nhật ổn định.
Tốc độ gửi dữ liệu trung bình: W3@0žZ30”}.
Sai số cảm biến thấp (<5%).
Cảnh báo đèn và buzzer hoạt động theo đúng logic ngưỡng.
13

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
Y*+(,! -!.&/
#
YVWV080B60EcD6c234aM239N23
Ứng dụng được xây dựng bằng Android Studio (Java), có chức năng hiển thị,
cảnh báo và thống kê chất lượng không khí theo thời gian thực, dựa trên dữ liệu từ cơ
sở dữ liệu MySQL.Ứng dụng được kết nối với server thông qua giao thức HTTP (API
PHP). Dữ liệu do ESP32 gửi lên MySQL sẽ được ứng dụng truy xuất, giải mã JSON,
và hiển thị bằng giao diện trực quan.
Cấu trúc ứng dụng gồm 4 Activity chính:
1. <02DB040B}* hiển thị dữ liệu realtime và trạng thái cảnh báo.
2. 6<uBDB040B}* hiển thị biểu đồ thống kê lịch sử.
3. -€BB023PDB040B}* cài đặt ngưỡng cảnh báo.
4. -€<uD6DB040B}* tra cứu và xuất dữ liệu theo thời gian.
YVXVQcBuvD6EB6o23?6w2AaA29u;09
6b26?6w2 6MD2x23D6T26
<02DB040B}VŸ<4< Nhận và hiển thị dữ liệu mới nhất từ server, đổi màu cảnh
báo.
6<uBDB040B}VŸ<4< Truy xuất và vẽ biểu đồ dữ liệu cảm biến bằng
MPAndroidChart.
-€BB023PDB040B}VŸ<4< Cho phép người dùng cài đặt và lưu ngưỡng cảnh báo.
-€<uD6DB040B}VŸ<4< Tìm kiếm dữ liệu theo ngày, giờ; xuất ra file CSV.
Trung gian giao tiếp giữa App và MySQL.
}-\&(<B<m<P€ Lưu trữ các giá trị cảm biến (temp, hum, pm25, mq2,
mq135...).
YVYV b26{26D6T26| <02DB040B}
<•6MD2x23*
Hiển thị dữ liệu cảm biến mới nhất (nhiệt độ, độ ẩm, bụi, khí gas...).
Đổi màu giao diện theo trạng thái cảnh báo (xanh, vàng, đỏ).
Cập nhật dữ liệu liên tục theo chu kỳ 3 giây.
m•;9€D6T26*
private void getLatestData() {
StringRequest request = new StringRequest(Request.Method.GET,
ServerConfig.GET_LATEST_URL,
response -> {
try {
JSONObject obj = new JSONObject(response);
14

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
tvTemp.setText(obj.getString("temp") + " °C");
tvHum.setText(obj.getString("hum") + " %");
tvPM.setText(obj.getString("pm25") + " µg/m³");
tvGas.setText(obj.getString("mq135"));
double pm = obj.getDouble("pm25");
if (pm < 35) mainLayout.setBackgroundColor(Color.GREEN);
else if (pm < 75) mainLayout.setBackgroundColor(Color.YELLOW);
else mainLayout.setBackgroundColor(Color.RED);
} catch (JSONException e) {
e.printStackTrace();
}
},
error -> Toast.makeText(this, "Lỗi kết nối server",
Toast.LENGTH_SHORT).show());
Volley.newRequestQueue(this).add(request);
}
D•010B6TD6*
Ứng dụng gửi yêu cầu GET đến get_latest.php.
Dữ liệu JSON trả về được parse và hiển thị lên giao diện.
Màu nền thay đổi tùy theo giá trị PM2.5.
Hàm được gọi định kỳ bằng Handler.postDelayed() để cập nhật realtime.
Hình 3. 1. Giao diện MainActivity – hiển thị chỉ số không khí và cảnh báo màu
15
Lối tắt
Cập nhật lần cuối
Real-time
Hiển thị thông số
Thanh menu
Tên app
Nút cảnh báo

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
YViV0kc`~B6o23L5|6<uBDB040B}
<•6MD2x23*
Hiển thị biểu đồ biến động của PM2.5, nhiệt độ, MQ2, MQ135 theo thời gian.
Cho phép người dùng chọn khoảng thời gian cần xem thống kê.
m•;9€D6T26*
private void loadChartData() {
StringRequest request = new StringRequest(Request.Method.GET,
ServerConfig.GET_HISTORY_URL,
response -> {
try {
JSONArray array = new JSONArray(response);
ArrayList<Entry> entries = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
JSONObject obj = array.getJSONObject(i);
entries.add(new Entry(i, (float) obj.getDouble("pm25")));
}
LineDataSet dataSet = new LineDataSet(entries, "PM2.5 (µg/m³)");
dataSet.setColor(Color.BLUE);
LineData lineData = new LineData(dataSet);
chart.setData(lineData);
chart.invalidate();
} catch (JSONException e) {
e.printStackTrace();
}
},
error -> Toast.makeText(this, "Không thể tải dữ liệu",
Toast.LENGTH_SHORT).show());
Volley.newRequestQueue(this).add(request);
}
D•010B6TD6*
Ứng dụng gửi yêu cầu tới get_history.php.
Dữ liệu trả về dưới dạng JSON array, mỗi phần tử chứa giá trị PM2.5 theo thời
gian.
MPAndroidChart được sử dụng để vẽ biểu đồ đường (LineChart).
Người dùng có thể chọn khoảng thời gian hiển thị (1 giờ, 1 ngày, 1 tuần...).
16

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
Hình 3. 2. Giao diện ChartActivity thống kê các thông số theo biểu đồ
YVZVb0`jB237•23D126mO;|-€BB023PDB040B}
<•6MD2x23*
Cho phép người dùng nhập ngưỡng giới hạn cho nhiệt độ, độ ẩm, khí gas và
bụi.
Lưu ngưỡng này vào -6<u€9u€„€u€2D€P và đồng bộ lên server.
m•;9€D6T26*
btnSave.setOnClickListener(v -> {
float pm25Limit = Float.parseFloat(edtPM25.getText().toString());
float gasLimit = Float.parseFloat(edtGas.getText().toString());
SharedPreferences.Editor editor = prefs.edit();
editor.putFloat("pm25_limit", pm25Limit);
editor.putFloat("gas_limit", gasLimit);
editor.apply();
Toast.makeText(this, "Đã lưu ngưỡng cảnh báo",
Toast.LENGTH_SHORT).show();
});
D•010B6TD6*
Khi người dùng nhấn “Lưu”, giá trị được ghi vào SharedPreferences.
App có thể gửi dữ liệu này đến server thông qua API save_threshold.php.
Ngưỡng được ESP32 tải về để cập nhật logic cảnh báo đèn và buzzer.
17

THỰC HÀNH LẬP TRÌNH DI ĐỘNG GVHD: Vũ Trung Dũng
Hình 3. 3. Giao diện SettingsActivity – nhập ngưỡng giới hạn và lưu
YV]V{AL0KA4b•cQB9‚>0Ec|-€<uD6DB040B}
<•6MD2x23*
Tra cứu dữ liệu theo ngày, giờ hoặc khoảng thời gian tùy chọn.
Hiển thị bảng kết quả chi tiết, cho phép xuất ra file txt.
m•;9€D6T26*
btnSearch.setOnClickListener(v -> {
String date = edtDate.getText().toString();
String url = ServerConfig.SEARCH_URL + "?date=" + date;
StringRequest request = new StringRequest(Request.Method.GET, url,
response -> {
try {
JSONArray arr = new JSONArray(response);
listData.clear();
for (int i = 0; i < arr.length(); i++) {
JSONObject obj = arr.getJSONObject(i);
listData.add("[" + obj.getString("datetime") + "] "
+ obj.getString("pm25") + " µg/m³");
}
adapter.notifyDataSetChanged();
} catch (JSONException e) { e.printStackTrace(); }
},
error -> Toast.makeText(this, "Không có dữ liệu",
Toast.LENGTH_SHORT).show());
Volley.newRequestQueue(this).add(request);
18
Bấm Tải xuống để xem toàn bộ.