We recently built a small application for a geology contractor who works on active oil rigs and needed a way for his gas detection equipment to communicate with the oil rig network. The communication with the oil rig network needed to happen over a serial port. My laptop doesn't have a serial port, and honestly sometimes I have a hard time remembering what a serial port looks like, yet I needed a way to test the send/receive functionality of my program. After a good bit of Googling I was able to piece together a solution that works pretty well using my OSX computer as a host with a Windows 10 Virtual Machine (VM) guest managed by VirtualBox.
The final application needed to run on a Windows computer and needed to be able to send and receive data to an oil rig computer network using the WITS level 0 protocol and a Serial Port connection. The WITS protocol level 0 is ASCII data, and specifically I needed to adhere to the Pason implementation. More on WITS and Pason here and here.
For development, I chose to use a Windows 10 VM managed by VirtualBox. Thus, I needed a way to send data from the guest as well as receive data on the guest.
Send from guest to host
This was quite straight forward. In VirtualBox->Machine->Settings->Ports page, I chose to enable a port, and chose "Raw File" as the Port Mode. I left the IRQ and I/O Port settings at their default, chose to connect to an existing file, and pointed to a file in my host's /tmp/
directory:
Then on the host computer, I created that file:
$ touch /tmp/test_C2
The next time I started the VM, I was able to see a port named COM2 enabled in the Windows Device Manager, and to interact with the port from my VB.Net code. On the host computer I was able to see data sent from the guest by reading the file:
# On host
$ tail -f /tmp/test_C2
Receive data from host
This took me a bit of reading to figure out, but ended up being quite easy:
With VirtualBox->Machine->Settings->Ports, set "Port Mode" as "Host Pipe", leave the IRQ and I/O defaults, de-select "connect to existing pipe/socket" and provide an address (on the host):
Next, on the host, we need an additional piece of software that can emulate a serial port. After some reading I found that the tool socat
would do the trick. socat
seems to be a super tool that can do about 1000 things more than I need right now. Reading, trial and error led to this:
# On host
$ socat - UNIX-CONNECT:/tmp/vsp_sock1
Which I believe takes the stdin stream (-
) and connects it to the the address provided to UNIX-CONNECT as a "unix domain socket". With the above socat
command issued on the host, I could see data sent over the guest's serial port streaming to the console on the host. Additionally, typing into the host's console and hitting enter would send a line of text to the guest. On the guest, data sent from the host successfully triggered an event on the .NET serial Port object, allowing me to run a callback and read a line of data. Two way communication!
Next step is to write a script on the host to build and send fake WITS packets so I can working on parsing them in the guest.
From the socat man
page:
UNIX-CONNECT:<filename>
Connects to <filename> assuming it is a UNIX domain socket. If
<filename> does not exist, this is an error; if <filename> is
not a UNIX domain socket, this is an error; if <filename> is a
UNIX domain socket, but no process is listening, this is an
error.
Option groups: FD,SOCKET,NAMED,RETRY,UNIX
) Useful options: bind
See also: UNIX-LISTEN, UNIX-SENDTO, TCP