Modbux is a library for network and serial Modbus communications.
This library currently supports behaviors for TCP (Client & Server) and RTU (Master & Slave) protocols.
The following list is the current supported protocols/behaviors and helpers:
- 
Modbus RTU: - Master
- Slave
- Framer
 
- 
Modbus TCP: - Client
- Server
 
- 
Helpers: - IEEE754 Float support
- Endianess
 
The package can be installed by adding modbux to your list of dependencies in mix.exs:
def deps do
  [
    {:modbux, "~> 0.1.0"}
  ]
endModbus RTU is an open serial protocol derived from the Master/Slave architecture originally developed by Modicon. This protocol primarily uses an RS-232 or RS-485 serial interfaces for communications.
To start a Modbus RTU Slave process use start_link/1.
The following options are available:
- tty- defines the serial port to spawn the Slave.
- gen_opts- defines extra options for the Genserver OTP configuration.
- uart_opts- defines extra options for the UART configuration.
- model- defines the DB initial state.
- active- (- trueor- false) enable/disable DB updates notifications (mailbox).
The messages (when active mode is true) have the following form:
  {:modbus_rtu, {:slave_request, payload}}or
  {:modbus_rtu, {:slave_error, payload, reason}}The following are some reasons:
- :ecrc- corrupted message (invalid crc).
- :einval- invalid function.
- :eaddr- invalid memory address requested.
The model or data base (DB) defines the slave/server memory map, the DB is defined by the following syntax:
%{slave_id => %{{memory_type, address_number} => value}}where:
- slave_id- specifies a unique unit address from 1 to 247.
- memory_type- specifies the memory between:- :c- Discrete Output Coils.
- :i- Discrete Input Contacts.
- :ir- Analog Input Registers.
- :hr- Analog Output Registers.
 
- address_number- specifies the memory address.
- value- the current value from that memory.
# DB inital state
model = %{
    80 => %{
      {:c, 1} => 1,
      {:c, 2} => 0,
      {:i, 1} => 1,
      {:i, 2} => 1,
      {:ir, 1} => 0,
      {:ir, 2} => 1,
      {:hr, 1} => 102,
      {:hr, 2} => 103
    }
  }
# Starts the Slave at "ttyUSB0"
{:ok, s_pid} = Modbux.Rtu.Slave.start_link(tty: "ttyUSB0", model: model, active: true)if needed, the Slave DB can be modified in runtime with elixir code by using request/2,
a cmd must be used to update the DB, the cmd is a 4 elements tuple, as follows:
- {:rc, slave, address, count}read- countcoils.
- {:ri, slave, address, count}read- countinputs.
- {:rhr, slave, address, count}read- countholding registers.
- {:rir, slave, address, count}read- countinput registers.
- {:fc, slave, address, value}force single coil.
- {:phr, slave, address, value}preset single holding register.
- {:fc, slave, address, values}force multiple coils.
- {:phr, slave, address, values}preset multiple holding registers.
To start a Modbus RTU Master process use start_link/1.
The following options are available:
- tty- defines the serial port to spawn the Master.
- timeout- defines slave timeout.
- active- (- trueor- false) specifies whether data is received as messages (mailbox) or by calling- request/2.
- gen_opts- defines extra options for the Genserver OTP configuration.
- uart_opts- defines extra options for the UART configuration (defaults: [speed: 115200, rx_framing_timeout: 1000]).
The messages (when active mode is true) have the following form:
  {:modbus_rtu, {:slave_response, cmd, values}}or
  {:modbus_rtu, {:slave_error, payload, reason}}The following are some reasons:
