Every once in a while, I'll see questions or bug reports come in about System.SerialPort and System.SerialPortCount. So today I'm going to talk about "how stuff works."
On OS X, the system has the ability to enumerate over serial devices using the IOService functionality. So on that platform, it's very simple: we just ask the OS for an iterator that we can then use to count and access the various serial ports installed on the system.
On Mac Classic, we use something similar called CRMSearch which essentially allows us to get a linked list of serial devices back from the system. So again, this is pretty straight forward.
Where it starts to get interesting is with operating systems which do not have port enumeration services.
On Windows, there's no sanctioned way to determine what serial devices are installed and functional on the OS. You can do some version-specific registry spelunking, but it's fragile, and it still doesn't yield correct results. However, *almost* all serial devices (and all devices installed by default) are installed under a COM port where the "name" of the device is COM followed by a number. We use this to our advantage and try to open up "files" which are named \\.\COMX -- where X is an ever-increasing number in our loop. So first we try to open COM1, then COM2, and so forth.
However, this is a very expensive operation (as you can imagine). Furthermore, there's no official stopping point (though there is an artificial limit imposed by the OS of 256), and the port could conceivably be named anything. So we arbitrarily halt the search after COM9, because it's pretty rare to find a COM port higher than that.
But this leads to a problem -- what if you have a serial port that's on COM10 which you need to access? System.SerialPortCount and System.SerialPort won't let you access that one because its out of range (assuming there's a COM1 thru COM9 already installed). That's why we now allow you to get a SerialPort object by name instead of by index. You can simply use System.SerialPort( "\\.\COM10" ) to open that serial device. An interesting caveat is that you can use "COM1" to refer to COM1, and "COM9" to refer to COM9. But if you want to refer to COM10 or higher, you must prepend the "\\.\", or the call will always fail. Since it's safe to always prepend that string, I'd recommend you get in the habit of always using it (if you get the port by name).
So here's a neat hack for you. What's the difference between a COM (serial) port and a LPT (printer) port? Heh, nothing really. Just pins. So if you need to access the parallel port in REALbasic, you could try making a Serial device and setting its SerialPort to be System.SerialPort( "LPT1" ). In theory, this works just fine (though I have no way to test it in practice because I have no parallel cable that I can hook up to a tester -- though if someone wants to give me one, I'd be happy to see what hacks I can come up with using it).
The way things work on Linux is very similar to Windows -- there's no such thing as an enumeration service that comes standard on Linux. So we're stuck looping over all the devices in the /dev/ directory which usually correspond to a serial device. We loop over files in /dev/tts/ or /dev/ttyS, while incrementing the trailing number. On Linux, getting the number of serial ports is even more strange than on Windows. We simply keep incrementing the suffix of the filename in the /dev directory until we're told that the particular file doesn't exist. So if you have /dev/ttyS1 and /dev/ttyS3 (but not ttyS2), then the serial port count will report only one device. It's hackish, but it's what we're given to work with. But here again, there's nothing that says a serial device must be named /dev/ttyS1. In fact, quite frequently, it's named something totally different. So there again, the named serial port idea works very well. When opening a port by path on Linux, you need to specify the full path -- not just the name of the file in the /dev/ directory.
And that's how getting the serial port and serial port count works in REALbasic. Questions?
Hey buddy! That's 3!! count 'em...3! boring posts in a row! I think I'm entitled to a personal post now. ppphht....can't have 2 personal posts in a row eh aaron? I think it's time for a "why my girlfriend is the best ever" blog post.
Aaron:
Does REALbasic handle the /dev/ttyUSBX serial ports in Linux in the same manner?
@Lis -- Lol, sorry for the "boring" posts dear. I'll put something up today that says "why my girlfriend complains if there are too many programming posts in a row." ;-)
@Chris -- We don't loop over those when trying to count serial ports. However, you can certainly attempt to open them by name. Some day, it'd be nice to get standard enumeration services for this sort of thing, because the fact that you can name them anything you want makes it almost impossible to find all the serial devices in a safe, quick manner.
You guys might want to check out HAL on the Linux platform. It's a hardware abstraction mechanism that should make it much easier to locate hardware features rather than by guessing. (It's not just a RedHat thing, I use it on my Gentoo box without any particular difficulty).
I'll check it out, but unless it exists in NLD (and a lump of other *nix flavors), it probably won't do much good. We want to keep the code paths pretty simple, and so far, the rule has been "unless we absolutely need it, it needs to be in the base install of most major distros."
Thanks for the link though, I'll read it and see what I can glean. :-)
Hey! I hope the post really helped me with my problem: I couldn't open ports above 9 in Win32::SerialPort. I had no clue about the '\\.\' prefix. will try it tomorrow!
Thanks!