|
|
@@ -0,0 +1,180 @@
|
|
|
+module(...,package.seeall)
|
|
|
+require "sx126x_reg"
|
|
|
+require "sx126x_driver"
|
|
|
+require "radio"
|
|
|
+local bit = require "bit"
|
|
|
+-- ---------------- 宏定义 ----------------
|
|
|
+local MASTER_ADDR = 0x00
|
|
|
+local MIN_SLAVE_ADDR = 0x01
|
|
|
+local MAX_SLAVE_ADDR = 0x05
|
|
|
+
|
|
|
+local FRAME_HEADER1 = 0x5A
|
|
|
+local FRAME_HEADER2 = 0xA5
|
|
|
+
|
|
|
+local FRAME_TYPE_QUERY = 0x03
|
|
|
+local FRAME_TYPE_DATA = 0x02
|
|
|
+
|
|
|
+local QUERY_TIMEOUT_MS = 6 --6s
|
|
|
+
|
|
|
+-- CRC16-Modbus (与 C 中保持一致)
|
|
|
+local function Modbus_CRC16(data)
|
|
|
+ local crc = 0xFFFF
|
|
|
+ for i = 1, #data do
|
|
|
+ -- 用bit.bxor替代~,实现按位异或
|
|
|
+ crc = bit.bxor(crc, data:byte(i))
|
|
|
+ for _ = 1, 8 do
|
|
|
+ -- 用bit.band替代&,实现按位与
|
|
|
+ if bit.band(crc, 0x0001) ~= 0 then
|
|
|
+ -- 先右移1位(bit.rshift),再异或0xA001
|
|
|
+ crc = bit.bxor(bit.rshift(crc, 1), 0xA001)
|
|
|
+ else
|
|
|
+ -- 右移1位
|
|
|
+ crc = bit.rshift(crc, 1)
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+ return crc
|
|
|
+end
|
|
|
+
|
|
|
+-- ---------------- 帧构造 ----------------
|
|
|
+local function build_query_frame(slave_addr)
|
|
|
+ local buf = string.char(
|
|
|
+ FRAME_HEADER1,
|
|
|
+ FRAME_HEADER2,
|
|
|
+ MASTER_ADDR,
|
|
|
+ slave_addr,
|
|
|
+ FRAME_TYPE_QUERY,
|
|
|
+ 0x00, 0x00 -- reserved
|
|
|
+ )
|
|
|
+ local crc = Modbus_CRC16(buf)
|
|
|
+ -- 修复按位运算符:& → bit.band,>> → bit.rshift
|
|
|
+ buf = buf .. string.char(
|
|
|
+ bit.band(crc, 0xFF), -- 取CRC低8位
|
|
|
+ bit.band(bit.rshift(crc, 8), 0xFF) -- 取CRC高8位
|
|
|
+ )
|
|
|
+ return buf
|
|
|
+end
|
|
|
+
|
|
|
+-- ---------------- 发送查询 ----------------
|
|
|
+local function send_query_frame(slave_addr)
|
|
|
+ local frame = build_query_frame(slave_addr)
|
|
|
+ local hexStr = sx126x_driver.stringToHex(frame)
|
|
|
+ sx126x_driver.RadioSend(hexStr, string.format("%02X", #hexStr/2), "00")
|
|
|
+
|
|
|
+ log.info("send_query_frame", "addr=0x" .. string.format("%02X", slave_addr))
|
|
|
+end
|
|
|
+
|
|
|
+-- ---------------- 数据帧解析 ----------------
|
|
|
+local function parse_received_frame(hexStr)
|
|
|
+ log.info("parse_received_frame", "hexStr=" .. hexStr)
|
|
|
+ -- local raw = hex_to_string(hexStr)
|
|
|
+ -- local len = #raw
|
|
|
+ -- if len < 9 then return false end
|
|
|
+
|
|
|
+ -- local b = {raw:byte(1, len)}
|
|
|
+ -- if b[1] ~= FRAME_HEADER1 or b[2] ~= FRAME_HEADER2 then
|
|
|
+ -- log.error("parse_received_frame", "帧头不对")
|
|
|
+ -- return false
|
|
|
+ -- end
|
|
|
+
|
|
|
+ -- local src = b[3]
|
|
|
+ -- local dst = b[4]
|
|
|
+ -- local ftype = b[5]
|
|
|
+ -- local count = b[6]
|
|
|
+ -- local datalen = b[7]
|
|
|
+
|
|
|
+ -- local crc_recv = b[len-1] | (b[len] << 8)
|
|
|
+ -- local crc_calc = Modbus_CRC16(raw:sub(1, len-2))
|
|
|
+ -- if crc_recv ~= crc_calc then
|
|
|
+ -- log.error("parse_received_frame", "CRC错误")
|
|
|
+ -- return false
|
|
|
+ -- end
|
|
|
+
|
|
|
+ -- log.info("收到数据帧", string.format("src=0x%02X type=0x%02X count=%d", src, ftype, count))
|
|
|
+
|
|
|
+ -- local offset = 8
|
|
|
+ -- for i = 1, count do
|
|
|
+ -- if offset + 3 > len-2 then break end
|
|
|
+ -- local sid = b[offset]; offset=offset+1
|
|
|
+ -- local dtype = b[offset]; offset=offset+1
|
|
|
+ -- local dlen = b[offset]; offset=offset+1
|
|
|
+
|
|
|
+ -- local data = {}
|
|
|
+ -- for j=1,dlen do
|
|
|
+ -- table.insert(data, b[offset]); offset=offset+1
|
|
|
+ -- end
|
|
|
+
|
|
|
+ -- if dtype == 0x01 and dlen==2 then
|
|
|
+ -- local t = (data[1]<<8)|data[2]
|
|
|
+ -- log.info("温度", t/100 .. "℃")
|
|
|
+ -- elseif dtype == 0x02 and dlen==2 then
|
|
|
+ -- local h = (data[1]<<8)|data[2]
|
|
|
+ -- log.info("湿度", h/100 .. "%")
|
|
|
+ -- elseif dtype == 0x04 and dlen==1 then
|
|
|
+ -- log.info("烟雾浓度", data[1])
|
|
|
+ -- else
|
|
|
+ -- log.info("未知传感器", dtype, dlen)
|
|
|
+ -- end
|
|
|
+ -- end
|
|
|
+ -- return true
|
|
|
+end
|
|
|
+
|
|
|
+-- ---------------- 主循环 ----------------
|
|
|
+local function master_query_loop()
|
|
|
+ for addr = MIN_SLAVE_ADDR, MAX_SLAVE_ADDR do
|
|
|
+ log.info("[host]", string.format("查询从机 0x%02X...", addr))
|
|
|
+ send_query_frame(addr)
|
|
|
+ -- RadioRx() -- 开始接收
|
|
|
+
|
|
|
+ local start = os.time() -- 标准 API,返回毫秒数
|
|
|
+ local response_received = false
|
|
|
+
|
|
|
+ while (os.time() - start) < QUERY_TIMEOUT_MS do
|
|
|
+ -- LoRa 中断回调里会触发数据读取
|
|
|
+ sys.wait(200) -- 轮询等待
|
|
|
+ -- if last_rx_hex ~= nil then
|
|
|
+ -- if parse_received_frame(last_rx_hex) then
|
|
|
+ -- response_received = true
|
|
|
+ -- end
|
|
|
+ -- last_rx_hex = nil
|
|
|
+ -- break
|
|
|
+ -- end
|
|
|
+ end
|
|
|
+
|
|
|
+ if not response_received then
|
|
|
+ log.info("[host]", string.format("从机 0x%02X 未响应", addr))
|
|
|
+ end
|
|
|
+ sys.wait(300) -- 查询间隔
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+-- ---------------- LoRa回调 ----------------
|
|
|
+last_rx_hex = nil
|
|
|
+sys.subscribe("LORA_CALL_BACK", function()
|
|
|
+ sx126x_driver.RadioClearIrqStatus()
|
|
|
+
|
|
|
+ local ret = sx126x_driver.SX126xReadCommand(sx126x_reg.RadioCommands.RADIO_GET_RXBUFFERSTATUS, 3)
|
|
|
+ local payloadLength = tonumber(ret:sub(3,4),16)
|
|
|
+ local hexStr = sx126x_driver.SX126xReadBuffer("00", payloadLength)
|
|
|
+ last_rx_hex = hexStr -- 缓存给主循环
|
|
|
+end)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+sys.taskInit(function()
|
|
|
+ sys.wait(5000)
|
|
|
+ log.info("master_query_loop", "查询开始")
|
|
|
+ radio.RadioInit()
|
|
|
+ radio.RadioStandby()
|
|
|
+ sx126x_driver.RadioSetTxConfig(sx126x_reg.RadioModems_t.MODEM_LORA,"22",0,1,7,"01","0c","00","01","00","00","00",3000)
|
|
|
+ sx126x_driver.RadioSetChannel(433000000)
|
|
|
+ log.info("master_query_loop111", "查询开始")
|
|
|
+ sys.wait(2000)
|
|
|
+ sx126x_driver.RadioRx(0)
|
|
|
+ while true do
|
|
|
+ -- master_query_loop()
|
|
|
+ sys.wait(1000)
|
|
|
+ log.info("master_query_loop", "查询完成")
|
|
|
+ end
|
|
|
+end)
|