Skip to content

Latest commit

 

History

History
316 lines (231 loc) · 8.81 KB

README.md

File metadata and controls

316 lines (231 loc) · 8.81 KB
modbux Logo


Valiot Logo

Modbux is a library for network and serial Modbus communications.

This library currently supports behaviors for TCP (Client & Server) and RTU (Master & Slave) protocols.

Index

Features

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

Installation

The package can be installed by adding modbux to your list of dependencies in mix.exs:

def deps do
  [
    {:modbux, "~> 0.1.0"}
  ]
end

Usage


Modbus RTU

Modbus 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.

Slave

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 - (true or 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.

Model (DB)

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.

Example

# 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 count coils.
  • {:ri, slave, address, count} read count inputs.
  • {:rhr, slave, address, count} read count holding registers.
  • {:rir, slave, address, count} read count input 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.

Master

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 - (true or 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.

Example

# 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]}

Modbux TCP

Modbus TCP (also Modbus TCP/IP) is simply the Modbus RTU protocol with a TCP interface that runs on a network.

Server

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 - (true or false) enable/disable DB updates notifications (mailbox).

The messages (when active mode is true) have the following form:

  {:modbus_tcp, {:slave_request, payload}}

Example

# 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)

Client

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 - (true or false) specifies whether data is received as messages (mailbox) or by calling confirmation/1 each time request/2 is 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.

Example

# 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]}

Helpers

IEEE754 Float

Several modbus register use IEEE754 float format, therefore this library also provides functions to encode and decode data.

Example

  # 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.

Endianess

Depending on the device / server the data can be encoded with different types of endianess, therefore this library also provides functions to encode data.

Example

  # 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:

Documentation

The docs can be found at https://hexdocs.pm/modbux.

Based on:

Contributing

  • Fork our repository on github.
  • Fix or add what is needed.
  • Commit to your repository.
  • Issue a github pull request (fill the PR template).

License

See LICENSE.

TODO

  • Add Modbux ASCII.
  • Add Modbux UDP.
  • Add more examples.
  • Improve error handling.