B4A Library Modbus Master TCP/IP Library

Hello everyone,
Here is a Modbus library that I just wrapped to test some industrial Modbus TCP valves, so I thought that I would share it with the community. Using this library, you can connect your B4A apps directly to industrial devices (PLCs) over TCP to read and write Modbus data. Your app should (in theory) connect to any Modbud Server (Slave) devices running over Ethernet or WiFi.

B4A library tab (Network, not really needed)
libs.jpg


Important:
There are 4 separate files in the attached library zip file, place all 4 files in your B4A additional libraries folder.

Updated - B4A Modbus Master test app:
Screenshot_20251210-140553.jpg


SS_ModbusMaster

Author:
Peter Simpson
Version: 1.01
  • ModbusMasterTCP
    • Events:
      • CoilRead (Success As Boolean, Data() As Boolean, Message As String)
      • ConnectionCheckDone (IsLive As Boolean, Message As String)
      • DiagnosticRequest (SubFunction As Int, Data As Int)
      • DiscreteInputRead (Success As Boolean, Data() As Boolean, Message As String)
      • HoldingRegisterRead (Success As Boolean, Data() As Short, Message As String)
      • InitResult (Success As Boolean, Message As String)
      • InputRegisterRead (Success As Boolean, Data() As Short, Message As String)
      • TestCompleted (Success As Boolean, Message As String)
      • WriteResult (Success As Boolean, Message As String)
    • Fields:
      • TEST_COIL As Int
      • TEST_DISCRETE As Int
      • TEST_HOLDING As Int
      • TEST_INPUT As Int
      • TEST_NONE As Int
        Constants to control the initial connection test behavior.
    • Functions:
      • DiagnosticRequest (SubFunction As Int, Data As Int) As Int
        Performs Modbus Function Code 8 (Diagnostics).
        Supports Subfunction 0 (Return Query Data / Loopback) — echoes the integer data.
        Raises the DiagnosticRequest event so B4A/B4J can receive the response.
        SubFunction: The diagnostics subfunction code (only 0 supported).
        Data: The integer data to echo back.
      • Disconnect
        Disconnects and closes the underlying Modbus TCP connection.
        After calling this, the connection must be re-initialised with Initialize() before further requests.
      • Initialize (EventName As String, Host As String, Port As Int, Encapsulated As Boolean, KeepAlive As Boolean, Timeout As Int, Retries As Int, TestFunction As Int, TestAddress As Int)
        Initializes the Modbus TCP connection parameters and attempts to configure the library.
        This function is asynchronous and raises the InitResult event upon completion.
        EventName: The prefix used for the callback events (e.g., "MB" will raise MB_InitResult).
        Host: The IP address or hostname of the Modbus TCP slave device (or TCP/RTU gateway).
        Port: The TCP port of the Modbus device (usually 502).
        Encapsulated: If true, uses Modbus RTU over TCP (Encapsulated). False for standard Modbus TCP.
        KeepAlive: If true, the TCP socket connection is kept alive between requests.
        Timeout: The connection and request timeout in milliseconds.
        Retries: The number of retries the library will perform for failed requests.
        TestFunction: The memory area to use for the connection heartbeat test. Use one of TEST_* constants (0–4).
        TestAddress: The address to read during the heartbeat test (ignored if TestFunction is TEST_NONE).
      • IsInitialized As Boolean
        Returns whether the Modbus connection is currently ready to process requests.
      • ReadCoil (Start As Int, Len As Int)
        Reads a contiguous block of Coil Statuses (Function Code 1).
        The result is returned asynchronously in the CoilRead event.
        Start: The starting address of the first coil to read (0-based).
        Len: The number of coils to read.
      • ReadDiscreteInput (Start As Int, Len As Int)
        Reads a contiguous block of Discrete Inputs (Function Code 2).
        The result is returned asynchronously in the DiscreteInputRead event.
        Start: The starting address of the first discrete input to read (0-based).
        Len: The number of discrete inputs to read.
      • ReadHoldingRegisters (Start As Int, Len As Int)
        Reads a contiguous block of Holding Registers (Function Code 3).
        The result is returned asynchronously in the HoldingRegisterRead event.
        Data is returned as an array of Short (16-bit signed integers).
        Start: The starting address of the first register to read (0-based).
        Len: The number of registers to read.
      • ReadInputRegisters (Start As Int, Len As Int)
        Reads a contiguous block of Input Registers (Function Code 4).
        The result is returned asynchronously in the InputRegisterRead event.
        Data is returned as an array of Short (16-bit signed integers).
        Start: The starting address of the first register to read (0-based).
        Len: The number of registers to read.
      • ReadWriteMultipleRegisters (ReadStart As Int, ReadLen As Int, WriteStart As Int, WriteValues As Short())
        Function Code 23: Read/Write Multiple Registers (implemented as FC16 followed by FC3).
        Reads a block of holding registers while writing another block in a chained two-step sequence.
        On success, results are raised via HoldingRegisterRead; write results and errors via WriteResult.
        ReadStart: The starting address of the registers to read (0-based).
        ReadLen: The number of registers to read.
        WriteStart: The starting address of the registers to write (0-based).
        WriteValues: The array of values to write starting at WriteStart.
      • TestFunction
        Executes a full self-test sequence based on the underlying Modbus connection state.
        The result is returned asynchronously in the TestCompleted event.
        Should be called after a successful Initialize().
      • WriteCoil (Offset As Int, Value As Boolean)
        Writes a single Coil value (Function Code 5).
        The result is returned asynchronously in the WriteResult event.
        Offset: The address of the coil to write to (0-based).
        Value: The boolean value (True/False) to write to the coil.
      • WriteCoils (Start As Int, Values As Boolean())
        Writes multiple Coil values (Function Code 15).
        The result is returned asynchronously in the WriteResult event.
        Start: The starting address of the first coil to write (0-based).
        Values: An array of boolean values written sequentially starting from Start.
      • WriteRegister (Offset As Int, Value As Int)
        Writes a single Holding Register value (Function Code 6).
        The result is returned asynchronously in the WriteResult event.
        Offset: The address of the register to write to (0-based).
        Value: The 16-bit integer value (Short-compatible) to write to the register.
      • WriteRegisters (Start As Int, Values As Short())
        Writes multiple Holding Register values (Function Code 16).
        The result is returned asynchronously in the WriteResult event.
        Start: The starting address of the first register to write (0-based).
        Values: An array of 16-bit integer values written sequentially starting from Start.
    • Properties:
      • HostAddress As String [read only]
        Returns the host address configured during Initialize().
      • SlaveID As Int
        Property: Gets and Sets the Modbus Slave Unit ID (1–247).
        This value is used for all subsequent requests while the connection is active.

