#!/usr/bin/env python # -*- coding: utf-8 -*- """ 猪舍主控系统主程序入口 """ import machine import time import struct import client_pb # 初始化RS485串口 # 使用UART2,连接到ESP32的GPIO16(RX)和GPIO17(TX) rs485_uart = machine.UART(2, baudrate=9600, bits=8, parity=None, stop=1, rx=16, tx=17) rs485_uart.init() # RS485收发控制引脚 rs485_re_de_pin = machine.Pin(5, machine.Pin.OUT) rs485_re_de_pin.value(0) # 默认接收模式 # ESP32设备地址(应该唯一标识这个ESP32设备) ESP32_ADDRESS = 1 # LoRaWAN模块地址 LORA_MODULE_ADDRESS = 254 def receive_lora_message(): """ 接收来自LoRaWAN模块的消息 返回: 字节数据 """ # 在共享的RS485总线上监听来自LoRaWAN模块的消息 # 需要检查消息是否是发给本设备的 buffer = bytearray() # 持续监听,不设置超时 while rs485_uart.any(): data = rs485_uart.read() if data: buffer.extend(data) # 简单的帧检测逻辑 if len(buffer) >= 3 and buffer[0] == 0xAA and buffer[-1] == 0x55: # 检查地址是否匹配 if len(buffer) >= 3 and buffer[2] == ESP32_ADDRESS: # 提取有效数据(去掉帧头、地址、校验和帧尾) return buffer[3:-2] else: break # 没有更多数据可读 return None def send_lora_message(data): """ 通过LoRaWAN模块发送消息 参数: data: 要发送的字节数据 """ # 切换到发送模式 rs485_re_de_pin.value(1) time.sleep_ms(10) try: # 构造发送给LoRaWAN模块的RS485帧 frame = bytearray() frame.append(0xAA) # 帧头 frame.append(LORA_MODULE_ADDRESS & 0xFF) # LoRaWAN模块地址 frame.append(ESP32_ADDRESS & 0xFF) # 本设备地址(作为源地址) # 添加数据 frame.extend(data) # 计算校验和 checksum = sum(frame[1:]) & 0xFF frame.append(checksum) frame.append(0x55) # 帧尾 # 发送命令 rs485_uart.write(frame) print(f"通过LoRa发送数据: {frame.hex()}") finally: # 切换回接收模式 time.sleep_ms(10) rs485_re_de_pin.value(0) def send_rs485_command(bus_number, bus_address, command, channel=None): """ 发送命令到RS485总线上的设备 参数: bus_number: 总线号 bus_address: 设备地址 command: 命令内容 channel: 通道号(可选) """ # 切换到发送模式 rs485_re_de_pin.value(1) time.sleep_ms(10) try: # 构造RS485命令帧 frame = bytearray() frame.append(0xAA) # 帧头 frame.append(bus_number & 0xFF) # 总线号 frame.append(bus_address & 0xFF) # 设备地址 # 添加命令数据 if isinstance(command, str): frame.extend(command.encode('utf-8')) elif isinstance(command, bytes): frame.extend(command) elif isinstance(command, int): frame.append(command & 0xFF) # 如果有通道号,则添加 if channel is not None: frame.append(channel & 0xFF) # 计算校验和 checksum = sum(frame[1:]) & 0xFF frame.append(checksum) frame.append(0x55) # 帧尾 # 发送命令 rs485_uart.write(frame) print(f"已发送RS485命令: {frame.hex()}") finally: # 切换回接收模式 time.sleep_ms(10) rs485_re_de_pin.value(0) def collect_sensor_data(bus_number, bus_address): """ 从传感器收集数据 参数: bus_number: 总线号 bus_address: 传感器地址 返回: 传感器数据 """ # 切换到发送模式 rs485_re_de_pin.value(1) time.sleep_ms(10) try: # 构造读取传感器数据的命令 frame = bytearray([0xAA, bus_number & 0xFF, bus_address & 0xFF, 0x01, 0x00, 0x55]) rs485_uart.write(frame) print(f"已发送传感器读取命令: {frame.hex()}") # 等待响应 time.sleep_ms(50) # 短暂等待响应 # 读取传感器返回的数据 buffer = bytearray() while rs485_uart.any(): data = rs485_uart.read() if data: buffer.extend(data) # 简单的帧检测逻辑 if len(buffer) >= 3 and buffer[0] == 0xAA and buffer[-1] == 0x55: # 检查地址是否匹配 if len(buffer) >= 3 and buffer[2] == ESP32_ADDRESS: # 提取有效数据 if len(buffer) >= 5: # 模拟一个浮点数值 value = float(buffer[3] + (buffer[4] << 8)) / 100.0 return value else: break if len(buffer) > 0: print(f"传感器响应不完整: {buffer.hex()}") else: print("传感器无响应") return None finally: # 切换回接收模式 time.sleep_ms(10) rs485_re_de_pin.value(0) def parse_instruction(data): """ 解析来自LoRaWAN的指令 参数: data: protobuf编码的指令数据 返回: 解析后的指令对象,失败时返回None """ try: instruction = client_pb.decode_instruction(data) return instruction except Exception as e: print(f"解析指令失败: {e}") return None def handle_switch_instruction(switch_msg): """ 处理开关指令 参数: switch_msg: Switch消息字典 """ action = switch_msg.get('device_action', '') bus_number = switch_msg.get('bus_number', 0) bus_address = switch_msg.get('bus_address', 0) channel = switch_msg.get('relay_channel', 0) print(f"处理开关指令: 动作={action}, 总线={bus_number}, 地址={bus_address}, 通道={channel}") if action.upper() == "ON": # 发送开启设备命令 send_rs485_command(bus_number, bus_address, 0x01, channel) elif action.upper() == "OFF": # 发送关闭设备命令 send_rs485_command(bus_number, bus_address, 0x00, channel) else: # 其他自定义命令 send_rs485_command(bus_number, bus_address, action, channel) def handle_collect_instruction(collect_msg): """ 处理采集指令 参数: collect_msg: Collect消息字典 """ bus_number = collect_msg.get('bus_number', 0) bus_address = collect_msg.get('bus_address', 0) print(f"处理采集指令: 总线={bus_number}, 地址={bus_address}") # 从传感器采集数据 value = collect_sensor_data(bus_number, bus_address) if value is not None: # 构造Collect响应消息 collect_data = client_pb.encode_collect(bus_number, bus_address, value) # 构造Instruction消息包装Collect数据 instruction_data = client_pb.encode_instruction(client_pb.METHOD_TYPE_COLLECT, collect_data) # 发送回上位机 send_lora_message(instruction_data) else: print("采集数据失败") def process_instruction(instruction): """ 处理解析后的指令 参数: instruction: 解析后的指令字典 """ method = instruction.get('method', -1) if method == client_pb.METHOD_TYPE_SWITCH: # 处理开关指令 if 'data' in instruction: switch_msg = client_pb.decode_switch(instruction['data']) handle_switch_instruction(switch_msg) else: print("开关指令缺少data字段") elif method == client_pb.METHOD_TYPE_COLLECT: # 处理采集指令 if 'data' in instruction: collect_msg = client_pb.decode_collect(instruction['data']) handle_collect_instruction(collect_msg) else: print("采集指令缺少data字段") else: print(f"不支持的指令类型: {method}") def main_loop(): """ 主循环 """ print("猪舍控制系统启动...") print(f"设备地址: {ESP32_ADDRESS}") while True: # 接收LoRaWAN消息 lora_data = receive_lora_message() if lora_data: print(f"收到LoRaWAN消息: {lora_data.hex()}") # 解析指令 instruction = parse_instruction(lora_data) if instruction: # 处理指令 process_instruction(instruction) else: print("无效的指令数据") # 其他周期性任务可以放在这里 # 例如定时采集传感器数据等 time.sleep(0.01) # 短暂休眠避免过度占用CPU # 程序入口 if __name__ == "__main__": try: main_loop() except KeyboardInterrupt: print("程序被中断") except Exception as e: print(f"程序异常: {e}")