====== An example of custom protocol for Modbus ASCII ====== ***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