Modbus TCP
modbustcp
module implements Modbus TCP communication protocol.
modbustcp.new
-- @param address string Address of Modbus device in a `host:port` format
-- @return table
function modbustcp.new(address)
end
Creates a new Modbus connection object (referred as modbus_conn
below). It does not yet initiate an actual TCP connection. Instead it will be initiated on a first Modbus request.
Example
local modbus_conn = modbustcp.new("192.168.1.110:502")
modbus_conn
Object
read_coils
-- @param unit_id number Unit ID of Modbus device
-- @param start_register number Number of the first register to read
-- @param registers_count number Number of registers to read
-- @param timeout number Time to wait for the response in milliseconds
-- @return table|nil, number
function modbus_conn:read_coils(unit_id, start_register, registers_count, timeout)
end
Reads coil registers, Modbus function 0x01
.
Returns Lua table with register contents or nil
in case of error. Error code is returned as a second return value (use modbustcp.err_to_str
to convert it to string representation).
Example
-- Read two coil registers (numbers 215 and 216), timeout 1 second
local registers, result = modbus_conn:read_coils(1, 215, 2, 1000)
if registers then
enapter.log("Coils: "..tostring(registers[1]).." "..tostring(registers[2]))
else
enapter.log("Error reading Modbus: "..tostring(result).." "..modbustcp.err_to_str(result), "error", true)
end
read_discrete_inputs
-- @param unit_id number Unit ID of Modbus device
-- @param start_register number Number of the first register to read
-- @param registers_count number Number of registers to read
-- @param timeout number Time to wait for the response in milliseconds
-- @return table|nil, number
function modbus_conn:read_discrete_inputs(unit_id, start_register, registers_count, timeout)
end
Reads discrete input registers, Modbus function 0x02
.
Returns Lua table with register contents or nil
in case of error. Error code is returned as a second return value (use modbustcp.err_to_str
to convert it to string representation).
Example
-- Read two discrete input registers (numbers 220 and 221), timeout 1 second
local registers, result = modbus_conn:read_discrete_inputs(1, 220, 2, 1000)
if registers then
enapter.log("Discrete input: "..tostring(registers[1]).." "..tostring(registers[2]))
else
enapter.log("Error reading Modbus: "..tostring(result).." "..modbustcp.err_to_str(result), "error", true)
end
read_holdings
-- @param unit_id number Unit ID of Modbus device
-- @param start_register number Number of the first register to read
-- @param registers_count number Number of registers to read
-- @param timeout number Time to wait for the response in milliseconds
-- @return table|nil, number
function modbus_conn:read_holdings(unit_id, start_register, registers_count, timeout)
end
Reads holding registers, Modbus function 0x03
.
Returns Lua table with register contents or nil
in case of error. Error code is returned as a second return value (use modbustcp.err_to_str
to convert it to string representation).
Example
-- Read two holding registers (numbers 230 and 231), timeout 1 second
local registers, result = modbus_conn:read_holdings(1, 230, 2, 1000)
if registers then
enapter.log("Holding: "..tostring(registers[1]).." "..tostring(registers[2]))
else
enapter.log("Error reading Modbus: "..tostring(result).." "..modbustcp.err_to_str(result), "error", true)
end
read_inputs
-- @param unit_id number Unit ID of Modbus device
-- @param start_register number Number of the first register to read
-- @param registers_count number Number of registers to read
-- @param timeout number Time to wait for the response in milliseconds
-- @return table|nil, number
function modbus_conn:read_inputs(unit_id, start_register, registers_count, timeout)
end
Reads input registers, Modbus function 0x04
.
Returns Lua table with register contents or nil
in case of error. Error code is returned as a second return value (use modbustcp.err_to_str
to convert it to string representation).
Example
-- Read two input registers (numbers 240 and 241), timeout 1 second
local registers, result = modbus_conn:read_inputs(1, 240, 2, 1000)
if registers then
enapter.log("Input: "..tostring(registers[1]).." "..tostring(registers[2]))
else
enapter.log("Error reading Modbus: "..tostring(result).." "..modbustcp.err_to_str(result), "error", true)
end
write_coil
-- @param unit_id number Unit ID of Modbus device
-- @param register number Register number to write to
-- @param value number Value to write
-- @param timeout number Time to wait for the response in milliseconds
-- @return number
function modbus_conn:write_coil(unit_id, register, value, timeout)
end
Writes coil register, Modbus function 0x05
.
Returns 0
if the value is written successfully, otherwise returns error code (use modbustcp.err_to_str
to convert it to string representation).
Example
-- Write 0 to coil register number 503, timeout 1 second
local result = modbus_conn:write_coil(1, 503, 0, 1000)
if result ~= 0 then
enapter.log("Error writing Modbus: "..tostring(result).." "..modbustcp.err_to_str(result), "error", true)
end
write_holding
-- @param unit_id number Unit ID of Modbus device
-- @param register number Register number to write to
-- @param value number Value to write
-- @param timeout number Time to wait for the response in milliseconds
-- @return number
function modbus_conn:write_holding(unit_id, register, value, timeout)
end
Writes holding register, Modbus function 0x06
.
Returns 0
if the value is written successfully, otherwise returns error code (use modbustcp.err_to_str
to convert it to string representation).
Example
-- Write 12 to holding register number 610, timeout 1 second
local result = modbus_conn:write_holding(1, 610, 12, 1000)
if result ~= 0 then
enapter.log("Error writing Modbus: "..tostring(result).." "..modbustcp.err_to_str(result), "error", true)
end
write_multiple_coils
-- @param unit_id number Unit ID of Modbus device
-- @param start_register number Number of the first register to write to
-- @param values table Array of values to write to registers
-- @param timeout number Time to wait for the response in milliseconds
-- @return number
function modbus_conn:write_multiple_coils(unit_id, start_register, values, timeout)
end
Writes the sequence of coil registers, Modbus function 0x0F
. The values
table must contain integer values to be written to the registers.
Returns 0
if values are written successfully, otherwise returns error code (use modbustcp.err_to_str
to convert it to string representation).
Example
-- Write zeroes to coil registers 700 and 701, timeout 1 second
local values = {0, 0}
local result = modbus_conn:write_multiple_coils(1, 700, values, 1000)
if result ~= 0 then
enapter.log("Error writing Modbus: "..tostring(result).." "..modbustcp.err_to_str(result), "error", true)
end
write_multiple_holdings
-- @param unit_id number Unit ID of Modbus device
-- @param start_register number Number of the first register to write to
-- @param values table Array of values to write to registers
-- @param timeout number Time to wait for the response in milliseconds
-- @return number
function modbus_conn:write_multiple_holdings(unit_id, start_register, values, timeout)
end
Writes the sequence of holding registers, Modbus function 0x10
. The values
table must contain integer values to be written to the registers.
Returns 0
if values are written successfully, otherwise returns error code (use modbustcp.err_to_str
to convert it to string representation).
Example
-- Write values to holding registers 800 and 801, timeout 1 second
local values = {14, 15}
local result = modbus_conn:write_multiple_coils(1, 800, values, 1000)
if result ~= 0 then
enapter.log("Error writing Modbus: "..tostring(result).." "..modbustcp.err_to_str(result), "error", true)
end
modbustcp.err_to_str
-- @param error number
-- @return string
function modbustcp.err_to_str(error)
end
Returns string representation of modbustcp
function return code.
Byte Order
A Modbus register is composed of two bytes. Typically, the register value is transferred in Big-Endian byte order. However, the most modern CPUs, including the one in Enapter Gateway, use Little-Endian byte order. Given this, the UCM automatically performs endianness conversion, under the assumption that the Modbus server transfers values in Big-Endian order. Consequently, there is no need for you to manually perform this conversion.
Let's take an example. Imagine you have the register number 112
which holds unsigned integer value 4660
(it may be 46.6
℃), which is equal to 0x1234
hexadecimal (two bytes – 0x12
and 0x34
). The value is transferred as 0x3412
(13330
decimal), but when you read it:
-- Create Modbus TCP connection object
local modbus_conn = modbustcp.new("192.168.1.110:502")
-- Read the value of the register 112
local registers, result = modbus_conn:read_holdings(1, 112, 1, 1000)
if registers then
value = register[1]
enapter.log("Register value: "..tostring(value))
end
You'll get this logged as expected without any conversions:
Register value: 4660