B4A Library Modbus TCP Library

Hello everyone,
Given the difficulties in finding a "ready-made" library for Modbus TCP communication.
I have decided to create a library, adapted for my needs, i decide to publish because maybe useful to someone, maybe someone want to improve on it in this forum.

I attach the library, the library project and a test project.

The available functions:
  • FC01 - Read Coil
  • FC03 - Read Holding
  • FC05 - Write Single Coil
  • FC06 - Write Single Holding
  • FC16 - Write Multiple Holding


How it works:

From code you create Query to indicate the function you want to execute cyclically.
Before recording it in a list, it analyses the "Count" parameter (this is because I have noticed that I can request data up to a maximum of 125)
Adapts the query and eventually creates as many queries as necessary to satisfy the initial request.

The list of "Queries" proceed cyclically and it advances only if the server responds to that Query
(My idea was: "No need to continue, if server can't reply to me")

All code run in background.

ModbusTCP
Author:
Walter Maniero
Version: 2.20


  • Functions/Routines:
    • Initialize(Caller As Object,EventName As String, unitId As Byte)
    • CreateConnection(IPaddress As String, Port As Int, Timeout As Int, ReadTime As Int) As ConnectionParams
      Return the type ConnectionParams that simply contain all informations
    • CreateQuery(Function As Short, StartAddress As Short, Count As Short) As ModbusQuery
      Create and return the query to add in list
    • AddToQuery(Value As ModbusQuery)
    • AddToQuery2(Value() As ModbusQuery)
    • RemoveFromQuery(Key As Short)
      Each query in list has a name, this name is Id field inside ModbusQuery
    • ResetQueryList()
      Clear the list
    • CreateNewQueryList()
      If you change the list in runtime, you need to apply the changes with this sub
    • StartCOM(COM As ConnectionParams)
    • StopCOM()
    • SetCoil(Address As Short, Value As Boolean)
    • SetHold(Address As Short, Value As Short)
    • SetMultipleHold(Address As Short, Value() As Short)
    • GetCoil() As Boolean()
      Coil(65536) As Boolean
    • GetHolds() As Short()
      Holds(65536) As Short
  • Type:
    • ModbusQuery
    • ConnectionParams
  • Eventi:
    • ServiceState(State As Boolean)

Example::
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Private xui As XUI
    Private ModbusClient As ModbusTCP
    Private COMParameters As ConnectionParams
    Private ModbusState As Boolean = False
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("Layout")
    ModbusClient.Initialize(Me,"ModbusClient",1)
    COMParameters.Initialize
    COMParameters = ModbusClient.CreateConnection("192.168.178.42",502,3600,200)
    SetQuery
End Sub

Sub SetQuery()
    Dim Myquery1 As ModbusQuery = ModbusClient.CreateQuery(1,0,100)
    Dim Myquery2 As ModbusQuery = ModbusClient.CreateQuery(3,0,100)
    ModbusClient.AddToQuery(Myquery1)
    ModbusClient.AddToQuery(Myquery2)
End Sub

Sub ModbusClient_ServiceState(State As Boolean)
    ModbusState = State
End Sub

Sub Activity_Resume
    If ModbusState = False Then
        ModbusClient.StartCOM(COMParameters)
    End If
End Sub

Sub Activity_Pause (UserClosed As Boolean)
    If UserClosed Then
        ModbusClient.StopCOM
    End If
End Sub

Sub Button1_Click
    Dim MyCoils() As Boolean = ModbusClient.GetCoil()
    Dim MyHolds() As Short = ModbusClient.GetHolds()
    ModbusClient.SetCoil(0,Not(MyCoils(0)))
    Dim Value(10) As Short
    For i = 0 To Value.Length - 1
        Value(i) = MyHolds(i) + 1
    Next
    ModbusClient.SetMultipleHold(1,Value)
End Sub
 

Attachments

  • ModbusTCP.zip
    158.1 KB · Views: 240
  • ModbusTCP-Example.zip
    10.2 KB · Views: 241
  • ModbusTCP-SourceCode.zip
    16.7 KB · Views: 215

Walter95

Member
Licensed User
I used my lib with S7-1200/1500 and ET200SP without problem.
If doesn't work the cause is Siemens, from V17 i found a bug on Modbus block that don't change the unit ID, so you need to force the unit id inside a DB Instance of FB_Modbus TCP
 

spunky1a

Member
hello, can i use this to read data from a pv inverter via modbus/tcp? the inverter has the following access data ip:192.168.178.97 port:1502 id:71. I want to read address 582 (1 byte) and address 100 (2 bytes). I tried your library, but I don't understand it properly. how can i do that, do you have a simple example?
 

DonManfred

Expert
Licensed User
Longtime User
do you have a simple example?
See #1

I guess it is this part
B4X:
Sub SetQuery()
    Dim Myquery1 As ModbusQuery = ModbusClient.CreateQuery(1,0,100)
    Dim Myquery2 As ModbusQuery = ModbusClient.CreateQuery(3,0,100)
    ModbusClient.AddToQuery(Myquery1)
    ModbusClient.AddToQuery(Myquery2)
End Sub
 

