Concurrency thread read write different UART linux C Programming - c

I try to read and write data by two different UART.
I create two thread to comunicate with theese UARTs. Every thread use diffent UART
Some Time, when I write data to UART, write function return with all byte whiuout error, but elettrical port not send all byte.
If I comment second thread, I haven't this error.
Why? Somebody know the problem?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <stdint.h>
#include "log4c.h"
#include <pthread.h>
pthread_t threadId[2]; /*!< Array di pthread_t. In presenza di micro, vengono avviati 2 thread, uno per ogni micro per la generazione del crc*/
/** \struct parameters
* \brief Struttura contenente i parametri scambiati con il thread che calcola il crc
*/
struct parameters {
uint8_t* Buffer; /*!< Puntatore al buffer contenente i dati di cui calcolare il crc */
uint32_t length; /*!< Lunghezza in byte del buffer contenente i dati */
uint32_t crc; /*!< Contiene il valore del crc calcolato dal micro */
uint8_t microNumber; /*!< Valore numerico che identifica il micro: 1->micro1, 2->micro2 */
};
int fdMicroFirst; /*!<Memorizza l'identificativo numerico del microcontrollore 1 */
int fdMicroSecond; /*!<Memorizza l'identificativo numerico del microcontrollore 2 */
/*
*
*/
int setInterfaceAttribs(int speed, int parity, int fd) {
struct termios tty;
if (tcgetattr(fd, &tty) != 0) {
printf("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_iflag &= ~(IXON | IXOFF | IXANY | INLCR | ICRNL); // shut off xon/xoff ctrl
//INLCR | ICRNL in questo modo viene disabilitata la conversione da Carriege return (CR) a new line(NL) e viceversa.
//Se non vengono disabilitate queste due configurazioni 0x0d viene letto come 0x0a
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) {
printf("error %d from tcsetattr", errno);
return -1;
}
return 0;
}
void setBlocking(int should_block, int fd) {
struct termios tty;
memset(&tty, 0, sizeof tty);
if (tcgetattr(fd, &tty) != 0) {
printf("error %d from tggetattr", errno);
return;
}
tty.c_cc[VMIN] = should_block ? 4 : 0;
tty.c_cc[VTIME] = 2.5; // 0.5 seconds read timeout era impostato a 5
if (tcsetattr(fd, TCSANOW, &tty) != 0)
printf("error %d setting term attributes", errno);
}
int openSerialCommunication(char *portname, int speed, int microNumber) {
int retVal = -1;
int fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
printf("error %d opening %s: %s", errno, portname, strerror(errno));
return -1;
}
if (speed == 115200) {
speed = B115200;
} else if (speed == 460800) {
speed = B460800;
} else if (speed == 921600) {
speed = B921600;
}
if (microNumber == 1) {
fdMicroFirst = fd;
//retVal = setInterfaceAttribs(B115200, 0, fdMicroFirst); // set speed to 115,200 bps, 8n1 (no parity)
retVal = setInterfaceAttribs(speed, 0, fdMicroFirst); // set speed to 115,200 bps, 8n1 (no parity)
//retVal = setInterfaceAttribs(B460800, 0, fdMicroFirst); // set speed to 115,200 bps, 8n1 (no parity)
setBlocking(0, fdMicroFirst); // set no blocking
if (tcflush(fdMicroFirst, TCOFLUSH) != 0) {
printf("Errore flush Output Buffer Micro %d\n", microNumber);
}
} else if (microNumber == 2) {
fdMicroSecond = fd;
//retVal = setInterfaceAttribs(B115200, 0, fdMicroSecond); // set speed to 115,200 bps, 8n1 (no parity)
retVal = setInterfaceAttribs(speed, 0, fdMicroSecond); // set speed to 115,200 bps, 8n1 (no parity)
setBlocking(0, fdMicroSecond); // set no blocking
if (tcflush(fdMicroSecond, TCOFLUSH) != 0) {
printf("Errore flush Output Buffer Micro %d\n", microNumber);
}
}
return retVal;
}
void closeSerialCommunication() {
if (fdMicroFirst != -1) {
close(fdMicroFirst);
}
if (fdMicroSecond != -1) {
close(fdMicroSecond);
}
}
uint32_t calculateCrc(uint8_t* buffer, uint16_t bufferLengthCrc, int microNumber) {
uint8_t write_buffer[1100];
uint32_t crc = 0;
int fdMicro = -1;
uint8_t readBuffer [4] = {0x00, 0x00, 0x00, 0x00};
int numB = 0;
int n = 0;
int nTemp;
uint8_t crcBuffer [4] = {0x00, 0x00, 0x00, 0x00};
int counter = 0;
struct timespec startTime, stopTime;
uint16_t dimBufferTosend = 0;
uint16_t numnByteSent = 0;
if (microNumber == 1) {
fdMicro = fdMicroFirst;
} else if (microNumber == 2) {
fdMicro = fdMicroSecond;
}
//I primi due byte che vengono inviati alla seriale rappresentano il numero di byte
//che costituiscono il dato inviato
if (bufferLengthCrc <= 0xFFFF) {
write_buffer[0] = (bufferLengthCrc & 0xFF00) >> 8;
write_buffer[1] = (bufferLengthCrc & 0x00FF);
//printf("UART Write Buffer: %x %x\n",write_buffer[0],write_buffer[1]);
for (int i = 0; i < bufferLengthCrc; i++) {
write_buffer[i + 2] = buffer[i];
//printf("Write Buffer: %x ", write_buffer[i + 2]);
/*
printf("Write Buffer: %x \n",write_buffer[i + 2]);
*/
}
printf("Flushing Micro %d with fd: %d\n", microNumber, fdMicro);
if (tcflush(fdMicro, TCOFLUSH) != 0) {
printf("Errore flush Output Buffer Micro %d\n", microNumber);
}
dimBufferTosend = bufferLengthCrc + 2;
numnByteSent = 0;
numB = 1;
while (numnByteSent < dimBufferTosend && numB > 0) {
numB = write(fdMicro, write_buffer + numnByteSent, dimBufferTosend - numnByteSent);
numnByteSent = numnByteSent + numB;
printf("Byte scritti su Micro %d:%d\n", microNumber, numnByteSent);
printf( "Byte rimanenti da scrivere su Micro %d:%d\n", microNumber, dimBufferTosend - numnByteSent);
}
if (numnByteSent >= dimBufferTosend) {
//printf("UART Buffer: \n");
//clock_gettime(CLOCK_REALTIME, &startTime);
while (n < 4) {
nTemp = read(fdMicro, readBuffer, sizeof readBuffer); // read up to 100 characters if ready to read
//printf("ntemp:%d ",nTemp);
if (nTemp != 0 && nTemp != -1) {
int j = 0;
for (int k = n; k < n + nTemp; k++) {
crcBuffer[k] = readBuffer[j];
printf(" 0x%x 0x%x ", crcBuffer[k], readBuffer[j]);
j++;
}
printf("UART Buffer Response Micro %d: \n", microNumber);
n = n + nTemp;
} else {
if (counter == 3) {
n = 4;
for (int i = 0; i < bufferLengthCrc + 2; i++) {
//printf("Write Buffer: %x ", write_buffer[i]);
}
printf("CRC Ricevuto: 0x%x 0x%x 0x%x 0x%x\n", crcBuffer[0], crcBuffer[1], crcBuffer[2], crcBuffer[3]);
printf( "Risposta CRC da MICRO %d non pervenuta\n", microNumber);
while (1) {
usleep(1000);
}
} else {
counter = counter + 1;
}
}
}
}
printf("CRC Ricevuto: 0x%x 0x%x 0x%x 0x%x\n", crcBuffer[0], crcBuffer[1], crcBuffer[2], crcBuffer[3]);
crc = (crcBuffer[0] << 24)+(crcBuffer[1] << 16)+(crcBuffer[2] << 8)+(crcBuffer[3]);
}
return crc;
}
///Thread calcolo CRC
void *
crcCalcThread(void *_args) {
printf("crccalcthread\n");
/* Cast the arguments to the usable struct type */
struct parameters *args = (struct parameters *) _args;
/* Place the result into the struct itself (on the heap) */
args->crc = calculateCrc(args->Buffer, args->length, args->microNumber);
printf("after crccalcthread\n");
return 0;
//pthread_exit(NULL);
}
int main(int argc, char** argv) {
uint8_t Buffer[1100];
uint32_t crc32pol1;
uint32_t crc32pol2;
//int retValInitMutex=pthread_mutex_init(&(lockWrite), NULL);
openSerialCommunication("/dev/ttySTM4",115200,1);
openSerialCommunication("/dev/ttySTM3",115200,2);
while(1)
{
for (int i = 0; i < 1024; i++) {
Buffer[i] = 0x03;
//printf("Write Buffer: %x ", write_buffer[i + 2]);
}
struct parameters *argsFirstMicro = calloc(sizeof (struct parameters), 1);
argsFirstMicro->Buffer = Buffer;
argsFirstMicro->length = 1024;
argsFirstMicro->microNumber = 1;
int err = pthread_create(&(threadId[0]), NULL, &crcCalcThread, argsFirstMicro);
if (err != 0)
printf("can't create thread :[%s]\n", strerror(err));
else
printf("crcCalcThread created successfully!\n");
//MICRO 2
/*crc32pol2 = crc32(Buffer, (packetLength + slPDU_SAFETY_CODE_NUM_BYTE+ paddingNum), CRC32_POLY_CH2_INV);
log4c_category_log(slcat, LOG4C_PRIORITY_DEBUG, "CRC2 NO MICRO:%x\n", crc32pol2);
printf("CRC2 NO MICRO:%x", crc32pol2);*/
struct parameters *argsSecondMicro = calloc(sizeof (struct parameters), 1);
argsSecondMicro->Buffer = Buffer;
argsSecondMicro->length = 1024;
argsSecondMicro->microNumber = 2;
err = pthread_create(&(threadId[1]), NULL, &crcCalcThread, argsSecondMicro);
if (err != 0)
printf( "can't create thread :[%s]\n", strerror(err));
else
printf("crcCalcThread created successfully!\n");
pthread_join(threadId[0], NULL);
pthread_join(threadId[1], NULL);
crc32pol1 = argsFirstMicro->crc;
/*
Clean up the struct instance
*/
free(argsFirstMicro);
argsFirstMicro = NULL;
crc32pol2 = argsSecondMicro->crc;
// Clean up the struct instance
free(argsSecondMicro);
argsSecondMicro = NULL;
usleep(4);
}
return (EXIT_SUCCESS);
}