B4A Modbus Master quick reference (FC → Method)

FC (Hex)FC (Dec)DescriptionData TypeR/W OperationMethod NameEvent Raised
0x01FC1Read CoilsCoil (Discrete Output)Read (R)ReadCoil(Start, Len)_coilread
0x02FC2Read Discrete InputsDiscrete InputRead (R)ReadDiscreteInput(Start, Len)_discreteinputread
0x03FC3Read Holding RegistersHolding RegisterRead (R)ReadHoldingRegisters(Start, Len)_holdingregisterread
0x04FC4Read Input RegistersInput RegisterRead (R)ReadInputRegisters(Start, Len)_inputregisterread
0x05FC5Write Single CoilCoil (Discrete Output)Write (W)WriteCoil(Offset, Value)_writeresult
0x06FC6Write Single RegisterHolding RegisterWrite (W)WriteRegister(Offset, Value)_writeresult
0x08FC8Diagnostic (Loopback)N/ADiagnosticDiagnosticRequest(SubFunction, Data)_diagnosticrequest
0x0FFC15Write Multiple CoilsCoil (Discrete Output)Write (W)WriteCoils(Start, Values)_writeresult
0x10FC16Write Multiple RegistersHolding RegisterWrite (W)WriteRegisters(Start, Values)_writeresult
0x17FC23Read/Write Multiple Registers.
Implemented as Chained FC16 + FC3
Holding RegisterRead/Write (R/W)ReadWriteMultipleRegisters
(ReadStart, ReadLen, WriteStart, WriteValues)
_holdingregisterread and _writeresult

