Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Linux serial port communications

478 views
Skip to first unread message

cerr

unread,
May 22, 2012, 5:47:48 PM5/22/12
to
Hi There,

I would like to communicate with an external device over the serial port on
an embedded Linux system. I can talk to my device when I launch "screen
/dev/ttyUSB0 19200" from the shell. Now I'm trying to write some code but
have troubles getting it going. My select keps timing out on me and i don't
know why. My device wants data in this matter: 8 data bits, 1 start bit, 1
stop bit, and no parity. at 19200 baud.

The code:

#include <stdio.h> // standard input / output functions
#include <string.h> // string function definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <errno.h> // Error number definitions
#include <termios.h> // POSIX terminal control definitionss
#include <time.h> // time calls


int open_port(void)
{
int fd; // file description for the serial port

fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);

if(fd == -1) // if open is unsucessful
{
perror("open_port: Unable to open /dev/ttyUSB0");
}
else
{
fcntl(fd, F_SETFL, 0);
}

return(fd);
}

int configure_port(int fd) // configure the port
{
struct termios port_settings; // structure to store the port settings in

cfsetispeed(&port_settings, B19200); // set baud rates
cfsetospeed(&port_settings, B19200);

port_settings.c_cflag &= ~PARENB; // set no parity, stop bits, data bits
//port_settings.c_cflag &= ~CSTOPB;
port_settings.c_cflag &= ~CSIZE;
port_settings.c_cflag |= CS8;

tcsetattr(fd, TCSANOW, &port_settings); // apply the settings to the port
return(fd);

}

int query_modem(int fd) // query modem with an AT command
{
int n;
fd_set rdfs;
struct timeval timeout;

// initialise the timeout structure
timeout.tv_sec = 10; // ten second timeout
timeout.tv_usec = 0;

write(fd, "TEST\n", 5); // send an AT command followed by a CR

// do the select
n = select(fd + 1, &rdfs, NULL, NULL, &timeout);

// check if an error has occured
if(n < 0)
{
perror("select failed\n");
}
else if (n == 0)
{
puts("Timeout!");
}
else
{
printf("\nBytes detected on the port!\n");
}

}

int main(void)
{
int fd = open_port();
configure_port(fd);
query_modem(fd);
return(0);
}

Scott Lurndal

unread,
May 22, 2012, 6:32:18 PM5/22/12
to
cerr <ron.e...@gmail.com> writes:
>Hi There,
>
>I would like to communicate with an external device over the serial port on
>an embedded Linux system. I can talk to my device when I launch "screen
>/dev/ttyUSB0 19200" from the shell. Now I'm trying to write some code but
>have troubles getting it going. My select keps timing out on me and i don't
>know why. My device wants data in this matter: 8 data bits, 1 start bit, 1
>stop bit, and no parity. at 19200 baud.
>

By default, the line discipline will be configured in 'cooked' mode. In cooked
mode, the select won't fire until the newline is transmitted. If you want
character-by-character input on the serial port, set it to raw with:

cfmakeraw(&s_term_attr);

before you call tcsetattr(fd, TCSANOW, s_term_attr).

scott

cerr

unread,
May 22, 2012, 6:58:51 PM5/22/12
to sl...@pacbell.net
Hi Scott,

