B4J Question Multi serial port

Zlgo

Member
Need advice how to open more then one serial port in the same time. For now I make duplication for Serial and Asyncstream object and belonging subs , but this is little bit clumsy!
Hope maybe exists better solution then copying subs for every port?
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Upvote 0

emexes

Expert
Licensed User
For now I make duplication for Serial and Asyncstream object
If by duplicate, you mean to Dim an instance for each serial port, of the one class - this is correct.
and belonging subs
but this does not duplicate the class's method code ("belonging subs"?) - the class's code is only included once in your compiled code, and used by all instances (ie serial ports).
 
Upvote 0

emexes

Expert
Licensed User
Does your code look something like:
B4X:
Dim SP1 As Serial
SP1.Initialize("SP1")
SP2.Open("COM11")
SP2.SetParams(2400, 8, 1, 0)
   
Dim SP2 As Serial
SP2.Initialize("SP2")
SP2.Open("COM12")
SP2.SetParams(19200, 8, 1, 0)
Eg the above code calls the .SetParams method twice, once for each serial port, but only one copy of the actual code for .SetParams exists in the program.
 
Upvote 0

Zlgo

Member
Right now because of lack of time I made simple duplication of modul(.bas) where is complete initialisation of Serial and AsyncStream object with event subs (New_data) and closing same object (serial and asyncstream). Basicaly I duplicate example for jSerial lib which can be found in forum. Reading string from both port then send with socketKlient to remote PC where is soket server listening on some port. Idea is that when found all ports on pc (installed multi serial card in pc) every one of them could be separately initialized and closed, of course also read and write on them
 
Upvote 0

emexes

Expert
Licensed User
Idea is that when found all ports on pc
Years ago, with a VAX computer at a school, we used multiplexers which were boxes that had 16 RS232 ports to connect to terminals or printers, that would squeeze all those ports over a single long cable (two twisted pairs, I think it was, one pair for each direction) with a matching box at the other end to expand it all back out again into a matching number of serial ports on the computer. I think we had more RS232 ports at that place than existed in the rest of the town put together!

So I'm kinda interested in what you're doing :)

1/ What are the serial devices that are hooked up to the PC(s)? Like, gas analysers, scales, keypads, GPS, ... ?
1b/ What do the packets look like? Fixed or variable size? ASCII or binary? Delimited? How large on average?
1c/ How many packets per second does an average device typically emit?
1d/ Is the dataflow unidirectional, or is there also data coming back from the computer to the devices?
2/ Are you using an actual multi-port serial card that plugs inside the computer, or a number of USB serial adapters via USB hub(s)?
2b/ If USB... can you stack up the hubs, ie plug hubs into hubs? Have you found that to be reliable?
3/ Are you thinking to put all the serial channels through the one IP network port number, or allocate one port number per serial channel?
4/ How many serial ports/devices are we looking at? Per agglomerator, and in total. Five? Fifty? Five hundred?
 
Last edited:
Upvote 0

emexes

Expert
Licensed User
It would have taken less time to implement it with a single class.
Yeah, I'm still confused about what is being duplicated.

I would have thought the biggest issue would be the establishment and security of communication between the main computer and the satellite agglomerators, assuming that it is going across the internet.
 
Upvote 0

Zlgo

Member
It will take significantly less time to maintain the single class vs. maintaining multiple duplicated modules.
I was hope that somebody break this "problem" and have piece of example! I,m trying to figure out better solution.
 
Upvote 0

emexes

Expert
Licensed User
Yeah, I'm still confused about what is being duplicated.
Perhaps the long-term goal is to handle a variable number of serial ports, ie, all the ports discovered on the computer, and handle them via a single Array or List, but...

the short-term reality is a first goal of getting it working with a known number of serial ports (eg, two) and handle them separately using simple variables.
 
Upvote 0

emexes

Expert
Licensed User
I was hope that somebody break this "problem" and have piece of example! I,m trying to figure out better solution.
Post #4 demonstrates handling two serial ports, without duplicating library or driver code. Easily extended to N serial ports, and I'm happy to write some sample code to get you started, but I'll wait until a definite goal is visible (ie, answers re: post #7) so that I don't go chasing shadows down dead-end alleys.

:)
 
Upvote 0

Zlgo

