Short version:
- I had a code A, allowing me to read/write bytes over the serial port (UART), which passed unit testing
- I wrote some code B, to read/write bytes over TCP/IP using boost asio (well, technically, standalone asio), which also passed unit testing
- When I try to put A and B together, A starts receiving messages when none are sent
- What I want to know is why, and what I can do about it
More details about code A:
Code A is mostly C code, using ioctl, fnctl, and unistd.
First I open the serial:
// Flag explanation:
// - O_RDWR
// = read & write
// (O_RDONLY = read-only)
// (O_WRONLY = write-only)
// - O_NOCTTY
// = if the path points to a terminal, that terminal will
// not become the controlling terminal for the process
// - O_NONBLOCK
// = read/write requests are non-blocking, and return failure status instead of waiting
_filestream = ::open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NONBLOCK);
After checking that the serial was successfully opened, I configure the UART
// Get options currently used by the USART
termios options;
tcgetattr(_filestream, &options);
// Set control options
{
// Set baudrate
cfsetispeed(&options, static_cast<speed_t>(baudrate));
cfsetospeed(&options, static_cast<speed_t>(baudrate));
// Enable receiver (those flags must always be set)
// CLOCAL = ignore modem status lines
// CREAD = enable receiver
options.c_cflag |= (CLOCAL | CREAD);
// Set character size
// CSIZE = bitmask for all sizes
// CS8 = specific flag for 8-bit
options.c_cflag &= ~static_cast<unsigned int>(CSIZE);
options.c_cflag |= CS8;
// Disable parity bit
// PARENB = parity enable flag
options.c_cflag &= ~static_cast<unsigned int>(PARENB);
// Set number of stop bits
// CSTOPB = use 2 stop bits insteqd of 1
options.c_cflag &= ~static_cast<unsigned int>(CSTOPB);
}
// Set local options
{
// Make sure to use raw input (pass character exactly as they are received)
// and not canonical input (which is line-oriented)
// ICANON = enable canonical input
// ECHO = enable echoing input characters
// ECHOE = make echo erases some characters
// ISIG = enable some signals
options.c_lflag &= ~static_cast<unsigned int>(ICANON | ECHO | ECHOE | ISIG);
}
// Set input options
{
// Enable software flow control
// IXON = Enable software flow control (outgoing)
// IXOFF = Enable software flow control (incoming)
// IXOFF = Allow any character to start flow again
options.c_iflag |= (IXON | IXOFF | IXANY);
}
// Set output options
{
// Choose raw output
// OPOST = choose postprocess output
options.c_oflag &= ~static_cast<unsigned int>(OPOST);
}
// Set options to use for the USART
// TCSANOW = change attributes immediately
tcsetattr(_filestream, TCSANOW, &options);
To write stuff on the serial, I do this:
int status = write(_filestream, &byte_array, array_size);
and look at the status to check that everything went well.
To know how many bytes are available for reading, I do this:
int nbAvailable = 0;
ioctl(_filestream, FIONREAD, &nbAvailable);
To actually read those bytes, I do this:
uint8_t c;
auto status = ::read(_filestream, &c, 1);
and look at the status to check that everything went well.
When I'm done, I simply close the filestream:
close(_filestream);
More details about code B:
There's not much to say, first I do this:
asio::io_context _ioContext;
asio::ip::tcp::resolver::query query(remoteIP, remotePort);
asio::ip::tcp::resolver resolver (_ioContext);
asio::error_code errCode;
asio::ip::tcp::resolver::results_type _endPoints = resolver.resolve(query, errCode);
if (errCode)
{
std::cerr << "Cannot resolve IP/port for the connection" << std::endl;
exit(0);
}
Then I connect like this
asio::error_code errCode;
asio::connect(_socket, _endPoints, errCode);
if (!errCode)
{
launch_thread();
}
The thread itself is a loop in which bytes are read using _socket.read_some()
then moved to a mutex-protected array, or moved from another mutex-protected array then sent using _socket.write_some()
What happens when I put the two codes together:
The serial-port device is currently unplugged, I'm expecting nbAvailable
to be 0 every time I check how many bytes are available from the serial port, but it isn't the case and many bytes are received when they should not be.
To the best I can tell, this seems to be the result of asio
doing things to the serial I'm using. It's not that far-fetched, since asio
also features serial-port i/o facilities in addition to their TCP/IP facilities, and all the evidence points to it:
- the problems only arise when the TCP/IP communication starts
- commenting the _socket.write_some() calls removes the issue
What I want to know:
Why is asio
interfering with my serial port communication, when I'm only using its TCP/IP facilities? Is there any way to solve this issue?