Correct Way To Loop Through Serial Devices To Find The Right One?

I’m working on a cross-platform app (win/mac) that needs to communicate with one of several serial devices connected to the computer. To better illustrate what I’m asking here, consider the following fictitious situation:

Picture a lab setting, where a single computer is connected to 3 digital scales. We’ll call them scale A, Scale B, and Scale C. Each scale is identical hardware (same make/model) but have individual serial numbers programmed into their firmware. Each scale additionally is using an FTDI chip to provide a USB<->Serial connection, so I can communicate with them using a Serial control. There is API available for me to ask a scale for its serial number, so I can uniquely identify it.

Let’s say that the app I’m writing ALWAYS wants to use Scale B, and that I know the serial number of this scale ahead of time.

Now, here’s my question: What is the correct way to use System.serialPortCount, System.SerialPort, and a Serial control to determine which of the various serial devices attached is the one I want?

I’d like to have things set up such that I can simply loop through the serial ports available on the system and query each device for its serial number. The problem with this is that since serial communication is asynchronous, there is no good way to wait around in my loop through all the serial ports for an answer to come back from the first one before asking the next one.

Can anyone point me to an example of the right way to do this?

Thanks!

Experts in event driven programming could probably do this better than me but what I do is something like this:

// Going to assume that you know how to set up all the serial ports...

For i as integer = 0 to SerialPrt.Ubound  // Where SerialPrt is an array of serial ports

SerialPrt(i).Write "GET FIRMWARE VERSION"  ' Or whatever you use

Do
   SerialPrt(i).Poll
   App.SleepCurrentThread(5)
Loop Until SerialPrt(i).LookAhead.Instr("SER#") <> 0   ' Assuming "SER#" si the first part of the serial number

Dim sernumber as String = SerialPrt(i).Readall

If sernumber = "My_Desired_Serial_Number" Then

Else
   Continue
End If

Next
 

Now maybe this isn’t “event driven” enough but I’ve not been able to find a way around this. When communication is asynchronous but you need to operate synchronously for a specific purpose, I don’t find any other way except for looping through a Do Loop and sleeping for a little bit in between.

If someone has a better idea, I’d love to hear it… :slight_smile:

You know I just had another idea which for your case could maybe work. I’ve not figured out how to implement it in my app and maybe it won’t work for you either.

The “event driven” way would be to write to each serial port in a loop. Then in the DataAvailable event for the serial port (and you set up a sub-class of Serial port with the proper code and then add the subclass to the array), you check to see if the data received is the serial number and if so, then you launch a method or continue on, etc. If not, you close the port.

I suppose that would work. In simple apps, it’s OK, but I start losing it when I have to do multiple reads and writes to a device and I need to know the response before I do something else. Having learned to write code long before the days of event driving programming and then not doing anything with code for nearly 20 years, I have never been formally trained in the art of event driven works. So I guess I stumble through!

I’ve tried this approach in the past, but am concerned with the app.sleepCurrentThread that it requires. I’m in a single-threaded app, so this would lock up the UI (granted, only for a short time) - especially problematic if there are serial ports that don’t send any data back (maybe there are 15 devices connected, some of which never respond to anything on the serial), so polling them has no effect… then I have to mess with counting how long it’s been since I started, etc.

I’ve got some ideas kicking around, I may see if I my implementation works, and if so I’ll share it here.

So leave out the app.sleepcurrentthread and just poll the serial port over and over. That does seem to work.

I’d go with an array of serial objects running async. When one gets the right response, have it assign itself to a global variable. Once that variable is non-nil, you know you’ve got the right port. The rest will either get the wrong result and quit or simply error out.

Loop through all the ports
Create a Serial object for each
Start the query process
Go on to the next one

I just finished writing up a slightly modified version of what @Tim Hare has suggested here. Here’s some pseudo code:

I wrote 2 subclasses of Serial: ssSerialLocator and ssSerial

for each serial port in system construct an instance of ssSerialLocator that is only meant to ask if the device is that I'm after add this instance to a global array send the "what's your serial number" command to the ssSerialLocator subclass. next

then, in the dataAvailable event of my ssSerialLocator

grab all the data in the incoming buffer see if is the serial number I'm looking for. if it is: close the serial port. construct a new instance of ssSerial and assign it to the global ssSerial variable for my api class. set the SerialPort for api.ssSerial to be me.serial, then open it for use, and send the "what's your serial number?" command again. //I send the serial number command again because when the dataAvailable event in the ssSerial fires, that will trigger asking the device for more information and set it up for use. end if

Seems to work just fine on the mac. Will test shortly on windows.

There was a thread about serial ports being slow on Windows a while ago :
https://forum.xojo.com/8601-serialports-listing-slow