|
|
@@ -0,0 +1,285 @@
|
|
|
+
|
|
|
+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
|
|
|
+
|
|
|
+local function print_sensor(s)
|
|
|
+ if s.data_type == 0x01 then
|
|
|
+ local t = bit.lshift(s.data[1],8) + s.data[2]
|
|
|
+ log.info("Sensor","温度", string.format("%.2f°C", t/100.0))
|
|
|
+ elseif s.data_type == 0x02 then
|
|
|
+ local h = bit.lshift(s.data[1],8) + s.data[2]
|
|
|
+ log.info("Sensor","湿度", string.format("%.2f%%", h/100.0))
|
|
|
+ elseif s.data_type == 0x03 then
|
|
|
+ log.info("Sensor","光照强度", s.data[1])
|
|
|
+ elseif s.data_type == 0x04 then
|
|
|
+ log.info("Sensor","烟雾浓度", s.data[1])
|
|
|
+ elseif s.data_type == 0x10 then
|
|
|
+ if #s.data == 4 then
|
|
|
+ local val = bytes_to_float(s.data[1],s.data[2],s.data[3],s.data[4])
|
|
|
+ log.info("Sensor","扩展类型0x10 (float)", string.format("%.4f L", val))
|
|
|
+ else
|
|
|
+ log.warn("Sensor","0x10 长度异常", #s.data)
|
|
|
+ end
|
|
|
+ else
|
|
|
+ log.info("Sensor","未知类型", s.data_type, "原始数据", table.concat(s.data,","))
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+local function parse_received_frame(hex_str)
|
|
|
+ log.info("Frame","收到数据", hex_str)
|
|
|
+
|
|
|
+ -- 转换 hex_str → buf
|
|
|
+ local buf = {}
|
|
|
+ for i = 1, #hex_str, 2 do
|
|
|
+ local hex_byte = hex_str:sub(i, i+1)
|
|
|
+ local dec_byte = tonumber(hex_byte, 16)
|
|
|
+ if not dec_byte then
|
|
|
+ log.error("Frame","十六进制转换失败", "非法字符:"..hex_byte)
|
|
|
+ return false
|
|
|
+ end
|
|
|
+ table.insert(buf, dec_byte)
|
|
|
+ end
|
|
|
+ local buf_len = #buf
|
|
|
+ if buf_len < 9 then
|
|
|
+ log.error("Frame","长度不足", buf_len)
|
|
|
+ return false
|
|
|
+ end
|
|
|
+
|
|
|
+ -- 帧头
|
|
|
+ if buf[1] ~= FRAME_HEADER1 or buf[2] ~= FRAME_HEADER2 then
|
|
|
+ log.error("Frame","帧头错误", string.format("实际=0x%02X%02X", buf[1], buf[2]))
|
|
|
+ return false
|
|
|
+ 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
|
|
|
+ log.error("Frame","地址/类型错误",
|
|
|
+ string.format("src=0x%02X dst=0x%02X type=0x%02X", src_addr,dst_addr,frame_type))
|
|
|
+ return false
|
|
|
+ end
|
|
|
+
|
|
|
+ -- 传感器数量+数据总长
|
|
|
+ local sensor_count = buf[6]
|
|
|
+ local data_total_len = buf[7]
|
|
|
+
|
|
|
+ -- CRC校验
|
|
|
+ local crc_low, crc_high = buf[buf_len-1], buf[buf_len]
|
|
|
+ local crc_recv = crc_low + bit.lshift(crc_high,8)
|
|
|
+ local crc_calc = Modbus_CRC16(buf, buf_len-2)
|
|
|
+ if crc_recv ~= crc_calc then
|
|
|
+ log.error("Frame","CRC错误", string.format("recv=0x%04X calc=0x%04X", crc_recv,crc_calc))
|
|
|
+ return false
|
|
|
+ end
|
|
|
+
|
|
|
+ log.info("Frame","地址=0x"..string.format("%02X",src_addr),
|
|
|
+ "传感器数="..sensor_count,
|
|
|
+ "数据长度="..data_total_len.."字节")
|
|
|
+
|
|
|
+ -- 逐个解析传感器
|
|
|
+ local offset = 8
|
|
|
+ local data_end = 8 + data_total_len - 1
|
|
|
+ for i = 1, sensor_count do
|
|
|
+ if offset + 2 > data_end then
|
|
|
+ log.warn("Frame","传感器头部越界", "offset="..offset)
|
|
|
+ break
|
|
|
+ end
|
|
|
+ local sensor = {
|
|
|
+ sensor_id = buf[offset],
|
|
|
+ data_type = buf[offset+1],
|
|
|
+ data_len = buf[offset+2],
|
|
|
+ data = {}
|
|
|
+ }
|
|
|
+ if offset + 3 + sensor.data_len - 1 > data_end then
|
|
|
+ log.warn("Frame","传感器数据越界 ID=0x"..string.format("%02X",sensor.sensor_id))
|
|
|
+ break
|
|
|
+ end
|
|
|
+ for j = 1, sensor.data_len do
|
|
|
+ table.insert(sensor.data, buf[offset+2+j])
|
|
|
+ end
|
|
|
+ print_sensor(sensor)
|
|
|
+ offset = offset + 3 + sensor.data_len
|
|
|
+ end
|
|
|
+
|
|
|
+ return true
|
|
|
+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(5,6) ~= slave_addr_hex then
|
|
|
+ log.info("从机地址不匹配")
|
|
|
+ break
|
|
|
+ end
|
|
|
+ if rx_buf:sub(7,8) ~= "00" then
|
|
|
+ log.info("主机地址不匹配")
|
|
|
+ break
|
|
|
+ end
|
|
|
+ if parse_received_frame(rx_buf) then
|
|
|
+ response = true
|
|
|
+ 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(5000)
|
|
|
+
|
|
|
+ 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)
|