Modbuzz

hexCIlicenseREUSE

Modbuzz icon

Yet another MODBUS library, supporting both TCP and RTU, providing gateway functionality.

Index

Usage

30-second overview

Modbuzz provides one consistent public API for:

If you are evaluating this library, start from Quick Start to see a basic client request, then explore Server setup and Gateway recipes based on your use case.

Quick Start (3 minutes)

This flow assumes a real MODBUS device is available:

  1. Start a TCP client or RTU client.
  2. Send one request to the device.
  3. Stop the client.
:ok = Modbuzz.start_tcp_client(:demo_tcp_client, {192, 168, 0, 10}, 502)
alias Modbuzz.PDU.WriteSingleCoil
req = %WriteSingleCoil.Req{output_address: 0, output_value: true}
{:ok, _res} = Modbuzz.request(:demo_tcp_client, req)
:ok = Modbuzz.stop_tcp_client(:demo_tcp_client)

Client usage (primary use case)

TCP client

Modbuzz.start_tcp_client/3 starts a TCP client instance.

Modbuzz.request/2 or Modbuzz.request/3 requests synchronously. Modbuzz.request_async/2 or Modbuzz.request_async/3 requests asynchronously. The 2nd argument, unit_id, can be omitted. If omitted, its value defaults to 0.

Common setup:

iex> :ok = Modbuzz.start_tcp_client(:your_tcp_client, {192, 168, 0, 10}, 502)
iex> alias Modbuzz.PDU.WriteSingleCoil
iex> req = %WriteSingleCoil.Req{output_address: 0, output_value: true}

Synchronous request:

iex> {:ok, _res} = Modbuzz.request(:your_tcp_client, req)

Asynchronous request:

iex> :ok = Modbuzz.request_async(:your_tcp_client, req)
iex> flush()
{:modbuzz, :your_tcp_client, 0, _request, {:ok, _response}}

RTU client

Modbuzz.start_rtu_client/3 starts an RTU client instance.

Modbuzz.request/2 or Modbuzz.request/3 requests synchronously. Modbuzz.request_async/2 or Modbuzz.request_async/3 requests asynchronously. The 2nd argument, unit_id, can be omitted. If omitted, its value defaults to 0.

Common setup:

iex> :ok = Modbuzz.start_rtu_client(:your_rtu_client, "ttyUSB0", [speed: 9600])
iex> alias Modbuzz.PDU.WriteSingleCoil
iex> req = %WriteSingleCoil.Req{output_address: 0, output_value: true}

Synchronous request:

iex> {:ok, _res} = Modbuzz.request(:your_rtu_client, 1, req)

Asynchronous request:

iex> :ok = Modbuzz.request_async(:your_rtu_client, 1, req)
iex> flush()
{:modbuzz, :your_rtu_client, 1, _request, {:ok, _response}}

Data server as value source

Modbuzz.start_data_server/1 starts a data server instance. Data server lets you expose your own application data (for example, sensor values) as Modbus values through TCP/RTU servers.

iex> :ok = Modbuzz.start_data_server(:your_data_server)
iex> alias Modbuzz.PDU.WriteSingleCoil
iex> req = %WriteSingleCoil.Req{output_address: 0, output_value: true}
iex> res = %WriteSingleCoil.Res{output_address: 0, output_value: true}
iex> :ok = Modbuzz.create_unit(:your_data_server, 1)
iex> :ok = Modbuzz.upsert(:your_data_server, 1, req, res)
iex> :ok = Modbuzz.start_tcp_server(:your_tcp_server, {192, 168, 1, 10}, 502, :your_data_server)

In this setup, external Modbus clients can read/write values through your TCP/RTU server, backed by the data server mappings.

API map

GoalAPI
Send a request and wait for resultModbuzz.request/2, Modbuzz.request/3, Modbuzz.request/4
Send a request and receive by messageModbuzz.request_async/2, Modbuzz.request_async/3, Modbuzz.request_async/4, Modbuzz.request_async/5
Start/stop data serverModbuzz.start_data_server/1, Modbuzz.stop_data_server/1
Manage data server contentModbuzz.create_unit/1, Modbuzz.create_unit/2, Modbuzz.upsert/3, Modbuzz.upsert/4, Modbuzz.delete/2, Modbuzz.delete/3, Modbuzz.dump/1, Modbuzz.dump/2
Start/stop TCP clientModbuzz.start_tcp_client/3, Modbuzz.stop_tcp_client/1
Start/stop RTU clientModbuzz.start_rtu_client/3, Modbuzz.stop_rtu_client/1
Start/stop TCP serverModbuzz.start_tcp_server/4, Modbuzz.stop_tcp_server/1
Start/stop RTU serverModbuzz.start_rtu_server/4, Modbuzz.stop_rtu_server/1

For detailed external API behavior, see External API Guide.

Synchronous vs asynchronous request

Modbuzz.request/2, /3, and /4 are synchronous and return result directly.

Modbuzz.request_async/2, /3, /4, and /5 return :ok immediately and send result as a message:

{:modbuzz, name, unit_id, request, {:ok, response}}
{:modbuzz, name, unit_id, request, {:error, error_response_or_error_reason}}

Use synchronous request for simple command flow. Use asynchronous request when you need non-blocking behavior or concurrent request orchestration.

Lifecycle (start and stop)

Start functions return:

Stop functions return:

It is safe to call stop even when the process may already be down, as long as you handle {:error, :not_started}.

Server setup

data_source for Modbuzz.start_tcp_server/4 and Modbuzz.start_rtu_server/4 can be:

TCP server example:

:ok = Modbuzz.start_tcp_server(:your_tcp_server, {192, 168, 1, 10}, 502, :your_data_source)

RTU server example:

:ok = Modbuzz.start_rtu_server(:your_rtu_server, "ttyUSB1", [speed: 19200], :your_data_source)

Gateway recipes

Use these recipes after you confirm Quick Start works.

TCP/RTU gateway

TCP server receives a request and passes it through to RTU client.

:ok = Modbuzz.start_rtu_client(:your_rtu_client, "ttyUSB0", [speed: 9600])
:ok = Modbuzz.start_tcp_server(:your_tcp_server, {192, 168, 1, 10}, 502, :your_rtu_client)

RTU/TCP gateway

RTU server receives a request and passes it through to TCP client.

:ok = Modbuzz.start_tcp_client(:your_tcp_client, {192, 168, 0, 10}, 502)
:ok = Modbuzz.start_rtu_server(:your_rtu_server, "ttyUSB1", [speed: 19200], :your_tcp_client)

TCP/TCP gateway

TCP server receives a request and passes it through to TCP client.

:ok = Modbuzz.start_tcp_client(:your_tcp_client, {192, 168, 0, 10}, 502)
:ok = Modbuzz.start_tcp_server(:your_tcp_server, {192, 168, 1, 10}, 502, :your_tcp_client)

RTU/RTU gateway

RTU server receives a request and passes it through to RTU client.

:ok = Modbuzz.start_rtu_client(:your_rtu_client, "ttyUSB0", [speed: 9600])
:ok = Modbuzz.start_rtu_server(:your_rtu_server, "ttyUSB1", [speed: 19200], :your_rtu_client)

Troubleshooting

Installation

If available in Hex, the package can be installed by adding modbuzz to your list of dependencies in mix.exs:

def deps do
[
{:modbuzz, "~> 0.3.0"}
]
end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/modbuzz.

License

This project is licensed under the Apache-2.0 license.

And this project follows the REUSE compliance. For more details, see the REUSE SOFTWARE.

MODBUS References