Skip to main content

Modbus RTU

modbus library implements Modbus RTU communication for ENP-RS232 and ENP-RS485 modules.

Communication initialization

Initialize communication interface with rs232.init or rs485.init function before using modbus.

modbus.read_coils

-- @param address number Address 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.read_coils(address, 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 modbus.err_to_str to convert it to string representation).

Example

-- Read two coil registers (numbers 215 and 216), timeout 1 second
registers, result = modbus.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).." "..modbus.err_to_str(result), "error", true)
end

modbus.read_discrete_inputs

-- @param address number Address 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.read_discrete_inputs(address, 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 modbus.err_to_str to convert it to string representation).

Example

-- Read two discrete input registers (numbers 220 and 221), timeout 1 second
registers, result = modbus.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).." "..modbus.err_to_str(result), "error", true)
end

modbus.read_holdings

-- @param address number Address 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.read_holdings(address, 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 modbus.err_to_str to convert it to string representation).

Example

-- Read two holding registers (numbers 230 and 231), timeout 1 second
registers, result = modbus.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).." "..modbus.err_to_str(result), "error", true)
end

modbus.read_inputs

-- @param address number Address 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.read_inputs(address, 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 modbus.err_to_str to convert it to string representation).

Example

-- Read two input registers (numbers 240 and 241), timeout 1 second
registers, result = modbus.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).." "..modbus.err_to_str(result), "error", true)
end

modbus.write_coil

-- @param address number Address 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.write_coil(address, register, value, timeout)
end

Writes coil register, Modbus function 0x05.

Returns 0 if the value is written successfully, otherwise returns error code (use modbus.err_to_str to convert it to string representation).

Example

-- Write 0 to coil register number 503, timeout 1 second
result = modbus.write_coil(1, 503, 0, 1000)
if result ~= 0 then
enapter.log("Error writing Modbus: "..tostring(result).." "..modbus.err_to_str(result), "error", true)
end

modbus.write_holding

-- @param address number Address 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.write_holding(address, register, value, timeout)
end

Writes holding register, Modbus function 0x06.

Returns 0 if the value is written successfully, otherwise returns error code (use modbus.err_to_str to convert it to string representation).

Example

-- Write 12 to holding register number 610, timeout 1 second
result = modbus.write_holding(1, 610, 12, 1000)
if result ~= 0 then
enapter.log("Error writing Modbus: "..tostring(result).." "..modbus.err_to_str(result), "error", true)
end

modbus.write_multiple_coils

-- @param address number Address 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.write_multiple_coils(address, 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 modbus.err_to_str to convert it to string representation).

Example

-- Write zeroes to coil registers 700 and 701, timeout 1 second
values = {0, 0}
result = modbus.write_multiple_coils(1, 700, values, 1000)
if result ~= 0 then
enapter.log("Error writing Modbus: "..tostring(result).." "..modbus.err_to_str(result), "error", true)
end

modbus.write_multiple_holdings

-- @param address number Address 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.write_multiple_holdings(address, 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 modbus.err_to_str to convert it to string representation).

Example

-- Write values to holding registers 800 and 801, timeout 1 second
values = {14, 15}
result = modbus.write_multiple_coils(1, 800, values, 1000)
if result ~= 0 then
enapter.log("Error writing Modbus: "..tostring(result).." "..modbus.err_to_str(result), "error", true)
end

modbus.err_to_str

-- @param error number
-- @return string
function modbus.err_to_str(error)
end

Returns string representation of modbus 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 UCM, 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:

-- Read the value of the register 112
registers, result = modbus.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
Hardware diversity is welcome. Integrate any device into a unified energy network.
© 2024 Enapter
Developer toolkit
DocumentationReference