Пример протокола ModBus ASCII — различия между версиями
Материал из WebHMI Wiki
(не показано 5 промежуточных версий 2 участников) | |||
Строка 1: | Строка 1: | ||
+ | <languages/> | ||
+ | <translate> | ||
+ | <!--T:1--> | ||
'''Type:''' Serial port<br/> | '''Type:''' Serial port<br/> | ||
'''Address validation:''' ^(C[0-9]+)$|^(DI[0-9]+)$|^(HR[0-9]+)$|^(IR[0-9]+)$<br/> | '''Address validation:''' ^(C[0-9]+)$|^(DI[0-9]+)$|^(HR[0-9]+)$|^(IR[0-9]+)$<br/> | ||
'''Validation error message:''' Invalid register address. Valid ModBus addresses are Cxxx, DIxxx, IRxxx, HRxxx.<br/> | '''Validation error message:''' Invalid register address. Valid ModBus addresses are Cxxx, DIxxx, IRxxx, HRxxx.<br/> | ||
+ | <!--T:2--> | ||
Code: | Code: | ||
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
-- MODBUS ASCII Demo Driver | -- MODBUS ASCII Demo Driver | ||
+ | <!--T:3--> | ||
function parseAddress () | function parseAddress () | ||
-- do something here | -- do something here | ||
end | end | ||
+ | <!--T:4--> | ||
function createDevices () | function createDevices () | ||
addDevice({name = "C", shift = 0, base = 10, xtraFields = {1, 5}}); | addDevice({name = "C", shift = 0, base = 10, xtraFields = {1, 5}}); | ||
Строка 18: | Строка 24: | ||
end | end | ||
+ | <!--T:5--> | ||
local errorCount = 0; | local errorCount = 0; | ||
+ | <!--T:6--> | ||
function intToPair(val, count) | function intToPair(val, count) | ||
local str = string.upper(string.format("%0" .. count .. "x", val)); | local str = string.upper(string.format("%0" .. count .. "x", val)); | ||
Строка 29: | Строка 37: | ||
end | end | ||
+ | <!--T:7--> | ||
function pairToInt(a, b) | function pairToInt(a, b) | ||
local str = string.char(a) .. string.char(b); | local str = string.char(a) .. string.char(b); | ||
Строка 35: | Строка 44: | ||
end | end | ||
+ | <!--T:8--> | ||
function LRC(table, start, count) | function LRC(table, start, count) | ||
local lrc = 0; | local lrc = 0; | ||
Строка 49: | Строка 59: | ||
+ | <!--T:9--> | ||
function readRegister (reg, device, unitId) | function readRegister (reg, device, unitId) | ||
local request = {}; | local request = {}; | ||
Строка 60: | Строка 71: | ||
request[3] = addr[2]; | request[3] = addr[2]; | ||
− | -- function code | + | <!--T:10--> |
+ | -- function code | ||
local fnCode = intToPair(device.xtraFields[1], 2); | local fnCode = intToPair(device.xtraFields[1], 2); | ||
request[4] = fnCode[1]; | request[4] = fnCode[1]; | ||
request[5] = fnCode[2]; | request[5] = fnCode[2]; | ||
− | -- address of register | + | <!--T:11--> |
+ | -- address of register | ||
local intAddr = intToPair(reg.internalAddr, 4); | local intAddr = intToPair(reg.internalAddr, 4); | ||
request[6] = intAddr[1]; | request[6] = intAddr[1]; | ||
Строка 72: | Строка 85: | ||
request[9] = intAddr[4]; | request[9] = intAddr[4]; | ||
− | -- count of registers | + | <!--T:12--> |
+ | -- count of registers | ||
local count = 1; | local count = 1; | ||
if (reg.dataType == 3) then -- double word | if (reg.dataType == 3) then -- double word | ||
Строка 83: | Строка 97: | ||
request[13] = count[4]; | request[13] = count[4]; | ||
− | -- LRC | + | <!--T:13--> |
+ | -- LRC | ||
local lrc = LRC(request, 2, 12); | local lrc = LRC(request, 2, 12); | ||
lrc = intToPair(lrc, 2); | lrc = intToPair(lrc, 2); | ||
− | request[14] = lrc[1]; | + | <!--T:14--> |
+ | request[14] = lrc[1]; | ||
request[15] = lrc[2]; | request[15] = lrc[2]; | ||
Строка 94: | Строка 110: | ||
request[17] = 10; | request[17] = 10; | ||
− | local res = sendBytes(request); | + | <!--T:15--> |
+ | local res = sendBytes(request); | ||
− | if (res | + | if (res == false) then |
DEBUG("Can't send bytes"); | DEBUG("Can't send bytes"); | ||
return false; | return false; | ||
Строка 102: | Строка 119: | ||
− | local response = {}; | + | <!--T:16--> |
+ | local response = {}; | ||
− | -- read Header | + | <!--T:17--> |
+ | -- read Header | ||
response = readBytes(7); | response = readBytes(7); | ||
+ | if (response == false) then | ||
+ | DEBUG("Can't read response"); | ||
+ | return false; | ||
+ | end | ||
res = #response; | res = #response; | ||
− | if (res ~= 7) then | + | <!--T:18--> |
+ | if (res ~= 7) then | ||
errorCount = errorCount + 1; | errorCount = errorCount + 1; | ||
if (errorCount > 3) then | if (errorCount > 3) then | ||
Строка 119: | Строка 143: | ||
− | if (response[1] ~= request[1]) then | + | <!--T:19--> |
+ | if (response[1] ~= request[1]) then | ||
ERROR("Wrong protocol"); | ERROR("Wrong protocol"); | ||
return false; | return false; | ||
end | end | ||
− | if (response[2] ~= request[2] or response[3] ~= request[3]) then | + | <!--T:20--> |
+ | if (response[2] ~= request[2] or response[3] ~= request[3]) then | ||
ERROR("Wrong UnitID in response"); | ERROR("Wrong UnitID in response"); | ||
return false; | return false; | ||
end | end | ||
− | if (response[4] ~= request[4] or response[5] ~= request[5]) then | + | <!--T:21--> |
+ | if (response[4] ~= request[4] or response[5] ~= request[5]) then | ||
ERROR("Wrong function code in response"); | ERROR("Wrong function code in response"); | ||
return false; | return false; | ||
end | end | ||
− | local length = pairToInt(response[6], response[7]); | + | <!--T:22--> |
+ | local length = pairToInt(response[6], response[7]); | ||
if (length < 1) then | if (length < 1) then | ||
ERROR("Wrong length in response"); | ERROR("Wrong length in response"); | ||
Строка 142: | Строка 170: | ||
local response2 = {}; | local response2 = {}; | ||
− | response2 = readBytes(length * 2 + 4); | + | <!--T:23--> |
+ | response2 = readBytes(length * 2 + 4); | ||
+ | if (response2 == false) then | ||
+ | DEBUG("Can't read response2"); | ||
+ | return false; | ||
+ | end | ||
res = #response2; | res = #response2; | ||
− | local result = {}; | + | <!--T:24--> |
+ | local result = {}; | ||
local resp = {}; | local resp = {}; | ||
Строка 164: | Строка 198: | ||
end | end | ||
− | if (length >= 1) then | + | <!--T:25--> |
+ | if (length >= 1) then | ||
for i = 1, length do | for i = 1, length do | ||
result[i] = pairToInt(response2[i*2 - 1], response2[i*2]); | result[i] = pairToInt(response2[i*2 - 1], response2[i*2]); | ||
Строка 170: | Строка 205: | ||
end | end | ||
− | return result; | + | <!--T:26--> |
+ | return result; | ||
end | end | ||
+ | <!--T:27--> | ||
function writeRegister (reg, device, unitId, newValue) | function writeRegister (reg, device, unitId, newValue) | ||
local request = {}; | local request = {}; | ||
Строка 187: | Строка 224: | ||
− | if (reg.dataType == 3) then -- double word | + | if (reg.dataType == 3) then -- double word should be handled in a special way |
− | if (device.xtraFields[3] == 0) then | + | <!--T:28--> |
+ | if (device.xtraFields[3] == 0) then | ||
ERROR("Can't write these type of registers (" .. device.name .. ")"); | ERROR("Can't write these type of registers (" .. device.name .. ")"); | ||
return false; | return false; | ||
Строка 206: | Строка 244: | ||
request[9] = intAddr[4]; | request[9] = intAddr[4]; | ||
− | local count = intToPair(2, 4); | + | <!--T:29--> |
+ | local count = intToPair(2, 4); | ||
request[10] = count[1]; | request[10] = count[1]; | ||
request[11] = count[2]; | request[11] = count[2]; | ||
Строка 212: | Строка 251: | ||
request[13] = count[4]; | request[13] = count[4]; | ||
− | -- message length | + | <!--T:30--> |
+ | -- message length | ||
local dataLen = intToPair(4, 2); | local dataLen = intToPair(4, 2); | ||
request[14] = dataLen[1]; | request[14] = dataLen[1]; | ||
Строка 222: | Строка 262: | ||
end | end | ||
− | + | -- LRC | |
local lrc = LRC(request, 2, 22); | local lrc = LRC(request, 2, 22); | ||
lrc = intToPair(lrc, 2); | lrc = intToPair(lrc, 2); | ||
Строка 233: | Строка 273: | ||
local res = sendBytes(request); | local res = sendBytes(request); | ||
− | + | if (res == false) then | |
− | if (res | + | DEBUG("Can't send request"); |
− | DEBUG("Can't send | + | |
return false; | return false; | ||
end | end | ||
− | |||
− | response = readBytes(17); | + | <!--T:31--> |
+ | local response = {}; | ||
+ | |||
+ | <!--T:32--> | ||
+ | response = readBytes(17); | ||
+ | if (response == false) then | ||
+ | DEBUG("Can't read response"); | ||
+ | return false; | ||
+ | end | ||
res = #response; | res = #response; | ||
Строка 250: | Строка 296: | ||
− | if (response[1] ~= request[1]) then | + | <!--T:33--> |
+ | if (response[1] ~= request[1]) then | ||
ERROR("Wrong protocol"); | ERROR("Wrong protocol"); | ||
return false; | return false; | ||
Строка 277: | Строка 324: | ||
end | end | ||
− | if (response[10] ~= request[10] or response[11] ~= request[11] or response[12] ~= request[12] or response[13] ~= request[13]) then | + | <!--T:34--> |
+ | if (response[10] ~= request[10] or response[11] ~= request[11] or response[12] ~= request[12] or response[13] ~= request[13]) then | ||
ERROR("Wrong register count"); | ERROR("Wrong register count"); | ||
return false; | return false; | ||
Строка 287: | Строка 335: | ||
end | end | ||
+ | <!--T:35--> | ||
+ | else | ||
− | + | <!--T:36--> | |
− | + | if (device.xtraFields[2] == 0) then | |
− | + | ||
ERROR("Can't write these type of registers (" .. device.name .. ")"); | ERROR("Can't write these type of registers (" .. device.name .. ")"); | ||
return false; | return false; | ||
Строка 307: | Строка 356: | ||
request[9] = intAddr[4]; | request[9] = intAddr[4]; | ||
− | -- data | + | <!--T:37--> |
+ | -- data | ||
local val = newValue; | local val = newValue; | ||
if (reg.dataType == 0) then | if (reg.dataType == 0) then | ||
Строка 336: | Строка 386: | ||
local res = sendBytes(request); | local res = sendBytes(request); | ||
− | if (res | + | if (res == false) then |
DEBUG("Can't send bytes"); | DEBUG("Can't send bytes"); | ||
return false; | return false; | ||
Строка 345: | Строка 395: | ||
response = readBytes(requestLen); | response = readBytes(requestLen); | ||
+ | if (response == false) then | ||
+ | DEBUG("Can't read response"); | ||
+ | return false; | ||
+ | end | ||
res = #response; | res = #response; | ||
− | if (res ~= requestLen) then | + | <!--T:38--> |
+ | if (res ~= requestLen) then | ||
DEBUG("Wrong response length"); | DEBUG("Wrong response length"); | ||
return false; | return false; | ||
Строка 362: | Строка 417: | ||
end | end | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | </translate> |
Текущая версия на 11:27, 1 марта 2018
Type: Serial port
Address validation: ^(C[0-9]+)$|^(DI[0-9]+)$|^(HR[0-9]+)$|^(IR[0-9]+)$
Validation error message: Invalid register address. Valid ModBus addresses are Cxxx, DIxxx, IRxxx, HRxxx.
Code:
-- MODBUS ASCII Demo Driver
function parseAddress ()
-- do something here
end
function createDevices ()
addDevice({name = "C", shift = 0, base = 10, xtraFields = {1, 5}});
addDevice({name = "DI", shift = 0, base = 10, xtraFields = {2, 0}});
addDevice({name = "HR", shift = 0, base = 10, xtraFields = {3, 6, 16}});
addDevice({name = "IR", shift = 0, base = 10, xtraFields = {4, 0}});
end
local errorCount = 0;
function intToPair(val, count)
local str = string.upper(string.format("%0" .. count .. "x", val));
local res = {};
for i = 1, count do
res[i] = string.byte(string.sub(str, i, i));
end
return res;
end
function pairToInt(a, b)
local str = string.char(a) .. string.char(b);
local n = tonumber(str, 16);
return n;
end
function LRC(table, start, count)
local lrc = 0;
local i = start;
while (i < (start+count)) do
local str = string.char(table[i]) .. string.char(table[i + 1]);
local n = tonumber(str, 16);
lrc = lrc + n;
i = i + 2;
end
lrc = bit.band(lrc, 255);
return 256 - lrc;
end
function readRegister (reg, device, unitId)
local request = {};
-- ASCII header
request[1] = 58;
-- slave address
local addr = intToPair(unitId, 2);
request[2] = addr[1];
request[3] = addr[2];
-- function code
local fnCode = intToPair(device.xtraFields[1], 2);
request[4] = fnCode[1];
request[5] = fnCode[2];
-- address of register
local intAddr = intToPair(reg.internalAddr, 4);
request[6] = intAddr[1];
request[7] = intAddr[2];
request[8] = intAddr[3];
request[9] = intAddr[4];
-- count of registers
local count = 1;
if (reg.dataType == 3) then -- double word
count = 2;
end
count = intToPair(count, 4);
request[10] = count[1];
request[11] = count[2];
request[12] = count[3];
request[13] = count[4];
-- LRC
local lrc = LRC(request, 2, 12);
lrc = intToPair(lrc, 2);
request[14] = lrc[1];
request[15] = lrc[2];
-- CR LF
request[16] = 13;
request[17] = 10;
local res = sendBytes(request);
if (res == false) then
DEBUG("Can't send bytes");
return false;
end
local response = {};
-- read Header
response = readBytes(7);
if (response == false) then
DEBUG("Can't read response");
return false;
end
res = #response;
if (res ~= 7) then
errorCount = errorCount + 1;
if (errorCount > 3) then
closeConnection();
errorCount = 0;
end
DEBUG("Can't read header");
return false;
end
if (response[1] ~= request[1]) then
ERROR("Wrong protocol");
return false;
end
if (response[2] ~= request[2] or response[3] ~= request[3]) then
ERROR("Wrong UnitID in response");
return false;
end
if (response[4] ~= request[4] or response[5] ~= request[5]) then
ERROR("Wrong function code in response");
return false;
end
local length = pairToInt(response[6], response[7]);
if (length < 1) then
ERROR("Wrong length in response");
return false;
end
local response2 = {};
response2 = readBytes(length * 2 + 4);
if (response2 == false) then
DEBUG("Can't read response2");
return false;
end
res = #response2;
local result = {};
local resp = {};
for i=1,#response do
resp[i] = response[i];
end
local c = #response;
for i=1,#response2 do
resp[c+i] = response2[i];
end
local lrc_resp = LRC(resp, 2, 6 + length * 2);
local lrc_actual = pairToInt(response2[length*2 + 1], response2[length*2 + 2]);
if (lrc_resp ~= lrc_actual) then
ERROR("Wrong LRC in response");
return false;
end
if (length >= 1) then
for i = 1, length do
result[i] = pairToInt(response2[i*2 - 1], response2[i*2]);
end
end
return result;
end
function writeRegister (reg, device, unitId, newValue)
local request = {};
-- ASCII header
request[1] = 58;
-- slave address
local addr = intToPair(unitId, 2);
request[2] = addr[1];
request[3] = addr[2];
if (reg.dataType == 3) then -- double word should be handled in a special way
if (device.xtraFields[3] == 0) then
ERROR("Can't write these type of registers (" .. device.name .. ")");
return false;
end
-- function code
local fnCode = intToPair(device.xtraFields[3], 2);
request[4] = fnCode[1];
request[5] = fnCode[2];
-- address of register
local intAddr = intToPair(reg.internalAddr, 4);
request[6] = intAddr[1];
request[7] = intAddr[2];
request[8] = intAddr[3];
request[9] = intAddr[4];
local count = intToPair(2, 4);
request[10] = count[1];
request[11] = count[2];
request[12] = count[3];
request[13] = count[4];
-- message length
local dataLen = intToPair(4, 2);
request[14] = dataLen[1];
request[15] = dataLen[2];
local valpairs = intToPair(newValue, 8);
for i=1,8 do
request[15+i] = valpairs[i];
end
-- LRC
local lrc = LRC(request, 2, 22);
lrc = intToPair(lrc, 2);
request[24] = lrc[1];
request[25] = lrc[2];
-- CR LF
request[26] = 13;
request[27] = 10;
local res = sendBytes(request);
if (res == false) then
DEBUG("Can't send request");
return false;
end
local response = {};
response = readBytes(17);
if (response == false) then
DEBUG("Can't read response");
return false;
end
res = #response;
if (res ~= 17) then
DEBUG("Wrong response length");
return false;
end
if (response[1] ~= request[1]) then
ERROR("Wrong protocol");
return false;
end
local lrc_resp = LRC(response, 2, 12);
local lrc_actual = pairToInt(response[14], response[15]);
if (lrc_resp ~= lrc_actual) then
ERROR("Wrong LRC in response");
return false;
end
if (response[2] ~= request[2] or response[3] ~= request[3]) then
ERROR("Wrong UnitID in response");
return false;
end
if (response[4] ~= request[4] or response[5] ~= request[5]) then
ERROR("Wrong function in response");
return false;
end
if (response[6] ~= request[6] or response[7] ~= request[7] or response[8] ~= request[8] or response[9] ~= request[9]) then
ERROR("Wrong register address in response");
return false;
end
if (response[10] ~= request[10] or response[11] ~= request[11] or response[12] ~= request[12] or response[13] ~= request[13]) then
ERROR("Wrong register count");
return false;
end
if (response[16] ~= request[16] or response[17] ~= request[17]) then
ERROR("Wrong footer in response");
return false;
end
else
if (device.xtraFields[2] == 0) then
ERROR("Can't write these type of registers (" .. device.name .. ")");
return false;
end
-- function code
local fnCode = intToPair(device.xtraFields[2], 2);
request[4] = fnCode[1];
request[5] = fnCode[2];
-- address of register
local intAddr = intToPair(reg.internalAddr, 4);
request[6] = intAddr[1];
request[7] = intAddr[2];
request[8] = intAddr[3];
request[9] = intAddr[4];
-- data
local val = newValue;
if (reg.dataType == 0) then
if (val > 0) then
val = 255*256;
else
val = 0;
end
end
local valpairs = intToPair(val, 4);
request[10] = valpairs[1];
request[11] = valpairs[2];
request[12] = valpairs[3];
request[13] = valpairs[4];
-- LRC
local lrc = LRC(request, 2, 12);
lrc = intToPair(lrc, 2);
request[14] = lrc[1];
request[15] = lrc[2];
-- CR LF
request[16] = 13;
request[17] = 10;
local res = sendBytes(request);
if (res == false) then
DEBUG("Can't send bytes");
return false;
end
local response = {};
local requestLen = #request;
response = readBytes(requestLen);
if (response == false) then
DEBUG("Can't read response");
return false;
end
res = #response;
if (res ~= requestLen) then
DEBUG("Wrong response length");
return false;
end
for i = 1,res do
if (response[i] ~= request[i]) then
DEBUG("Wrong response");
return false;
end
end
end
return true;
end