module(..., package.seeall) require"radio" require "sx126x_driver" -- --------------------- 常量定义 --------------------- local MASTER_ADDR = 0x00 local MIN_SLAVE_ADDR = 0x01 local MAX_SLAVE_ADDR = 0x05 local QUERY_TIMEOUT_MS = 6 local FRAME_HEADER1 = 0x5A local FRAME_HEADER2 = 0xA5 local FRAME_TYPE_QUERY = 0x03 local FRAME_TYPE_DATA = 0x02 -- --------------------- 工具函数 --------------------- -- CRC16 (Modbus) local function Modbus_CRC16(data, len) local crc = 0xFFFF for i = 1, len do crc = bit.bxor(crc, data[i]) for _ = 1, 8 do if bit.band(crc, 0x0001) ~= 0 then crc = bit.rshift(crc,1) crc = bit.bxor(crc,0xA001) else crc = bit.rshift(crc,1) end end end return bit.rshift(crc,8) + bit.lshift(bit.band(crc,0xFF),8) end -- 工具函数:将“十进制字节表”转为“十六进制字符串”(如 {0x5A, 0xA5} → "5AA5") local function byte_table_to_hex_str(byte_table) local hex_str = "" for _, byte_val in ipairs(byte_table) do -- 确保每个字节转为两位十六进制(不足两位补0,如0x5 → "05") hex_str = hex_str .. string.format("%02X", byte_val) end return hex_str end -- 将 {0x5A,0xA5} 转成 "\x5A\xA5" 这种二进制字符串 local function byte_table_to_bin_str(byte_table) local chars = {} for i = 1, #byte_table do chars[i] = string.char(byte_table[i]) end return table.concat(chars) end -- --------------------- 发送查询帧 --------------------- local function send_query_frame(slave_addr) log.info("Frame","发送查询帧",slave_addr) local buf = { FRAME_HEADER1, FRAME_HEADER2, MASTER_ADDR, slave_addr, FRAME_TYPE_QUERY, 0x00, 0x00 -- reserved } local crc = Modbus_CRC16(buf, #buf) table.insert(buf, bit.band(crc,0xFF)) -- CRC低字节 table.insert(buf, bit.rshift(crc,8)) -- CRC高字节 log.info("查询帧构建完毕,长度="..#buf.."字节") -- ✅ 转换成二进制字符串 local hex_buf = byte_table_to_hex_str(buf) local lenHex = string.format("%02X", #buf) sx126x_driver.RadioSend(hex_buf, lenHex, "00") -- sys.wait(200) log.info("查询帧发送完毕,配置的PayloadLength="..#buf) end -- -- --------------------- 解析传感器数据 --------------------- local function bytes_to_float(b1,b2,b3,b4) local sign = bit.rshift(b4,7) local expo = bit.band(bit.rshift(b4,0),0x7F) * 2 + bit.rshift(b3,7) local mant = bit.lshift(bit.band(b3,0x7F),16) + bit.lshift(b2,8) + b1 if expo == 0 then return 0.0 elseif expo == 255 then return (mant == 0) and (sign==0 and math.huge or -math.huge) or 0/0 end return (sign==1 and -1 or 1) * (1 + mant/0x800000) * 2^(expo-127) end -- 解码传感器数据,返回 (value, unit, type) local function decode_sensor(sensor) if sensor.data_type == 0x01 then local t = bit.lshift(sensor.data[1],8) + sensor.data[2] return t / 100.0, "°C", "temperature" elseif sensor.data_type == 0x02 then local h = bit.lshift(sensor.data[1],8) + sensor.data[2] return h / 100.0, "%", "humidity" elseif sensor.data_type == 0x03 then return sensor.data[1], "lux", "light" elseif sensor.data_type == 0x04 then return sensor.data[1], "ppm", "smoke" elseif sensor.data_type == 0x10 then if #sensor.data == 4 then local val = bytes_to_float(sensor.data[1],sensor.data[2],sensor.data[3],sensor.data[4]) return val, "L", "liquid" end end return sensor.data, "raw", "unknown" end -- 解析接收到的帧,返回符合 JSON 格式的表 local function parse_received_frame(hex_str) local buf = {} for i = 1, #hex_str, 2 do local hex_byte = hex_str:sub(i, i+1) table.insert(buf, tonumber(hex_byte, 16)) end if #buf < 9 then return nil end if buf[1] ~= FRAME_HEADER1 or buf[2] ~= FRAME_HEADER2 then return nil end local src_addr = buf[3] local dst_addr = buf[4] local frame_type = buf[5] if dst_addr ~= MASTER_ADDR or frame_type ~= FRAME_TYPE_DATA then return nil end local sensor_count = buf[6] local data_total_len = buf[7] -- CRC 校验 local crc_low, crc_high = buf[#buf-1], buf[#buf] local crc_recv = crc_low + bit.lshift(crc_high,8) local crc_calc = Modbus_CRC16(buf, #buf-2) if crc_recv ~= crc_calc then return nil end -- ✅ 返回简洁 JSON local result = { ts = os.time(), addr = src_addr, sensors = {} } local offset = 8 local data_end = 8 + data_total_len - 1 for i = 1, sensor_count do if offset + 2 > data_end then break end local sensor = { id = buf[offset], data_type = buf[offset+1], data_len = buf[offset+2], data = {} } for j = 1, sensor.data_len do table.insert(sensor.data, buf[offset+2+j]) end local value, unit, type = decode_sensor(sensor) table.insert(result.sensors, { id = sensor.id, type = type, value = value, unit = unit }) offset = offset + 3 + sensor.data_len end return result end -- --------------------- 主机轮询 --------------------- function master_query_loop() for addr=MIN_SLAVE_ADDR, MAX_SLAVE_ADDR do sx126x_driver.ReceiveData = "" log.info("Host","查询从机地址",string.format("0x%02X",addr)) send_query_frame(addr) local start = os.time() local response = false while (os.time()-start) < QUERY_TIMEOUT_MS do if sx126x_driver.dataReady() then local rx_buf = sx126x_driver.RadioGetBuffer() log.info("Host","收到从机数据", "十六进制字符串="..rx_buf) if rx_buf == "" then log.info("返回空") break end local slave_addr_hex = string.format("%02X", addr) -- 把当前查询的从机地址转成两位十六进制字符串 log.info("slave_addr_hex",slave_addr_hex) -- log.info("rx_buf:sub(7,8)",rx_buf:sub(7,8)) -- log.info("rx_buf:sub(5,6)",rx_buf:sub(5,6)) if rx_buf:sub(1,2) ~= "5A" then log.info("帧头错误") break end if rx_buf:sub(5,6) ~= slave_addr_hex then log.info("从机地址不匹配") break end if rx_buf:sub(7,8) ~= "00" then log.info("主机地址不匹配") break end local result = parse_received_frame(rx_buf) if result then -- 发布事件,外部来处理 sys.publish("SENSOR_UPDATE",result) break end end sys.wait(50) end if not response then -- log.warn("Host",string.format("从机0x%02X无响应",addr)) end sys.wait(5000) end end sys.taskInit(function() sys.wait(10000) radio.RadioInit() radio.RadioStandby() sx126x_driver.RadioSetTxConfig(sx126x_reg.RadioModems_t.MODEM_LORA,"16",0,1,7,"01","0c","00","01","00","00","00",3000) sx126x_driver.RadioSetChannel(433000000) sys.wait(2000) sx126x_driver.RadioRx(0) local payload = "Hehehe123456" local num = 1 while true do master_query_loop() -- send_query_frame(0x01) -- RadioStandby() -- sx126x_driver.sentString("hello,My_name_is_XuXinyi\n") sys.wait(1500) log.info("Radio", "测试接收数据中。。。") -- sx126x_driver.sentString("5AA50005030000093C") -- sx126x_driver.sentString(payload) -- sx126x_driver.RadioRx(0) -- -- -- sx126x_driver.RadioRx(0) -- sx126x_driver.SX126xWakeup() -- payload = payload .. num -- num = num + 1 end end)