1、概述
DL645協議是智能電表常用的通訊協議。此協議規定了非常多的數據點,但是目前的智能電表通常只支持幾個常用數據點,而且所支持數據點并無統一標準。為協議處理和轉換造成了比較大的難度。
我公司的AiMaster、AiMaker系列可編程產品內置了DL645-2007處理算法。通過Lua函數調用DL645庫接口函數,這樣可以根據不同的電表,靈活進行數據處理。
在取得DL645數據后,就可以放入Modbus虛擬機的Modbus寄存器,提供給多個上位機同時讀取。無需編寫任何Modbus通訊代碼。如果使用AiMaker600物聯網PLC,還可以根據取得的數據做出控制動作。
AiMaster、AiMaker產品也可以對取得DL645數據,使用算法做進一步運算處理,也可以傳輸至云端,提供給APP讀取。
2、讀取電能數據
此例子讀取總電量數據,所有電表都支持此功能。電表是否支持規約中的其他電能數據,請咨詢電表廠家。
print("讀取DL645正向有功總電能數據。")
--表號
local saddr={20,19,01,01,50,02}
--標識碼,此標識碼為DL645-2007協議所規定的電能數據標識碼
--格式為BCD碼,可以直接套用協議中的標識碼規定
--為
local idcode={0x00,0x01,0x00,0x00}
--電表應答的原始數據
local rawdata={0}
local res=0.0
while (true) do
rawdata={0}
--readpwvalue讀取電能數據函數
--參數1:串口號,為0起,0為串口1,1為串口2,以此類推
--參數2:6個數字的表號,通常在電表上可以找到表號,直接填入即可,表號為BCD數字格式。
--參數3:為數組格式的標識碼,用于指出要讀取的電能數據,應符合DL645-2007的定義,
--例如:為讀取正向有功總電能,為讀取反向有功總電能。
--參數4:接收到的原始數據數組,從電表收到的原始數據,如果讀取錯誤,此數據無效。
--參數5:通訊超時,發送讀取指令后,等待電表應答的超時,如果達到超時而未收到電表
--應答數據,則認為電表無應答,將返回錯誤。
--返回:如果返回數據為nil說明讀取失敗。否則返回讀取到的電能數據值,為帶2位小數的浮點數。
res=dl645.readpwvalue(2,saddr,idcode,rawdata,1000)
if (res==nil) then
print(string.format("
讀取失敗"));
else
--res為浮點數格式的總電量數據
print(string.format("
res:%6.2f",res));
end
debug.printarray(rawdata);
--setvaluefc3函數,將電能數據值,設置進Modbus寄存器值
--參數1:Modbus寄存器地址,2個地址保存4個字節
--參數2:下位機地址碼
--參數3:數據類型2-ushort,3-long,4-float,5-int,6-short;
--參數4:此Modbus寄存器地址的數據
--相同類型的數據,應組織在各自的連續的寄存器地址,不應混和組合.
--數據類型為u_short,u_short為16bit數據,占用1個寄存器地址,寄存器讀取數量為1
--設置進Modbus寄存器提供給上位機讀取。
--對應的Modbus參數為,下位機地址1,寄存器地址0起,讀取2個寄存器,格式為反轉float
lib_vmb.setvaluefc3(0,1,4,res);
syslib.ossleep(1000);
end
3、讀取變量數據
并不是所有電表都支持此例子所讀取的變量數據,請咨詢電表廠家以獲得更多信息。
print("讀取DL645變量數據。")
--啟用發送前導字符4個0xFE
--setopt函數設置DL645庫的全局參數。
--參數1:為選項編號,1-是否啟用發送前導字符4個0xFE
--參數2:為選項數據,1-發送(默認),0-不發送前導字符
--dl645.setopt(1,1)
while (true) do
--表號
local saddr={20,19,01,01,50,02}
--idcode為16進制格式的4字節標識碼
--字節存儲順序,按照DL645-2007協議規定的DI3、DI2、DI1、DI0順序排列。
--idcode數組的第1個字節是DI3。以此類推。
--第1個字節0x02為讀取變量數據,第2個字節0x01是讀取電壓
--第3個字節0x01為讀取A相電壓,第3個字節未用,設置為0.
--讀取其他變量數據的標識碼,請查閱DL645-2007協議手冊。或參考"常用DL645數據轉Modbus"例程
local idcode={0x02,0x01,0x01,0x00}
--rawdata用于存儲電表應答的原始數據
local rawdata={0}
--res為讀取到的數據。
local res=0.0
--readvaldata讀取變量數據函數
--參數1:串口號,為0起,0為串口1,1為串口2,以此類推
--參數2:6個數字的表號,通常在電表上可以找到表號,直接填入即可,表號為BCD數字格式。
--參數3:為數組格式的標識碼,用于指出要讀取的電能數據,應符合DL645-2007,1997的定義,
--例如:為讀取A相電壓
--參數4:接收到的原始數據數組,從電表收到的原始數據,如果讀取錯誤,此數據無效。
--參數5:通訊超時,發送讀取指令后,等待電表應答的超時,如果達到超時而未收到電表
--應答數據,則認為電表無應答,將返回錯誤。
--返回:如果返回數據為nil說明讀取失敗。
--否則返回讀取到的數據,返回的數據小數位數已經根據DL645-2007協議的規定進行了處理。
res=dl645.readvaldata(2,saddr,idcode,rawdata,1000)
if (res==nil) then
print(string.format("
讀取失敗"));
else
print(string.format("
A相電壓:%6.2f",res));
--A相電壓數據在Modbus寄存器地址100,讀取2個寄存器地址。
lib_vmb.setvaluefc3(100,1,4,res);
end
--查看返回的原始數據
debug.printarray(rawdata);
--讀取A相電流
idcode={0x02,0x02,0x01,0x00}
res=dl645.readvaldata(2,saddr,idcode,rawdata,1000)
if (res==nil) then
print(string.format("
讀取失敗"));
else
print(string.format("
A相電流:%6.2f",res));
--A相電流數據在Modbus寄存器地址102,讀取2個寄存器地址。
lib_vmb.setvaluefc3(102,1,4,res);
end
--查看返回的原始數據
debug.printarray(rawdata);
--讀取電網頻率
idcode={0x02,0x80,0x00,0x02}
res=dl645.readvaldata(2,saddr,idcode,rawdata,1000)
if (res==nil) then
print(string.format("
讀取失敗"));
else
print(string.format("
電網頻率:%6.2f",res));
--電網頻率數據在Modbus寄存器地址104,讀取2個寄存器地址。
lib_vmb.setvaluefc3(104,1,4,res);
end
--查看返回的原始數據
debug.printarray(rawdata);
--讀取A相相角
idcode={0x02,0x07,0x01,0x00}
res=dl645.readvaldata(2,saddr,idcode,rawdata,1000)
if (res==nil) then
print(string.format("
讀取失敗"));
else
print(string.format("
A相相角:%6.2f",res));
--A相相角在Modbus寄存器地址106,讀取2個寄存器地址。
lib_vmb.setvaluefc3(106,1,4,res);
end
--查看返回的原始數據
debug.printarray(rawdata);
--讀取瞬時總有功功率
idcode={0x02,0x80,0x00,0x05}
res=dl645.readvaldata(2,saddr,idcode,rawdata,1000)
if (res==nil) then
print(string.format("
讀取失敗"));
else
print(string.format("
瞬時總有功功率:%6.4f",res));
--瞬時總有功功率在Modbus寄存器地址108,讀取2個寄存器地址。
lib_vmb.setvaluefc3(108,1,4,res);
end
--查看返回的原始數據
debug.printarray(rawdata);
syslib.ossleep(1000);
end
3、讀取需量數據
很多電表不支持需量數據,請咨詢電表廠家以獲得相關信息。
print("讀取DL645需量數據。")
--啟用發送前導字符4個0xFE
--setopt函數設置DL645庫的全局參數。
--參數1:為選項編號,1-是否啟用發送前導字符4個0xFE
--參數2:為選項數據,1-發送(默認),0-不發送前導字符
--dl645.setopt(1,1)
while (true) do
--表號
local saddr={20,19,01,01,50,02}
--idcode為16進制格式的4字節標識碼
--字節存儲順序,按照DL645-2007協議規定的DI3、DI2、DI1、DI0順序排列。
--請查閱DL645-2007協議手冊。或參考"常用DL645數據轉Modbus"例程
--0x01,0x01,0x00,0x00標識碼為讀取正向有功需量及發生日期
local idcode={0x01,0x01,0x00,0x00}
--rawdata用于存儲電表應答的原始數據
local rawdata={0}
--maxqrdata為需量數據,為浮點數
local res=0.0
--maxqrdt為需量發生日期
local maxqrdt
--readmaxrqdata讀取需數據及發生日期函數
--參數1:串口號,為0起,0為串口1,1為串口2,以此類推
--參數2:6個數字的表號,通常在電表上可以找到表號,直接填入即可,表號為BCD數字格式。
--參數3:為數組格式的標識碼,用于指出要讀取的電能數據,應符合DL645-2007的定義,
--例如:為讀取正向有功需量數據及發生日期
--參數4:接收到的原始數據數組,從電表收到的原始數據,如果讀取錯誤,此數據無效。
--參數5:通訊超時,發送讀取指令后,等待電表應答的超時,如果達到超時而未收到電表
--應答數據,則認為電表無應答,將返回錯誤。
--返回2個數據:
--數據1:需量數據(maxqrdata),為浮點數。
--數據2:需量發生日期(maxqrdt),數據為數組格式,數值按照{年,月,日,小時,分鐘}存儲。
maxqrdt,maxqrdata=dl645.readmaxrqdata(2,saddr,idcode,rawdata,1000)
if (maxqrdata==nil) then
print(string.format("
讀取失敗"));
else
print(string.format("
正向有功需量值:%2.4f",maxqrdata));
print(string.format("
正向有功需量發生日期:%d年%d月%d日%d時%d分",maxqrdt[1],maxqrdt[2],maxqrdt[3],
maxqrdt[4],maxqrdt[5]));
end
debug.printarray(rawdata);
--例子2 ,讀取A相正向有功需量數據
local idcode={0x01,0x15,0x00,0x00}
maxqrdt,maxqrdata=dl645.readmaxrqdata(2,saddr,idcode,rawdata,1000)
if (maxqrdata==nil) then
print(string.format("
讀取失敗"));
else
print(string.format("
A相正向有功需量值:%2.4f",maxqrdata));
print(string.format("
A相正向有功需量發生日期:%d年%d月%d日%d時%d分",maxqrdt[1],maxqrdt[2],maxqrdt[3],
maxqrdt[4],maxqrdt[5]));
end
debug.printarray(rawdata);
syslib.ossleep(1000);
end