I have written a Linux serial port sample in C++ that just uses the Linux API open/read/write/close functions to communicate over a null modem cable to a Windows machine where I am testing with putty.
The code works fine when run at 9600 baud but at faster rates get gibberish received on remote end and read from remote end on Linux and at 57600 or higher no data communication at all.
It must be the code because I can run minicom on Linux end at 115200 baud and communication is as expected.
What do I need to change to work at higher baud rates?
For example running test at 9600 baud, output on Linux side:
angus@angus-Latitude-3550:~/Documents/code$ sudo ./serial_test
Attempting to open /dev/ttyUSB0
open returned 3
Setting timeout 2000 milliseconds
write returned 14
Enter some text on remote side and press any key to read the data
read returned 8
read data:
H i g u y s !
close returned 0
End of test
and Hello World! printed on putty screen on Windows
But if baud rate increased to 19200:
angus@angus-Latitude-3550:~/Documents/code$ sudo ./serial_test
Attempting to open /dev/ttyUSB0
open returned 3
Setting timeout 2000 milliseconds
write returned 14
Enter some text on remote side and press any key to read the data
read returned 8
read data:
� � � � � � � �
close returned 0
End of test
and gibberish printed on putty on Windows
The gibberish data received characters 0xFE, 0xFC, 0xF7 – ie not valid ascii characters.
The Null modem cable is a FTDI Chip, Null Modem Cable USB – USB NMC-2.5M – see link:
https://uk.rs-online.com/web/p/development-tool-accessories/0537420?searchId=bbca5b6e-3586-4f43-8c3c-85c61071c58d&gb=s
On the Windows side I am using the following putty serial port settings:
baudrate 9600
data bits 8
stop bits 1
parity none
flow control none
9600 works – but not higher speeds.
setup is Windows side – Windows 7 64 bit installed on Dell Precision T3600 using putty for serial port test. Linux side is Dell Latitude 3550 running Ubuntu 22.04 LTS.
Code heavily uses this example:
https://blog.mbedded.ninja/programming/operating-systems/linux/linux-serial-ports-using-c-cpp/
The code:
#include <iostream>
#include <string>
#include <cstdint>
// operating system includes
#include <unistd.h> // posix api, includes ssize_t, read, write, close
#include <fcntl.h> // file control operations, eg open
#include <termios.h> // terminal
#include <sys/types.h> // ssize_t
static int set_speed(struct termios tty, int speed)
{
speed_t sp;
switch(speed) {
case 1200: sp = B1200; break;
case 1800: sp = B1800; break;
case 2400: sp = B2400; break;
case 4800: sp = B4800; break;
case 9600: sp = B9600; break;
case 19200: sp = B19200; break;
case 38400: sp = B38400; break;
case 57600: sp = B57600; break;
case 115200: sp = B115200; break;
default:
return -1; // unsupported
}
int baudset = cfsetospeed(&tty, sp);
baudset = cfsetispeed(&tty, sp);
return baudset;
}
static int get_term(int fd, struct termios* ptty)
{
/* Upon successful completion, 0 shall be returned. Otherwise, -1
shall be returned and errno set to indicate the error. */
return tcgetattr(fd, ptty);
}
/* configure tty */
static int set_terminal(int fd, struct termios* ptty, int timeout_ms)
{
// setting CLOCAL disables modem specific signals such as carrier detect
// we are not using a modem, so disable
// CREAD is important - allows us to read data
ptty->c_cflag |= (CLOCAL | CREAD);
ptty->c_cflag &= ~CSIZE; /* clear all size bits and use CS8 as set below */
ptty->c_cflag |= CS8; /* 8-bit characters */
ptty->c_cflag &= ~PARENB; /* no parity bit - most serial comms don't set parity bit - check */
ptty->c_cflag &= ~CSTOPB; /* only need 1 stop bit */
ptty->c_cflag &= ~CRTSCTS; /* no hardware flowcontrol - most common */
// input modes - disable sw flow control and any special handling of specfic bytes received
ptty->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY);
// setup for non-canonical mode. Canonical mode means input is only processed when a
// newline is encountered, but we have custom protocol with no newlines
// disabling some canonical mode options, eg we don't need to echo bytes we write
// also disable signal chars - ISIG
ptty->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
// we do not need any special interpretation of output bytes, eg newline chars
ptty->c_oflag &= ~OPOST;
// same with carriage returns/linefeed chars
ptty->c_oflag &= ~ONLCR;
// we do not want to block if no data available - otherwise will block forever
ptty->c_cc[VMIN] = 0; // VMIN is minimum no. of bytes to wait for
// block until either any amount of data available or timeout occurs
ptty->c_cc[VTIME] = static_cast<uint8_t>(timeout_ms / 100); // VTIME is time in deciseconds (10ths of a second)
if (tcsetattr(fd, TCSANOW, ptty) != 0) {
// Error from tcsetattr- use strerror(errno)
return -1;
}
return 0;
}
int main() {
termios restore_tty;
termios current_tty;
std::cout << "Attempting to open /dev/ttyUSB0n";
int fd = ::open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);
std::cout << "open returned " << fd << std::endl;
int baudrate = 9600; // any higher and get gibberish on other end or no data
int timeout_ms = 2000; // timeout in milliseconds
std::cout << "Setting timeout " << timeout_ms << " millisecondsn";
if (fd != -1) {
if(get_term(fd, ¤t_tty) != 0)
{
// cache port - should we fail if not possible?
restore_tty = current_tty;
}
// set port speed
if(set_speed(current_tty, baudrate) != 0)
{
// error setting tty speed - should we fail?
std::cerr << "error setting port speedn";
}
// set terminal attribs
if(set_terminal(fd, ¤t_tty, timeout_ms) < 0)
{
// error configuring port - should we fail?
std::cerr << "error setting terminal attributesn";
}
// flush serial buffer
tcflush(fd, TCIFLUSH);
} else {
std::cout << "open failed, abortingn";
return 1;
}
const char data[] {"Hello World!n"};
int length = sizeof(data);
ssize_t retcode = write(fd, data, length);
std::cout << "write returned " << retcode << std::endl;
std::cout << "Enter some text on remote side and press any key to read the datan";
getchar();
char readbuffer[20] {};
retcode = read(fd, readbuffer, 20);
std::cout << "read returned " << retcode << std::endl;
if (retcode > 0) {
std::cout << "read data:n";
for (int i = 0; i < retcode; ++i) {
std::cout << readbuffer[i] << ' ';
}
std::cout << std::endl;
}
retcode = close(fd);
std::cout << "close returned " << retcode << std::endl;
std::cout << "End of testn";
}