ichigokurosaki
asked on
Read from USB in Ubuntu with C
I'm using my pc with Ubuntu 11.04 to read data from an USB device mounted on /dev/ttyACM0.
I can read it by using bash utility like cat or tail, for example:
Since, I have to integrate the USB read and write functionality in my C program, i searched on google and I've find a C code sample to read from serial port (i want to read arduino data on Ubuntu via USB).
The problem is that this code sample works great only if i previous use cat or tail command, otherwise, if I do not use these commands, it is not capable to read from USB.
For example, if i directly call ./code_sample it does not display any information; on the contrary, if I call tail command and then I stop it and call ./code_sample, now it is able to print on screen the information received from USB.
Why it happens?
This is the code:
I can read it by using bash utility like cat or tail, for example:
@bash> tail -f /dev/ttyACM0
read: Hello World sent from USB!
..
@bash>cat /dev/ttyACM0
read: Hello World sent from USB!
Since, I have to integrate the USB read and write functionality in my C program, i searched on google and I've find a C code sample to read from serial port (i want to read arduino data on Ubuntu via USB).
The problem is that this code sample works great only if i previous use cat or tail command, otherwise, if I do not use these commands, it is not capable to read from USB.
For example, if i directly call ./code_sample it does not display any information; on the contrary, if I call tail command and then I stop it and call ./code_sample, now it is able to print on screen the information received from USB.
Why it happens?
This is the code:
#include <stdio.h> /* Standard input/output definitions */
#include <stdlib.h>
#include <stdint.h> /* Standard types */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <sys/ioctl.h>
#include <getopt.h>
void usage(void);
int serialport_init(const char* serialport, int baud);
int serialport_writebyte(int fd, uint8_t b);
int serialport_write(int fd, const char* str);
int serialport_read_until(int fd, char* buf, char until);
void usage(void) {
printf("Usage: arduino-serial -p <serialport> [OPTIONS]\n"
"\n"
"Options:\n"
" -h, --help Print this help message\n"
" -p, --port=serialport Serial port Arduino is on\n"
" -b, --baud=baudrate Baudrate (bps) of Arduino\n"
" -s, --send=data Send data to Arduino\n"
" -r, --receive Receive data from Arduino & print it out\n"
" -n --num=num Send a number as a single byte\n"
" -d --delay=millis Delay for specified milliseconds\n"
"\n"
"Note: Order is important. Set '-b' before doing '-p'. \n"
" Used to make series of actions: '-d 2000 -s hello -d 100 -r' \n"
" means 'wait 2secs, send 'hello', wait 100msec, get reply'\n"
"\n");
}
int main(int argc, char *argv[])
{
int fd = 0;
char serialport[256];
int baudrate = B9600; // default
char buf[256];
int rc,n;
if (argc==1) {
usage();
exit(EXIT_SUCCESS);
}
/* parse options */
int option_index = 0, opt;
static struct option loptions[] = {
{"help", no_argument, 0, 'h'},
{"port", required_argument, 0, 'p'},
{"baud", required_argument, 0, 'b'},
{"send", required_argument, 0, 's'},
{"receive", no_argument, 0, 'r'},
{"num", required_argument, 0, 'n'},
{"delay", required_argument, 0, 'd'}
};
while(1) {
opt = getopt_long (argc, argv, "hp:b:s:rn:d:",
loptions, &option_index);
if (opt==-1) break;
switch (opt) {
case '0': break;
case 'd':
n = strtol(optarg,NULL,10);
usleep(n * 1000 ); // sleep milliseconds
break;
case 'h':
usage();
break;
case 'b':
baudrate = strtol(optarg,NULL,10);
break;
case 'p':
strcpy(serialport,optarg);
fd = serialport_init(optarg, baudrate);
if(fd==-1) return -1;
break;
case 'n':
n = strtol(optarg, NULL, 10); // convert string to number
rc = serialport_writebyte(fd, (uint8_t)n);
if(rc==-1) return -1;
break;
case 's':
strcpy(buf,optarg);
rc = serialport_write(fd, buf);
if(rc==-1) return -1;
break;
case 'r':
serialport_read_until(fd, buf, '\n');
printf("read: %s\n",buf);
break;
}
}
exit(EXIT_SUCCESS);
} // end main
int serialport_writebyte( int fd, uint8_t b)
{
int n = write(fd,&b,1);
if( n!=1)
return -1;
return 0;
}
int serialport_write(int fd, const char* str)
{
int len = strlen(str);
int n = write(fd, str, len);
if( n!=len )
return -1;
return 0;
}
int serialport_read_until(int fd, char* buf, char until)
{
char b[1];
int i=0;
do {
int n = read(fd, b, 1); // read a char at a time
if( n==-1) return -1; // couldn't read
if( n==0 ) {
usleep( 10 * 1000 ); // wait 10 msec try again
continue;
}
buf[i] = b[0]; i++;
} while( b[0] != until );
buf[i] = 0; // null terminate the string
return 0;
}
// takes the string name of the serial port (e.g. "/dev/tty.usbserial","COM1")
// and a baud rate (bps) and connects to that port at that speed and 8N1.
// opens the port in fully raw mode so you can send binary data.
// returns valid fd, or -1 on error
int serialport_init(const char* serialport, int baud)
{
struct termios toptions;
int fd;
//fprintf(stderr,"init_serialport: opening port %s @ %d bps\n",
// serialport,baud);
fd = open(serialport, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror("init_serialport: Unable to open port ");
return -1;
}
if (tcgetattr(fd, &toptions) < 0) {
perror("init_serialport: Couldn't get term attributes");
return -1;
}
speed_t brate = baud; // let you override switch below if needed
switch(baud) {
case 4800: brate=B4800; break;
case 9600: brate=B9600; break;
#ifdef B14400
case 14400: brate=B14400; break;
#endif
case 19200: brate=B19200; break;
#ifdef B28800
case 28800: brate=B28800; break;
#endif
case 38400: brate=B38400; break;
case 57600: brate=B57600; break;
case 115200: brate=B115200; break;
}
cfsetispeed(&toptions, brate);
cfsetospeed(&toptions, brate);
// 8N1
toptions.c_cflag &= ~PARENB;
toptions.c_cflag &= ~CSTOPB;
toptions.c_cflag &= ~CSIZE;
toptions.c_cflag |= CS8;
// no flow control
toptions.c_cflag &= ~CRTSCTS;
toptions.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
toptions.c_oflag &= ~OPOST; // make raw
// see: http://unixwiz.net/techtips/termios-vmin-vtime.html
toptions.c_cc[VMIN] = 0;
toptions.c_cc[VTIME] = 20;
if( tcsetattr(fd, TCSANOW, &toptions) < 0) {
perror("init_serialport: Couldn't set term attributes");
return -1;
}
return fd;
}
arduino-serial.c
ASKER
I changed VMIN and VTIME as your suggestion, but nothing changed.
This is the strace result:
and this is the stty result:
So, do you think the problem is with Termios?
What i should do in order to solve it?
This is the strace result:
console@pc:~/Desktop$ strace tail -f /dev/ttyACM0
execve("/usr/bin/tail", ["tail", "-f", "/dev/ttyACM0"], [/* 38 vars */]) = 0
brk(0) = 0x9648000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77c1000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=100105, ...}) = 0
mmap2(NULL, 100105, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb77a8000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\220o\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1434180, ...}) = 0
mmap2(NULL, 1444360, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x27e000
mprotect(0x3d8000, 4096, PROT_NONE) = 0
mmap2(0x3d9000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15a) = 0x3d9000
mmap2(0x3dc000, 10760, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x3dc000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77a7000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb77a78d0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0x3d9000, 8192, PROT_READ) = 0
mprotect(0x8054000, 4096, PROT_READ) = 0
mprotect(0x201000, 4096, PROT_READ) = 0
munmap(0xb77a8000, 100105) = 0
brk(0) = 0x9648000
brk(0x9669000) = 0x9669000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=8318400, ...}) = 0
mmap2(NULL, 2097152, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb75a7000
mmap2(NULL, 4096, PROT_READ, MAP_PRIVATE, 3, 0x7ed) = 0xb77c0000
close(3) = 0
open("/dev/ttyACM0", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFCHR|0660, st_rdev=makedev(166, 0), ...}) = 0
read(3, "o world!\r\n1650 Hello world!\r\n165"..., 8192) = 4004
read(3, " Hello world!\r\n1861 Hello world!"..., 8192) = 353
read(3, "d!\r\n1879 Hello world!\r\n1880 Hell"..., 8192) = 363
read(3, " Hello world!\r\n31592 Hello world", 8192) = 32
read(3, "!\r\n31593 Hello world!\r\n31594 Hel"..., 8192) = 125
and this is the stty result:
speed 115200 baud; line = 0;
eof = ^A; min = 1; time = 0;
-brkint -icrnl -imaxbel
-opost -onlcr
-isig -icanon -iexten -echo -echoe -echok -echoctl -echoke
So, do you think the problem is with Termios?
What i should do in order to solve it?
tail absolutely doesn't use termios. I would just open the device and read from it.
Was that really the output from stty -F /dev/ttyACM0 -a? I get a little less from stty -F /dev/ttyS0 and a great deal more if I append -a. Certainly I get min and time: 1 and 0 respectively.
I wouldn't try setting the baud rate if I felt I really had to use tcsetattr but why do you think you need to use it?
Was that really the output from stty -F /dev/ttyACM0 -a? I get a little less from stty -F /dev/ttyS0 and a great deal more if I append -a. Certainly I get min and time: 1 and 0 respectively.
I wouldn't try setting the baud rate if I felt I really had to use tcsetattr but why do you think you need to use it?
ASKER
This is the result of stty -F /dev/ttyACM0 -s:
I tested the same program on my Mac with Snow Leopard and it works great so it doesn't work only on my Ubuntu.
The baud rate setting is because I have to read data sent by Arduino board to my pc; in Arduino you can specify the baud rate of the port, so I use the baud rate in the program in order to change the setting according on the Arduino script.
Just a question: Can i just open and read the file in /dev/ttyACM0 in order to read the data sent from Arduino in C? In this way i can avoid all these problem and get my program more easier.
console@pc:~/Desktop$ stty -F /dev/ttyACM0 -a
speed 115200 baud; rows 0; columns 0; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^A; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff
-iuclc -ixany -imaxbel -iutf8
-opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt
-echoctl -echoke
I tested the same program on my Mac with Snow Leopard and it works great so it doesn't work only on my Ubuntu.
The baud rate setting is because I have to read data sent by Arduino board to my pc; in Arduino you can specify the baud rate of the port, so I use the baud rate in the program in order to change the setting according on the Arduino script.
Just a question: Can i just open and read the file in /dev/ttyACM0 in order to read the data sent from Arduino in C? In this way i can avoid all these problem and get my program more easier.
ASKER
I've almost solved the problem and now the program is able to read data from USB without having to call tail or cat before.
Now, the only problem is that when i stop the program by pressing "q" or "ctrl+z", it is not possible to use the shell anymore because it doesn't take input from keyboard anymore and i have to close it and open the shell again.
I did this change in my code during the port initialization:
What can i do in order to avoid the shell blocking?
Now, the only problem is that when i stop the program by pressing "q" or "ctrl+z", it is not possible to use the shell anymore because it doesn't take input from keyboard anymore and i have to close it and open the shell again.
I did this change in my code during the port initialization:
int serialport_init(const char* serialport)
{
struct termios tio;
struct termios stdio;
int fd;
memset(&stdio,0,sizeof(stdio));
stdio.c_iflag=0;
stdio.c_oflag=0;
stdio.c_cflag=0;
stdio.c_lflag=0;
stdio.c_cc[VMIN]=1;
stdio.c_cc[VTIME]=0;
tcsetattr(STDOUT_FILENO,TCSANOW,&stdio);
tcsetattr(STDOUT_FILENO,TCSAFLUSH,&stdio);
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); // make the reads non-blocking
memset(&tio,0,sizeof(tio));
// 8N1
tio.c_cflag &= ~PARENB;
tio.c_cflag &= ~CSTOPB;
tio.c_cflag &= ~CSIZE;
tio.c_cflag |= CS8;
// no flow control
tio.c_cflag &= ~CRTSCTS;
tio.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
tio.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
tio.c_oflag &= ~OPOST; // make raw
fd = open(serialport, O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd == -1) {
perror("init_serialport: Unable to open port ");
return -1;
}
cfsetospeed(&tio,B115200); // 115200 baud
cfsetispeed(&tio,B115200); // 115200 baud
tcsetattr(fd,TCSANOW,&tio);
return fd;
}
void serialport_read_until(int fd)
{
while (c!='q')
{
if (read(fd,&c,1)>0) write(STDOUT_FILENO,&c,1); // if new data is available on the serial port, print it out
if (read(STDIN_FILENO,&c,1)>0) write(fd,&c,1); // if new data is available on the console, send it to the serial port
}
}
What can i do in order to avoid the shell blocking?
ASKER
May be the problem is on lines 15 and 16:
because sometimes it happens that the program does not catch the "q" key and i have to terminate the program by pressing ctrl+c.
tcsetattr(STDOUT_FILENO,TCSANOW,&stdio);
tcsetattr(STDOUT_FILENO,TCSAFLUSH,&stdio);
because sometimes it happens that the program does not catch the "q" key and i have to terminate the program by pressing ctrl+c.
Ctrl-Z only stops the program - it's still there as a stopped process.
You only need one tcsetattr call. Definitely I would keep TCSAFLUSH and lose the other. Why would your program be making that call if it hasn't read 'q'? (I'm assuming that code snippet resets to original settings).
If you leave the terminal settings in an odd state, it is quite likely that the shell can't make sense of its input. Make a note of the pty you are running on before you start (ps will show it). In another window, use stty -F to display the shell's settings before you start. If it hangs after, check its settings to see if they are right. If you used Ctrl-Z, you will need to kill the program from another window.
You only need one tcsetattr call. Definitely I would keep TCSAFLUSH and lose the other. Why would your program be making that call if it hasn't read 'q'? (I'm assuming that code snippet resets to original settings).
If you leave the terminal settings in an odd state, it is quite likely that the shell can't make sense of its input. Make a note of the pty you are running on before you start (ps will show it). In another window, use stty -F to display the shell's settings before you start. If it hangs after, check its settings to see if they are right. If you used Ctrl-Z, you will need to kill the program from another window.
ASKER
The problem is that if I keep TCSAFLUSH, the program is not able to read 'q' key anymore and i must use CTRL-Z to stop the program; then, i have to kill the process from the shell.
I do not know why when the program start to receive data from USB port, it is not able to read 'q' key anymore and it can't stop.
I do not know why when the program start to receive data from USB port, it is not able to read 'q' key anymore and it can't stop.
ASKER
The program wasn't able to read 'q' because i was using getchar() to read it:
However, the problem is that when the program correctly reads 'q' key, it terminates but when i type in the terminal, i'm not able to see what i write.
This is the whole code i'm using:
while ( (c = getchar()) != 'q')
{
if (read(fd,&c,1)>0) write(STDOUT_FILENO,&c,1); // if new data is available on the serial port, print it out
if (read(STDIN_FILENO,&c,1)>0) write(fd,&c,1); // if new data is available on the console, send it to the serial port
}
However, the problem is that when the program correctly reads 'q' key, it terminates but when i type in the terminal, i'm not able to see what i write.
This is the whole code i'm using:
int main(int argc, char *argv[])
{
int fd = 0;
char serialport[256];
int baudrate = B9600; // default
char buf[256];
int rc,n;
if (argc==1) {
usage();
exit(EXIT_SUCCESS);
}
/* parse options */
int option_index = 0, opt;
static struct option loptions[] = {
{"help", no_argument, 0, 'h'},
{"port", required_argument, 0, 'p'},
{"baud", required_argument, 0, 'b'},
{"send", required_argument, 0, 's'},
{"receive", no_argument, 0, 'r'},
{"num", required_argument, 0, 'n'},
{"delay", required_argument, 0, 'd'}
};
while(1) {
opt = getopt_long (argc, argv, "hp:b:s:rn:d:",
loptions, &option_index);
if (opt==-1) break;
switch (opt) {
case '0': break;
case 'd':
n = strtol(optarg,NULL,10);
usleep(n * 1000 ); // sleep milliseconds
break;
case 'h':
usage();
break;
case 'b':
baudrate = strtol(optarg,NULL,10);
break;
case 'p':
strcpy(serialport,optarg);
fd = serialport_init(optarg, baudrate);
if(fd==-1) return -1;
break;
case 'n':
n = strtol(optarg, NULL, 10); // convert string to number
rc = serialport_writebyte(fd, (uint8_t)n);
if(rc==-1) return -1;
break;
case 's':
strcpy(buf,optarg);
rc = serialport_write(fd, buf);
if(rc==-1) return -1;
break;
case 'r':
serialport_read_until(fd);
break;
}
}
exit(EXIT_SUCCESS);
} // end main
int serialport_writebyte( int fd, uint8_t b)
{
int n = write(fd,&b,1);
if( n!=1)
return -1;
return 0;
}
int serialport_write(int fd, const char* str)
{
int len = strlen(str);
int n = write(fd, str, len);
if( n!=len )
return -1;
return 0;
}
int serialport_read_until(int fd)
{
while (c !='q')
{
if (read(fd,&c,1)>0) write(STDOUT_FILENO,&c,1); // if new data is available on the serial port, print it out
if (read(STDIN_FILENO,&c,1)>0) write(fd,&c,1); // if new data is available on the console, send it to the serial port
}
return 0;
}
// takes the string name of the serial port (e.g. "/dev/tty.usbserial","COM1")
// and a baud rate (bps) and connects to that port at that speed and 8N1.
// opens the port in fully raw mode so you can send binary data.
// returns valid fd, or -1 on error
int serialport_init(const char* serialport, int baud)
{
struct termios tio;
struct termios stdio;
int fd;
//fprintf(stderr,"init_serialport: opening port %s @ %d bps\n",
// serialport,baud);
memset(&stdio,0,sizeof(stdio));
stdio.c_iflag=0;
stdio.c_oflag=0;
stdio.c_cflag=0;
stdio.c_lflag=0;
stdio.c_cc[VMIN]=0;
stdio.c_cc[VTIME]=1;
tcsetattr(STDOUT_FILENO,TCSANOW,&stdio);
tcsetattr(STDOUT_FILENO,TCSAFLUSH,&stdio);
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); // make the reads non-blocking
memset(&tio,0,sizeof(tio));
// 8N1
tio.c_cflag &= ~PARENB;
tio.c_cflag &= ~CSTOPB;
tio.c_cflag &= ~CSIZE;
tio.c_cflag |= CS8;
// no flow control
tio.c_cflag &= ~CRTSCTS;
tio.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
tio.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
tio.c_oflag &= ~OPOST; // make raw
fd = open(serialport, O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd == -1) {
perror("init_serialport: Unable to open port ");
return -1;
}
cfsetospeed(&tio,B115200); // 115200 baud
cfsetispeed(&tio,B115200); // 115200 baud
tcsetattr(fd,TCSANOW,&tio);
return fd;
}
You must reset the termios settings of stdout (your terminal) to what they were when the program started before leaving the program. That means you must save a copy of toptions as soon as you read it with tcgetattr and re-assert it with tcsetattr as you exit (you can use the atexit function to do it - this would cater for multiple exit points).
ASKER
I tryed to do as you suggested: I saved the current settings with tcgetattr and i restored it at the exit with atexit, but the problem is still here because shell does not show what i type after the program termination.
Can you check the code i've posted below, please?
May be, I did some kind of errors which i'm not able to detect (i'm not very good in programming).
Can you check the code i've posted below, please?
May be, I did some kind of errors which i'm not able to detect (i'm not very good in programming).
void usage(void);
int serialport_init(const char* serialport, int baud);
int serialport_writebyte(int fd, uint8_t b);
int serialport_write(int fd, const char* str);
int serialport_read_until(int fd);
struct termios oldtio;
int fd;
void close_fd(){ <- I RESTORE THE PREVIOS SETTINGS
printf("\nOK\n");
tcsetattr(fd,TCSANOW,&oldtio);
}
void usage(void) {
printf("Usage: arduino-serial -p <serialport> [OPTIONS]\n"
"\n"
"Options:\n"
" -h, --help Print this help message\n"
" -p, --port=serialport Serial port Arduino is on\n"
" -b, --baud=baudrate Baudrate (bps) of Arduino\n"
" -s, --send=data Send data to Arduino\n"
" -r, --receive Receive data from Arduino & print it out\n"
" -n --num=num Send a number as a single byte\n"
" -d --delay=millis Delay for specified milliseconds\n"
"\n"
"Note: Order is important. Set '-b' before doing '-p'. \n"
" Used to make series of actions: '-d 2000 -s hello -d 100 -r' \n"
" means 'wait 2secs, send 'hello', wait 100msec, get reply'\n"
"\n");
}
int main(int argc, char *argv[])
{
int fd = 0;
char serialport[256];
int baudrate = B9600; // default
char buf[256];
int rc,n;
atexit (close_fd);
if (argc==1) {
usage();
exit(EXIT_SUCCESS);
}
/* parse options */
int option_index = 0, opt;
static struct option loptions[] = {
{"help", no_argument, 0, 'h'},
{"port", required_argument, 0, 'p'},
{"baud", required_argument, 0, 'b'},
{"send", required_argument, 0, 's'},
{"receive", no_argument, 0, 'r'},
{"num", required_argument, 0, 'n'},
{"delay", required_argument, 0, 'd'}
};
while(1) {
opt = getopt_long (argc, argv, "hp:b:s:rn:d:",
loptions, &option_index);
if (opt==-1) break;
switch (opt) {
case '0': break;
case 'd':
n = strtol(optarg,NULL,10);
usleep(n * 1000 ); // sleep milliseconds
break;
case 'h':
usage();
break;
case 'b':
baudrate = strtol(optarg,NULL,10);
break;
case 'p':
strcpy(serialport,optarg);
fd = serialport_init(optarg, baudrate);
if(fd==-1) return -1;
break;
case 'n':
n = strtol(optarg, NULL, 10); // convert string to number
rc = serialport_writebyte(fd, (uint8_t)n);
if(rc==-1) return -1;
break;
case 's':
strcpy(buf,optarg);
rc = serialport_write(fd, buf);
if(rc==-1) return -1;
break;
case 'r':
serialport_read_until(fd);
break;
}
}
exit(EXIT_SUCCESS);
} // end main
int serialport_writebyte( int fd, uint8_t b)
{
int n = write(fd,&b,1);
if( n!=1)
return -1;
return 0;
}
int serialport_write(int fd, const char* str)
{
int len = strlen(str);
int n = write(fd, str, len);
if( n!=len )
return -1;
return 0;
}
int serialport_read_until(int fd)
{
unsigned char c='D';
while (c !='q')
{
if (read(fd,&c,1)>0) write(STDOUT_FILENO,&c,1); // if new data is available on the serial port, print it out
if (read(STDIN_FILENO,&c,1)>0) write(fd,&c,1); // if new data is available on the console, send it to the serial port
}
return 0;
}
// takes the string name of the serial port (e.g. "/dev/tty.usbserial","COM1")
// and a baud rate (bps) and connects to that port at that speed and 8N1.
// opens the port in fully raw mode so you can send binary data.
// returns valid fd, or -1 on error
int serialport_init(const char* serialport, int baud)
{
struct termios tio;
struct termios stdio;
//fprintf(stderr,"init_serialport: opening port %s @ %d bps\n",
// serialport,baud);
tcgetattr(fd,&oldtio); <-- I SAVE THE CURRENT SETTINGS, IS IT CORRECT?
memset(&stdio,0,sizeof(stdio));
stdio.c_iflag=0;
stdio.c_oflag=0;
stdio.c_cflag=0;
stdio.c_lflag=0;
stdio.c_cc[VMIN]=0;
stdio.c_cc[VTIME]=1;
tcsetattr(STDOUT_FILENO,TCSANOW,&stdio);
tcsetattr(STDOUT_FILENO,TCSAFLUSH,&stdio);
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); // make the reads non-blocking
memset(&tio,0,sizeof(tio));
// 8N1
tio.c_cflag &= ~PARENB;
tio.c_cflag &= ~CSTOPB;
tio.c_cflag &= ~CSIZE;
tio.c_cflag |= CS8;
// no flow control
tio.c_cflag &= ~CRTSCTS;
tio.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
tio.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
tio.c_oflag &= ~OPOST; // make raw
fd = open(serialport, O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd == -1) {
perror("init_serialport: Unable to open port ");
return -1;
}
cfsetospeed(&tio,B115200); // 115200 baud
cfsetispeed(&tio,B115200); // 115200 baud
tcsetattr(fd,TCSANOW,&tio);
return fd;
}
close_fd looks OK. Do you see the OK message?
Did you try checking your shell's termios settings before / after running your program as I suggested in http:#a37409584 ?
Did you try checking your shell's termios settings before / after running your program as I suggested in http:#a37409584 ?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Also you should check the return code from tcsetattr and print a message if it failed
ASKER
Thanks a lot for your advices!
Finally, my code works great!
I had to save and restore the correct configuration before closing the program.
Finally, my code works great!
I had to save and restore the correct configuration before closing the program.
ASKER
Thanks for your help!
Also I think you have VMIN and VTIME wrong way round - I use VMIN=1 and VTIME=0 always.
I suspect tail cat &c. do not use termios at all - you could verify this with strace. If they don't, then maybe neither should you.Possibly the USB serial driver ignores termios settings once there has been some i/o. Otherwise it's still a mystery why it works after you use them.
USB serial devices don't usually care about baud rate in my experience
The settings when the device is first plugged in may be fine. Use stty -F /dev/ttyACM0 -a to check this (likely needs to be root or use sudo)