Modbus多主机通讯网关 串口服务器

可编程串口以太网网关 全新的极简编程

物联网可编程控制器

可编程CAN-bus物联网网关

高性价比多网口串口服务器

以太网IO模块 模拟量采集、输出模块

常用代码下载

大道至简 极简物联网平台方案

联系我们

虚拟串口软件

诚邀代理

项目合作及代客研发服务

特价商品

/ 淘宝购买
产品名称: 环保HJ212-2017协议转Modbus方案
规  格:
产品备注:
产品类别: 环保HJ212-2017协议转Modbus
点击量: 1464

此方案符合污染物在线监控(监测)系统数据传输标准HJ212-2017规范。HJ212环保协议用于用于向数据中心上传锅炉、水质等各类环保数据,采用TCP/IP Client传输字符串的方式进行通讯。

此方案下,AiMaster网关作为Server,接受HJ212的客户端连接,并接收其上传的数据。然后利用自身强大字符串处理功能,解析出HJ212数据,并放入Modbus寄存器提供给上位机读取。上位机可以是各类组态软件、PLC、触摸屏等等。

也可以将解析出的HJ212数据转换为任何其他协议,例如:通过mqtt协议传输环保数据至云服务器等。


具体代码示例如下,此代码经过实际项目验证,可以直接下载到AiMaster网关运行。


--print("此例子演示启动可支持10个客户端的TCP/IP Server,并通过消息模式读取客户端string数据")
--启动TCP/IP Server,监听端口6003,消息模式,支持10个客户端。

--hj212_3020命令对应的Modbus寄存器地址
mb3020addr=0000
--hj212_2051命令对应的Modbus寄存器地址
mb2051addr=1000
--hj212_2061命令对应的Modbus寄存器地址
mb2061addr=2000
--启动智能Server,监听端口6003,超时20秒,支持10个客户端,消息方式处理数据。
res=lns.startserver(0,6003,20,10,1,1);


local itemlst={}
local maindata={}
local hj212value={}
local keyvallst={}
local itemdata=""
local headdata=""
local rawstr=""
local alldata=""
local rechj212state=0
local sendback=0
local arraydata={0,0}
local mbdatacnt=0
recconn=0
while(true) do
itemlst={}
maindata={}
hj212value={}
keyvallst={}
itemdata=""
headdata=""
rawstr=""
alldata=""
rechj212state=0
sendback=0
recconn=0
arraydata={0,0}
mbdatacnt=0
   while(true) do
   --网络处理循环
     sendback=0
     recconn=lns.srvwaitrec(0);
--     print(string.format("\r\n收到数据的HJ212客户端连接索引:%d",recconn));
     rawstr=lns.srvreadrecstr(0,recconn)
--     print(string.format("\r\n收到HJ212数据:%s",rawstr));

        while(true) do
             --HJ212协议处理循环
             --收到字符串为nil,超时错误。
            if (rawstr==nil) then
                  rechj212state=0
                  break
                end
             --分割主数据,报文头之后的&&...&&之间为主数据   
             itemdata,headdata,crcdata,alldata=splitmaindata(rawstr)
             ---数据错误退出循环,重新接收。
            if (itemdata==nil) or (headdata==nil) then
                 rechj212state=0
                 break
               end
               
             --判断报文头212标识,取命令码。
            cncode=getheadinfo(headdata)

            if cncode==nil then
                  rechj212state=0
                 break;
               end;
               print(cncode)               
               rechj212state=1
               break;
         end
     
         
            if (rechj212state==0) then
                  --如果接收的数据错误,继续网络循环,
                  lns.srvresp(0)
               else
                 --协议接收正常进入处理阶段
                 break
             end
  end

   while(true) do    
        
        itemdata=itemdata..";"
        
        --分割项目数据列表,数据存储在itemlist中
        itemlst=splititemdata(itemdata)
        if (itemlst==nil) then break end
        --分割出HJ212的数据,格式为key=value,例如01-Avg=2.065        
        keyvallst=split212value(itemlst)
        
        if (keyvallst==nil) then break end
        
        if (cncode==2061) then
        --处理2061命令
--            print("conv 2061")
            convres,hj212datatime=convtovmb_2061(keyvallst)
--           print(hj212datatime)
            print(convres)
            sendback=1
        end
        if (cncode==2051) then
        --处理2051命令        
--            print("conv 2051")
            convres,hj212datatime=convtovmb_2051(keyvallst)