Related

VTIME and VMIN don't seem to change the blocking behaviour of the posix read() call

I am trying to understand how VMIN and VTIME works to control the blocking behaviour of the read() posix call.
In my example, I have set VTIME to 10 (and have tried other combinations too) which should block the read for 1 second until it's unblocked, yes? That's my understanding but that doesn't seem to be the case.
I opened minimum on my host, and I see read() would only unblock as soon as I hit enter in minicom as opposed to waiting for 1 second to unblock.
Is my understanding incorrect? If not so, what could be wrong?
int Serial_Open(char *port)
{
int serial_port = open(port, O_RDWR);
struct termios tty;
// Read in existing settings, and handle any error
if(tcgetattr(serial_port, &tty) != 0)
{
printf("Error from tcgetattr: %s\n", strerror(errno));
}
tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common)
tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common)
tty.c_cflag &= ~CSIZE; // Clear all bits that set the data size
tty.c_cflag |= CS8; // 8 bits per byte (most common)
tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common)
tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)
tty.c_lflag &= ~ICANON;
tty.c_lflag &= ~ECHO; // Disable echo
tty.c_lflag &= ~ECHOE; // Disable erasure
tty.c_lflag &= ~ECHONL; // Disable new-line echo
tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes
tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
tty.c_cc[VTIME] = 10; // Wait for up to 1s, returning as soon as any data is received.
tty.c_cc[VMIN] = 0;
cfsetspeed(&tty, B115200);
if (tcsetattr(serial_port, TCSANOW, &tty) != 0)
{
printf("Error tcsetattr %s\n", strerror(errno));
}
return serial_port;
}
int main(void)
{
char buffer[100];
int ret;
int fd = Serial_Open("/dev/ttyUSB4");
while(1)
{
ret = read(fd, buffer, sizeof(buffer));
if (ret <= 0)
{
printf ("No data or error\n");
}
else
{
printf ("Rxd data: %s\n", buffer);
}
}
return 1;
}
I cannot duplicate the claimed behaviour, using the following example program:
// SPDX-License-Identifier: CC0-1.0
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
static int tty_fd = -1;
static struct termios tty_old;
static void tty_close(void)
{
if (tty_fd == -1)
return;
tcsetattr(tty_fd, TCSAFLUSH, &tty_old);
if (tty_fd != STDIN_FILENO &&
tty_fd != STDOUT_FILENO &&
tty_fd != STDERR_FILENO)
close(tty_fd);
tty_fd = -1;
}
static int tty_open(const char *ttypath)
{
struct termios tty_new;
int fd;
if (tty_fd != -1)
tty_close();
if (!ttypath || !*ttypath)
return errno = ENOENT;
do {
fd = open(ttypath, O_RDWR | O_NOCTTY | O_CLOEXEC);
} while (fd == -1 && errno == EINTR);
if (fd == -1)
return errno;
if (!isatty(fd)) {
close(fd);
return errno = ENOTTY;
}
if (tcgetattr(fd, &tty_old) == -1) {
const int saved_errno = errno;
close(fd);
return errno = saved_errno;
}
tty_new = tty_old;
/* No input processing. No input flow control. Ignore parity. Break reads as '\0'. */
tty_new.c_iflag &= ~(IGNBRK | BRKINT | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IUCLC | IXON | IXANY | IXOFF);
tty_new.c_iflag |= IGNPAR;
/* No output processing. */
// tty_new.c_oflag &= ~(OPOST | OLCUC | ONLCR | OCRNL | ONOCR | ONLRET);
tty_new.c_oflag &= ~OPOST;
/* 8 data bits, No parity, 1 stop bit, no hardware flow control, ignore modem control lines. */
tty_new.c_cflag &= ~(CSIZE | PARENB | CSTOPB | CRTSCTS);
tty_new.c_cflag |= CS8 | CREAD | CLOCAL;
/* Raw mode, no signals, no echo. */
tty_new.c_lflag &= ~(ICANON | ISIG | ECHO | IEXTEN);
/* VMIN=0, VTIME=10 */
tty_new.c_cc[VMIN] = 0;
tty_new.c_cc[VTIME] = 10;
if (tcsetattr(fd, TCSANOW, &tty_new) == -1) {
const int saved_errno = errno;
close(fd);
return errno = saved_errno;
}
/* Some of the above settings may not have been applied.
We could check, but really, we don't care that much. */
tty_fd = fd;
return 0;
}
#ifndef TTY_BUFSIZ
#define TTY_BUFSIZ 128
#endif
static unsigned char tty_buf[TTY_BUFSIZ];
static volatile unsigned char *tty_head = tty_buf;
static volatile unsigned char *tty_tail = tty_buf;
#define TTY_NONE -1
#define TTY_EOF -2
#define TTY_ERROR -3
static int tty_getc_read(void)
{
tty_head = tty_tail = tty_buf;
if (tty_fd == -1)
return TTY_EOF;
ssize_t n = read(tty_fd, tty_buf, sizeof tty_buf);
if (n > 0) {
tty_tail = tty_buf + n;
return *(tty_head++);
} else
if (n == 0) {
return TTY_NONE;
} else
if (n != -1 || (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)) {
return TTY_ERROR;
} else {
return TTY_NONE;
}
}
static inline int tty_getc(void)
{
if (tty_tail > tty_head)
return *(tty_head++);
else
return tty_getc_read();
}
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
if (!done)
done = (signum > 0) ? signum : -1;
}
static int install_done(int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
int main(int argc, char *argv[])
{
if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *cmd = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", cmd);
fprintf(stderr, " %s TTY-DEVICE\n", cmd);
fprintf(stderr, "\n");
fprintf(stderr, "This reads in raw mode from the TTY device.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
if (install_done(SIGINT) || install_done(SIGTERM) ||
install_done(SIGHUP) || install_done(SIGQUIT)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_SUCCESS;
}
if (tty_open(argv[1])) {
fprintf(stderr, "%s: Cannot open TTY: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
printf("Press CTRL+C to exit.\r\n");
fflush(stdout);
int last_none = 0;
while (!done) {
int ch = tty_getc();
if (ch == TTY_NONE) {
printf(".");
fflush(stdout);
last_none = 1;
continue;
} else
if (last_none) {
printf("\r\n");
fflush(stdout);
last_none = 0;
}
if (ch == TTY_EOF) {
printf("End-of-input received.\r\n");
fflush(stdout);
break;
} else
if (ch == TTY_ERROR) {
printf("Read error occurred.\r\n");
fflush(stdout);
break;
} else
if (ch == 3) {
printf("Received 0x03 = \\003, assuming Ctrl+C. Exiting.\r\n");
fflush(stdout);
break;
} else {
if (ch >= 32 && ch <= 126)
printf("Received 0x%02x = \\%03o = %3d = '%c'\r\n", (unsigned int)ch, (unsigned int)ch, ch, ch);
else
printf("Received 0x%02x = \\%03o = %3d\r\n", (unsigned int)ch, (unsigned int)ch, ch);
fflush(stdout);
}
}
if (last_none) {
printf("\r\n");
fflush(stdout);
}
tty_close();
return EXIT_SUCCESS;
}
Specify the serial or terminal or pseudoterminal device path on the command line; use $(tty) for the current terminal/pseudoterminal.
Comparing OP's, Serial_Open() and my tty_open(), I do believe the terminal settings are essentially the same (and moreover, any differences there are do not explain the difference in behaviour).
You can compile the above example (example.c) as e.g. gcc -Wall -O2 example.c -o example and then run it via ./example $(tty) to read input from the same terminal window.
This leads me to believe the problem is in the other end: that whatever OP uses to generate the data read by this end, is line-buffered.

How can I use a loopback device (or something similar) with epoll?

I am writing a small library module which accesses the UART and uses epoll to react on incoming data. It works fine with the tty device, however, I want to write test cases for this module. When I open a loop device (/dev/loop*) instead of a tty, epoll_ctl fails with EPERM. The Manual states:
EPERM: fd does not support epoll.
So the question is: Is there any kind of loopback device which could be used with epoll?
Here's a gist with my example code: https://gist.github.com/0815fox/9ce9f19648ce2dc9b23b37dbbb9adab4
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/epoll.h>
#include <linux/loop.h>
#include <sys/ioctl.h>
#include <pthread.h>
static int getFreeLoopDevice(const char * backingfileName) {
int loopctlfd, loopfd, backingfile;
long devnr;
char loopname[4096];
loopctlfd = open("/dev/loop-control", O_RDWR);
devnr = ioctl(loopctlfd, LOOP_CTL_GET_FREE);
close(loopctlfd);
return devnr;
sprintf(loopname, "/dev/loop%ld", devnr);
loopfd = open(loopname, O_RDWR);
backingfile = open(backingfileName, O_RDWR);
ioctl(loopfd, LOOP_SET_FD, backingfile);
return loopfd;
}
static int set_interface_attribs (int fd, int speed, int parity) {
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0) {
fprintf (stderr, "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) {
fprintf (stderr, "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)
{
fprintf (stderr, "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)
fprintf (stderr, "error %d setting term attributes", errno);
}
static void * uart_thread(void * handle) {
const int uartHandle = *(int*)handle;
char buffer[100];
// the epoll stuff:
const int ePollFileDescriptor = epoll_create(1);
struct epoll_event ePollEvent;
ePollEvent.events = EPOLLIN;
ePollEvent.data.fd = uartHandle;
const int epoll_ctl_result = epoll_ctl(ePollFileDescriptor, EPOLL_CTL_ADD, uartHandle, &ePollEvent);
if (epoll_ctl_result) perror("epoll_ctl\n");
for (;;) {
static struct epoll_event events[1] __attribute__((aligned(8)));
const int eventCount = epoll_wait(ePollFileDescriptor, events, 1, -1);
if (eventCount < 0) {
perror("epoll_wait\n");
} else {
const int bytesRead = read(uartHandle, buffer, 99);
if (bytesRead >= 0) buffer[bytesRead] = 0;
else buffer[0] = 0;
fprintf(stderr, "Buffer: %s\nBytesread:%d\n", buffer, bytesRead);
}
}
}
int main (void) {
// working:
// char *portname = "/dev/ttyUSB0";
// const int uartHandle = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
// not working:
const int uartHandle = getFreeLoopDevice("/tmp/my-loop-back");
if (uartHandle < 0) {
fprintf (stderr, "error %d opening: %s\n", errno, strerror (errno));
return errno;
}
set_interface_attribs (uartHandle, B115200, 0); // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (uartHandle, 0); // set no blocking
// the pthread stuff:
pthread_t threadHandle;
const int pthread_create_result = pthread_create( &threadHandle, NULL, uart_thread, (void *) &uartHandle);
while (1) {
fprintf(stderr, "writing to uart\n");
write(uartHandle, "hello!\n", 7);
sleep(10);
}
}

Code produces incorrect outcome

The following code compiles in (Geany) and Runs.
--->
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
int set_interface_attribs(int fd, int speed)
{
struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return -1;
}
cfsetospeed(&tty, (speed_t)speed);
cfsetispeed(&tty, (speed_t)speed);
tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
/* setup for non-canonical mode */
// tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
// tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tty.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 1;
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* sets Raw mode */
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}
void set_mincount(int fd, int mcount)
{
struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
printf("Error tcgetattr: %s\n", strerror(errno));
return;
}
tty.c_cc[VMIN] = mcount ? 1 : 0;
tty.c_cc[VTIME] = 5; /* half second timer */
if (tcsetattr(fd, TCSANOW, &tty) < 0)
printf("Error tcsetattr: %s\n", strerror(errno));
}
int main()
{
char *portname = "/dev/ttyS0";
int fd;
int i;
int wlen;
fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
printf("Error opening %s: %s\n", portname, strerror(errno));
return -1;
}
/*baudrate 2400, 8 bits, no parity, 1 stop bit */
set_interface_attribs(fd, B2400);
//set_mincount(fd, 0); /* set to pure timed read */
unsigned char str[] = {0x56, 0x45, 0x52, 0x0D};
write(fd,str,strlen(str));
/* simple output */
/*
wlen = write(fd, "*\n", 2);
if (wlen != 2) {
printf("Error from write: %d, %d\n", wlen, errno);
}
wlen = write(fd, "1\n", 2);
if (wlen != 2) {
printf("Error from write: %d, %d\n", wlen, errno);
}
wlen = write(fd, "8\n", 2);
if (wlen != 2) {
printf("Error from write: %d, %d\n", wlen, errno);
}
*/
tcdrain(fd); /* delay for output */
/* simple noncanonical input */
do {
unsigned char buf[80];
int rdlen;
rdlen = read(fd, buf, sizeof(buf) - 1);
if (rdlen > 0) {
#ifdef DISPLAY_STRING
buf[rdlen] = 0;
printf("Read %d: \"%s\"\n", rdlen, buf);
#else /* display hex */
unsigned char *p;
printf("Read %d:", rdlen);
for (p = buf; rdlen-- > 0; p++)
printf(" 0x%x", *p);
printf("\n");
#endif
} else if (rdlen < 0) {
printf("Error from read: %d: %s\n", rdlen, strerror(errno));
}
/* repeat read to get full message */
} while (i++ < 10);
}
<---
I need the following output at the 'Serial Port' to invoke a certain outcome:
From the keyboard "* 1 8 cr" works perfectly under Windows (Turbo Basic) but not under my Code running under Linux (compiled by GCC):
-->
For " * 1 8 cr " (star one eight enter(cr)) the four "binary" "chars" required are:
*= binary 00101010
1= binary 00110001
8= binary 00111000
cr= binary 00001101
<--
Unfortunately my code is not creating the correct output (Serial settings are believed to be set to (2400 N,8,1 with start and end bit) using 'termios' structure.
I have attached a old OscilloScope and there are bits being sent but I do not know how to capture them to check their correctness.
What is wrong with the Code?

Serial communication on Linux with flow control enabled - bad behaviour

I wrote common functions in order to manage serial ports, based on the following structure:
typedef struct
{
int PHandle;
unsigned int Port;
unsigned int BaudRate;
unsigned char Parity;
unsigned char FlowControl;
char Device[MAX_SIZE];
} Tst_SPort;
I am calling these functions (see below) in another file in order to test an RS232 serial port. The flow control needs to be enabled.
int iInit(Tst_SPort *port, const char *device, int baudRate, unsigned char parity, unsigned char flowControl)
{
strncpy(port->Device, device, MAX_SIZE);
port->PHandle = iOpen(port);
port->Port = -1;
port->BaudRate = baudRate;
port->Parity = parity;
port->FlowControl = flowControl;
if(port->PHandle > 0)
{
setuart(port, port->BaudRate);
}
return port->PHandle;
}
int iOpen(Tst_SPort *port)
{
port->PHandle = open(port->Device, O_RDWR | O_NOCTTY);
if(port->PHandle < 0)
{
perror("open:");
return (-1);
}
return (port->PHandle);
}
void setuart(Tst_SPort *port, int baudRate)
{
struct termios opt, optCmp;
struct serial_struct info;
if(port->PHandle > 0)
{
bzero(&opt, sizeof(opt));
bzero(&optCmp, sizeof(optCmp));
if(ioctl(port->PHandle, TIOCGSERIAL, &info) == 0)
port->Port = info.port;
fcntl(port->PHandle, F_SETFL, O_NONBLOCK);
if (tcgetattr(port->PHandle, &opt) < 0)
perror("tcgetattr Get:");
if(baudRate > 0)
{
cfsetospeed (&opt, baudRate);
cfsetispeed (&opt, baudRate);
}
opt.c_iflag = IGNPAR;
opt.c_oflag &= ~OPOST
opt.c_oflag &= ~ONLCR;
opt.c_lflag = 0;
opt.c_cflag |= (CLOCAL | CREAD);
opt.c_cflag &= ~(PARENB | PARODD);
opt.c_cflag |= port->Parity;
opt.c_cflag &= ~CSTOPB;
opt.c_cflag &= ~CSIZE;
opt.c_cflag |= CS8;
if(!port->FlowControl)
opt.c_cflag &= ~CRTSCTS;
else
opt.c_cflag |= CRTSCTS;
opt.c_cc[VMIN] = 0;
opt.c_cc[VTIME] = 50;
if(tcsetattr(opt->PHandle, TCSANOW, &opt) < 0)
perror("tcgetattr Update :");
if (tcgetattr(opt->PHandle, &optCmp) < 0)
perror("tcgetattr Read:");
/* Compare */
if (memcmp((void *)&opt, (void *)&optCmp, sizeof(opt)) != 0)
printf("Conf failed");
tcflush(port->PHandle, TCIFLUSH);
}
}
int iRead(Tst_SPort *port, char *buffer, unsigned long buffLength)
{
struct timeval tv;
fd_set recv;
int s32Read = 0;
int s32Offset = 0;
int s32SRes = 0;
tv.tv_sec = 0;
tv.tv_usec = 100000; /* 100ms */
if ((port) && (port->PHandle > 0))
{
while (s32Offset < buffLength)
{
FD_ZERO(&recv);
FD_SET(port->PHandle, &recv);
s32SRes = select(port->PHandle + 1, &recv, NULL, NULL, &tv);
if ((s32SRes == -1) && (errno == EINTR))
{
continue;
}
else if(s32SRes > 0)
{
if (FD_ISSET(port->PHandle, &recv))
{
s32Read = read(port->PHandle, buffer + s32Offset, buffLength - s32Offset);
if(s32Read > 0)
{
tv.tv_sec = 0;
tv.tv_usec = 5000;
s32Offset += s32Read;
continue;
}
}
}
break;
}
}
return s32Offset;
}
int iClose(Tst_SPort *port)
{
return (close(port->Phandle));
}
In order to validate the implementation, the pinouts Tx and Rx have been connected together, idem for CTS and RTS. Everything works fine, the message sent can be read correctly. In addition, when Tx is disconnected from the Rx nothing is reading as expected.
But when the CTS is unplugged from the RTS the test blocks after the port closing step (~20 seconds).
However, if the function setuart() is called with flowControl == 0, the test does not block and returns the exepected error code without delay.
I probably understood something wrongly especially in port configuration. Is it the good way to do ?
The problem you are facing is a correct behaviour.
Leaving CTS unconnected with flow control enabled, means DTE (AKA PC) cannot send data to DCE (slave device).
When you try to write to the UART output buffer it, probably, is full and application temporarily stops running and waits until some buffer space becomes available.

Arduino serial works fine with Debian but hangs with Raspbian

I'm working on a personal home automation project.
On the server side, I have an Arduino Pro Mini with:
a 433 MHz TX module on pin 2
a 433 MHz RX module on pin 3
a DHT22 probe on pin 4 (with 10k pull-up)
a DHT22 probe on pin 5 (with 10k pull-up)
I have two absolutely identical of these modules; one will be the radio relay (and DHT "server") and the other a secondary DHT "server".
When it is linked to my laptop (Debian Wheezy) through an FTDI cable, it can read both local probes and switch my wall plugs on/off thanks to a C program I wrote. I'd like to use it from a Raspberry Pi. But on the Raspberry Pi (with the same FTDI cable on USB), it executes the first command I send and then hangs my terminal, forcing me to use CTRL+C.
Here is the sketch on the Arduino side (header) :
/**
* probe.h
*
* #author David Mézière <...>
*/
/**
* DHT probe result
*
*/
struct Probe {
float temperature;
float humidity;
};
Main file :
/**
* probe.ino
*
* #author David Mézière <...>
*/
#include "probe.h"
/**
* Uses DHT sensor library, from Adafruit.
* #see https://github.com/adafruit/DHT-sensor-library
*/
#include <DHT.h>
/**
* Uses RC Switch library, from sui77.
* #see https://github.com/sui77/rc-switch
*/
#include <RCSwitch.h>
// Pinout definitions
#define TX 2 // 433 MHz transmitter pin number
#define RX 3 // 433 MHz receiver pin number
#define PROBE1 4 // First DHT22 probe pin number
#define PROBE2 5 // Second DHT22 probe pin number
#define LED 13 // On-board status LED pin number
RCSwitch radio = RCSwitch();
// DHT probes definition
DHT dht1(PROBE1, DHT22);
DHT dht2(PROBE2, DHT22);
// Incomming command buffer
byte cmd[9];
/**
* Setup
*
* #return void
*/
void setup()
{
pinMode(LED, OUTPUT);
digitalWrite(LED, LOW);
Serial.begin(9600);
Serial.setTimeout(1000); // doesn't fix the problem
// Attach receiver to interrupt 1, meaning pin 3
radio.enableReceive(1);
radio.enableTransmit(TX);
dht1.begin();
dht2.begin();
// Debug: Internal LED will blink 3 times rapidly to show when a reboot occurs.
for (int i = 0; i < 3; i++) {
digitalWrite(LED, HIGH);
delay(250);
digitalWrite(LED, LOW);
delay(250);
}
}
/**
* Loop
*
* #return void
*/
void loop()
{
if (Serial.available() == 9 && readCommand()) {
// Lights-up internal LED to show when a command has been executed
digitalWrite(LED, HIGH);
delay(1000);
digitalWrite(LED, LOW);
}
}
/**
* Query probe
*
* Query provided [dht] probe until [retry] times for both temperature and humidity.
*
* #param DHT dht Pointer to DHT object
* #param int retry Number of tries
* #return Probe Probe result (float temperature in °C, float humidity in %)
*/
Probe queryProbe(DHT* dht, int retry)
{
Probe probe;
// Query DHT22 probe for temperature, a maximum of [retry] times.
for (int t = 0; t < retry; t++) {
probe.temperature = dht->readTemperature(false);
if (!isnan(probe.temperature)) {
break;
}
delay(50);
}
// Query DHT22 probe for humidity, a maximum of [retry] times.
for (int h = 0; h < retry; h++) {
probe.humidity = dht->readHumidity();
if (!isnan(probe.humidity)) {
break;
}
delay(50);
}
return probe;
}
/**
* Read command
*
* If serial buffer contains 2 bytes, move them to a local buffer and return true. else return false.
*
* #return boolean
*/
boolean readCommand()
{
// Reads the current buffer
Serial.readBytes(cmd, 9);
// Calculates the check sum of payload
int sum = cmd[2] ^ cmd[3] ^ cmd[4] ^ cmd[5] ^ cmd[6] ^ cmd[7];
// Checking header and checksum a header of 0xBA 0xB1 means DHT query
if (cmd[0] == 0xBA && cmd[1] == 0xB1 && cmd[8] == sum) {
unsigned int module = cmd[2];
unsigned int probe = (cmd[4] << 24) + (cmd[5] << 16) + (cmd[6] << 8) + cmd[7];
Probe result;
switch (module) {
case 1:
// Selects the right probe
if (probe == 1) {
result = queryProbe(&dht1, 3);
} else if (probe == 2) {
result = queryProbe(&dht2, 3);
}
// Send status repport to client
Serial.print("1;");
Serial.print(module);
Serial.print(";");
Serial.print(probe);
Serial.print(";");
Serial.print(result.temperature);
Serial.print(";");
Serial.println(result.humidity);
Serial.flush(); // Doesn't fix the problem
break;
}
return true;
// A header of 0xBA 0xB2 means rf wall plugs query
} else if (cmd[0] == 0xBA && cmd[1] == 0xB2 && cmd[8] == sum) {
unsigned int proto = cmd[2];
unsigned int length = cmd[3];
unsigned int value = (cmd[4] << 24) + (cmd[5] << 16) + (cmd[6] << 8) + cmd[7];
radio.send(value, length);
// Send status repport to client
Serial.print("2;");
Serial.print(proto);
Serial.print(";");
Serial.print(length);
Serial.print(";");
Serial.print(value);
Serial.print(";");
Serial.println("OK");
Serial.flush(); // Doesn't fix the problem
return true;
} else {
Serial.println("KO");
Serial.flush(); // Doesn't fix the problem
return false;
}
}
And on the client side :
/**
* probe.c
*
* #author David Mézière <...>
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <getopt.h>
const char* device;
static int module = 0; // uint_8 ?
static int probe = 0; // uint_8 ?
const char* proto;
static int length = 0; // uint_8 ?
static int value = 0; // uint_32 ?
static int verbose = 0; // uint_8 ?
void help()
{
printf("usage:\n");
printf("\n");
printf("probe [options] [arguments]\n");
printf("\n");
printf("options:\n");
printf(" -h|--help: Displays this help and exit\n");
printf(" -v|--verbose: Be more verbose\n");
printf("\n");
printf("arguments:\n");
printf(" -d|--device: string Serial device to use (ex: /dev/ttyUSB0)\n");
printf(" -m|--module: integer DHT22 module to query\n");
printf(" -p|--probe: integer DHT22 probe to query\n");
printf(" -r|--proto: string Radio / IR protocol\n");
printf(" -l|--length: integer Radio / IR value length in bits\n");
printf(" -a|--value: integer Radio / IR value\n");
printf("\n");
printf("examples:\n");
printf(" probe --device /dev/ttyUSB0 --module 1 --probe 1 : Will query first DHT22 probe of first module\n");
printf(" probe --proto radio1 --length 12 --value 5393 : Will send value 5393 on 12 bits over the air using protocol 1\n");
printf(" probe --proto ir11 --length 64 --value 3772793023 : Will send value 3772793023 on 64 bits by infra red using protocol 11\n");
}
void parseArgs(int argc, char **argv)
{
int c;
while (1) {
static struct option long_options[] = {
{"device", required_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{"module", required_argument, 0, 'm'},
{"probe", required_argument, 0, 'p'},
{"proto", required_argument, 0, 'r'},
{"length", required_argument, 0, 'l'},
{"value", required_argument, 0, 'a'},
{"verbose", no_argument, 0, 'v'},
{0, 0, 0, 0}
};
/* getopt_long stores the option index here. */
int option_index = 0;
c = getopt_long(argc, argv, "d:hm:p:v", long_options, &option_index);
/* Detect the end of the options. */
if (c == -1) {
break;
}
switch (c) {
case 0:
/* If this option set a flag, do nothing else now. */
if (long_options[option_index].flag != 0) {
break;
}
printf("option %s", long_options[option_index].name);
if (optarg) {
printf (" with arg %s", optarg);
}
printf("\n");
break;
case 'd':
device = optarg;
break;
case 'h':
help();
exit(0);
break;
case 'm':
module = atoi(optarg);
break;
case 'p':
probe = atoi(optarg);
break;
case 'r':
proto = optarg;
break;
case 'l':
length = atoi(optarg);
break;
case 'a':
value = atoi(optarg);
break;
case 'v':
verbose = 1;
break;
case '?':
/* getopt_long already printed an error message. */
break;
default:
abort();
}
}
/* Print any remaining command line arguments (not options). */
if (optind < argc) {
printf("non-option ARGV-elements: ");
while (optind < argc) {
printf("%s ", argv[optind++]);
}
putchar('\n');
}
if (&device[0] == '\0') {
fprintf(stderr, "--device is mandatory\n");
exit(1);
} else if (verbose) {
printf("Device: %s\n", device);
}
if (verbose) {
printf("Querying probe %i of module %i.\n", probe, module);
}
}
void initSerial(int fd)
{
struct termios toptions;
/* get current serial port settings */
tcgetattr(fd, &toptions);
/* set 9600 baud both ways */
cfsetispeed(&toptions, B9600);
cfsetospeed(&toptions, B9600);
/* 8 bits, no parity, no stop bits */
toptions.c_cflag &= ~PARENB;
toptions.c_cflag &= ~CSTOPB;
toptions.c_cflag &= ~CSIZE;
toptions.c_cflag |= CS8;
/* Canonical mode */
toptions.c_lflag |= ICANON;
/* commit the serial port settings */
tcsetattr(fd, TCSANOW, &toptions);
}
int main(int argc, char **argv)
{
// Parses command line arguments
parseArgs(argc, argv);
int fd, n, i;
char buf[64] = "temp text";
/* open serial port */
fd = open(device, O_RDWR | O_NOCTTY);
if (verbose) {
printf("Device %s opened as %i\n", device, fd);
}
/*
* Note: Most Arduinos models will reboot upon connection, and they need
* some time for it. I use a pro/mini that doesn't, so i commented it out.
*/
// usleep(3500000);
// Sets the serial port settings (9600 bps, 8 bits, no parity, no stop bits)
initSerial(fd);
/**
* 72 bits
* | Header | Param 1 | Param 2 | Param 3 | sum |
* | 16 b | 8 b | 8 b | 32 b | 8 b |
* Cas 1 : Requête DHT | 0xba 0xb1 | module | 0x00 | sonde | sum |
* Cas 2 : Requête radio | 0xba 0xb2 | proto | length | value | sum |
* Cas 3 : Requête IR | 0xba 0xb3 | proto | length | value | sum |
*/
unsigned char oBuf[9];
// printf("%s\n", proto);
// printf("%i\n", length);
if (module > 0 && probe > 0) {
if (verbose) {
printf("DHT mode\n");
}
oBuf[0] = 0xBA;
oBuf[1] = 0xB1; // DHT query
oBuf[2] = module;
oBuf[3] = 0x00;
oBuf[4] = (probe >> 24) & 0xFF;
oBuf[5] = (probe >> 16) & 0xFF;
oBuf[6] = (probe >> 8) & 0xFF;
oBuf[7] = probe & 0xFF;
oBuf[8] = oBuf[2];
oBuf[9] = '\n';
// Calculates the XOR sum
for (i = 3; i < 8; i++) {
oBuf[8] ^= oBuf[i];
}
// sprintf(oBuff, "%c%c%c%c%c%c", 0xba, 0xb1, module, 0x00, probe, sum);
} else if (strcmp((const char*)proto, "radio1") == 0 && length > 0) {
if (verbose) {
printf("Radio mode\n");
}
oBuf[0] = 0xBA;
oBuf[1] = 0xB2; // Radio query
oBuf[2] = 0x01; // Protocol 1
oBuf[3] = length;
oBuf[4] = (value >> 24) & 0xFF;
oBuf[5] = (value >> 16) & 0xFF;
oBuf[6] = (value >> 8) & 0xFF;
oBuf[7] = value & 0xFF;
oBuf[8] = oBuf[2];
oBuf[9] = '\n';
// Calculates the XOR sum
for (i = 3; i < 8; i++) {
oBuf[8] ^= oBuf[i];
}
} else {
if (verbose) {
printf("Unknown mode\n");
}
}
/* Send the buffer */
write(fd, oBuf, 9);
/* Receive string from Arduino */
n = read(fd, buf, 64);
/* insert terminating zero in the string */
buf[n] = 0;
if (verbose) {
printf("%i bytes read, buffer contains: %s\n", n, buf);
} else {
printf("%s", buf);
}
return 0;
}
I compile it using just gcc probe.c -o probe.
On Debian, I can use the system as much as I want, it works:
dmeziere#portable2-wlan:~/dev/probe$ gcc probe.c -o probe
dmeziere#portable2-wlan:~/dev/probe$ ./probe --device /dev/ttyUSB0 --module 1 --probe 1
1;1;1;23.60;43.10
dmeziere#portable2-wlan:~/dev/probe$ ./probe --device /dev/ttyUSB0 --module 1 --probe 2
1;1;2;23.60;38.50
dmeziere#portable2-wlan:~/dev/probe$ ./probe --device /dev/ttyUSB0 --proto radio1 --length 24 --value 5396
2;1;24;5396;OK
dmeziere#portable2-wlan:~/dev/probe$ ./probe --device /dev/ttyUSB0 --proto radio1 --length 24 --value 5393
2;1;24;5393;OK
On Raspbian, the first call works, but the second hangs my terminal and I have to do CTRL+C:
dmeziere#raspberrypi:~/probe$ ./probe --device /dev/ttyUSB0 --module 1 --probe 1
1;1;1;23.90;39.00
dmeziere#raspberrypi:~/probe$ ./probe --device /dev/ttyUSB0 --module 1 --probe 2
^C
I found it !
It seems like on Raspbian, the communications were terminated by a NULL character. But not on Debian. And this NULL character was polluting my calculations of incomming buffer length. So i ended up by eventually suppress it on the Arduino side :
boolean readCommand()
{
// Reads the current buffer
Serial.readBytes(cmd, 9);
// If sender sends a null character, remove it.
int test = Serial.peek();
if (test == 0x00) {
test = Serial.read();
}
// ...
}

Resources