How to detect USB disconnect while blocking reading? - c

I am quite new to this stuff, so be aware. I want to read a single char from an arduino. Therefore I have enabled the serial communication on the arduino with a baud rate of 9600 (more than enough for my needs). This following code works - sometimes. Sometimes I read the correct data and sometimes I get some invaid characters. Also there is the problem that if the device is initialized but then the USB is disconnected (for example just pull the cable), this program does not terminate. What am I doing wrong?
int main() {
int fd = open("/dev/ArduinoDeviceName", O_RDWR | O_NOCTTY | O_NDELAY);
if(fd < 0) {
printf("Failed to connect to device!\n");
return EXIT_FAILURE;
}
struct termios SerialPortSettings;
tcgetattr(fd, &SerialPortSettings);
cfsetispeed(&SerialPortSettings,B9600);
cfsetospeed(&SerialPortSettings,B9600);
if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) {
printf("Error while setting attributes \n");
return EXIT_FAILURE;
}
printf("Init done!\n");
printf("read: _%c_", read_next_char(fd));
close(fd);
return EXIT_SUCCESS;
}
EDIT 1:
Now with some additional settings I could achieve to read the correct data values, see code below:
char read_next_char(int fd) {
char buffer[1] = {0};
fcntl(fd, F_SETFL);
switch (read(fd, &buffer, sizeof(buffer))) {
case 1: printf("Read bytes correct!\n"); break;
case 0: printf("Reached EOF\n"); break;
case -1: printf("Error occured while reading!\n"); break;
default: break;
};
return buffer[0];
}
int main() {
int fd = open("/dev/tty.usbmodem14101", O_RDWR | O_NOCTTY | O_NDELAY);
if(fd < 0) {
printf("Failed to connect to device!\n");
return EXIT_FAILURE;
}
struct termios SerialPortSettings;
tcgetattr(fd, &SerialPortSettings);
cfsetispeed(&SerialPortSettings,B9600);
cfsetospeed(&SerialPortSettings,B9600);
SerialPortSettings.c_iflag=0;
SerialPortSettings.c_oflag=0;
SerialPortSettings.c_cflag=CS8|CREAD|CLOCAL;
SerialPortSettings.c_lflag=0;
SerialPortSettings.c_cc[VMIN]=1;
SerialPortSettings.c_cc[VTIME]=5;
if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) {// Set the attributes to the termios structure
printf("Error while setting attributes \n");
return EXIT_FAILURE;
}
printf("Init done!\n");
char read;
while((read = read_next_char(fd)) != 0) {
printf("Correctly read: _%c_", read);
}
close(fd);
return EXIT_SUCCESS;
}
The only thing that sometimes can be annoying is, that the arduino sends some data via serial in a loop with some delay. If the computer connects there are always some data sent, before the arduino resets. How can I achieve that the arduino / serial port gets reset before I read any data?

Related

How to wait for byte to be written to serial GSM modem?