I included that but still doesn't seem to work and my device actually will respond with "TEST\n"(new line at the end). But the select doesn't fire...:(

Okay, I just figured out that the sending part of my code doesn't seem to work correctly either... :( So it's not just a thing of the select() not working correctly... :(

Johann Klammer

unread,
May 23, 2012, 7:45:48 AM5/23/12
to
cerr wrote:
>
> Okay, I just figured out that the sending part of my code doesn't seem to work correctly either... :( So it's not just a thing of the select() not working correctly... :(
Are you using the handshake lines(RTS,CTS). Without them, sending 8Bit
clean gets (ugly/impossible). Unless you are fast enough to get away
without flow control altogether..

Ron Eggler

unread,
May 23, 2012, 12:30:46 PM5/23/12
to
I don't think there's any RTS nor CTS as the document doesn't say anything.
<i have a simple read() in my code and can retrieve data just fine, the
select() for some reason just doesn't kick in and i couldn't figure out the
reason yet... :(


--- Posted via news://freenews.netfront.net/ - Complaints to ne...@netfront.net ---

Ralf Fassel

unread,
May 23, 2012, 1:22:39 PM5/23/12
to
* Ron Eggler <ronDOT...@tscheemail.com>
| <i have a simple read() in my code and can retrieve data just fine, the
| select() for some reason just doesn't kick in and i couldn't figure out the
| reason yet... :(

In the code you posted earlier in Message-ID: <2c4e62e7-b14b-4b51...@googlegroups.com>
the sequence is

fd_set rdfs;
...
n = select(fd + 1, &rdfs, NULL, NULL, &timeout);

Note that you don't set any fds in rdfs, so it will contain garbage and
the bit for your fd is probably simply not set. I'd say you need a
FD_ZERO()/FD_SET() before the select() somehow...

Example code from select man page:

fd_set rfds;
struct timeval tv;
int retval;

/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds);
FD_SET(0, &rfds);

/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;

retval = select(1, &rfds, NULL, NULL, &tv);

HTH
R'

Ron Eggler

unread,
May 24, 2012, 12:55:41 PM5/24/12
to
Hi Ralph,

The code I have now looks like this and I still keep getting timeouts only
but if you look at the read() code that's commented out, it actually works,
thus I am receiving valid data but my select doesn't kick-in... :(

int query_data(int fd) // query data with an LOOP command
{
int n;
fd_set rdfs;
struct timeval timeout;
ssize_t retval;
char bufptr[100];
char chr;
int cnt = 0;
int i = 0;

// initialise the timeout structure for the select()
timeout.tv_sec = 2; // ten second timeout
timeout.tv_usec = 0;

FD_ZERO(&rdfs);
FD_SET(0, &rdfs);

// request weather data from station
retval = write(fd, "LOOP 4\r", 7);

// wait and retrieve the string of weather data
usleep(50);
/*for (i = 0;i<=99; i++) {
read(fd, &chr, 1);
bufptr[i] = chr;
usleep(10);
}

// parse and print the weather data
printf("inside temp: %03d F\n",(long)(bufptr[10]|(bufptr[11]<<8))/10);
printf("outside temp: %03d F\n",(long)(bufptr[13]|(bufptr[14]<<8))/10);
printf("wind speed: %02d mph\n",bufptr[15]);
printf("wind direction: %03d degrees\n",(long)(bufptr[17]|
(bufptr[18]<<8))/10);
printf("10min avg wind speed: %02d mph\n",bufptr[16]);
printf("relative humidity: %02d %c\n",bufptr[34],0x25);
printf("rain rate: %02d in/hr\n",(long)(bufptr[42]|(bufptr[43]<<8))/100);
*/
// do the select
n = select(fd + 1, &rdfs, NULL, NULL, &timeout);

// check if an error has occured
if(n < 0)
{
perror("select failed\n");
}
else if (n == 0)
{
puts("Timeout!");
}
else
{
printf("\nBytes detected on the port!\n");
}

}

What's going on here? :o

Scott Lurndal

unread,
May 24, 2012, 1:20:27 PM5/24/12
to
Ron Eggler <ronDOT...@tscheemail.com> writes:

>
>Hi Ralph,
>
>The code I have now looks like this and I still keep getting timeouts only
>but if you look at the read() code that's commented out, it actually works,
>thus I am receiving valid data but my select doesn't kick-in... :(
>
>int query_data(int fd) // query data with an LOOP command
>{
>int n;
>fd_set rdfs;
>struct timeval timeout;
>ssize_t retval;
>char bufptr[100];
>char chr;
>int cnt = 0;
>int i = 0;
>
>// initialise the timeout structure for the select()
>timeout.tv_sec = 2; // ten second timeout
>timeout.tv_usec = 0;
>
>FD_ZERO(&rdfs);
>FD_SET(0, &rdfs);

Try FD_SET(fd, &rdfs) instead.

If this is for a lacross station, there's an open source utility (open2300)
that will read it and transform the data into various forms including
xml.

http://www.lavrsen.dk/foswiki/bin/view/Open2300/WebHome

scott

Ron Eggler

unread,
May 24, 2012, 2:15:03 PM5/24/12
to
Scott Lurndal wrote:

> Ron Eggler <ronDOT...@tscheemail.com> writes:
>
>>
>>Hi Ralph,
>>
>>The code I have now looks like this and I still keep getting timeouts only
>>but if you look at the read() code that's commented out, it actually
>>works, thus I am receiving valid data but my select doesn't kick-in... :(
>>
>>int query_data(int fd) // query data with an LOOP command
>>{
>>int n;
>>fd_set rdfs;
>>struct timeval timeout;
>>ssize_t retval;
>>char bufptr[100];
>>char chr;
>>int cnt = 0;
>>int i = 0;
>>
>>// initialise the timeout structure for the select()
>>timeout.tv_sec = 2; // ten second timeout
>>timeout.tv_usec = 0;
>>
>>FD_ZERO(&rdfs);
>>FD_SET(0, &rdfs);
>
> Try FD_SET(fd, &rdfs) instead.

HI Scott,
Yep, that was it! Thanks! That made it work! However, my select always
returns 1 (even if I wait second only but there's a whole bunch of more
Bytes coming back from my device...
I want to use the return value for my read() function.

My select() code:
n = select(fd + 1, &rdfs, NULL, NULL, &timeout);

// check if an error has occured
if(n < 0)
{
perror("select failed\n");
}
else if (n == 0)
{
puts("Timeout!");
}
else
{
printf("%d Bytes detected on the port!\n", n);

Scott Lurndal

unread,
May 24, 2012, 2:48:05 PM5/24/12
to
Ron Eggler <ronDOT...@tscheemail.com> writes:
>Scott Lurndal wrote:
>

>>>FD_ZERO(&rdfs);
>>>FD_SET(0, &rdfs);
>>
>> Try FD_SET(fd, &rdfs) instead.
>
>HI Scott,
>Yep, that was it! Thanks! That made it work! However, my select always
>returns 1 (even if I wait second only but there's a whole bunch of more
>Bytes coming back from my device...

Select returns the number of file descriptors which have had read/write
events triggered, not the number of bytes ready to read (man select(2)).

You must either read one byte and return to the select, or read multiple
bytes on a file descriptor set to non-blocking (man open(2)) and return
to the select. In either case, message boundary detection is your responsibility.

There is no standard API to determine how many bytes are pending to be
read. Some implementations support a non-standard PEEK ioctl(2) that
can be used, but the traditional approach is to use non-blocking file
descriptors with select (or I prefer poll(2)).

scott

This is parts of the serial port receive thread from an application I wrote, I
left out the code to detect end of message since that is protocol specific
(likely to be a CRLF pair for a weather station) and the protocol I use
with this code is decidedly non-standard (from an old mainframe).

int s_fd;
ssize_t diag;
char recvbuf[2048];
char *rbp = recvbuf;
size_t received = 0ul;
pollfd fdlist[2];
int timeout = -1;

s_fd = ::open(s_devname, O_RDWR|O_NOCTTY|O_NONBLOCK, 0);
if (s_fd == -1) {
lp->log("%s Unable to open serial device '%s': %s\n",
s_logstr, s_devname, strerror(errno));
goto leave;
}

if (!::isatty(s_fd)) {
lp->log("%s '%s' is not a serial device\n",
s_logstr, s_devname);
goto leave;
}

diag = ::tcgetattr(s_fd, &s_term_attr_orig);
if (diag == -1) {
lp->log("%s Unable to get terminal attributes: %s\n",
s_logstr, strerror(errno));
goto leave;
}
cfmakeraw(&s_term_attr);
cfsetispeed(&s_term_attr, termios_baud);
cfsetospeed(&s_term_attr, termios_baud);
s_term_attr.c_lflag = 0;

diag = ::tcsetattr(s_fd, TCSANOW, &s_term_attr);
if (diag == -1) {
lp->log("%s Unable to set terminal attributes: %s\n",
s_logstr, strerror(errno));
goto leave;
}

memset(fdlist, 0, sizeof(fdlist));
fdlist[0].fd = s_fd;
fdlist[0].events = POLLIN|POLLERR|POLLHUP;
fdlist[1].fd = s_cmd_pipe[0];
fdlist[1].events = POLLIN|POLLERR|POLLHUP;

while (!s_terminate) {
lock_thread();
if (s_send_queue.is_empty()) {
fdlist[0].events &= ~POLLOUT;
} else {
fdlist[0].events |= POLLOUT;
}
unlock_thread();

fdlist[0].revents = fdlist[1].revents = 0;

diag = ::poll(fdlist, 2, timeout);
if (diag == -1) {
if (errno == EINTR) continue;

s_logger->log("%s RS232 poll failed on device '%s': %s\n",
s_logstr, s_devname, strerror(errno));
s_terminate = true;
continue;
}

if (diag == 0) {
s_logger->log("%s timeout detected on poll\n", s_logstr);
continue;
}
if (fdlist[0].revents & POLLOUT) {
/*
* The serial port is ready for output. Send the next
* pending message.
*/
lock_thread();

c_dlist_iterator di(&s_send_queue);
while (di.next()) {
s_msg *mp = (s_msg *)di.curr();

mp->remove();
send_message(mp, false, NULL);
break;
}

unlock_thread();
}

if ((fdlist[0].revents & (POLLIN|POLLHUP)) == 0) continue;

diag = ::read(s_fd, rbp, sizeof(recvbuf) - received);
if (diag == -1) {
s_logger->log("%s Unable to read from RS232 device '%s': %s\n",
s_logstr, s_devname, strerror(errno));
continue;
}
if (complete message received) { /// ADD YOUR CODE HERE TO DETERMINE END OF MESSAGE
get_transport()->receive_callback(this, (uint8 *)recvbuf, received);
} else {
// skip past bytes already received and go back and poll for more
rbp += diag, received += diag;
}
}

Ike Naar

unread,
May 24, 2012, 6:09:50 PM5/24/12
to
On 2012-05-24, Ron Eggler <ronDOT...@tscheemail.com> wrote:
> timeout.tv_sec = 2; // ten second timeout

Are you serious ? ;-)
0 new messages