FabianGS

Member
Hello everyone,
Given the difficulties in finding a "ready-made" library for Modbus TCP communication.
I have decided to create a library, adapted for my needs, i decide to publish because maybe useful to someone, maybe someone want to improve on it in this forum.

I attach the library, the library project and a test project.

The available functions:
  • FC01 - Read Coil
  • FC03 - Read Holding
  • FC05 - Write Single Coil
  • FC06 - Write Single Holding
  • FC16 - Write Multiple Holding


How it works:

From code you create Query to indicate the function you want to execute cyclically.
Before recording it in a list, it analyses the "Count" parameter (this is because I have noticed that I can request data up to a maximum of 125)
Adapts the query and eventually creates as many queries as necessary to satisfy the initial request.

The list of "Queries" proceed cyclically and it advances only if the server responds to that Query
(My idea was: "No need to continue, if server can't reply to me")

All code run in background.

ModbusTCP
Author:
Walter Maniero
Version: 2.20


  • Functions/Routines:
    • Initialize(Caller As Object,EventName As String, unitId As Byte)
    • CreateConnection(IPaddress As String, Port As Int, Timeout As Int, ReadTime As Int) As ConnectionParams
      Return the type ConnectionParams that simply contain all informations
    • CreateQuery(Function As Short, StartAddress As Short, Count As Short) As ModbusQuery
      Create and return the query to add in list
    • AddToQuery(Value As ModbusQuery)
    • AddToQuery2(Value() As ModbusQuery)
    • RemoveFromQuery(Key As Short)
      Each query in list has a name, this name is Id field inside ModbusQuery
    • ResetQueryList()
      Clear the list
    • CreateNewQueryList()
      If you change the list in runtime, you need to apply the changes with this sub
    • StartCOM(COM As ConnectionParams)
    • StopCOM()
    • SetCoil(Address As Short, Value As Boolean)
    • SetHold(Address As Short, Value As Short)
    • SetMultipleHold(Address As Short, Value() As Short)
    • GetCoil() As Boolean()
      Coil(65536) As Boolean
    • GetHolds() As Short()
      Holds(65536) As Short
  • Type:
    • ModbusQuery
    • ConnectionParams
  • Eventi:
    • ServiceState(State As Boolean)

Example::
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Private xui As XUI
    Private ModbusClient As ModbusTCP
    Private COMParameters As ConnectionParams
    Private ModbusState As Boolean = False
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("Layout")
    ModbusClient.Initialize(Me,"ModbusClient",1)
    COMParameters.Initialize
    COMParameters = ModbusClient.CreateConnection("192.168.178.42",502,3600,200)
    SetQuery
End Sub

Sub SetQuery()
    Dim Myquery1 As ModbusQuery = ModbusClient.CreateQuery(1,0,100)
    Dim Myquery2 As ModbusQuery = ModbusClient.CreateQuery(3,0,100)
    ModbusClient.AddToQuery(Myquery1)
    ModbusClient.AddToQuery(Myquery2)
End Sub

Sub ModbusClient_ServiceState(State As Boolean)
    ModbusState = State
End Sub

Sub Activity_Resume
    If ModbusState = False Then
        ModbusClient.StartCOM(COMParameters)
    End If
End Sub

Sub Activity_Pause (UserClosed As Boolean)
    If UserClosed Then
        ModbusClient.StopCOM
    End If
End Sub

Sub Button1_Click
    Dim MyCoils() As Boolean = ModbusClient.GetCoil()
    Dim MyHolds() As Short = ModbusClient.GetHolds()
    ModbusClient.SetCoil(0,Not(MyCoils(0)))
    Dim Value(10) As Short
    For i = 0 To Value.Length - 1
        Value(i) = MyHolds(i) + 1
    Next
    ModbusClient.SetMultipleHold(1,Value)
End Sub
Ey Walter! Thank you very much for this Library.
I have been exploring this library everything works just fine but im having a issue when im trying to read specific coil, in this case the Y1281 from a PLC Allen Bradley, works well but when im trying to read it again, always returns false when i know its TRUE even on my HMI Panel, this varaible goes on TRUE but i can NOT read it

Any help would be great!

This works well:
Sub T_BTN_CheckedChange(Checked As Boolean)
    
    ToggleBTN1 = Sender
    
    Select Checked
        Case True:
            ToggleBTN1.Color = Colors.Green
            ModbusClient.SetCoil(ToggleBTN1.Tag - 1, True)
            Log("TRUE")
            
        Case False:
            ToggleBTN1.Color = Colors.Black
            ModbusClient.SetCoil(ToggleBTN1.Tag - 1, False)
            Log("FALSE")
    End Select
    
End Sub
This doesnt work:
Sub TEXT_TXTREAD_Click
    
    EditText1 = Sender
    'ModbusClient.(EditText1.Tag - 1)
    Log(EditText1.Tag)
    
    Dim MyCoils() As Boolean = ModbusClient.GetCoil()
    Dim STRBool As Boolean

    STRBool = MyCoils(EditText1.Tag - 1)
    
    EditText1.Text = STRBool
    
    Log(STRBool)
    
End Sub
 
Top