Thanks in advance for the help.
Using the following sample Canonical Mode Linux Serial Port, I start writing a little API in Cto send an AT command and receive the response via serial port.
I've no problem reading the response (used non blocking read with a poll) and no problem discovering the "at command" enabled device.
The problem I'm facing is with the write function. Most of the commands work (the smallest command like AT, ATI, ATI+CIMI etc). Sometimes a command like send SMS fails.
I think the problem is the speed of the write (quicker than serial).
All the problems DO NOT occur if I set a timer between a write and the next write.
The following is the code
int serial_write(int fd, char * command){
size_t len = strlen(command);
int wlen = write(fd, command, len);
if (wlen != len) {
return -1;
}
usleep(80*1000L);
if ( tcdrain(fd) != 0){
return -2;
}
return 0;
}
int open_tty(char *portname){
int fd;
/*Aperta NON bloccante, con la poll che aspetta 1 secondo*/
fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK);
if (fd < 0) {
printf("Error opening %s: %s\n", portname, strerror(errno));
return -1;
}
/*baudrate 115200, 8 bits, no parity, 1 stop bit */
if (set_interface_attribs(fd, B115200) < 0 ){
printf("Error set_interface_attribs: %s\n", strerror(errno));
return -1;
}
return fd;
}
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;
}
if ( cfsetospeed(&tty, (speed_t)speed) < 0 ){
printf("Error from cfsetospeed: %s\n", strerror(errno));
return -1;
}
if ( cfsetispeed(&tty, (speed_t)speed) < 0 ){
printf("Error from cfsetispeed: %s\n", strerror(errno));
return -1;
}
tty.c_cflag |= CLOCAL | CREAD;
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 */
tty.c_lflag |= ICANON | ISIG; /* canonical input */
tty.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);
tty.c_iflag &= ~IGNCR; /* preserve carriage return */
tty.c_iflag &= ~INPCK;
tty.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
tty.c_iflag &= ~(IXON | IXOFF | IXANY); /* no SW flowcontrol */
tty.c_oflag &= ~OPOST;
tty.c_cc[VEOL] = 0;
tty.c_cc[VEOL2] = 0;
tty.c_cc[VEOF] = 0x04;
tty.c_cc[VTIME] = 10;
tty.c_cc[VMIN] = 0;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}
int read_response(int fd, char ** res){
int count=1; /* contatore realloc 1 per lo \0*/
tcdrain(fd); /* waits until all of the data that has been written has been sent */
struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN ;
do {
unsigned char buf[MAXBUF];
unsigned char *p;
int rdlen;
int n = poll( fds, 1, 1000);
if (n>0){
rdlen = read(fd, buf, sizeof(buf) - 1);
if (rdlen > 0) {
buf[rdlen] = 0;
for (p = buf; rdlen-- > 0; p++) {
if (*p < ' ')
*p = '\0'; /* replace any control chars */
}
if ( (strcmp((char *)buf, "") != 0) || (buf[0] == '^') ){
count += (strlen((char *)buf)+1); /* 2 per ; e ' ' */
*res = realloc (*res, count);
strncat(*res, (char *)buf, strlen((char *)buf));
strcat(*res, ";");
}
if (strcmp((char *)buf, ATCMD_OK) == 0){
return 0;
}
if (strcmp((char *)buf, ATCMD_ERROR) == 0){
return -1;
}
} else if (rdlen < 0) {
return -2;
} else { /* rdlen == 0 */
return -3;
}
} else {
return -4;
}
/* repeat read */
} while (1);
}
int send_sms(int fd, char *tel, char *text){
int wlen = 0;
char *res = malloc(sizeof(char*));
char at_send[strlen(ATCMD_CMGS) + strlen(tel) + 3]; //3=2apici+"\0"
strcpy(at_send, ATCMD_CMGS);
strcat(at_send, DL_QUOTE);
strcat(at_send, tel);
strcat(at_send, DL_QUOTE);
printf("Setting to sms text mode... ");
if ( (wlen = serial_write(fd, ATCMD_CMGF)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if ( (wlen = serial_write(fd, C_R)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if (read_response(fd, &res) < 0 ) {
printf("FAIL\n");
}
else {
printf("OK, RES: %s\n",res);
}
free(res);
printf("Sending SMS...");
if ( (wlen = serial_write(fd, at_send)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if ( (wlen = serial_write(fd, C_R)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if ( (wlen = serial_write(fd, text)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if ( (wlen = serial_write(fd, CTRL_Z)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if (read_response(fd, &res) < 0 ) {
printf("FAIL\n");
free(res);
return -1;
}
else {
printf("OK, RES: %s\n",res);
free(res);
return 0;
}
}
These are the incriminated functions. You can see, in the serial_write(), I'm using usleep() and all works correctly. Removing the usleep() causes problems (also if there's a tcdrain).
All kinds of help will be appreciated.
Thanks.
Sometimes a command like send SMS fails.
That is not a helpful or detailed description of the problem. Simply stating that there is a problem and then expecting a solution is unreasonable.
All the problems DO NOT occur if I set a timer between a write and the next write.
That is an indication that your program is not properly waiting for a response from the modem before it transmits a new command/message. Receiving the response (rather than waiting for transmission to complete, i.e. calling tcdrain(), and/or delaying for an arbitrary time interval, e.g. calling usleep()) is the proper indication that the modem is now ready to receive.
The commands that you are not having an issue are characterized as a basic command & response dialog. A one-line message is transmitted, and in short order a one-line message is received as a response.
But sending a SMS message using the CMGS command does not follow that simple dialog.
Yet your program tries to force that one-message->one-response construct anyway (resulting is apparently unreliable results).
According to the GSM Technical Specification, the CMGS command can/should be handled as a write/read exchange of
[w]command -> [r]prompt_response -> [w]text -> [r]prompt_response ...
[w]text -> [r]prompt_response -> [w]text + ^Z -> [r]2-line response
where prompt_response is specified as
a four character sequence <CR><LF><greater_than><space>
Note that this response is ill-suited for a canonical read (i.e. the conventional line termination characters are at the start instead of the end of this sequence).
Also note that every carriage return character in the transmitted message text will generate the transmission of a prompt_response by the modem.
Given the complexity of the CMGS command, the transmission of multiple lines by your program, and then expecting to handle just one response is prone to unreliable results.
I see other issues with your code, but none as serious as this mishandling of the CMGS command.
This "answer" will only point out this major flaw in your program (i.e. offer "help") rather than provide a solution and/or rewrite.
In summary:
How to wait for byte to be written to serial GSM modem?
Your program should wait until it has read the response to the previous transmission before it sends the (next) message/command. The bytes within that message can be sent without any delays.
Refer to the GSM specification as what generates responses.

What is the proper way to send an AT command with termios and get the reply

I am using Termios in an (ressource constrained) embedded Linux platform (in C) to send commands and receive data from various tty peripherals (CP2102 USB-UART). Apparently there are various ways to do that and many are improper. I tried few with mitigated success, I came across something that works but i am not sure it is optimal or even correct :
#define BAUDRATE 115200
#define DEVICE "/dev/ttyUSB0"
#define DATA "BLK\r"
int handler(void){
char buf[255];
struct pollfd fds[1];
int fd, ret, res, retry = 0;
connect:
fd = open(DEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd == 0){
perror(DEVICE);
printf("Failed to open %s\n",DEVICE);
sleepms(2000);
if(retry++<5) goto connect;
//exit(-1);
}
set_interface_attribs (fd, BAUDRATE, 0); // 8n1 no parity
set_blocking (fd, 0); // not blocking
fds[0].fd = fd; // streams
fds[0].events = POLLRDNORM;
for (;;)
{
int count = write(fd, DATA, strlen(DATA));
ret = poll(fds, 1, 1000);
if (ret > 0){
if (fds[0].revents & POLLHUP){ printf("Hangup\n"); close(fd); goto connect;}
if (fds[0].revents & POLLRDNORM){
res = read(fd,buf,255);
if(!res){close(fd); goto connect;}
buf[res-2]=0;
printf("Received %d bytes : %s",res,buf);
}
}
}
}
Basically the command is sent, then its polling until some data come or timeout occurs.
This worked for over 24h and shown no issue communication wise however there is one problem : if the peripheral is disconnected then i get the "hangout" notification but it never reconnects, i was expecting it would since i close the file and retry connection to interface.
Also, is poll is the best approach here? i dont want to spend CPU time in pure lost by polling (unless polling has some mecanism to release the CPU time to other threads) but i still want the call to be blocking until the timeout occurs or response arrive (i.e. i dont want to send command and get the response later with a callback). The response come from peripheral under few millisecond.
n.b. i know some eyes are bleeding because of the goto statement, no worries, goto has been used here to test quickly the "reconnect" approach (which didn't worked) but in the end if the connection has to be restarted it will be in a separate function, goto will never be used in the final implementation.
EDIT:
After rewrite. It works but there are still problems, mainly, when i disconnect the peripheral, Linux will randomly keep the port name or change it. Sometimes it will keep the port name for multiple consecutive disconnect, sometime it will rename it and stick with the new name for a while. So i have to find a way to identify the peripheral from USB and get current port name whatever it is.
On disconnect it throws error 9 from tcgetattr (coming from set_interface_attribs or set_blocking i think) but once it is reconnected and if Linux does not change the port name then it reconnects right away and restart to send and receive as it should, however, when Linux rename the port then it fail.
int fd=-1,retry=0;
struct pollfd fds[1];
int connect(void){
if(fd==-1) fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
else return 0; // already opened
printf("Connecting to fd #%d\n",fd);
if (fd==0){
perror(MODEMDEVICE);
printf("Failed to open %s\n",MODEMDEVICE);
retry++;
sleepms(2000);
return -1;
}
set_interface_attribs (fd, BAUDRATE, 0); // 8n1 no parity
set_blocking (fd, 0); // not blocking
fds[0].fd = fd; // streams
fds[0].events = POLLERR|POLLHUP|POLLRDNORM;
return 0;
}
int handlePoll(){
int ret=0,res=0;
char buf[255];
ret = poll(fds, 1, 1000); // 1000ms
if (ret > 0){
if (fds[0].revents & POLLERR){ close(fd); fd=-1; return -1; } // IO error
if (fds[0].revents & POLLHUP){ close(fd); fd=-1; return -2; } // interface closed
if (fds[0].revents & POLLRDNORM){
res = read(fd,buf,255);
if(!res){ close(fd); return -3; } // data receive error
buf[res-2]=0;
printf("Received %d bytes : %s\n",res,buf);
return 0;
}
return -4; // unknown error
}
return -5; // timeout
}
int sendCMD(char* cmd){
int res=1;
retry=0;
while(res && retry<5){ res=connect(); }
if(res) return -7;
if(retry>5) return -8;
int len = strlen(cmd);
int count = write(fd, cmd, len);
if(count<len){ return -6;}
return handlePoll();
}
int main(void){
while(1){
switch(sendCMD(DATA)){
case -1: printf("IO error\n"); break;
case -2: printf("Interface closed error\n"); break;
case -3: printf("data receive error\n"); break;
case -4: printf("Unknown error\n"); break;
case -5: printf("Timeout\n"); break;
case -6: printf("Command send error\n"); sleepms(200); break;
case -7: printf("Interface open error\n"); break;
case -8: printf("Cannot open interface after 5 try\n"); break;
default: break;
}
}
}
I think there should be a better way to deal with disconnect (Detecting if a character device has disconnected in Linux in with termios api (c++))
connect:
fd = open(DEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd == 0){
perror(DEVICE);
The open(2) doesn't return 0, but -1 in case of error. 0 is a valid file descriptor -- by convention the standard input.
int count = write(fd, DATA, strlen(DATA));
ret = poll(fds, 1, 1000);
if (ret > 0){
Why aren't you checking the return value of write()? If the open() above actually failed and returned -1, this write() will fail too, return -1 and set errno to EBADF.
res = read(fd,buf,255);
if(!res){close(fd); goto connect;}
buf[res-2]=0;
Ditto, you don't care if read() fails, which it will certainly do if the open() above failed to open the file. Just like open(), write() and all system calls, read() will return -1 in case of error.
Since your file descriptor is non-blocking, be prepared to handle transient errors like EAGAIN separately [2].
In the case where res == -1 the buf[res-2]=0 will corrupt memory by writing before the start of buf.
Notice that poll() will not set POLLERR in revents if passed an invalid fd -- if will either ignore it if it's negative (like the fd returned by your failing open()), or set POLLNVAL if it's positive.
if (fds[0].revents & POLLHUP){ printf("Hangup\n"); close(fd); goto connect;}
if (fds[0].revents & POLLRDNORM){
POLLHUP and POLLRDNORM[1] can be returned together in revents, signaling the last possible read, which your code will miss.
I strongly suggest you strace(1) your program, which you could do while it's running, with strace -p PID.
[1] btw, POLLRDNORM is equivalent to POLLIN in Linux.
[2] and EINTR, if your program or the libraries it's using are setting any signal handler.

What is the meaning of No such device or address(Error code 6)

I am trying to open a serial port with using c code and I have created a node with the following command;
mknod /tmp/ttyACM0 c 100 0;
chmod 700 /tmp/ttyACM0;
and then run an executable file that opens a serial port with the following method;
static int OpenSerialPort(const char *bsdPath)
{
int fileDescriptor = -1;
struct termios options;
fileDescriptor = open(bsdPath, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fileDescriptor == -1 || flock(fileDescriptor, LOCK_EX) == -1 )
{
printf("Error opening serial port %s - %s(%d).\n",
bsdPath, strerror(errno), errno);
goto error;
}
if (fcntl(fileDescriptor, F_SETFL, 0) == -1)
{
printf("Error clearing O_NONBLOCK %s - %s(%d).\n",
bsdPath, strerror(errno), errno);
goto error;
}
if (ioctl(fileDescriptor, TIOCEXCL, (char *) 0) < 0) {
printf("Error setting TIOCEXCL %s - %s(%d).\n",
bsdPath, strerror(errno), errno);
goto error;
}
memset(&options,0,sizeof(options));
options.c_iflag=0;
options.c_oflag=0;
options.c_cflag=CS8|CREAD|CLOCAL;
options.c_lflag=0;
options.c_cc[VMIN]=1;
options.c_cc[VTIME]=5;
cfsetospeed(&options, B115200);
cfsetispeed(&options, B115200);
if (tcsetattr(fileDescriptor, TCSANOW, &options) == -1)
{
printf("Error setting tty attributes %s - %s(%d).\n",
bsdPath, strerror(errno), errno);
goto error;
}
return fileDescriptor;
error:
if (fileDescriptor != -1)
{
close(fileDescriptor);
}
exit(1);
return -1;
}
and it returns;
Error opening serial port /tmp/ttyACM0 - No such device or address(6).
There is actually a ttyACM0 file under /tmp directory but it returns me the error message. What can I do to pass this error?
EDIT:
When I look at the /proc/devices file, there is not a ttyACM0 device. Now I think my problem's reason can be this.
A device node does not mean that the device actually exists. When you open it the kernel tries to find the matching device and if it doesn't exists you get the above error.
Any Linux system form the last decade will create device nodes for existing devices automatically through udevd. That you had to create it manually is a strong indication the device doesn't exist at all just as the error says.

writing to /dev/ttyO3 not visible

I am writing the data to /dev/ttyO3 in my application .The write is succeeded but not visible int he cat /dev/ttyO3.
ttyO3 is the device name for the uart4 instance of omap4460 pandaboard.
Edit 1:But some pulses are showing up when I probe through CRO.
How should I change my code to enable the software loop back?ie(I want the same pulse to be seen at rx pin without shorting Tx-Rx)
#define DEVICE "/dev/ttyO3"
int main()
{
int fd;
int write_fd1;
struct termios options;
fd = open(DEVICE,O_RDWR);
if(fd < 0)
printf("unable to open the device\n");
else
printf("device opened %d \n",fd);
tcgetattr(fd,&options);
cfsetospeed(&options,B300);
cfsetispeed(&options,B300);
tcsetattr(fd,TCSANOW,&options);
tcgetattr(fd,&options);
if((cfgetospeed(&options) != B300) || (cfgetispeed(&options)!= B300));
{
printf("Baud rate not set");
}
while(1)
{
write_fd1 = write(fd,"a",2) ;
printf("write_fd %d \n",write_fd1);
}
close(fd);
return 0;
}

C read call blocking on serial port operation

I am trying to write a C program in Linux to send and receive data from a microcontroller over the serial port. As a test, I have configured the microcontroller to immediately echo all characters sent. I have verified that this works in minicom and also by using "cat" and "echo" to send and receive data.
However, when I try to do the same in a C program, my read call blocks forever. I am setting the serial port to non-canonical mode, with a MIN of '1' and TIME of '0'. My minicom test proves that the microcontroller is returning characters as they are typed, so I expect read to return after the write call has sent characters. I have compared my code to several online examples, and I haven't found anything that I am missing. I have tried several permutations of the code below with no luck. Can someone spot the problem?
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
#define UART_SPEED B115200
char buf[512];
void init_serial (int fd)
{
struct termios termios;
int res;
res = tcgetattr (fd, &termios);
if (res < 0) {
fprintf (stderr, "Termios get error: %s\n", strerror (errno));
exit (-1);
}
cfsetispeed (&termios, UART_SPEED);
cfsetospeed (&termios, UART_SPEED);
termios.c_iflag &= ~(IGNPAR | IXON | IXOFF);
termios.c_iflag |= IGNPAR;
termios.c_cflag &= ~(CSIZE | PARENB | CSTOPB | CREAD | CLOCAL);
termios.c_cflag |= CS8;
termios.c_cflag |= CREAD;
termios.c_cflag |= CLOCAL;
termios.c_lflag &= ~(ICANON | ECHO);
termios.c_cc[VMIN] = 1;
termios.c_cc[VTIME] = 0;
res = tcsetattr (fd, TCSANOW, &termios);
if (res < 0) {
fprintf (stderr, "Termios set error: %s\n", strerror (errno));
exit (-1);
}
}
int main (int argc, char **argv)
{
int fd;
int res;
int i;
if (argc < 2) {
fprintf (stderr, "Please enter device name\n");
return -1;
}
fd = open (argv[1], O_RDWR | O_NOCTTY);
if (fd < 0) {
fprintf (stderr, "Cannot open %s: %s\n", argv[1], strerror(errno));
return -1;
}
init_serial (fd);
res = write (fd, "P=20\r\n", 6);
if (res < 0) {
fprintf (stderr, "Write error: %s\n", strerror(errno));
return -1;
}
tcdrain (fd);
res = read (fd, buf, 512);
printf ("%d\n", res);
if (res < 0) {
fprintf (stderr, "Read error: %s\n", strerror(errno));
return -1;
}
for (i=0; i<res; i++) {
printf ("%c", buf[i]);
}
return 0;
}
You might want to insert some delays, or loop waiting for input.
After setting the bit rate, some types of UART hardware takes one or two characters at the new speed to synchronize to the new speed. It is possible the first few characters are being lost on the write.
After the six character write, the read is issued immediately with a 0.1 second timeout. It is possible that not all the characters from the write() have finished being transmitted before the read(), let alone any time for the remote device to respond.
For example, one solution is:
init_serial (fd);
usleep (100000); // delay 0.1 seconds (Linux) so term parameters have time to change
res = write (fd, "P=20\r\n", 6);
if (res < 0) {
fprintf (stderr, "Write error: %s\n", strerror(errno));
return -1;
}
tcdrain (fd);
usleep (250000); // delay 0.25 for device to respond and return data
res = read (fd, buf, 512);
Another approach would be to continue reading until a sufficient number of characters arrive or a reasonable amount of time passes.

Resources