valiot / modbux

Elixir Modbus library for network and serial communications.
https://hexdocs.pm/modbux
MIT License
40 stars 15 forks source link
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:

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:

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:

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:

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:

Master

To start a Modbus RTU Master process use start_link/1.

The following options are available:

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:

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:

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:

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

License

See LICENSE.

TODO