Abstracting serial device from an OS layer

Today a bit about serial communication as it will be a base communication layer ELM327 and CAN communication, just like IP is a base layer for TCP/UDP.

Why abstraction though? Application is currently for OS X so this is not really a must have. On the other hand this is how it usually should be done, especially if we consider multiplatform in the future. This is also not a really big change big difference too and makes code a bit cleaner.

So what do we need? Shortly: a common interface and common types for all parameters and return values. I divided it into two parts, may be the same library finally, but can be also easily split in two. Just the matter of preference. It is:

  • Serial devices enumeration and recognition.
  • Serial device access: open, read, write, close with parameters as baud rate, parity control etc.

First thing is a really OS specific thing, but interestingly quite easy to create an interface. I made a decision to support both Bluetooth and USB serial adapters and tested it with:

Enumeration will be explained in next post, now just an interface:

Easy to use, one method that returns a list of SerialDevice objects that describes serial port and parent objects that describes a specific device that handles serial communication: USB. Bluetooth or Other, unknown device type.

For USB and Bluetooth devices I try to get more specific information about name, vendor etc.

Current implementation is here:

https://github.com/killpl/obd_cougar/blob/master/cougar_lib/serial/IInterfaces.h

Later on I’ve opened the man page for open(): x-man-page://2/open  and made some assumptions for possible errors and parameters:

Man page for open() in Terminal

Next thing, which is currently in progress, is device access and read write. The interface will be much more complicated, as it has to support a lot of open() flags, configuration parameters and errors,

First thing to notice is that we have specific errors in enum, but SerialOpenFlag is a bitmask, so it had to be implemented with consecutive powers of 2: 0x0001, 0x0002, 0x0004, 0x0008… so in a result we might set any bit of the flag without affecting other ones – READ_WRITE and NOBLOCK is for example b’00001100′.

On the other hand the interface cannot be platform specific, so we have to map this later to system specific codes, I decided for it to look like this:

Similar way for error handling for open() operation:

Easy to maintain, easily readable and extendable. Moreover, totally abstract from the OS specific functions 😉

Next post will be about macOS serial devices enumeration and recognition, stay tuned 😉

Leave a Reply

Your email address will not be published. Required fields are marked *