modbus_tcp_server v1.0.2 ModbusServer
This module implements the Modbus Server and is called from the Modbus TCP Server.
The purpose of this module is to handle the requests from the TCP server and execute the reads and writes. It returns the correct response or exception.
This module expects the ModbusDatabase to be running which will be read from or written to. See the examples.
The following Modbus Function Codes are supported:
- 01->Read Coils
- 02->Read Discrete Inputs
- 03->Read Holding Registers
- 04->Read Input Registers
- 05->Write Single Coil
- 06->Write Single Register
- 15->Write Multiple Coils
- 16->Write Multiple Registers
- 22->Mask Write Register
- 23->Read/Write Multiple Registers
The following Modbus Function Codes are NOT supported:
- 24->Read FIFO Queue
- 20->Read File Record
- 21->Write File Record
- 07->Read Exception Status
- 08->Diagnostic
- 11->Get Com Event Counter
- 12->Get Com Event Log
- 17->Report Server ID
- 43->Read Device Information
- 43->Encapsulated Interface Transport
- 43->CANopen General Reference
The following Modbus Exception Codes can be expected to be returned from this server:
- 01->Illegal Function
- 02->Illegal Data Address
- 03->Illegal Data Value
This server will never return any of the following Modbus Exception Codes:
- 04->Server Device Failure
- 05->Acknowledge
- 06->Server Device Busy
- 08->Memory Parity Error
- 0A->Gateway Path Unavailable
- 0B->Gateway Target Device Failed to Respond
Refer to the technical documents section of the http://modbus.org website for more information about the Modbus protocol.
Examples
The follwing examples show how to start the Modbus Server and send requests to it.
Example 1: Read Coils
iex> ModbusServer.start_link()
iex> database = %{0 => true, 1 => false, 2 => true, 3 => true, 4 => false}
iex> ModbusDatabase.start_link(database, :modbus_coil_database)
iex> function_code = 1
iex> starting_coil = 0
iex> read_quantity = 1
iex> message = {function_code, [starting_coil, read_quantity]}
iex> {:response, values} = ModbusServer.request(ModbusServer, message)
{:response, [true]}
iex> GenServer.stop(:modbus_coil_database)
:ok
iex> GenServer.stop(ModbusServer)
:ok
Example 2: Read Discrete Inputs
iex> ModbusServer.start_link()
iex> database = %{0 => true, 1 => false, 2 => true, 3 => true, 4 => false}
iex> ModbusDatabase.start_link(database, :modbus_discrete_input_database)
iex> function_code = 2
iex> starting_coil = 0
iex> read_quantity = 1
iex> message = {function_code, [starting_coil, read_quantity]}
iex> {:response, values} = ModbusServer.request(ModbusServer, message)
{:response, [true]}
iex> GenServer.stop(:modbus_discrete_input_database)
:ok
iex> GenServer.stop(ModbusServer)
:ok
Example 3: Read Holding Registers
iex> ModbusServer.start_link()
iex> database = %{0 => 100, 1 => 65535, 2 => 32768, 3 => -100, 4 => 95}
iex> ModbusDatabase.start_link(database, :modbus_holding_register_database)
iex> function_code = 3
iex> starting_register = 0
iex> read_quantity = 1
iex> message = {function_code, [starting_register, read_quantity]}
iex> {:response, values} = ModbusServer.request(ModbusServer, message)
{:response, [100]}
iex> GenServer.stop(:modbus_holding_register_database)
:ok
iex> GenServer.stop(ModbusServer)
:ok
Example 4: Read Input Registers
iex> ModbusServer.start_link()
iex> database = %{0 => 100, 1 => 65535, 2 => 32768, 3 => -100, 4 => 95}
iex> ModbusDatabase.start_link(database, :modbus_input_register_database)
iex> function_code = 4
iex> starting_register = 0
iex> read_quantity = 1
iex> message = {function_code, [starting_register, read_quantity]}
iex> {:response, values} = ModbusServer.request(ModbusServer, message)
{:response, [100]}
iex> GenServer.stop(:modbus_input_register_database)
:ok
iex> GenServer.stop(ModbusServer)
:ok
Example 5: Single Coil Write
iex> ModbusServer.start_link()
iex> database = %{0 => true, 1 => false, 2 => true, 3 => true, 4 => false}
iex> ModbusDatabase.start_link(database, :modbus_coil_database)
iex> function_code = 5
iex> starting_register = 0
iex> write_value = 0x0000
iex> message = {function_code, [starting_register, write_value]}
iex> {:response, values} = ModbusServer.request(ModbusServer, message)
{:response, {0, false}}
iex> GenServer.stop(:modbus_coil_database)
:ok
iex> GenServer.stop(ModbusServer)
:ok
Example 6: Single Register Write
iex> ModbusServer.start_link()
iex> database = %{0 => 100, 1 => 65535, 2 => 32768, 3 => -100, 4 => 95}
iex> ModbusDatabase.start_link(database, :modbus_holding_register_database)
iex> function_code = 6
iex> starting_register = 0
iex> write_value = 976
iex> message = {function_code, [starting_register, write_value]}
iex> {:response, values} = ModbusServer.request(ModbusServer, message)
{:response, {0, 976}}
iex> GenServer.stop(:modbus_holding_register_database)
:ok
iex> GenServer.stop(ModbusServer)
:ok
Example 6: Multiple Coil Write
iex> ModbusServer.start_link()
iex> database = %{0 => true, 1 => false, 2 => true, 3 => true, 4 => false}
iex> ModbusDatabase.start_link(database, :modbus_coil_database)
iex> function_code = 15
iex> starting_register = 0
iex> quantity = 2
iex> byte_count = 1
iex> write_value1 = false
iex> write_value2 = true
iex> message = {function_code, [starting_register, quantity, byte_count, write_value1, write_value2]}
iex> {:response, values} = ModbusServer.request(ModbusServer, message)
{:response, {0, 2}}
iex> GenServer.stop(:modbus_coil_database)
:ok
iex> GenServer.stop(ModbusServer)
:ok
Example 8: Multiple Register Write
iex> ModbusServer.start_link()
iex> database = %{0 => 100, 1 => 65535, 2 => 32768, 3 => -100, 4 => 95}
iex> ModbusDatabase.start_link(database, :modbus_holding_register_database)
iex> function_code = 16
iex> starting_register = 1
iex> quantity = 2
iex> byte_count = 4
iex> write_value1 = 100
iex> write_value2 = -100
iex> message = {function_code, [starting_register, quantity, byte_count, write_value1, write_value2]}
iex> {:response, values} = ModbusServer.request(ModbusServer, message)
{:response, {1, 2}}
iex> GenServer.stop(:modbus_holding_register_database)
:ok
iex> GenServer.stop(ModbusServer)
:ok
Example 9: Mask Write Register
iex> ModbusServer.start_link()
iex> database = %{0 => 18, 1 => 65535, 2 => 32768, 3 => -100, 4 => 95}
iex> ModbusDatabase.start_link(database, :modbus_holding_register_database)
iex> function_code = 22
iex> starting_register = 0
iex> and_mask = 0xF2
iex> or_mask = 0x25
iex> message = {function_code, [starting_register, and_mask, or_mask]}
iex> {:response, values} = ModbusServer.request(ModbusServer, message)
{:response, {0, 0xF2, 0x25}}
iex> GenServer.stop(:modbus_holding_register_database)
:ok
iex> GenServer.stop(ModbusServer)
:ok
Example 10: Read/Write Holding Registers - Read Registers 0-1, Write 50 to Register 1
iex> ModbusServer.start_link()
iex> database = %{0 => 18, 1 => 65535, 2 => 32768, 3 => -100, 4 => 95}
iex> ModbusDatabase.start_link(database, :modbus_holding_register_database)
iex> function_code = 23
iex> starting_register = 0
iex> read_count = 2
iex> write_register = 0
iex> write_count = 1
iex> write_value = 50
iex> byte_count = 2
iex> message = {function_code, [starting_register, read_count, write_register, write_count, byte_count, write_value]}
iex> {:response, values} = ModbusServer.request(ModbusServer, message)
{:response, [50, 65535]}
iex> GenServer.stop(:modbus_holding_register_database)
:ok
iex> GenServer.stop(ModbusServer)
:ok
Summary
Functions
This function calls the GenServer identified by the passed pid, passing it the message
This function starts the ModbusServer process
Functions
This function calls the GenServer identified by the passed pid, passing it the message.
The pid parameter is the GenServer pid to send the message to.
The message parameter is the message to pass to the ModbusServer.