- :ecrc- corrupted message (invalid crc).
- :einval- invalid function.
- :eaddr- invalid memory address requested.
use request/2 to send a cmd (command) to a Modbus RTU Slave.
# Starts the Master at "ttyUSB1" (in the example is connected to ttyUSB1)
{:ok, m_pid} = Modbux.Rtu.Master.start_link(tty: "ttyUSB1")
# Read 2 holding registers at 1 (memory address) from the slave 80
resp = Modbux.Rtu.Master.request(m_pid, {:rhr, 80, 1, 2})
# resp == {:ok, [102, 103]}Modbus TCP (also Modbus TCP/IP) is simply the Modbus RTU protocol with a TCP interface that runs on a network.
To start a Modbus TCP Server process use start_link/1.
The following options are available:
- port- is the Modbux TCP Server tcp port number.
- timeout- is the connection timeout.
- model- defines the DB initial state.
- sup_otps- server supervisor OTP options.
- active- (- trueor- false) enable/disable DB updates notifications (mailbox).
The messages (when active mode is true) have the following form:
  {:modbus_tcp, {:slave_request, payload}}# DB initial state
model = %{80 => %{{:c, 20818} => 0, {:hr, 20818} => 0}}
# Starts the Server at tcp port: 2000
Modbux.Tcp.Server.start_link(model: model, port: 2000)To start a Modbus TCP Client process use start_link/1.
The following options are available:
- ip- is the internet address of the desired Modbux TCP Server.
- tcp_port- is the desired Modbux TCP Server tcp port number.
- timeout- is the connection timeout.
- active- (- trueor- false) specifies whether data is received as messages (mailbox) or by calling- confirmation/1each time- request/2is called.
The messages (when active mode is true) have the following form:
  {:modbus_tcp, cmd, values}to connect to a Modbus TCP Server connect/1.
Use request/2 to send a cmd (command) to a Modbus TCP Server and confirmation/1 to parse the server response.
# Starts the Client that will connect to a Server with tcp port: 2000
{:ok, cpid} = Modbux.Tcp.Client.start_link(ip: {127,0,0,1}, tcp_port: 2000, timeout: 2000)
# Connect to the Server
Modbux.Tcp.Client.connect(cpid)
# Read 1 coil at 20818 from the device 80
Modbux.Tcp.Client.request(cpid, {:rc, 0x50, 20818, 1})
# Parse the Server response
resp = Modbux.Tcp.Client.confirmation(cpid) 
# resp == {:ok, [0]}Several modbus register use IEEE754 float format, therefore this library also provides functions to encode and decode data.
  # Encode
  +5.0 = Modbux.IEEE754.from_2_regs(0x40a0, 0x0000, :be)
  [-5.0, +5.0] = Modbux.IEEE754.from_2n_regs([0xc0a0, 0x0000, 0x40a0, 0x0000], :be)
  # Decode
  [0xc0a0, 0x0000] = Modbux.IEEE754.to_2_regs(-5.0)
  [0xc0a0, 0x0000, 0x40a0, 0x0000] = Modbux.IEEE754.to_2n_regs([-5.0, +5.0])Based on https://www.h-schmidt.net/FloatConverter/IEEE754.html.
Depending on the device / server the data can be encoded with different types of endianess, therefore this library also provides functions to encode data.
  # Encode
  2.3183081793789774e-41 = Modbux.IEEE754.from_2_regs(0x40a0, 0x0000, :le)
  [6.910082987278538e-41, 2.3183081793789774e-41] = Modbux.IEEE754.from_2n_regs([0xc0a0, 0x0000, 0x40a0, 0x0000], :le)Good to know:
- Erlang default endianess is BIG
- MODBUS default endianess is BIG (p.34)
- MODBUS CRC endianess is LITTLE (p.16)
The docs can be found at https://hexdocs.pm/modbux.
Based on:
- http://modbus.org/docs/PI_MBUS_300.pdf
- http://modbus.org/docs/Modbux_Messaging_Implementation_Guide_V1_0b.pdf
- http://modbus.org/docs/Modbux_over_serial_line_V1_02.pdf
- http://www.simplymodbus.ca/index.html
- Fork our repository on github.
- Fix or add what is needed.
- Commit to your repository.
- Issue a github pull request (fill the PR template).
See LICENSE.
- Add Modbux ASCII.
- Add Modbux UDP.
- Add more examples.
- Improve error handling.

