modbus_ascii_custom
no way to compare when less than two revisions
Differences
This shows you the differences between two versions of the page.
— | modbus_ascii_custom [2019/01/09 12:21] (current) – created emozolyak | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== An example of custom protocol for Modbus ASCII ====== | ||
+ | |||
+ | ***Type**: Serial port | ||
+ | ***Address validation**: | ||
+ | ***Validation error message**: Invalid register address. Valid ModBus addresses are Cxxx, DIxxx, IRxxx, HRxxx. | ||
+ | |||
+ | Code: | ||
+ | |||
+ | <code lua> | ||
+ | -- MODBUS ASCII Demo Driver | ||
+ | |||
+ | function parseAddress () | ||
+ | -- do something here | ||
+ | end | ||
+ | |||
+ | function createDevices () | ||
+ | addDevice({name = " | ||
+ | addDevice({name = " | ||
+ | addDevice({name = " | ||
+ | addDevice({name = " | ||
+ | end | ||
+ | |||
+ | local errorCount = 0; | ||
+ | |||
+ | function intToPair(val, | ||
+ | local str = string.upper(string.format(" | ||
+ | local res = {}; | ||
+ | for i = 1, count do | ||
+ | res[i] = string.byte(string.sub(str, | ||
+ | end | ||
+ | return res; | ||
+ | end | ||
+ | |||
+ | function pairToInt(a, | ||
+ | local str = string.char(a) .. string.char(b); | ||
+ | local n = tonumber(str, | ||
+ | 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, | ||
+ | lrc = lrc + n; | ||
+ | i = i + 2; | ||
+ | end | ||
+ | lrc = bit.band(lrc, | ||
+ | return 256 - lrc; | ||
+ | end | ||
+ | | ||
+ | |||
+ | function readRegister (reg, device, unitId) | ||
+ | local request = {}; | ||
+ | | ||
+ | -- ASCII header | ||
+ | request[1] = 58; | ||
+ | | ||
+ | -- slave address | ||
+ | local addr = intToPair(unitId, | ||
+ | request[2] = addr[1]; | ||
+ | request[3] = addr[2]; | ||
+ | |||
+ | -- function code | ||
+ | local fnCode = intToPair(device.xtraFields[1], | ||
+ | request[4] = fnCode[1]; | ||
+ | request[5] = fnCode[2]; | ||
+ | |||
+ | -- address of register | ||
+ | local intAddr = intToPair(reg.internalAddr, | ||
+ | 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, | ||
+ | request[10] = count[1]; | ||
+ | request[11] = count[2]; | ||
+ | request[12] = count[3]; | ||
+ | request[13] = count[4]; | ||
+ | |||
+ | -- LRC | ||
+ | local lrc = LRC(request, | ||
+ | lrc = intToPair(lrc, | ||
+ | |||
+ | 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(" | ||
+ | return false; | ||
+ | end | ||
+ | |||
+ | |||
+ | local response = {}; | ||
+ | |||
+ | -- read Header | ||
+ | response = readBytes(7); | ||
+ | if (response == false) then | ||
+ | DEBUG(" | ||
+ | return false; | ||
+ | end | ||
+ | res = #response; | ||
+ | |||
+ | if (res ~= 7) then | ||
+ | errorCount = errorCount + 1; | ||
+ | if (errorCount > 3) then | ||
+ | closeConnection(); | ||
+ | errorCount = 0; | ||
+ | end | ||
+ | DEBUG(" | ||
+ | return false; | ||
+ | end | ||
+ | |||
+ | |||
+ | if (response[1] ~= request[1]) then | ||
+ | ERROR(" | ||
+ | return false; | ||
+ | end | ||
+ | |||
+ | if (response[2] ~= request[2] or response[3] ~= request[3]) then | ||
+ | ERROR(" | ||
+ | return false; | ||
+ | end | ||
+ | |||
+ | if (response[4] ~= request[4] or response[5] ~= request[5]) then | ||
+ | ERROR(" | ||
+ | return false; | ||
+ | end | ||
+ | |||
+ | local length = pairToInt(response[6], | ||
+ | if (length < 1) then | ||
+ | ERROR(" | ||
+ | return false; | ||
+ | end | ||
+ | | ||
+ | local response2 = {}; | ||
+ | |||
+ | response2 = readBytes(length * 2 + 4); | ||
+ | if (response2 == false) then | ||
+ | DEBUG(" | ||
+ | return false; | ||
+ | end | ||
+ | res = #response2; | ||
+ | |||
+ | local result = {}; | ||
+ | | ||
+ | local resp = {}; | ||
+ | for i=1,# | ||
+ | resp[i] = response[i]; | ||
+ | end | ||
+ | local c = #response; | ||
+ | for i=1,# | ||
+ | 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(" | ||
+ | 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, | ||
+ | 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(" | ||
+ | return false; | ||
+ | end | ||
+ | | ||
+ | -- function code | ||
+ | local fnCode = intToPair(device.xtraFields[3], | ||
+ | request[4] = fnCode[1]; | ||
+ | request[5] = fnCode[2]; | ||
+ | | ||
+ | -- address of register | ||
+ | local intAddr = intToPair(reg.internalAddr, | ||
+ | request[6] = intAddr[1]; | ||
+ | request[7] = intAddr[2]; | ||
+ | request[8] = intAddr[3]; | ||
+ | request[9] = intAddr[4]; | ||
+ | |||
+ | local count = intToPair(2, | ||
+ | request[10] = count[1]; | ||
+ | request[11] = count[2]; | ||
+ | request[12] = count[3]; | ||
+ | request[13] = count[4]; | ||
+ | |||
+ | -- message length | ||
+ | local dataLen = intToPair(4, | ||
+ | request[14] = dataLen[1]; | ||
+ | request[15] = dataLen[2]; | ||
+ | | ||
+ | local valpairs = intToPair(newValue, | ||
+ | for i=1,8 do | ||
+ | request[15+i] = valpairs[i]; | ||
+ | end | ||
+ | | ||
+ | -- LRC | ||
+ | local lrc = LRC(request, | ||
+ | lrc = intToPair(lrc, | ||
+ | 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(" | ||
+ | return false; | ||
+ | end | ||
+ | |||
+ | |||
+ | local response = {}; | ||
+ | |||
+ | response = readBytes(17); | ||
+ | if (response == false) then | ||
+ | DEBUG(" | ||
+ | return false; | ||
+ | end | ||
+ | res = #response; | ||
+ | | ||
+ | if (res ~= 17) then | ||
+ | DEBUG(" | ||
+ | return false; | ||
+ | end | ||
+ | | ||
+ | |||
+ | if (response[1] ~= request[1]) then | ||
+ | ERROR(" | ||
+ | return false; | ||
+ | end | ||
+ | | ||
+ | local lrc_resp = LRC(response, | ||
+ | local lrc_actual = pairToInt(response[14], | ||
+ | if (lrc_resp ~= lrc_actual) then | ||
+ | ERROR(" | ||
+ | return false; | ||
+ | end | ||
+ | | ||
+ | if (response[2] ~= request[2] or response[3] ~= request[3]) then | ||
+ | ERROR(" | ||
+ | return false; | ||
+ | end | ||
+ | | ||
+ | if (response[4] ~= request[4] or response[5] ~= request[5]) then | ||
+ | ERROR(" | ||
+ | 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(" | ||
+ | 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(" | ||
+ | return false; | ||
+ | end | ||
+ | | ||
+ | if (response[16] ~= request[16] or response[17] ~= request[17]) then | ||
+ | ERROR(" | ||
+ | return false; | ||
+ | end | ||
+ | |||
+ | else | ||
+ | |||
+ | if (device.xtraFields[2] == 0) then | ||
+ | ERROR(" | ||
+ | return false; | ||
+ | end | ||
+ | | ||
+ | -- function code | ||
+ | local fnCode = intToPair(device.xtraFields[2], | ||
+ | request[4] = fnCode[1]; | ||
+ | request[5] = fnCode[2]; | ||
+ | | ||
+ | -- address of register | ||
+ | local intAddr = intToPair(reg.internalAddr, | ||
+ | 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, | ||
+ | request[10] = valpairs[1]; | ||
+ | request[11] = valpairs[2]; | ||
+ | request[12] = valpairs[3]; | ||
+ | request[13] = valpairs[4]; | ||
+ | | ||
+ | -- LRC | ||
+ | local lrc = LRC(request, | ||
+ | lrc = intToPair(lrc, | ||
+ | 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(" | ||
+ | return false; | ||
+ | end | ||
+ | | ||
+ | local response = {}; | ||
+ | local requestLen = #request; | ||
+ | | ||
+ | response = readBytes(requestLen); | ||
+ | if (response == false) then | ||
+ | DEBUG(" | ||
+ | return false; | ||
+ | end | ||
+ | res = #response; | ||
+ | |||
+ | if (res ~= requestLen) then | ||
+ | DEBUG(" | ||
+ | return false; | ||
+ | end | ||
+ | | ||
+ | for i = 1,res do | ||
+ | if (response[i] ~= request[i]) then | ||
+ | DEBUG(" | ||
+ | return false; | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | return true; | ||
+ | end | ||
+ | </ | ||
+ | |||
modbus_ascii_custom.txt · Last modified: 2019/01/09 12:21 by emozolyak