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)
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:
SS_ModbusMaster
Author: Peter Simpson
Version: 1.01
PLEASE TAKE NOTE:
D = 61+64
Enjoy...
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)
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:
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.
- DiagnosticRequest (SubFunction As Int, Data As Int) As Int
- 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.
- HostAddress As String [read only]
- Events:
B4A Modbus Master quick reference (FC → Method)
| FC (Hex) | FC (Dec) | Description | Data Type | R/W Operation | Method Name | Event Raised |
|---|---|---|---|---|---|---|
| 0x01 | FC1 | Read Coils | Coil (Discrete Output) | Read (R) | ReadCoil(Start, Len) | _coilread |
| 0x02 | FC2 | Read Discrete Inputs | Discrete Input | Read (R) | ReadDiscreteInput(Start, Len) | _discreteinputread |
| 0x03 | FC3 | Read Holding Registers | Holding Register | Read (R) | ReadHoldingRegisters(Start, Len) | _holdingregisterread |
| 0x04 | FC4 | Read Input Registers | Input Register | Read (R) | ReadInputRegisters(Start, Len) | _inputregisterread |
| 0x05 | FC5 | Write Single Coil | Coil (Discrete Output) | Write (W) | WriteCoil(Offset, Value) | _writeresult |
| 0x06 | FC6 | Write Single Register | Holding Register | Write (W) | WriteRegister(Offset, Value) | _writeresult |
| 0x08 | FC8 | Diagnostic (Loopback) | N/A | Diagnostic | DiagnosticRequest(SubFunction, Data) | _diagnosticrequest |
| 0x0F | FC15 | Write Multiple Coils | Coil (Discrete Output) | Write (W) | WriteCoils(Start, Values) | _writeresult |
| 0x10 | FC16 | Write Multiple Registers | Holding Register | Write (W) | WriteRegisters(Start, Values) | _writeresult |
| 0x17 | FC23 | Read/Write Multiple Registers. Implemented as Chained FC16 + FC3 | Holding Register | Read/Write (R/W) | ReadWriteMultipleRegisters (ReadStart, ReadLen, WriteStart, WriteValues) | _holdingregisterread and _writeresult |
PLEASE TAKE NOTE:
- The _Initialize function was updated adding TestFunction & TestAddress to the end of the signature
- Renamed the library from ModbusMaster to ModbusMasterTCP for continuity with the Slave library
- Removed SLAVE_ID = x (No longer needed)
- 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).
- 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)
D = 61+64
Enjoy...
Attachments
Last edited: