一. 代码分为两部分,使用VScode下载luatIDE可在线调试。
二. ModBus通讯协议 V1.1.5
1. main.lua
a.main函数里提供了几个测试报文,通过设定不同的模拟报文,检测函数的逻辑性和可用性。
PROJECT = "M40协议"
VERSION = "2.0.0"
require "M40"
---------------------1.测试报文read-----------------------
--[[
Demo: 读取当前水位值;data:命令报文
]]
--local data = M40.Read_DATA(defaultadd,ReadCode,WaterLevel,2)
--for i=1,8,1 do
-- print(string.format("%02x",data[i]))
--end
---------------------测试报文-----------------------
---------------------2.测试报文write-----------------------
--[[
Demo: 设定量程值;data:命令报文
]]
--local data = M40.Write_DATA(defaultadd,SWriteCode,Range_Max,0)
--for i=1,8,1 do
-- print(string.format("%02x",data[i]))
--end
---------------------测试报文-----------------------
---------------------3.测试报文cut_data-----------------------
--[[
Demo: 读取数据;Revdata:应答报文
]]
--{(多)双寄存器读取数据}
print("Read value of multiple registers")
local RevData1 = {0x01,0x03,0x04,0x00,0x00,0x27,0xD8,0x99,0xE1}
local Data1 = M40.Cut_data(defaultadd,0x02,RevData1)
print(Data1)
--{(单)单寄存器读取数据}
print("Single register reading")
local RevData2 = {0x01,0x03,0x02,0x25,0xFF,0x99,0xE1}
local Data2 = M40.Cut_data(defaultadd,0x01,RevData2)
print(Data2)
---------------------测试报文-----------------------
2. M40.lua
a.每个功能的寄存器数量最多是两个,所以读函数分为单次读取和多次读取两个函数实现。故在解析应答报文的函数中,通过判断寄存器数量来截取数据。
b.应答报文错误类型检测:应答报文存在每一位存在固定的信息码,因此通过判断信息码分辨不同错误信息。
c.CRC校验位根据ModBus协议的校验方式实现,这里不多赘述。
M40 = {}
ReadCode = 0x03 --读寄存器功能码
SWriteCode = 0x06 --单个写寄存器功能码
MWriteCode = 0x10 --连续写寄存器功能码
defaultadd = 0x01 --设备默认地址
------------------寄存器地址---------------------
groundDistance = 0x00 --空高值寄存器-->num=2
WaterLevel = 0x02 --水位值寄存器-->num=2
WaterPercent = 0x04 --水位百分比寄存器-->num=2
Low_position = 0x08 --低位设置寄存器-->num=2
High_position = 0x0A --高位设置寄存器-->num=2
Range_Max = 0x0D --量程设置寄存器-->num=2
Range_min = 0x0F --盲区设置寄存器-->num=2
distance_offset = 0x11 --距离偏量寄存器-->num=1
Integration_time = 0x12 --积分时间寄存器-->num=1
X_angle = 0x15 --X轴与水平面夹角寄存器-->num=1
Y_angle = 0x16 --Y轴与水平面夹角寄存器-->num=1
Z_angle = 0x17 --Z轴与水平面夹角寄存器-->num=1
Signal_noise = 0x1A --雷达信噪比寄存器-->num=1
Text1_Regis = 0x1F --测试寄存器-->num=1
Text2_Regis = 0x22 --测试寄存器-->num=1
temperature = 0x1E --温度寄存器-->num=1
Regis_Format = 0x23 --输出单位及格式寄存器-->num=1
Run_time = 0x24 --运行时间寄存器-->num=1
Sleep_time = 0x25 --休眠寄存器-->num=1
Equipment_status = 0x26 --设备状态寄存器-->num=1
Modbus_add = 0x64 --Modbus地址寄存器-->num=1
Modbus_Baud = 0x65 --Modbus波特率寄存器-->num=1
Modbus_Parity = 0x66 --Modbus奇偶校验寄存器-->num=1
Birth_year = 0x67 --设备生产年份寄存器-->num=1
Birth_month = 0x68 --设备生产月份寄存器-->num=1
Birth_day = 0x69 --设备生产日期寄存器-->num=1
Device_IME = 0x6A --设备序列号寄存器-->num=2
Version = 0x6C --设备序列号寄存器-->num=2
------------------寄存器地址---------------------
--[[
函数名: Cut_data
功能 : 从应答报文中获取数据
参数 : deviceAdd(设备地址),RevData(返回报文),RegisNum(寄存器数量)
返回值: info(数据)
]]
function M40.Cut_data(deviceAdd,RegisNum,RevData)
local ret = M40.Errinfo(deviceAdd,RevData) --判断应答报文是否正确
if RegisNum==1 and 1==ret then --读单个寄存器值的应答报文数据裁剪
local Hdata = RevData[4] << 8
local Ldata = RevData[5]
local Data1 = Hdata | Ldata
return Data1
elseif RegisNum==2 and 1==ret then --读两个寄存器值的应答报文数据裁剪
local HData1 = RevData[4] << 24
local HData2 = RevData[5] << 16
local LData1 = RevData[6] << 8
local LData2 = RevData[7]
local Data2 = HData1 | HData2 | LData1 | LData2
return Data2
elseif ret == -1 then
return -1
end
end
--[[
函数名: Errinfo
功能 : 返回应答报文错误信息
参数 : deviceAdd(设备地址),Data(应答报文)
返回值: 返回错误信息码
01H:非法的功能码 -1
02H:非法的数据地址 -2
03H:非法的数据值 -3
04H:CRC校验错误 -4
05H:接收正确 -5
06H:接收错误 -6
07H:参数错误 -7
]]
function M40.Errinfo(deviceAdd,RevData)
local flag
if RevData[1]==deviceAdd then
if RevData[2]==0x83 or RevData[2]==0x86 or RevData[2]==0x90 then
if RevData[3] == 0x01 then
flag=-1
elseif RevData[3] == 0x02 then
flag=-2
elseif RevData[3] == 0x03 then
flag=-3
elseif RevData[3] == 0x04 then
flag=-4
elseif RevData[3] == 0x05 then
flag=-5
elseif RevData[3] == 0x06 then
flag=-6
elseif RevData[3] == 0x07 then
flag=-7
end
else
flag=1
end
else
flag=0
end
return flag
end
--[[
函数名: CRC_cal
功能 : 获取命令报文的CRC校验位
参数 : Data(待发送报文(未连接校验位))
返回值: CRCData(校验位数据)
]]
function M40.CRC_cal(Data)
local DataLen = #Data
local CRCin = 0xFFFF
local CRCData = 0
for j=1,DataLen,1 do
CRCin = (CRCin ~ Data[j]) & 0xFFFF
for i=1,8,1 do
if ((CRCin & 0x0001) > 0) then
CRCin = CRCin >> 1
CRCin = (CRCin ~ 0xA001) & 0xFFFF
else
CRCin = CRCin >> 1
end
end
end
CRCData = (CRCin >> 8) & 0xFF
CRCData = CRCData | ((CRCin & 0xFF) << 8)
return CRCData
end
--[[
函数名: Read_DATA
功能 : 读寄存器值
参数 : ADD(设备地址),FuncCode(功能码),RegisAdd(寄存器地址)
RegisNum(寄存器数量)
返回值: SendData(发送报文)
]]
function M40.Read_DATA(Add,FuncCode,RegisAdd,RegisNum)
local SendData = {}
SendData[1] = Add
SendData[2] = FuncCode
SendData[3] = 0x00
SendData[4] = RegisAdd
SendData[5] = 0x00
SendData[6] = RegisNum
local CRC = M40.CRC_cal(SendData)
SendData[7] = (CRC >> 8) & 0xFF
SendData[8] = CRC & 0xFF
return SendData
end
--[[
函数名: Write_DATA
功能 : 单次写寄存器的值
参数 : Add(设备地址),FuncCode(功能码),RegisAdd(寄存器地址),info(数据)
返回值: SendData(命令报文)
]]
function M40.Write_DATA(Add,FuncCode,RegisAdd,info)
local SendData = {}
SendData[1] = Add
SendData[2] = FuncCode
SendData[3] = 0x00
SendData[4] = RegisAdd
SendData[5] = (info >> 8) & 0xFF
SendData[6] = info & 0xFF
local CRC = M40.CRC_cal(SendData)
SendData[7] = (CRC >> 8) & 0xFF
SendData[8] = CRC & 0xFF
return SendData
end
--[[
函数名: MWrite_DATA
功能 : 连续写多个寄存器的值(实际操作就是两个寄存器,故信息字节数为四位)
参数 : Add(设备地址),FuncCode(功能码),RegisAdd(寄存器地址)
RegisNum(寄存器数量),info(数据)
返回值: SendData(命令报文)
]]
function M40.MWrite_DATA(Add,FuncCode,RegisAdd,RegisNum,info)
local SendData = {}
SendData[1] = Add
SendData[2] = FuncCode
SendData[3] = 0x00
SendData[4] = RegisAdd
SendData[5] = (RegisNum >> 8) & 0xFF
SendData[6] = RegisNum & 0xFF
SendData[7] = 0x04
SendData[8] = (info >> 24) & 0xFF
SendData[9] = (info >> 16) & 0xFF
SendData[10] = (info >> 8) & 0xFF
SendData[11] = info & 0xFF
local CRC = M40.CRC_cal(SendData)
SendData[12] = (CRC >> 8) & 0xFF
SendData[13] = CRC & 0xFF
return SendData
end
return M40