--            print(hj212datatime)
            print(convres)
            sendback=1
        end
        
        if (cncode==3020) then
       --处理3020命令
--          print("conv 3020")
            convres,hj212datatime=convtovmb_3020(keyvallst)
            print(convres)
            sendback=1
        end
        break;
   end

  if  (sendback==1) then
  --返回HJ212应答报文
       responstr=buildsenddata(headdata,hj212datatime)
--       print(responstr)
       if (responstr~=nil) then
           --向客户端返回数据,客户端索引reconn也是可以写数据的。      
           res=lns.srvwritestr(0,recconn,responstr)
          end
      end

--此客户端处理完毕        
lns.srvresp(0);
    
 
end



函数定义:


local function getdelistrdata(rawstr,deli)
odata={}
zzbds="(.-)"..deli
if (rawstr==nil) then return nil end
--string.gmatch是支持正则表达式的字符串分割函数,更详细的使用方法请参考互联网上的相关教程。
--正则表达式"(.-),"中的.表示匹配任何字符,-表示短匹配,,为匹配分割符。
for w in string.gmatch(rawstr,zzbds) do
    --w输出由,分割的字符序列。
    table.insert(odata,w);
 end
    return odata
end

local function splitmaindata(rawstr)
--分割主数据,报文头之后的&&...&&之间为主数据
if ((string.byte(string.sub(rawstr,string.len(rawstr)-1)))~=13) or
   ((string.byte(string.sub(rawstr,string.len(rawstr))))~=10) then
      return nil
   end
s,e=string.find(rawstr, "&&")
if (s==nil) or (e==nil) then return nil end
--项目数据
itemdata=string.sub(rawstr,e+1,string.len(rawstr)-8)
--报文头数据
headdata=string.sub(rawstr,1,s-1)
--crc字节
crcdata=string.sub(rawstr,string.len(rawstr)-5,string.len(rawstr)-2)
--所有有效数据
alldata=string.sub(rawstr,7,string.len(rawstr)-6)
return itemdata,headdata,crcdata,alldata
end

local function getheadinfo(rawheaddata)
--判断报文头212标识
if string.sub(rawheaddata,1,2)~="##" then return nil end
headvalue=getdelistrdata(rawheaddata,";")
--取命令码CN
key,value = string.match(headvalue[2],"(.-)=(.+)")
if (value==nil) or (key~="CN") then
      return  nil
   end
--口令
key,pwd = string.match(headvalue[3],"(.-)=(.+)")
if (value==nil) or (key~="PW") then
      return  nil
   end
--设备号号码
key,mn = string.match(headvalue[4],"(.-)=(.+)")
if (value==nil) or (key~="MN") then
      return  nil
   end
   
--返回命令码CN
return tonumber(value)
end

local function splititemdata(rawstr)
--分割项目数据列表
return getdelistrdata(rawstr,";")
end

local function split212value(itemlst)
--分割项目数据                
--输入数据
--DataTime=20190704190046
--B02-Cou=81219.430
--01-Min=1.063, 01-Max=16.938, 01-Avg=2.065, 01-Cou=0.168, 01-ZsMin=0.771, 01-ZsMax=13.231, 01-ZsAvg=1.564, 01-ZsCou=0.127
local odata={}
  for i=1,#itemlst do
      --分割以,分割的key=value数据
       itemlst[i]=itemlst[i]..","
       keyvalelst=getdelistrdata(itemlst[i],",")
       for i2=1,#keyvalelst do
--           print(keyvalelst[i2])       
           table.insert(odata,keyvalelst[i2]);       
       end
  end
