After three weeks I can't get past this problem. I have the code below running on Ubuntu 18.04.3 which sends a string successfully to another device. When the remote device receives the string ... it sends another back ... but the code below (even with 1 sec set) times out on select().
When I comment out the select() and just do the read() ... fails to see any data as well?
It was working three weeks ago ... but recent code changes broke it ... and I cannot see why. How could a write() on a file descriptor go out the serial port ok ... but a select() and read() using the same file descriptor get nothing back. I have a third device (a PC with putty) so I can see everything on the wire. All three are on an RS-485 bus.
Any other issues with the code would be greatly appreciated!
Thanks!
// main.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <term.h>
#include <signal.h>
#include <sys/time.h>
#include "SER.h"
static
struct sigaction mySigActTerm;
volatile
int terminate = 0;
void terminateHandler(int signum, siginfo_t *info, void *ptr)
{
//------------------------------------------------------------
// set a flag here and get out.
terminate = 1;
}
int main()
{
int rtn;
pthread_t serialThdID;
SER* mySER;
//------------------------------------------------------------
// setup terminate signal
memset(&mySigActTerm, 0, sizeof(mySigActTerm));
mySigActTerm.sa_sigaction = terminateHandler;
mySigActTerm.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &mySigActTerm, NULL);
//------------------------------------------------------------
// initialize the serial port.
mySER = SERinit("/dev/ttyUSB0", 2);
if (mySER == NULL)
{
fprintf(stderr, "main() - SERinit() returned NULL");
exit(EXIT_FAILURE);
}
//------------------------------------------------------------
// start the serial thread.
rtn = pthread_create(&serialThdID, NULL, serialThread, mySER);
if(rtn == 0)
fprintf(stderr, "starting serial thread.\n");
else
{
fprintf(stderr, "main() - pthread_create() returned %d\n%s\n", rtn, strerror(errno));
free(mySER);
exit(EXIT_FAILURE);
}
//------------------------------------------------------------
// wait till serialThread() indicates it is running.
while (mySER->ThreadStatus != threadRuning)
{
fprintf(stderr, "waiting for thread running status.\n");
sleep(1);
}
//------------------------------------------------------------
// main loop here.
while (terminate == 0)
{
// do stuff here.
}
//------------------------------------------------------------
// tell the serial thread to stop.
mySER->ThreadCtrl = threadCtrlKill;
//------------------------------------------------------------
// verify serial thread is dead!
while (mySER->ThreadStatus != threadStopped)
{
}
//------------------------------------------------------------
// clean up.
SERclose(mySER);
free(mySER);
}
serialThread.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sys/time.h>
#include <term.h>
#include <inttypes.h>
#include "SER.h"
void* serialThread(void* arg)
{
char* rtn;
SER* mySER = arg;
mySER->tid = pthread_self();
mySER->ThreadStatus = threadRuning;
// thread Loop!
while(mySER->ThreadCtrl != threadCtrlKill)
{
rtn = SERwrapperFunc(mySER);
// code to print the response here
printf("%.*s\n", 8, rtn);
sleep(30);
}
mySER->ThreadStatus = threadStopped;
pthread_exit(NULL);
}
SERmaster.c
#define responseSize 4584
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <termios.h>
//#include <linux/serial.h>
//#include <aio.h>
#include <sys/time.h>
#include "SER.h"
// array used to get termios BAUD.
const
int BAUDarray[9] = { 0, // not used.
B4800, // 208
B9600, // 104
B19200, // 52
B38400, // 26
B57600, // 17.363636
B115200, // 8.727272
B230400, // 4.363636
B460800 // 2.181818
};
// delay (in uS) per character transmitted.
// 1 start, Even parity, 7bits, 1 stop.
// bit time (times 10 bits)
// Plus one bit time between characters.
const
int BAUDdelay[9] = { 0, // not used.
2288,
1144,
572,
286,
191,
96,
48,
24
};
static
char response[4584];
static
unsigned
int respIndex;
static
struct termios newtio, oldtio;
extern
volatile
int terminate;
static
int sendRecieve(SER* mySER, const char* msgBuffer, int msgCnt, int sendFlag, int receiveFlag)
{
int rtn;
char myChar;
fd_set myfds;
struct
timeval tm_out;
if (sendFlag == true)
{
while (1)
{
rtn = write(mySER->sfd, msgBuffer, msgCnt);
if (rtn == -1)
{
if (errno == EINTR)
{
fprintf(stderr, "sendRecieve() - write() EINTR !\n");
if (terminate == 1)
break; // deal with SIGTERM !
continue; // if not SIGTERM then retry.
}
else
{
fprintf(stderr, "sendRecieve() - write()\n%s\n", strerror(errno));
return EXIT_FAILURE;
}
}
else
{
if (rtn == msgCnt)
break;
else
{
fprintf(stderr, "sendRecieve() - write() returned less than msgCnt !\n");
return EXIT_FAILURE;
}
}
}
}
if (receiveFlag == true)
{
respIndex = 0;
while (1)
{
tm_out.tv_sec = 1;
tm_out.tv_usec = mySER->BAUDmult * msgCnt;
FD_ZERO(&myfds);
FD_SET(mySER->sfd, &myfds);
rtn = select(mySER->sfd + 1, &myfds, NULL, NULL, &tm_out);
if (rtn == 0)
{
fprintf(stderr, "sendRecieve() - select() timeout!\n");
return EXIT_FAILURE;
}
if (rtn == -1)
{
if (errno == EINTR)
{
fprintf(stderr, "sendRecieve() - select() EINTR !\n");
if (terminate == 1)
break;
continue;
}
else
{
fprintf(stderr, "sendRecieve() - select()\n%s\n", strerror(errno));
return EXIT_FAILURE;
}
}
while (1)
{
rtn = read(mySER->sfd, &myChar, 1);
if (rtn == -1)
{
if (errno == EINTR)
{
fprintf(stderr, "sendRecieve() - read() EINTR !\n");
if (terminate == 1)
break;
continue;
}
else
{
fprintf(stderr, "sendRecieve() - read()\n%s\n", strerror(errno));
return EXIT_FAILURE;
}
}
else
break;
response[respIndex] = myChar;
if (respIndex < responseSize - 1)
respIndex++;
else
break;
if (myChar == '\n')
return EXIT_SUCCESS;
}
}
fprintf(stderr, "sendRecieve() - select/read while loop Dumped (response frame too big)!!\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
char* SERwrapperFunc(SER* mySER)
{
char myCharArray[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
int myCharArrayCountToSend = sizeof(myCharArray);
sendRecieve(mySER, myCharArray, myCharArrayCountToSend, true, true);
return response;
}
void serPrint()
{
printf("NCCS = %d OLD: NEW:\n", NCCS);
printf("c_iflag - %08x %08x\n", oldtio.c_iflag, newtio.c_iflag);
printf("c_oflag - %08x %08x\n", oldtio.c_oflag, newtio.c_oflag);
printf("c_cflag - %08x %08x\n", oldtio.c_cflag, newtio.c_cflag);
printf("c_lflag - %08x %08x\n", oldtio.c_lflag, newtio.c_lflag);
printf("c_line - %08x %08x\n", oldtio.c_line, newtio.c_line);
printf("c_ispeed - %08x %08x\n", oldtio.c_ispeed, newtio.c_ispeed);
printf("c_ospeed - %08x %08x\n", oldtio.c_ospeed, newtio.c_ospeed);
printf("\n");
printf("VINTR - %02x %02x\n", oldtio.c_cc[VINTR], newtio.c_cc[VINTR]);
printf("VQUIT - %02x %02x\n", oldtio.c_cc[VQUIT], newtio.c_cc[VQUIT]);
printf("VERASE - %02x %02x\n", oldtio.c_cc[VERASE], newtio.c_cc[VERASE]);
printf("VKILL - %02x %02x\n", oldtio.c_cc[VKILL], newtio.c_cc[VKILL]);
printf("VEOF - %02x %02x\n", oldtio.c_cc[VEOF], newtio.c_cc[VEOF]);
printf("VTIME - %02x %02x\n", oldtio.c_cc[VTIME], newtio.c_cc[VTIME]);
printf("VMIN - %02x %02x\n", oldtio.c_cc[VMIN], newtio.c_cc[VMIN]);
printf("VSWTC - %02x %02x\n", oldtio.c_cc[VSWTC], newtio.c_cc[VSWTC]);
printf("VSTART - %02x %02x\n", oldtio.c_cc[VSTART], newtio.c_cc[VSTART]);
printf("VSTOP - %02x %02x\n", oldtio.c_cc[VSTOP], newtio.c_cc[VSTOP]);
printf("VSUSP - %02x %02x\n", oldtio.c_cc[VSUSP], newtio.c_cc[VSUSP]);
printf("VEOL - %02x %02x\n", oldtio.c_cc[VEOL], newtio.c_cc[VEOL]);
printf("VREPRINT - %02x %02x\n", oldtio.c_cc[VREPRINT], newtio.c_cc[VREPRINT]);
printf("VDISCARD - %02x %02x\n", oldtio.c_cc[VDISCARD], newtio.c_cc[VDISCARD]);
printf("VWERASE - %02x %02x\n", oldtio.c_cc[VWERASE], newtio.c_cc[VWERASE]);
printf("VLNEXT - %02x %02x\n", oldtio.c_cc[VLNEXT], newtio.c_cc[VLNEXT]);
printf("VEOL2 - %02x %02x\n", oldtio.c_cc[VEOL2], newtio.c_cc[VEOL2]);
printf("\n");
printf("\n");
}
SER* SERinit(const char* strPort, int myBAUD)
{
SER* mySER;
//------------------------------------------------------------
// create the global SER struct instance.
if ((mySER = malloc(sizeof(SER))) == NULL)
{
fprintf(stderr, "SERinit() - mySER malloc()\n%s\n", strerror(errno));
return NULL;
}
memset(mySER, 0, sizeof(SER));
//------------------------------------------------------------
// setup the BAUD.
mySER->BAUDindex = myBAUD;
mySER->BAUDvalue = BAUDarray[myBAUD];
mySER->BAUDmult = BAUDdelay[myBAUD];
//------------------------------------------------------------
// open the serial port.
mySER->sfd = open(strPort, O_RDWR | O_NOCTTY);
if (mySER->sfd < 0)
{
fprintf(stderr, "SERInit() - open()\n%s\n", strerror(errno));
free(mySER);
return NULL;
}
//------------------------------------------------------------
// save old port settings for when we exit.
tcgetattr(mySER->sfd, &oldtio);
//------------------------------------------------------------
// prepare the newtio struct with current settings.
newtio = oldtio;
//------------------------------------------------------------
// set BAUD
if (cfsetspeed(&newtio, B9600) != 0)//mySER->BAUDvalue
{
fprintf(stderr, "SERInit() - cfsetspeed()\n%s\n", strerror(errno));
free(mySER);
return NULL;
}
//------------------------------------------------------------
// set for non-canonical (raw).
cfmakeraw(&newtio);
newtio.c_cflag |= (CLOCAL | CREAD);
newtio.c_cflag &= ~(CRTSCTS | CSTOB)
// read() blocks until one char or until 100 mS timeout.
newtio.c_cc[VTIME] = 1;
newtio.c_cc[VMIN] = 1;
// flush the toilet.
tcflush(mySER->sfd, TCIFLUSH);
// write new port settings.
tcsetattr(mySER->sfd, TCSANOW, &newtio);
serPrint();
return mySER;
}
void SERclose(SER* mySER)
{
// restore old port settings.
tcsetattr(mySER->sfd, TCSANOW, &oldtio);
close(mySER->sfd);
}
SER.h
#ifndef SER_H_
#define SER_H_
#define threadInit 0x00
#define threadStarting 0x01
#define threadRuning 0x02
#define threadFailed 0x03
#define threadStopped 0x0f
#define threadCtrlRestart 0xFE
#define threadCtrlKill 0xFF
#include <stdint.h>
#include <pthread.h>
typedef struct SER
{
int BAUDindex; // the BAUD rate.
int BAUDmult; // uS per character ... plus one bite time between characters.
// used as a multiplier used to calculate sleep times after write().
// (bit time x 10 bits) 71E.
int BAUDvalue; // array used to set termios BAUD and get BAUDmult.
// 4800 = 1 2080 uS
// 9600 = 2 1040
// 19,200 = 3 520
// 38,400 = 4 260
// 76,800 = 5 130
// 115,200 = 6 65
// 230,400 = 7 32.5
// 460,800 = 8 16.25
pthread_t tid; // Stores thread ID.
uint8_t ThreadStatus; // written only by thread.
uint8_t ThreadCtrl; // written only by main.
int sfd; // serial port file descriptor.
}SER;
char* SERwrapperFunc(SER* mySER);
SER* SERinit(const char* strPort, int myBAUD);
void SERclose(SER* mySER);
void* serialThread(void* arg);
#endif /* SER_H_ */