Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read serial data without high CPU use

I want to read messages sent from an Arduino via the FTDI (serial) interface in a simple C or C++ program under Linux. The Arduino sends a two character 'header', a command byte followed by a few bytes of data depending on the command.

My first attempt was to simply poll the data using open() and read() but doing so causes about 12% CPU use. This didn't seem to be the appropriate way of doing things.

Second I read up on libevent on implemented an event loop that fires an event when data is present on the file descriptor. My cpu usage was next to nothing but I couldn't read the entire message before another event was called. The events didn't fire when an entire message was received but as soon as any/some data was available on the file descriptor. Looking at it more it was obvious that this wouldn't work quite the way I wanted it. This is my event code: http://pastebin.com/b9W0jHjb

Third I implemented a buffered event with libevent. It seemed to work somewhat better but still split some of the messages up. My event code is: http://pastebin.com/PQNriUCN

Fourth I dumped libevent and tried out Boost's ASIO class. The example I was following was http://www.webalice.it/fede.tft/serial_port/serial_port.html. It seemed to work alright but the "event loop" was a "while(1) {}" which caused CPU usage to go up again. The loop just checks for error status while serial reading happens in a callback on a different thread. I added a usleep(1) to the while loop and it brought my CPU usage to 2% which is ok, but still seems heavy for such a light program.

Most of the examples of libevent and even the underlying epoll use TCP sockets which doesn't seem to behave quite the same as serial port data.

So my main question is: what is a good lightweight way to read messages from a serial port without heavy polling? (in linux, using C or C++)

like image 535
darkadept Avatar asked Aug 12 '11 04:08

darkadept


1 Answers

The OP has probably long since solved this, but for the sake of anyone who gets here by google:

#include <sys/poll.h>

struct pollfd fds[1];
fds[0].fd = serial_fd;
fds[0].events = POLLIN ;
int pollrc = poll( fds, 1, 1000);
if (pollrc < 0)
{
    perror("poll");
}
else if( pollrc > 0)
{
    if( fds[0].revents & POLLIN )
    {
        char buff[1024];
        ssize_t rc = read(serial_fd, buff, sizeof(buff) );
        if (rc > 0)
        {
            /* You've got rc characters. do something with buff */
        }
    }
}    

Make sure the serial port is opened in nonblocking mode as poll() can sometimes return when there are no characters waiting.

like image 94
Simon Elliott Avatar answered Nov 07 '22 07:11

Simon Elliott