Read and Write on Serial Port in Ubuntu with C/C++ and Libserial

Read and Write on serial port in Ubuntu with C/C++ and LibSerial

I think you just need to use while( serial_port.rdbuf()->in_avail() > 0 ) as a condition for your last while loop. Then it'll read out all the available data (“answer”) and you can send the second command after that.

Reading from serial port on Ubuntu Linux (11.10) in C++

I opened the gtkterm source code and I finally found a solution : in fact, you need to override the terminos structure (and yes, you should not have to do it normally).

If someone finds a better solution, feel free to post it. In the meantime, here is the working code, with english comments :

To open the serial port :

// Doc : http://www.easysw.com/~mike/serial/serial.html#2_4

m_serialHandle = open(ttyPort.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); // Open serial port
if(m_serialHandle < 0)
{
MY_THROW("Impossible d'ouvrir le port '" << ttyPort << "' !\nerrno = " << errno);
}

// Read mode
//if(fcntl(m_serialHandle, F_SETFL, 0) == -1) // Blocking read
if(fcntl(m_serialHandle, F_SETFL, O_NONBLOCK) == -1) // Non-blocking read
{
MY_THROW("fcntl failed !\nerrno = " << errno);
}

// Get current terminos configuration
struct termios options;
tcgetattr(m_serialHandle, &options);

// Force termios values (should not be needed, but is)
options.c_cflag = B9600;
options.c_oflag = 0;
options.c_lflag = 0;
options.c_iflag = IGNPAR | IGNBRK;
options.c_cc[VTIME] = 0;
options.c_cc[VMIN] = 1;

// Set data rate
cfsetispeed(&options, B9600); // In speed
cfsetospeed(&options, B9600); // Output speed

// Set communication flags
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_cflag |= (CLOCAL | CREAD); // Enable the receiver and set local mode...
options.c_cflag &= ~PARENB; // Desactive bit de parité
options.c_cflag &= ~CSTOPB; // Désactive 2 stop bits -> Active 1 stop bits
options.c_cflag &= ~CSIZE; // Désactive le bit "CSIZE"
options.c_cflag |= CS8; // Communication sur 8 bits

options.c_oflag &= ~OPOST; // Raw output is selected by resetting the OPOST option in the c_oflag member:

// Disable flow control
options.c_iflag &= ~(IXON | IXOFF);

// Apply
if(tcsetattr(m_serialHandle, TCSANOW, &options) == -1) // Vidage buffer & application immédiate
{
MY_THROW("tcsetattr failed !\nerrno = " << errno);
}

// Empty buffers
tcflush(m_serialHandle, TCIOFLUSH);

To read (non-blocking)

const int buffSize = 1024;
char buffer[buffSize] = {'\0'};
int count = 0;
std::string wholeAnswer = "";

int noDataTime = 0;

while(noDataTime < 3) // while there is data to be read
{
count = read(m_serialHandle, buffer, buffSize - 1);
// May fail in NON-BLOCKING mode if there is nothing to be read
/*if(count == -1)
{
MY_THROW("Impossible de lire sur le port serie. Verifiez la connexion avec l'imprimante !")
}*/

if(count > 0)
{
noDataTime = 0;

buffer[count] = '\0';
/*for(int i = 0; i < count; i++)
{
buffer[i] &= ~128; // Delete first binary bit. Could be useful for 7 bit communication, if the first bit is set to 1
}*/

wholeAnswer += std::string(buffer);
}
else
{
noDataTime++;
usleep(100000);
}
}

if(!wholeAnswer.empty())
{
cerr << "----------- Answer -----------" << endl;
cerr << "Size = " << wholeAnswer.size() << endl;
cerr << wholeAnswer << endl;
}

return wholeAnswer;

How to open, read, and write from serial port in C?

I wrote this a long time ago (from years 1985-1992, with just a few tweaks since then), and just copy and paste the bits needed into each project.

You must call cfmakeraw on a tty obtained from tcgetattr. You cannot zero-out a struct termios, configure it, and then set the tty with tcsetattr. If you use the zero-out method, then you will experience unexplained intermittent failures, especially on the BSDs and OS X. "Unexplained intermittent failures" include hanging in read(3).

#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

int
set_interface_attribs (int fd, int speed, int parity)
{
struct termios tty;
if (tcgetattr (fd, &tty) != 0)
{
error_message ("error %d from tcgetattr", errno);
return -1;
}

cfsetospeed (&tty, speed);
cfsetispeed (&tty, speed);

tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
tty.c_iflag &= ~IGNBRK; // disable break processing
tty.c_lflag = 0; // no signaling chars, no echo,
// no canonical processing
tty.c_oflag = 0; // no remapping, no delays
tty.c_cc[VMIN] = 0; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout

tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag |= parity;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;

if (tcsetattr (fd, TCSANOW, &tty) != 0)
{
error_message ("error %d from tcsetattr", errno);
return -1;
}
return 0;
}

void
set_blocking (int fd, int should_block)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
error_message ("error %d from tggetattr", errno);
return;
}

tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout

if (tcsetattr (fd, TCSANOW, &tty) != 0)
error_message ("error %d setting term attributes", errno);
}

...
char *portname = "/dev/ttyUSB1"
...
int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0)
{
error_message ("error %d opening %s: %s", errno, portname, strerror (errno));
return;
}

set_interface_attribs (fd, B115200, 0); // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (fd, 0); // set no blocking

write (fd, "hello!\n", 7); // send 7 character greeting

usleep ((7 + 25) * 100); // sleep enough to transmit the 7 plus
// receive 25: approx 100 uS per char transmit
char buf [100];
int n = read (fd, buf, sizeof buf); // read up to 100 characters if ready to read

The values for speed are B115200, B230400, B9600, B19200, B38400, B57600, B1200, B2400, B4800, etc. The values for parity are 0 (meaning no parity), PARENB|PARODD (enable parity and use odd), PARENB (enable parity and use even), PARENB|PARODD|CMSPAR (mark parity), and PARENB|CMSPAR (space parity).

"Blocking" sets whether a read() on the port waits for the specified number of characters to arrive. Setting no blocking means that a read() returns however many characters are available without waiting for more, up to the buffer limit.


Addendum:

CMSPAR is needed only for choosing mark and space parity, which is uncommon. For most applications, it can be omitted. My header file /usr/include/bits/termios.h enables definition of CMSPAR only if the preprocessor symbol __USE_MISC is defined. That definition occurs (in features.h) with

#if defined _BSD_SOURCE || defined _SVID_SOURCE
#define __USE_MISC 1
#endif

The introductory comments of <features.h> says:

/* These are defined by the user (or the compiler)
to specify the desired environment:

...
_BSD_SOURCE ISO C, POSIX, and 4.3BSD things.
_SVID_SOURCE ISO C, POSIX, and SVID things.
...
*/

Extra Characters Reading from Serial Port Using libserial on Linux

Zack & keshlam: Thank you for the help. Zack's suggestion to write a NULL to the end of the string resolved the problem, but I actually stumbled across a simpler method while working with another string (trying to grab a substring of the output).

Rather than define the C string like this:

char next_char[100];

I did it like this:

char next_char[100] = "";

It seems that assigning a value to the char before reading from the serial port properly null terminated it. I can now remove the memset command from my code and it now works fine:

root@dc5000:~# ./serialtest
Sending Command:
Result:
V
ADD111C

Thanks for the help!



Related Topics



Leave a reply



Submit