Member
Important part of program is that through socket protocol application with same defining commands can open and close and read serial ports, but amount of serial ports can vary from 2 to 16 , depends of type of serial card installed in pc. My testing app work with 2 ports with simple duplicating code down below but forming in two separate modul : Ser1 and Ser2 where variable name is different myserial1 and myserial2 and so on. Sending command on socket "com2 ON (or OFF) then naming port will be ready to send info(remote barcode scanners) to socket server and stored in some database. This is clumsy way but it works for 2 ports, problem is that I need to new in forward how many ports will be installed and change app for this solution. Here is biger part of Serx modul which handle serial port!
Working version exist made in Revolution ("LiveCode") for Windows , but now target is Linux machine.
Critical moment is that all port's need to be able to sand and receive data in the same time.


#Region Project Attributes
'#CommandLineArgs:
'#MergeLibraries: True
#End Region

Sub Process_Globals
Private myserial1 As Serial
Private astream1 As AsyncStreams
Dim uart_list As List

End Sub


'******************************* dio otvaranja serijskog porta ***********************
Sub SerijskoCitanje_start

If astream1.IsInitialized = False Then
myserial1.Initialize("aktivport")
'uart_list = myserial.ListPorts
If Main.gvComSet <> "" Then
myserial1.Open(Main.gvComSet)
myserial1.SetParams(9600,8,1,0)
astream1.Initialize(myserial1.GetInputStream,myserial1.GetOutputStream,"astream1")
Log("Serijski port:" & Main.gvComSet & " inicijaliziran")
Main.VratiPoruku("ok")
Else
Main.VratiPoruku("nok")
Log("nije izabran serijski port")
End If

Else
Purge_port ' ciscenje porta od zaostataka
End If

End Sub




Sub SerijskoCitanje_stop
Try

If astream1.IsInitialized Then
Purge_port
astream1.Close
myserial1.Close
End If

Log("Serijski port:" & " Zatvoren") ' za probu
Catch
Log(LastException)
End Try
End Sub



Sub Purge_port

Try
myserial1.PurgePort(1)
myserial1.PurgePort(2)
myserial1.PurgePort(4)
myserial1.PurgePort(8)
Catch
Log("neuspjelo ciscenje porta")

End Try

End Sub



Sub astream1_NewData (Buffer() As Byte)

Dim rcvStr As String = BytesToString(Buffer, 0, Buffer.Length, "UTF8")

Main.VratiPoruku(rcvStr) 'slanje podataka sa serijskog porta na soket klijenta


End Sub
 
Last edited:
Upvote 0

Zlgo

Member
B4X:
 Important part of program is that through socket protocol application with same defining commands can open and close and read serial ports, but amount of serial ports can vary from 2 to 16 , depends of type of serial card installed in pc. My testing app work with 2 ports with simple duplicating code down below but forming in two separate modul : Ser1 and Ser2 where variable name is different myserial1 and myserial2 and so on. Sending command on socket "com2 ON (or OFF) then naming port will be ready to send info(remote barcode scanners) to socket server and stored in some database. This is clumsy way but it works for 2 ports, problem is that I need to new in forward how many ports will be installed and change app for this solution. Here is biger part of Serx modul which handle serial port!
Working version exist made in Revolution ("LiveCode") for Windows , but now target is Linux machine.
Critical moment is that all port's need to be able to sand and receive data in the same time.


#Region Project Attributes
'#CommandLineArgs:
'#MergeLibraries: True
#End Region

Sub Process_Globals
Private myserial1 As Serial
Private astream1 As AsyncStreams
Dim uart_list As List

End Sub


'******************************* dio otvaranja serijskog porta ***********************
Sub SerijskoCitanje_start

If astream1.IsInitialized = False Then
myserial1.Initialize("aktivport")
'uart_list = myserial.ListPorts
If Main.gvComSet <> "" Then
myserial1.Open(Main.gvComSet)
myserial1.SetParams(9600,8,1,0)
astream1.Initialize(myserial1.GetInputStream,myserial1.GetOutputStream,"astream1")
Log("Serijski port:" & Main.gvComSet & " inicijaliziran")
Main.VratiPoruku("ok")
Else
Main.VratiPoruku("nok")
Log("nije izabran serijski port")
End If

Else
Purge_port ' ciscenje porta od zaostataka
End If

End Sub




Sub SerijskoCitanje_stop
Try

If astream1.IsInitialized Then
Purge_port
astream1.Close
myserial1.Close
End If