--           print(#odata)  

return odata
end

local function convhj212tovmb(keyvaluelst,convmbregaddr)
--转换HJ212数据至Modbus
local fdata
local keyandvalue
local lhj212value={}
local i2=0
local lhj212datatime=""
--DataTime=20190704190046
--B02-Cou=81219.430
--01-Min=1.063, 01-Max=16.938, 01-Avg=2.065, 01-Cou=0.168, 01-ZsMin=0.771, 01-ZsMax=13.231, 01-ZsAvg=1.564, 01-ZsCou=0.127
--转换从第二个元素开始,第一个元素是时间和日期

key,lhj212datatime = string.match(keyvaluelst[1],"(.-)=(.+)")

     
for i=2,#keyvaluelst do
--        print(keyvaluelst[i])
         --用正则表达式解析出key=value数据。
         key,value = string.match(keyvaluelst[i],"(.-)=(.+)")
         if (value==nil) then
             return nil
            end
        --将字符串转换为float数据
        fdata=tonumber(value)

        if (fdata==nil) then
             return nil
            end
        --输出至数组
        table.insert(lhj212value,fdata);
  end
 
  for i=1,#lhj212value do
--        print(lhj212value[i])
--        print(convmbregaddr+((i-1)*2))
        --将数组的值,放入Modbus寄存器,以供上位机读取。
        lib_vmb.setvaluefc3(convmbregaddr+((i-1)*2),1,4,lhj212value[i]);
  end
  return #lhj212value,lhj212datatime
end

local function convtovmb_2051(keyvaluelst)
  return convhj212tovmb(keyvaluelst,mb2051addr)
end

local function convtovmb_2061(keyvaluelst)
  return convhj212tovmb(keyvaluelst,mb2061addr)
end

local function convtovmb_3020(keyvaluelst)
  return convhj212tovmb(keyvaluelst,mb3020addr)
end

local function strtoarray (idata,odata)
  for i=1,#idata do
  table.insert(odata,string.byte(string.sub(idata,i,i)));

  end
    return #odata
end

--****************************************************************************************
--函 数: CRC16_Checkout
--描 述: CRC16 循环冗余校验算法。
--参 数 一: *puchMsg:需要校验的字符串指针
--参 数 二: usDataLen:要校验的字符串长度
--返 回 值: 返回CRC16 校验码
--****************************************************************************************/
local function calhj212crc(idata)
crc_reg = 0xFFFF
larraydata={0,0}
  for i=1,#idata do
--     crc_reg = (crc_reg>>8) ^ puchMsg[i];
    --右移8位
     crc_reg =lib_bit.shr(crc_reg,8,1)  
     
     crc_reg =lib_bit.lxor(crc_reg,idata[i],1)
--              syslib.osresetwdog()
     
        for j=0,7  do
--         check = crc_reg & 0x0001;
         check=lib_bit.land(crc_reg ,0x0001,1)

--         crc_reg >>= 1;
         crc_reg=lib_bit.shr(crc_reg,1,1)
              --crc_reg=lib_bit.lxor(crc_reg,0xA001,1)         
        if(check==0x0001) then
--            crc_reg ^= 0xA001;
              crc_reg=lib_bit.lxor(crc_reg,0xA001,1)
            end
        end
    end
        calc.wordtoarray(larraydata,crc_reg,1,0);
       crcstr=string.format("%2x%2x",larraydata[1],larraydata[2])
return crcstr,crc_reg;
end

local function checkhj212crc(idata,icrcdata)
--检查212协议的crc校验是否正确
--输入的是212
--转换字符串为ANSI数组
checkarray={}
--idata="QN=20160801085000001;ST=91;CN=9014;PW=123456;MN=010000A8900016F000169DC0;Flag=4;CP=&&&&"
--idata="ST=31;CN=3020;PW=123456;MN=34080331WNZK03;Flag=1;CP=&&DataTime=20190704195800;T10-Info=894.427;T11-Info=966.605;T12-Info=900.502;T20-Info=1011.820;T21-Info=1008.292;T22-Info=999.574;T30-Info=1031.684;T31-Info=1052.480;T32-Info=1035.924&&"
strtoarray(idata,checkarray);            
crcstr,crcword=calhj212crc(checkarray)
--print(crcstr)
--if (crcstr==icrcdata) then return 1 end
return 1
end

local function buildsenddata(headdata,idatetime)
--生成返回的应答数据
sendtobackdatafmt="QN=%s;ST=91;CN=9014;PW=%s;MN=%s;Flag=4;CP=&&&&"
headlst={}
headlst=getdelistrdata(headdata,";")
--口令
key,pwd = string.match(headlst[3],"(.-)=(.+)")
--设备号号码
key,mn = string.match(headlst[4],"(.-)=(.+)")

if (idatetime==nil) or (pwd==nil) or (mn==nil) then return nil end   
respdata=string.format(sendtobackdatafmt,idatetime,pwd,mn);
resplen=string.len(respdata)

senddata=string.format("##%04d%s\r\n",resplen,respdata)
return senddata
end


版权所有:深圳市一天广联科技有限公司 邮箱:getit95@163.com 粤ICP备18087671号