Yêu cầu 1: Bố cục trình bày nội dung bài báo cáo, tỉ lệ đóng góp hoàn thiện báo cáo của từng thành viên. Yêu cầu 2: Sơ đồ nguyên lý chi tiết của hệ thống Yêu cầu 3: Lưu đồ giải thuật Yêu cầu 4: Mã nguồn của chương trình Yêu cầu 5: Minh chứng kết quả thực nghiệm (ảnh chụp dữ liệu đo đạc, trạng thái mô hình khi đang vận hành). • Hình ảnh minh chứng kết nối dây giữa mô-đun Raspberry với các mô-đun ngoại vi. • Hình ảnh minh chứng kết quả đo đạc bằng các thiết bị đo (Logic Analyzer, Oscillator,…). • Hình ảnh minh chứng kết quả hiển thị trên cửa sổ Terminal, màn hình LCD 16x2,…
Trang 1BỘ CÔNG THƯƠNG TRƯỜNG ĐẠI HỌC CÔNG NGHIỆP THÀNH PHỐ HỒ CHÍ MINH
KHOA: CÔNG NGHỆ ĐIỆN TỬ
BÁO CÁO THỰC HÀNH CHUYÊN ĐỀ IOT BUỔI 7: LẬP TRÌNH TRAO ĐỔI DỮ LIỆU QUA
GIAO THỨC TCP/UDP Giảng viên hướng dẫn: ThS Phạm Quang Trí Nhóm: 5
Nguyễn Phước Hòa 22633931
Thành phố Hồ Chí Minh, ngày 24 tháng 9 năm 2025
Trang 2YÊU CẦU BÀI BÁO CÁO
Yêu cầu 1: Bố cục trình bày nội dung bài báo cáo, tỉ lệ đóng góp hoàn thiện báo
cáo của từng thành viên
Yêu cầu 2: Sơ đồ nguyên lý chi tiết của hệ thống
Yêu cầu 3: Lưu đồ giải thuật
Yêu cầu 4: Mã nguồn của chương trình
Yêu cầu 5: Minh chứng kết quả thực nghiệm (ảnh chụp dữ liệu đo đạc, trạng thái
mô hình khi đang vận hành)
Hình ảnh minh chứng kết nối dây giữa mô-đun Raspberry với các mô-đun ngoại vi
Hình ảnh minh chứng kết quả đo đạc bằng các thiết bị đo (Logic
Analyzer, Oscillator,…)
Hình ảnh minh chứng kết quả hiển thị trên cửa sổ Terminal, màn hình LCD 16x2,…
Trang 3Bài tập mức độ 3 (10 điểm):
Hệ thống IoT bao gồm 2 thành phần: mô-đun Raspberry “Master” (có thể thay thế bằng máy tính) và mô-đun Raspberry “Slave”
Master: Hiển thị giá trị nhiệt độ, độ ẩm nhận được từ Slave ra cửa sổ Terminal Thu thập và xác định giá trị trung bình của nhiệt độ, độ ẩm trong mỗi 20 giây Đồng thời gửi các dữ liệu trung bình này lên Server của ThingSpeak sau mỗi 20 giây cho một gói tin Việc gửi các gói tin lên Server phải được thực hiện liên tục trong ít nhất là 30 phút Ngoài ra, sau mỗi 1 giây sẽ gửi tín hiệu điều khiển 3 LED sáng đuổi liên tục (Đỏ Vàng Xanh)
Slave: Đọc giá trị nhiệt độ, độ ẩm của môi trường và hiển thị các giá trị này lên màn hình LCD 16x2 sau mỗi 1 giây Đồng thời gửi dữ liệu nhiệt độ, độ ẩm này đến Master sau mỗi 1 giây Nhận tín hiệu điều khiển từ Master để thay đổi trạng thái hoạt động của 3 LED
Bắt buộc phải tự xây dựng được Frame truyền dữ liệu đáp ứng yêu cầu trên Frame truyền phải được thiết kế có tính logic, bao quát được tất cả các trường hợp Giải thích chi tiết ý nghĩa các thông tin trong Frame truyền vào báo cáo Trong Frame truyền dữ liệu phải có CRC
Bắt buộc nhóm 1, 3, 5, 7, 9 và 11 sử dụng giao thức UDP; nhóm 2, 4, 6, 8, 10 và
12 sử dụng giao thức TCP/IP để trao đổi dữ liệu
Trang 4Yêu cầu 1: Bố cục trình bày nội dung bài báo cáo, tỉ lệ đóng góp hoàn thiện
báo cáo của từng thành viên.
- Viết code
- Hình ảnh minh chứng
- Viết code
- Hình ảnh minh chứng
- Viết code
- Hình ảnh minh chứng
Yêu cầu 2: Sơ đồ nguyên lý chi tiết của hệ thống
Hình 1: Sơ đồ nguyên lý của hệ thống
Trang 5Yêu cầu 3: Lưu đồ giải thuật
Trang 7Yêu cầu 4: Mã nguồn của chương trình
CLIENT
import socket,struct
from urllib import request, parse
from time import sleep, time
import paho.mqtt.client as mqtt
from datetime import datetime
# ================== CẤU HÌNH THINGSPEAK
==================
# CH_ID_HTTP = 3050174x
# K_API_HTTP = "PVD9JHVMFWIMNC6S"
# HTTP_UPDATE_URL = "http://api.thingspeak.com/update"
CH_ID_MQTT = 3050167
username = "HQYdMBYqIAgiJSEiJiUnKhQ"
client_ID = "HQYdMBYqIAgiJSEiJiUnKhQ"
password = "Qa02xaIRFt5b9uLAen1Lwq2Y"
MQTT_HOST = "mqtt3.thingspeak.com"
MQTT_PORT = 1883
MQTT_TOPIC = f"channels/{CH_ID_MQTT}/publish"
#
========================================================= mqtt_client = mqtt.Client(client_ID)
mqtt_client.username_pw_set(username=username, password=password)
def mqtt_connect_if_needed():
try:
if not getattr(mqtt_client, "_is_connected", False):
mqtt_client.connect(MQTT_HOST, MQTT_PORT, 60)
mqtt_client.loop_start()
mqtt_client._is_connected = True
Trang 8print("[MQTT] Connected")
except Exception as e:
mqtt_client._is_connected = False
print(f"[MQTT] Connect error: {e}")
def guidl_tsp_mqtt(ndtb, datb):
mqtt_connect_if_needed()
payload = f"field1={ndtb:.2f}&field2={datb:.2f}&status=OK"
try:
mqtt_client.publish(MQTT_TOPIC, payload, qos=0, retain=False)
print("[MQTT] da gui ->", payload)
except Exception as e:
print(f"[MQTT] error: {e}")
#======================================================== def tao_frame_ctrl(id_byte, cmd_byte, data_bytes):
Start_byte = 0xAA
Stop_byte = 0x55
length = len(data_bytes)
header = struct.pack('!BBBB', Start_byte, id_byte, cmd_byte, length)
crc = crc8(header[1:] + data_bytes)
frame = header + data_bytes + struct.pack('!B', crc) + struct.pack('!B',
Stop_byte)
return frame
def crc8(data: bytes):
crc = 0
for b in data:
crc ^= b
for _ in range(8):
if crc & 0x80:
crc = (crc << 1) ^ 0x07
Trang 9else:
crc <<= 1
crc &= 0xFF
return crc
dl_nhietdo, dl_doam = [], []
GIẢI THÍCH VỀ FRAME TRUYỀN
| Start (0xAA) | ID | CMD | Length | Data[ ] | CRC | Stop (0x55) | Start byte (0xAA): Ký hiệu bắt đầu frame.
ID byte: Xác định thiết bị đích hoặc nguồn gửi.
CMD byte (lệnh): Quy định loại lệnh hoặc loại dữ liệu.
Length (số byte dữ liệu): Số byte trong trường Data[ ].
Data[ ] (payload): Nội dung chính của gói tin.
CRC (1 byte): Giá trị kiểm tra lỗi.
Stop byte (0x55): Ký hiệu kết thúc frame.
WINDOW_SEC = 20 # tính trung bình 20 giây
window_start = time()
ServerAddressPort = ("10.187.24.222",8000)
bufferSize = 1024
msgToServer = "Hello UDP Server"
bytesToSend = str.encode(msgToServer)
UDP_Client = socket.socket(family=socket.AF_INET,
type=socket.SOCK_DGRAM)
UDP_Client.sendto(bytesToSend, ServerAddressPort)
last_send = time()
while True:
ServerMsg = UDP_Client.recvfrom(bufferSize)
frame_bytes= ServerMsg[0]
ServerMsg_address = ServerMsg[1]
Trang 10if len(frame_bytes) >= 7 and frame_bytes[0] == 0xAA and frame_bytes[-1] == 0x55:
id_byte = frame_bytes[1]
cmd_byte = frame_bytes[2]
length = frame_bytes[3]
data_bytes = frame_bytes[4:4+length]
crc_received = frame_bytes[4+length]
crc_calculated = crc8(frame_bytes[1:4+length])
if crc_received == crc_calculated:
print(f"ID={id_byte}, CMD={cmd_byte}, Length={length} (CRC OK)")
if length == 8:
t, h = struct.unpack('!ff', data_bytes)
print(f"Nhiệt độ: {t:.1f}°C Độ ẩm: {h:.1f}%")
dl_nhietdo.append(t)
dl_doam.append(h)
else:
print(f"CRC lỗi! Nhận={crc_received}, Tính={crc_calculated}")
if time() - window_start >= WINDOW_SEC:
if dl_nhietdo and dl_doam:
ndtb = round((sum(dl_nhietdo)/len(dl_nhietdo)),2)
datb = round((sum(dl_doam)/len(dl_doam)),2)
print(f"[MQTT] Gửi dữ liệu: Nhiệt độ={ndtb}, Độ ẩm={datb}")
guidl_tsp_mqtt(ndtb, datb)
dl_nhietdo.clear()
dl_doam.clear()
window_start = time()
if time() - last_send >= 1:
led_ctrl = 1 # hoặc 0, theo logic bạn muốn
Trang 11data_bytes = struct.pack('!B', led_ctrl) # 1 byte
frame_ctrl = tao_frame_ctrl(id_byte=2, cmd_byte=0x20,
data_bytes=data_bytes)
UDP_Client.sendto(frame_ctrl, ServerAddressPort)
print(f"[SEND] Frame điều khiển LED -> {led_ctrl} ({datetime.now()})") last_send = time()
# message= "Message from Server {}".format(ServerMsg_message)
# address = "Server IP + Port {}".format(ServerMsg_address)
# print(message)
# print(address)
SEVER
import socket,struct,time
from time import sleep, time
from seeed_dht import DHT
from gpiozero import LED
from grove.display.jhd1802 import JHD1802
import select
import math
# phần cứng
lcd = JHD1802()
ss=DHT('22',5)
led1 = LED(22) # LED đỏ
led2 = LED(24) # LED vàng
led3 = LED(26) # LED xanh
localIP = "0.0.0.0"
localPort = 8000
bufferSize = 1024
msgToClient = "Hello LOC"
bytesToSend = str.encode(msgToClient)
Trang 12Stop_byte=0x55
# Create a datagram socket
UDP_Server = socket.socket(family=socket.AF_INET,
type=socket.SOCK_DGRAM)
# Bind to address and ip
UDP_Server.bind((localIP, localPort))
print("UDP server up and listening")
# Listen for incoming
def crc8(data: bytes):
crc = 0
for b in data:
crc ^= b
for _ in range(8):
if crc & 0x80:
crc = (crc << 1) ^ 0x07
else:
crc <<= 1
crc &= 0xFF
return crc
def tao_frame(id_byte, cmd_byte, data_bytes):
length = len(data_bytes)
header = struct.pack('!BBBB', Start_byte, id_byte, cmd_byte, length)
crc = crc8(header[1:] + data_bytes) # tính CRC cho ID|CMD|Length|Data frame = header + data_bytes + struct.pack('!B', crc) + struct.pack('!B',
Stop_byte)
return frame
def hop_le_nhietdo(x):
return isinstance(x, (int, float)) and not (isinstance(x, float) and math.isnan(x)) and (-20 <= x <= 80)
Trang 13def hop_le_doam(x):
return isinstance(x, (int, float)) and not (isinstance(x, float) and math.isnan(x)) and (0 <= x <= 100)
UDP_Server.setblocking(False) # socket non-blocking
last_send = time()
last_client_address = None # lưu client gần nhất
while(True):
# Kiểm tra xem có dữ liệu từ client không
ready = select.select([UDP_Server], [], [], 0.1)[0]
if ready:
ClientMsg=UDP_Server.recvfrom(bufferSize)
ClientMsg_message = ClientMsg[0]
ClientMsg_address = ClientMsg[1]
last_client_address = ClientMsg_address
if len(ClientMsg_message) >= 7 and ClientMsg_message[0] == 0xAA and ClientMsg_message[-1] == 0x55:
id_byte = ClientMsg_message[1]
cmd_byte = ClientMsg_message[2]
length = ClientMsg_message[3]
data_bytes = ClientMsg_message[4:4+length]
crc_received = ClientMsg_message[4+length]
crc_calculated = crc8(ClientMsg_message[1:4+length])
if crc_received == crc_calculated:
if id_byte == 2 and cmd_byte == 0x20 and length == 1:
led_ctrl = data_bytes[0]
if led_ctrl:
led1.on(); sleep(0.1); led1.off()
led2.on(); sleep(0.1); led2.off()
led3.on(); sleep(0.1); led3.off()
Trang 14else:
led1.off(); led2.off(); led3.off()
print(f"[LED] LED set to {led_ctrl}")
# print message and IP address
# message = "Message from Client:{}".format(ClientMsg_message)
# address = "Client IP + Port:{}".format(ClientMsg_address)
# print(message)
# print(address)
# đọc cảm biến
if time() - last_send >= 1 and last_client_address is not None:
h,t=ss.read()
if hop_le_nhietdo(t) and hop_le_doam(h):
print("Nhiệt độ: {:.1f}*C Độ ẩm: {:.1f}%".format(t,h))
lcd.setCursor(0,0)
lcd.write("Nhiet do:{:>5.1f}C".format(t)) # format độ dài cố định để che chữ cũ
lcd.setCursor(1,0)
lcd.write("Do am:{:>6.1f}%".format(h))
data_bytes = struct.pack('!ff', t, h) # ! = big endian, 2 số float
frame_bytes = tao_frame(id_byte=1, cmd_byte=0x10,
data_bytes=data_bytes)
# Sending a message to client
UDP_Server.sendto(frame_bytes, last_client_address)
else:
print("Loi cam bien")
lcd.clear()
lcd.setCursor(0, 0)
lcd.write("Loi cam bien")
last_send = time()
Trang 15Yêu cầu 5: Minh chứng kết quả thực nghiệm (ảnh chụp dữ liệu đo đạc,
trạng thái mô hình khi đang vận hành).
Minh chứng dữ liệu kênh sever( sever chanel):
Nhom5_Bai7
Channel ID: 3050167
Author: mwa0000038323954
Access: Public
Thời điểm đầu và cuối của các dữ liệu nhiệt độ, độ ẩm, điện áp: