实现 lora
This commit is contained in:
@@ -37,6 +37,9 @@ LORA_CONFIG = {
|
|||||||
# (ED + 05(消息长度) + 0201(地址) + "Hello"(消息本体))
|
# (ED + 05(消息长度) + 0201(地址) + "Hello"(消息本体))
|
||||||
# 接收: ED 05 02 01 48 65 6c 6c 6f
|
# 接收: ED 05 02 01 48 65 6c 6c 6f
|
||||||
'lora_mesh_mode': 'EC',
|
'lora_mesh_mode': 'EC',
|
||||||
|
|
||||||
|
# 单包最大用户数据数据长度, 模块限制240, 去掉两位自定义包头, 还剩238
|
||||||
|
'max_chunk_size': 238
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- 总线配置 ---
|
# --- 总线配置 ---
|
||||||
|
|||||||
@@ -10,11 +10,14 @@ LoRa模块的具体实现 (UART Passthrough for LoRa Mesh)
|
|||||||
|
|
||||||
from .lora_interface import ILoraManager
|
from .lora_interface import ILoraManager
|
||||||
from main.logs.logger import log
|
from main.logs.logger import log
|
||||||
|
from machine import UART
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
class LoRaMeshUartPassthroughManager(ILoraManager):
|
class LoRaMeshUartPassthroughManager(ILoraManager):
|
||||||
"""
|
"""
|
||||||
通过UART与LoRa Mesh模块通信的处理器实现 (透传模式)。
|
通过UART与LoRa Mesh模块通信的处理器实现 (ED模式)。
|
||||||
|
实现了自动分片与重组逻辑。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, lora_config: dict):
|
def __init__(self, lora_config: dict):
|
||||||
@@ -24,41 +27,148 @@ class LoRaMeshUartPassthroughManager(ILoraManager):
|
|||||||
Args:
|
Args:
|
||||||
lora_config (dict): 来自全局配置文件的LoRa配置字典。
|
lora_config (dict): 来自全局配置文件的LoRa配置字典。
|
||||||
"""
|
"""
|
||||||
log("LoRaMeshUartPassthroughHandler: 初始化...")
|
log("LoRaMeshUartPassthroughManager: 初始化...")
|
||||||
|
|
||||||
# --- 将配置注入到实例变量 ---
|
# --- 配置注入 ---
|
||||||
self.master_address = lora_config.get('master_address')
|
self.master_address = lora_config.get('master_address')
|
||||||
self.uart_id = lora_config.get('uart_id')
|
self.uart_id = lora_config.get('uart_id')
|
||||||
self.baudrate = lora_config.get('baudrate')
|
self.baudrate = lora_config.get('baudrate')
|
||||||
self.pins = lora_config.get('pins')
|
self.pins = lora_config.get('pins')
|
||||||
self.lora_mesh_mode = lora_config.get('lora_mesh_mode')
|
self.max_chunk_size = lora_config.get('max_chunk_size')
|
||||||
|
self.lora_mesh_mode = b'\xed'
|
||||||
|
# TODO 目前这个配置没用, 完全按ED处理的
|
||||||
|
if lora_config.get('lora_mesh_mode') == 'EC':
|
||||||
|
self.lora_mesh_mode = b'\xec'
|
||||||
|
|
||||||
# 在这里可以添加真实的硬件初始化代码,例如初始化UART
|
# --- 硬件初始化 ---
|
||||||
# self.uart = UART(self.uart_id, self.baudrate, tx=self.pins['tx'], rx=self.pins['rx'])
|
self.uart = UART(self.uart_id, self.baudrate, tx=self.pins['tx'], rx=self.pins['rx'])
|
||||||
|
|
||||||
log(f"LoRaMeshUartPassthroughHandler: 配置加载完成. UART ID: {self.uart_id}, Baudrate: {self.baudrate}")
|
# --- 内部状态变量 ---
|
||||||
|
self._rx_buffer = bytearray() # UART接收缓冲区
|
||||||
|
self._reassembly_cache = {} # 分片重组缓冲区 { chunk_index: chunk_data }
|
||||||
|
self._expected_chunks = 0 # 当前会话期望的总分片数
|
||||||
|
|
||||||
def receive_packet(self):
|
log(f"LoRaMeshUartPassthroughManager: 配置加载完成. UART ID: {self.uart_id}, Baudrate: {self.baudrate}")
|
||||||
|
|
||||||
|
def send_packet(self, payload: bytes) -> bool:
|
||||||
"""
|
"""
|
||||||
【实现】非阻塞地检查并接收一个数据包。
|
【实现】发送一个数据包,自动处理分片。
|
||||||
(当前为存根实现)
|
|
||||||
"""
|
|
||||||
# 具体的实现将在这里...
|
|
||||||
# e.g. self.uart.read()
|
|
||||||
pass
|
|
||||||
|
|
||||||
def send_packet(self, data_bytes: bytes) -> bool:
|
|
||||||
"""
|
|
||||||
【实现】发送一个数据包。
|
|
||||||
(当前为存根实现)
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data_bytes (bytes): 需要发送的字节数据。
|
payload (bytes): 需要发送的完整业务数据。
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True表示发送成功,False表示失败。
|
bool: True表示所有分片都已成功提交发送,False表示失败。
|
||||||
"""
|
"""
|
||||||
# 具体的实现将在这里...
|
max_chunk_size = self.max_chunk_size
|
||||||
# e.g. self.uart.write(data_bytes)
|
if not payload:
|
||||||
log(f"LoRaMeshUartPassthroughHandler: 模拟发送数据 -> {data_bytes}")
|
total_chunks = 1
|
||||||
|
else:
|
||||||
|
total_chunks = (len(payload) + max_chunk_size - 1) // max_chunk_size
|
||||||
|
|
||||||
|
try:
|
||||||
|
for i in range(total_chunks):
|
||||||
|
chunk_index = i
|
||||||
|
start = i * max_chunk_size
|
||||||
|
end = start + max_chunk_size
|
||||||
|
chunk_data = payload[start:end]
|
||||||
|
|
||||||
|
# --- 组装物理包 ---
|
||||||
|
header = b'\xed'
|
||||||
|
dest_addr_bytes = self.master_address.to_bytes(2, 'big')
|
||||||
|
total_chunks_bytes = total_chunks.to_bytes(1, 'big')
|
||||||
|
current_chunk_bytes = chunk_index.to_bytes(1, 'big')
|
||||||
|
|
||||||
|
# 计算后续长度(总包数和当前包序号是自定义包头, 各占一位, 标准包头算在长度内)
|
||||||
|
length_val = 2 + len(chunk_data)
|
||||||
|
length_bytes = length_val.to_bytes(1, 'big')
|
||||||
|
|
||||||
|
# 拼接成最终的数据包
|
||||||
|
packet_to_send = header + length_bytes + dest_addr_bytes + total_chunks_bytes + current_chunk_bytes + chunk_data
|
||||||
|
|
||||||
|
self.uart.write(packet_to_send)
|
||||||
|
log(f"LoRa: 发送分片 {chunk_index + 1}/{total_chunks} 到地址 {self.master_address}")
|
||||||
|
|
||||||
|
# 让出CPU, 模块将缓存区的数据发出去本身也需要时间
|
||||||
|
time.sleep_ms(10)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log(f"LoRa: 发送数据包失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def receive_packet(self) -> bytes | None:
|
||||||
|
"""
|
||||||
|
【实现】非阻塞地检查、解析并重组一个完整的数据包。
|
||||||
|
"""
|
||||||
|
# 1. 从硬件读取数据到缓冲区
|
||||||
|
if self.uart.any():
|
||||||
|
self._rx_buffer.extend(self.uart.read())
|
||||||
|
|
||||||
|
# 2. 循环尝试从缓冲区解析包
|
||||||
|
while True:
|
||||||
|
# 2.1 检查头部和长度字段是否存在
|
||||||
|
if len(self._rx_buffer) < 2:
|
||||||
|
return None # 数据不足,无法读取长度
|
||||||
|
|
||||||
|
# 2.2 检查帧头是否正确
|
||||||
|
if self._rx_buffer[0] != 0xED:
|
||||||
|
log(f"LoRa: 接收到错误帧头: {hex(self._rx_buffer[0])},正在寻找下一个ED...")
|
||||||
|
next_ed = self._rx_buffer.find(b'\xed', 1)
|
||||||
|
if next_ed == -1:
|
||||||
|
self._rx_buffer.clear()
|
||||||
|
else:
|
||||||
|
self._rx_buffer = self._rx_buffer[next_ed:]
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 2.3 检查包是否完整
|
||||||
|
payload_len = self._rx_buffer[1]
|
||||||
|
total_packet_len = 1 + 1 + payload_len
|
||||||
|
if len(self._rx_buffer) < total_packet_len:
|
||||||
|
return None # "半包"情况,等待更多数据
|
||||||
|
|
||||||
|
# 3. 提取和解析一个完整的物理包
|
||||||
|
packet = self._rx_buffer[:total_packet_len]
|
||||||
|
self._rx_buffer = self._rx_buffer[total_packet_len:]
|
||||||
|
|
||||||
|
addr = int.from_bytes(packet[2:4], 'big')
|
||||||
|
total_chunks = packet[4]
|
||||||
|
current_chunk = packet[5]
|
||||||
|
chunk_data = packet[6:]
|
||||||
|
|
||||||
|
# --- 长度反向校验 ---
|
||||||
|
# 根据协议,Length字段 = 2 (自定义头) + N (数据块)
|
||||||
|
expected_payload_len = 2 + len(chunk_data)
|
||||||
|
if payload_len != expected_payload_len:
|
||||||
|
log(f"LoRa: 收到损坏的数据包!声明长度 {payload_len} 与实际计算长度 {expected_payload_len} 不符。已丢弃。")
|
||||||
|
# 包已从缓冲区移除,直接continue进入下一次循环,尝试解析缓冲区的后续内容
|
||||||
|
continue
|
||||||
|
# --- 校验结束 ---
|
||||||
|
|
||||||
|
# 4. 重组逻辑
|
||||||
|
if total_chunks == 1:
|
||||||
|
log(f"LoRa: 收到单包消息,来自地址 {addr},长度 {len(chunk_data)}")
|
||||||
|
return chunk_data
|
||||||
|
|
||||||
|
if current_chunk == 0:
|
||||||
|
log(f"LoRa: 开始接收新的多包会话 ({total_chunks}个分片)...")
|
||||||
|
self._reassembly_cache.clear()
|
||||||
|
self._expected_chunks = total_chunks
|
||||||
|
|
||||||
|
self._reassembly_cache[current_chunk] = chunk_data
|
||||||
|
log(f"LoRa: 收到分片 {current_chunk + 1}/{self._expected_chunks},已缓存 {len(self._reassembly_cache)} 个")
|
||||||
|
|
||||||
|
if len(self._reassembly_cache) == self._expected_chunks:
|
||||||
|
log("LoRa: 所有分片已集齐,正在重组...")
|
||||||
|
full_payload = bytearray()
|
||||||
|
for i in range(self._expected_chunks):
|
||||||
|
if i not in self._reassembly_cache:
|
||||||
|
log(f"LoRa: 重组失败!缺少分片 {i}。")
|
||||||
|
self._reassembly_cache.clear()
|
||||||
|
return None
|
||||||
|
full_payload.extend(self._reassembly_cache[i])
|
||||||
|
|
||||||
|
log(f"LoRa: 重组完成,总长度 {len(full_payload)}")
|
||||||
|
self._reassembly_cache.clear()
|
||||||
|
return bytes(full_payload)
|
||||||
|
|||||||
Reference in New Issue
Block a user