Emulating a serial port on UNIX

Call: +44 (0)23 8098 8890

Posted 15th November 2019, By Simon H
Internet of Things image

Emulating a serial port on UNIX

Sometimes a project presents an assortment of constraints that leads to an unusual problem. We were working on an embedded Linux project with a need to communicate with a sensor that uses the Modbus RTU protocol over RS-485. Not wanting to re-implement a standard protocol, we found libmodbus, a free software library. This looked good, except for one problem. The library expects to talk to a serial port but we were using an RS-485 driver chip with an SPI interface, like this:

Linux CPU driver image

Arguably the ‘proper’ thing to do would be to write a device driver that provided a serial-port-like interface. On the other hand, this is a proof of concept embedded device already doing plenty of low level hardware control in the user application. The project was being developed for a hard deadline and pragmatic solutions were required.

The obvious option was simply to drop libmodbus and implement our own Modbus library. This would be reasonable. Modbus is not very complicated and we would only need the subset of the protocol used by our peripherals. We were wary of this temptation though; things tend to look much simpler than they really are. Instead, we took a step back and considered building an adapter. 

In software pattern jargon, an adapter is a module that connects two different interfaces. In this case one side of the adapter would be the serial-port-like interface that libmodbus could use. The other side would be the module that handles the SPI / RS-485 driver chip.

There are several options for building a serial port adapter. First the ideas we didn’t use:

Named pipes – An IPC channel that appears as a file in the filesystem. This might work on systems with bidirectional pipes1 but Linux only supports unidirectional pipes.

Dummy serial port – e.g. tty0tty – A program that installs pairs of virtual serial ports that communicate with each other as if connected by a null-modem cable. These are permanently available in known locations, e.g. /dev/tnt0 <=> /dev/tnt1. This would be a good choice for communicating with a program that expects a fixed serial device name and is awkward to reconfigure.

Relay/pipe utility – e.g. socat – A program that dynamically connects two endpoints of various types.

The workable solutions listed above required things we would prefer to avoid; installing external dependencies; installing kernel modules; managing an extra process at runtime; or writing and debugging a new kernel module. It would simplify installation and usage if we could incorporate the adapter within the main application. For rapid development, we'd rather not write a lot of new code. It turns out we could satisfy both aims using a few standard POSIX functions to create a type of device called a pseudoterminal.

A pseudoterminal is a pair of connected pseudodevices2, one of which implements a teletype (TTY) interface, the other a device file. Data written to one end of the pseudoterminal may be read at the other. A pseudoterminal is dynamically created by a process usually for its own use and it does not persist after its creator exits. Pseudoterminals are typically used to implement terminal emulators and services such as ssh.

This diagram expands the CPU device to show the software design. The main application has a sensor module for high level access to the sensor. This module uses the C API of libmodbus to make Modbus message requests of the sensor. Libmodbus uses the I-can’t-believe-it’s-not-a-serial-port3 (TTY) end of the pseudoterminal to write serialised messages. Back inside our application, the adapter reads the message data from the device file end of the pseudoterminal and writes this back out to the device file representing the SPI port. The RS485 driver chip transmits the data on the bus to the sensor. The sensor generates a Modbus reply to the request which travels back along the same path in reverse.

Linux CPU image

At this point we’ll illustrate use of a pseudoterminal with snippets of C code4.

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>

These headers allow use of pseudoterminals and low level I/O.

#include <modbus/modbus.h>

In this example we’re using the pseudoterminal to interface to libmodbus.

int fileDescriptor = posix_openpt(O_RDWR | O_NOCTTY);

Start by opening a new pseudoterminal. The options here specify a read/write device that is not the controlling terminal for the process (i.e. it is not the user interface terminal).

int result = grantpt(fileDescriptor);
result = unlockpt(fileDescriptor);

These calls are required before using the new pseudoterminal5.

char* deviceName = ptsname(fileDescriptor);
modbus_t* context = modbus_new_rtu(deviceName, BAUD, PARITY, DATA_BITS, STOP_BITS);

We get the device name (i.e. a file path under /dev) and pass it to libmodbus. If these two calls are not in the same function, then it would be better to make a copy of the deviceName string; this will be overwritten at the next call to ptsname.

The Modbus context can now be used to open the device and set the station address. We can send Modbus messages, but the thread calling into libmodbus will block until it gets a reply, error, or a timeout message. We will need to start a new thread to handle the other end. This thread will loop indefinitely copying the data back and forth:

ssize_t readCount = read(fileDescriptor, requestBuffer, sizeof requestBuffer); // from libmodbus
/* send the request to the device over SPI */
/* receive a reply from the device */
ssize_t writeCount = write(fileDescriptor, replyBuffer, replySize); // to libmodbus

This part of the solution is potentially awkward in the general case. How do we know how many bytes should be read and written at each iteration of the loop?

In this example we are helped by the Modbus protocol which specifies timing boundaries within and between messages. The message boundaries are identified by timeout without any other protocol logic required. To read, we request a large amount of data (sizeof requestBuffer) which will be set according to the maximum possible size of a Modbus message. We are returned the actual size of the next message sent by libmodbus. To write the reply, we read bytes from the SPI device until we identify the message boundary by timeout then send this batch to libmodbus.

In other request-response protocols it might be necessary to apply some basic message parsing to identify length fields or start-stop markers. In the worst case, or to support full-duplex operation, the read and write connections can be handled separately and in parallel6.

result = close(fileDescriptor);

The program should dispose of the pseudoterminal by closing both ends of the connection.


When you need to emulate a serial port interface on a UNIX system, consider using a pseudoterminal.


1 There may be other problems, for example a named pipe could return an error when the client attempts to set terminal attributes such as baud rate.

2 A pseudodevice provides interfaces typical of hardware devices but has no actual hardware associated with it.

3 In UNIX systems, teletype devices and serial ports are closely related. For example, the typical naming of a hardware serial port /dev/ttyS0 assumes it will be used to connect a hardware terminal.

4 The samples omit lines of code not directly concerned with operating the pseudoterminal. Within a snippet omitted lines are indicated by ellipses. (…) This is most commonly error checking, though we do show where result codes are available by capturing returned values.

5 These library functions exist due to historical concerns about user terminal security.

6 The question still exists of how much data to transfer per iteration but this is a performance (latency and throughput) concern, not one of correctness.

Interested in other ITDev Linux blogs?

Here at ITDev, we often work on client projects that use the Linux operating system, see this search index to other Linux related blogs.

Embedded Linux Interest Group

ITDev runs an Embedded Linux Interest Group to inform and support industry. If you are interested in learning more about Linux as well as attending events and workshops, sign up to our Interest Group here:

How ITDev Can Help

As a provider of software and electronics design services, we are often working with the Linux and Android operating systems. We have extensive experience in developing, modifying, building, debugging and bring up of these operating systems be it for new developments or working with existing kernels to demonstrate new device technologies and features.

We offer advice and assistance to companies considering to use or already using Linux or Android on their embedded system. If you have any questions or would like to find out more, we would be delighted to speak to you. Email us or call us on +44 (0)23 8098 8890.


Senior Software Engineer

MSci - Physics, Imperial College London

I joined ITDev at the start of 2019. Previously I worked on banknote detection systems and automotive engineering research.

My engineering specialities are embedded software, instrumentation and control systems.

Outside work, I run a book group, practice yoga and go on adventures with my family.


View all posts by Simon    |    View all ITDev team members

IET Enterprise Partners logo

Latest Blog Posts

Ladybug in sight
Posted 3rd August 2020, By James H
If debugging a Linux driver is bugging you, this 'how to' guide explains how to approach this using the function tracer within ftrace to find out what’s going ...more
Linux penguin sending an email
Posted 19th June 2020, By Quentin D
In this final part of his blog, Quentin explains the final part of the process: submitting your changes to the Linux Maintainers.
Linux penguin
Posted 5th June 2020, By Quentin D
In his second 'Contributing to the Linux kernel' blog, Quentin looks at finding something to fix, coding and committing changes.
Jenkins for FPGA
Posted 29th May 2020, By Hugh J
FPGA development should be enjoyable but when bugs slip through the net, it can be a headache to find them later on. Hugh looks at a few of the plugins ...more

Latest News

Posted 11th September 2020
From adapting to our new working situation to recruiting new staff members and engaging new clients, it’s been a busy summer. Find out more in our latest ...more
Spring 2020
Posted 17th April 2020
Following the beautiful weather over the Easter weekend, we are proud to be able to provide an update on our current activities, showing business is continuing ...more
Business as usual
Posted 20th March 2020
With a strong commitment to keeping our staff and clients safe during this period, we would like to reassure all our business contacts that we remain fully ...more
South Coast Tech Awards Trophy
Posted 6th December 2019
ITDev was excited to attend the first South Coast Technology Awards on Thursday 5th December at the Ageas Bowl. A much needed celebratory awards for a sector ...more