Log("Serijski port:" & " Zatvoren") ' za probu
Catch
Log(LastException)
End Try
End Sub



Sub Purge_port

Try
myserial1.PurgePort(1)
myserial1.PurgePort(2)
myserial1.PurgePort(4)
myserial1.PurgePort(8)
Catch
Log("neuspjelo ciscenje porta")

End Try

End Sub



Sub astream1_NewData (Buffer() As Byte)

Dim rcvStr As String = BytesToString(Buffer, 0, Buffer.Length, "UTF8")

Main.VratiPoruku(rcvStr) 'slanje podataka sa serijskog porta na soket klijenta


End Sub
 
Upvote 0

emexes

Expert
Licensed User
How do you feel about a B4J program that opens every serial port it can find (within a specified range, eg: from COM20 to COM60) and then sends data from all of those ports over a single network connection (Main.VratiPoruku ?) to the master computer, in the plain ASCII (ie, human readable) format like:

ppp:hhhhhhhhhh..

where ppp is the serial port number or name that received data, and hhhh... is the received data in hex format (compatible with B4X no-space hex conversion functions), eg:

11:48656C6C6E0D0A

which would be "Hello" CR LF received on COM11.

And optionally log all serial data to a dated log file eg 20190920.SER for diagnostic/debugging/recovery/archive/double-checking purposes. Use same format, or possibly add timestamp so can replay at same speed.

The master computer can transmit back to the serial device by sending data back through the network connection, in the same format.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
You are so close in creating a class for the code module you posted. With some slight modifications, your class could look something like this (for this example, I'm using the very unimaginative name of serialclass for the class name):
B4X:
Sub Class_Globals
   Private myserial1 As Serial
   Private astream1 As AsyncStreams
   Dim myPort As String
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(port As String)
   myPort = port
End Sub
    '******************************* dio otvaranja serijskog porta ***********************
Sub Start

   If astream1.IsInitialized = False Then
       myserial1.Initialize("aktivport")
       'uart_list = myserial.ListPorts
       If myPort <> "" Then
           myserial1.Open(myPort)
           myserial1.SetParams(9600,8,1,0)
           astream1.Initialize(myserial1.GetInputStream,myserial1.GetOutputStream,"astream1")
           Log($"Serijski port: ${myPort} inicijaliziran"$)
           Main.VratiPoruku("ok")
       Else
           Main.VratiPoruku("nok")
           Log("nije izabran serijski port")
       End If

   Else
       Purge ' ciscenje porta od zaostataka
   End If

End Sub

Sub Stop
   Try
       If astream1.IsInitialized Then
           Purge
           astream1.Close
           myserial1.Close
       End If
       Log($"Serijski port: ${myPort} Zatvoren"$) ' za probu
   Catch
       Log(LastException)
   End Try
End Sub

Sub Purge
   Try
       myserial1.PurgePort(1)
       myserial1.PurgePort(2)
       myserial1.PurgePort(4)
       myserial1.PurgePort(8)
   Catch
       Log("neuspjelo ciscenje porta")
   End Try
End Sub

Sub astream1_NewData (Buffer() As Byte)
   Dim rcvStr As String = BytesToString(Buffer, 0, Buffer.Length, "UTF8")
   Main.VratiPoruku(rcvStr) 'slanje podataka sa serijskog porta na soket klijenta
End Sub

Crude usage example (non-ui app):

B4X:
'Non-UI application (console / server application)
#Region Project Attributes
   #CommandLineArgs:
   #MergeLibraries: True
#End Region

Sub Process_Globals
   Dim uart_list As List
   Dim sPorts As Serial
End Sub

Sub AppStart (Args() As String)
   uart_list.Initialize
   Dim portsFound As List = sPorts.ListPorts
   If portsFound.Size > 0 Then
       For Each port As String In portsFound
           Log($"Creating a serialclass instance for port ${port}"$)
           Dim newPortInstance As serialclass
           newPortInstance.Initialize(port)
           uart_list.Add(newPortInstance)
       Next
   Else
       Log("No COM ports found!")
   End If
   
    'Flushing all  ports
   For Each portInstance As serialclass In uart_list
       portInstance.Start
       portInstance.Purge
       portInstance.Stop
   Next
   
End Sub

Sub VratiPoruku(aString As String)
   Log(aString)
End Sub

'Return true to allow the default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
   Return True
End Sub
Note: Untested code
 
Upvote 0
Top