Giới thiệu
Trong bài viết này, chúng ta sẽ tìm hiểu cách xây dựng một mạng lưới master-slave cho các thiết bị ESP32 bằng Micropython và giao thức ESP-Now. ESP-Now là một giao thức giao tiếp đơn giản, cho phép các thiết bị ESP32 trao đổi thông tin mà không cần thông qua HTTP. Mặc dù có nhiều tài liệu về chủ đề này, nhưng hầu hết chỉ đề cập đến những tình huống đơn giản, và khi các tình huống trở nên phức tạp hơn, những kỹ thuật đơn giản này bắt đầu bộc lộ những giới hạn của chúng. Bài viết này là kết quả của nhiều tuần thử nghiệm và tìm hiểu, với sự hỗ trợ từ DeepSeek, một AI đã giúp tôi giải quyết những câu hỏi khó khăn.
Tóm tắt nội dung
Hệ thống này bao gồm một thiết bị master (hub) hoạt động như một cổng kết nối với một số lượng không giới hạn các thiết bị slave được sắp xếp theo kiểu mạng sao. Trong bài viết trước, tôi đã mô tả cách mở rộng mạng lưới này bằng cách cho phép các slave chạy mạng cục bộ của riêng chúng, vượt qua vấn đề về phạm vi wifi và số lượng thiết bị mà một nút có thể hỗ trợ. Mã mà tôi trình bày đã có những hạn chế, vì vậy ở đây tôi sẽ mô tả một lớp mã được cải tiến để giải quyết những vấn đề đã phát hiện trong quá trình phát triển.
Cấu trúc của mã
Dưới đây là mã nguồn của lớp ESPComms, nơi xử lý việc gửi và nhận dữ liệu giữa các thiết bị ESP32:
python
import asyncio, network, time, random, machine
from espnow import ESPNow
class ESPComms():
e = ESPNow()
def __init__(self, config):
self.config = config
if config.isMaster():
print('Bắt đầu ở chế độ master')
try:
self.sta = network.WLAN(network.WLAN.IF_STA)
self.sta.active(True)
ssid = self.config.getSSID()
password = self.config.getPassword()
print(ssid, password)
t = 0
print('Đang kết nối...', end='')
self.sta.connect(ssid, password)
while not self.sta.isconnected():
time.sleep(1)
t += 1
if t > 30:
print('Thời gian chờ hết')
config.clearAndReset()
print('.', end='')
ipaddr = self.sta.ifconfig()[0]
self.channel = self.sta.config('channel')
self.config.setIPAddr(ipaddr)
print(f'{ipaddr} ch {self.channel}')
except: machine.reset()
else:
self.channel = config.getChannel()
print('Bắt đầu ở chế độ slave trên kênh', self.channel)
ap = network.WLAN(network.AP_IF)
ap.active(True)
ap.config(channel=self.channel)
mac = ap.config('mac').hex()
config.setMAC(mac)
self.ssid = f'RBR-Now-{mac}'
ap.config(essid=self.ssid, password='00000000')
ap.ifconfig(('192.168.9.1', '255.255.255.0', '192.168.9.1', '8.8.8.8'))
self.ap = ap
config.setAP(ap)
print(config.getName(), mac)
if not config.isMaster():
self.sta = network.WLAN(network.WLAN.IF_STA)
self.sta.active(True)
self.e.active(True)
print('ESP-Now đã được khởi tạo')
self.requestToSend = False
self.sending = False
def closeAP(self):
password = str(random.randrange(100000, 999999))
print('Mật khẩu:', password)
self.ap.config(essid='-', password=password)
def addPeer(self, peer):
h = peer.hex()
if not hasattr(self, 'peers'):
self.peers = []
if h in self.peers:
return True
try:
self.e.add_peer(peer, channel=self.channel)
except OSError as ex:
print(f'Không thể thêm peer {h} vào ESP-NOW: {ex}')
return False
self.peers.append(h)
print('Đã thêm', h, 'vào danh sách peers trên kênh', self.channel)
return True
def espSend(self, peer, msg):
if self.addPeer(peer):
try:
self.e.send(peer, msg)
except Exception as ex:
print('espSend:', ex)
def send(self, mac, msg):
self.requestToSend = True
while not self.sending:
await asyncio.sleep(.1)
self.requestToSend = False
peer = bytes.fromhex(mac)
if self.addPeer(peer):
try:
result = self.e.send(peer, msg)
if result:
result = None
counter = 100
while counter > 0:
while self.e.any():
_, reply = self.e.irecv()
if reply:
reply = reply.decode()
if reply == 'ping':
continue
result = reply
break
if result:
break
await asyncio.sleep(.1)
counter -= 1
if counter == 0:
result = 'Thất bại (không có phản hồi)'
else:
print(f'{msg[0:20]} đến {mac}: {result}')
self.config.resetCounter()
else:
result = 'Thất bại (không có kết quả)'
except Exception as ex:
result = f'Thất bại ({ex})'
print(result)
else:
result = 'Thất bại (thêm peer)'
self.sending = False
return result
async def receive(self):
print('Bắt đầu nhận dữ liệu ESPNow')
while True:
while True:
if self.e.active():
while self.e.any():
mac, msg = self.e.recv()
msg = msg.decode()
print('Nhận được', msg)
if msg == 'ping':
try:
self.addPeer(mac)
self.e.send(mac, 'pong')
except Exception as ex:
print('ping:', ex)
else:
if msg[0] == '!':
comma = msg.index(',')
slave = msg[1:comma]
msg = msg[comma + 1:]
response = await self.send(slave, msg)
else:
response = self.config.getHandler().handleMessage(msg)
response = f'{response} {self.getRSS(mac)}'
self.addPeer(mac)
try:
self.e.send(mac, response)
print(response)
self.config.resetCounter()
if not self.config.getMyMaster() and not self.config.isMaster():
self.config.setMyMaster(mac.hex())
except Exception as ex:
print('Không thể phản hồi', ex)
if self.requestToSend:
self.sending = True
while self.sending:
await asyncio.sleep(.1)
else:
print('Không hoạt động')
await asyncio.sleep(.1)
self.config.kickWatchdog()
def getRSS(self, mac):
try:
return self.e.peers_table[mac][0]
except:
return 0
def restartESPNow(self):
self.e = ESPNow()
self.e.active(True)
Các tính năng chính của lớp ESPComms
Lớp này có nhiệm vụ gửi các gói dữ liệu nhỏ đến các địa chỉ MAC được chỉ định và trả kết quả lại cho người gọi. Do ESP-Now là một giao thức cấp thấp, nó không xử lý các địa chỉ IP, tên điểm truy cập hay bất kỳ lớp nào khác trong mô hình OSI. ESP-Now yêu cầu thiết bị phải được thiết lập trong chế độ Access Point hoặc Station. Giao tiếp của nó không bị phát hiện bởi các chức năng cấp cao hơn như HTTP, do đó nó có thể chạy đồng thời với các chức năng này.
Thực hành tốt
- Kiểm tra kết nối: Đảm bảo rằng thiết bị master được kết nối với router và có thể nhận dữ liệu từ các slave.
- Quản lý peer: Theo dõi các peer đã được thêm vào để tránh lỗi trong quá trình giao tiếp.
- Bảo mật: Thay đổi mật khẩu và SSID của AP sau 2 phút để bảo vệ thiết bị khỏi truy cập không mong muốn.
Các vấn đề thường gặp
- Lỗi không thể thêm peer: Đảm bảo rằng các peer đã được thêm vào danh sách trước khi gửi dữ liệu.
- Không nhận được phản hồi: Kiểm tra xem thiết bị có đang hoạt động và có kết nối mạng không.
Mẹo hiệu suất
- Tối ưu hóa kênh: Chọn kênh wifi ít tắc nghẽn để cải thiện hiệu suất truyền dữ liệu.
- Giảm thời gian chờ: Giảm thời gian chờ trong quá trình gửi và nhận để tăng tốc độ giao tiếp.
Giải quyết sự cố
- Nếu gặp lỗi, thử khởi động lại thiết bị hoặc kiểm tra lại cấu hình.
- Sử dụng công cụ gỡ lỗi để theo dõi quá trình truyền dữ liệu và tìm ra nguyên nhân gây ra lỗi.
Kết luận
Bài viết đã cung cấp cái nhìn tổng quan về cách thiết lập mạng lưới master-slave sử dụng ESP-Now trong Micropython cho các thiết bị ESP32. Hy vọng những thông tin này sẽ hữu ích cho bạn trong quá trình phát triển. Nếu bạn có bất kỳ câu hỏi nào, hãy để lại câu hỏi bên dưới và tôi sẽ hỗ trợ bạn. Hãy theo dõi các bài viết tiếp theo để tìm hiểu thêm về các tính năng nâng cao của ESP-Now!
Câu hỏi thường gặp (FAQ)
1. ESP-Now có hỗ trợ mã hóa không?
Không, ESP-Now không hỗ trợ mã hóa mặc định. Bạn cần tự triển khai nếu cần bảo mật.
2. Có thể kết nối bao nhiêu thiết bị slave với một master?
Số lượng thiết bị slave kết nối với một master là không giới hạn, tùy thuộc vào phạm vi và băng thông.
3. Có thể sử dụng ESP-Now với các phiên bản ESP32 khác nhau không?
Có, mã này đã được kiểm tra trên ESP32-C3 và có thể hoạt động trên các phiên bản khác.
4. Làm thế nào để khắc phục lỗi kết nối?
Kiểm tra xem SSID và mật khẩu router đã được nhập chính xác hay chưa.
5. Có cần thiết phải khởi động lại thiết bị thường xuyên không?
Không cần thiết, nhưng nếu gặp lỗi, khởi động lại có thể giúp khắc phục vấn đề.