PLEASE TAKE NOTE:
  1. The _Initialize function was updated adding TestFunction & TestAddress to the end of the signature
  2. Renamed the library from ModbusMaster to ModbusMasterTCP for continuity with the Slave library
  3. Removed SLAVE_ID = x (No longer needed)
  4. Removed ALL references of 'SlaveId As Int' from all of the Read and Write signatures. If need be, you can now set or switch the SlaveID at will by using ModBus.SlaveID = x (x being the ID number).
Update: V1.01
  • Cleaned up the JavaDoc comments
  • Updated all _write events to show wrote values
  • Added TestCompleted Event
  • Added DiagnosticRequest Event
  • Added InputRegisterRead Event
  • Added DiscreteInputRead Event
  • Added Modbus Function 8 (Loopback)
  • Added Modbus Function 15
  • Added Modbus Function 23 (FC16 + FC3 chained)
  • Added TestFunction Function
  • Added WriteCoils Function
  • Added ReadWriteMultipleRegisters Function
  • Added TEST_NONE Field
  • Added TEST_COIL Field
  • Added TEST_DISCRETE Field
  • Added TEST_HOLDING Field
  • Added TEST_INPUT Field
  • Added isModbusReady Field
  • Updated the test app (attached)
Plus a few other improvements.

D = 61+64


Enjoy...
 

Attachments

  • ModbusMaster.zip
    27.1 KB · Views: 6
  • ModbusMasterLib.zip
    242 KB · Views: 10
Last edited:

SimonAndroid

Active Member
Licensed User
Longtime User
Sorry for my curiosity, but was this library converted for B4A from Python or another language? I needed this a few months ago and created some procedures in Python, where the library works very well. Best regards.
 

Peter Simpson

Expert
Licensed User
Longtime User
Hello @SimonAndroid.
but was this library converted for B4A from Python or another language?
No, not at all, it's a straightforward Java library wrap. Once I got my head around the six basic FCs, I then added the other Function Code features. It only took a few hours to wrap the original java library which I fully tested with a Windows slave simulator, extending and testing this library took another day.

Below is a link that some developers might find useful.

Modbus CRC-16 checksum function with detailed breakdown for each hex value:


Enjoy...
 
Last edited:

Peter Simpson

Expert
Licensed User
Longtime User
Hello Everyone,
I've updated the library to V1.01. Please read carefully the PLEASE TAKE NOTE: section at the bottom of post #1 as the Initialize signature has changed and so has the library name.

Enjoy...

XUI (B4A/B4J) - Future cross-platform libraries:
I’ll be releasing a Modbus TCP Server (Slave) library that simulates Modbus hardware. I’ll also be releasing a second Modbus TCP Client (Master) library for reading and writing to and from Modbus hardware. This library uses FC23 without chaining.
 
Last edited:

thetrueman

Active Member
Thank you very much for this classic sharing. Just to request if it would be addition for serial ports modbus to be use with tablets as HMI. Thus we can experience the versatility of B4A power as a HMI user interface. I hope, I have asked in the proper way. Thanks.
 

Peter Simpson

Expert
Licensed User
Longtime User
Hello @thetrueman,
Just to request if it would be addition for serial ports modbus to be use with tablets as HMI.
This library wrap was WiFi only.

Below is another Modbus Master library that I wrapped, I will be releasing it shortly. This library did actually have both WiFi and serial, but I deliberately removed the serial part of the code because I had no way of testing it 100% successfully, otherwise I would have kept the serial code in. I might look into it at some point, but for now I can't.

I do have an Modbus TCP simulator, I'll be releasing that library and example app. I now have a hardware actuator and also a hardware valve that both work over TCP. Sadly, I had no need for serial communications.

Coming soon: Below are screenshots from a new XUI Modbus Client library.

XUI (B4A) - Easy Modbus Client screen

1000085798.png


XUI (B4J) - Easy Modbus Client screen
1765474260381.png



Enjoy 🥂
 
Last edited:
Top