Serial port programming in Linux - c

I want to use RS232 port on the board to communicate with a PC. I understand that I can use "dev/ttyS0" for that purpose.
I can open and write data to PC by using write() function. But the problem is I can't read from "dev/ttyS0". Every time I use read function, I get "Resource temporarily unavailable". Can u guys tell me how to solve this problem?
Here is my code:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
int main()
{
int n = 0, fd = 0, bytes = 0;
char ch = 0;
char buffer[10], *bufPtr;
int nBytes = 0, tries = 0, x = 0;
struct termios term;
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
perror("open");
return;
}
else
{
fcntl(fd, F_SETFL, 0);
perror("Port");
}
if (n = tcgetattr(fd, &term) == -1)
{
perror("tcgetattr");
return;
}
if (n = cfsetispeed(&term, B115200) == -1)
{
perror("cfsetispeed");
return;
}
if (n = cfsetospeed(&term, B115200) == -1)
{
perror("cfsetospeed");
return;
}
term.c_cflag |= (CLOCAL | CREAD);
term.c_cflag &= ~PARENB;
term.c_cflag &= ~CSTOPB;
term.c_cflag &= ~CSIZE;
term.c_cflag |= CS8;
term.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
term.c_iflag &= ~(IXON | IXOFF | IXANY);
term.c_cflag &= ~CRTSCTS;
term.c_oflag &= ~OPOST;
if (n = tcsetattr(fd, TCSANOW, &term) == -1)
{
perror("tcsetattr");
return;
}
write(fd,"LINUX",5);
perror("write");
fcntl(fd, F_SETFL, FNDELAY);
perror("fcntl");
bytes = read(fd, buffer, sizeof(buffer));
perror("read");
printf("Bytes : %d and data: %s\n", bytes, buffer);
}

What is your test? Are you shortcutting pin 2 and 3 of your RS232 DB9 connector?
Otherwise read will always return -1 if there are no data to be read.
This is what your code is supposed to do using O_NDELAY flag opening the serial line.
To avoid to read if there are no data to be read, you could use select
In your case:
struct timeval tv;
fd_set rset;
int retV;
int timeout = 5000; // 5 seconds
tv.tv_usec = (timeout * 1000) % 1000000;
tv.tv_sec = timeout / 1000;
FD_ZERO ( &rset );
FD_SET ( fd, &rset );
retV = select ( fd+1, &rset, NULL, NULL, &tv );
if( retV == 0 )
{
// timeout stuff
}
else if( retV < 0 )
{
// Error stuff. Read errno to see what happened
}
else
{
// read data
}
EDIT
Remove fcntl(fd, F_SETFL, FNDELAY);
If you want a normal blocking read, unset that flag.
In your code you are also assuming that read will return all sent chars but is not true. read will return chars that are available to be read. This means that if you send "LINUX" a 5 times read could be requested to read all 5 chars sent.
Last thing
printf("Bytes : %d and data: %s\n", bytes, buffer);
is Undefined Behavior because of buffer is not a NULL terminated string. You could solve it looping on received chars and print it with %c format, or NULL terminating your buffer with:
if (bytes > 0)
buffer[bytes] = '\0';
or
char stringToSend[] = "LINUX";
size_t len = strlen(stringToSend) +1 ;
write(fd,"LINUX", len);
perror("write");
size_t receivedBytes = 0;
bytes = 0;
while (receivedBytes<len)
{
bytes = read(fd, &buffer[receivedBytes], sizeof(buffer)-1);
perror("read");
if (bytes > 0)
receivedBytes += bytes;
}
printf("Bytes : %d and data: %s\n", receivedBytes, buffer);

Related

How to read serial with interrupt serial?

I'm trying to read NMEA message in Linux. But I can't get a completely message:
54.441,V,,,,,0.00,0.00,010720,,,N*42
$GPVTG,0.00,T,,M,0.00,N,0.00,K,N*32
$GPGGA,020954.441,,,,,0,0,,,M,,M,,*43
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,1,1,00*79
$GLGSV,1,1,00*65
$GPGLL,,,,,020954.441,V,N*71
$GP
The first line and last line is the one message but it have been splited. I thing, It's cause by sleep 1 second. And It's not right at all. I think I should use interrupt serial.
My idea is when data in come, interrupt serial will run a function which read serial and handle it. After that, system will sleep until next message. I searched some material but It's doesn't help.
This is my new code and it's not working:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <errno.h>
#include <termios.h>
void signal_handler_IO ();
int fd;
int connected;
struct termios termAttr;
struct sigaction saio;
int main(int argc, char *argv[])
{
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
perror("open_port: Unable to open port\n");
exit(1);
}
saio.sa_handler = signal_handler_IO;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO,&saio,NULL);
fcntl(fd, F_SETFL, FNDELAY);
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, O_ASYNC );
tcgetattr(fd,&termAttr);
cfsetispeed(&termAttr,B9600);
cfsetospeed(&termAttr,B9600);
termAttr.c_cflag &= ~PARENB;
termAttr.c_cflag &= ~CSTOPB;
termAttr.c_cflag &= ~CSIZE;
termAttr.c_cflag |= CS8;
termAttr.c_cflag |= (CLOCAL | CREAD);
termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
termAttr.c_oflag &= ~OPOST;
tcsetattr(fd,TCSANOW,&termAttr);
printf("UART1 configured....\n");
while(1){
sleep(1);
}
close(fd);
exit(0);
}
void signal_handler_IO ()
{
FILE *csv;
char buff [1024];
int n = read(fd, &buff, sizeof(buff));
char * token = strtok(buff, ",");
csv=fopen("csvfile.csv","w");
while( token != NULL ) {
fprintf(csv,"%s\n",token);
token = strtok(NULL, ",");
}
fclose(csv);
}
What should I do now ?
NMEA message are lines, ending with a '\n'.
Change read() to fgets() (open using fopen()) and read as a line into a string for later strtok() processing.
See also #Craig Estey ideas.
This is prefaced by my top comment.
thank you for your positive response. Did you mean I should use read() function like my old code ? And actually, I have never working with select before. But I very interesting with your idea. And I hope you can show me more the way which apply on my case.
Okay, here's a simple [and untested] version that does not use a signal handler. And, I'm using poll instead of select [they are similar] because it's easier to use.
Note that you opened the TTY device file with O_NDELAY, so the read call is non-blocking.
Also note that the open device will not produce an EOF condition either the way you did it or the way I'm doing it.
So, you'll have to have code that looks for a line that signifies the last line (e.g. $GP).
Or, after an initial wait the first time in the loop, a subsequent timeout should indicate no more input
Anyway, here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <errno.h>
#include <termios.h>
#if 1
#include <poll.h>
#endif
void signal_handler_IO(); /* definition of signal handler */
int fd;
struct termios termAttr;
struct sigaction saio;
struct pollfd fdpoll;
int
main(int argc, char *argv[])
{
int timout;
FILE *fout = NULL;
int buf_has_GP = 0;
int lastchr = -1;
int curchr;
int err;
int rlen;
int idx;
char buf[1000];
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror("open_port: Unable to open port\n");
exit(1);
}
#if 0
saio.sa_handler = signal_handler_IO;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO, &saio, NULL);
#endif
fcntl(fd, F_SETFL, FNDELAY);
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, O_ASYNC);
tcgetattr(fd, &termAttr);
cfsetispeed(&termAttr, B9600);
cfsetospeed(&termAttr, B9600);
termAttr.c_cflag &= ~PARENB;
termAttr.c_cflag &= ~CSTOPB;
termAttr.c_cflag &= ~CSIZE;
termAttr.c_cflag |= CS8;
termAttr.c_cflag |= (CLOCAL | CREAD);
termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
termAttr.c_oflag &= ~OPOST;
tcsetattr(fd, TCSANOW, &termAttr);
printf("UART1 configured....\n");
fout = fopen("csvfile.csv","w");
fdpoll.fd = fd;
fdpoll.events = POLLIN;
timout = 10000;
while (1) {
err = poll(&fdpoll,1,timout);
// timeout
if (err == 0)
break;
// error
if (err < 0) {
fprintf(stderr,"error -- %s\n",strerror(errno));
break;
}
// err will always be _one_ because poll's second arg is 1
while (1) {
rlen = read(fd,buf,sizeof(buf));
if (rlen <= 0)
break;
fwrite(buf,1,rlen,fout);
// need to check buf looking for last line (e.g. $GP)
// to know when to stop
// since read is _not_ line oriented we have to check for G followed
// by P [and they may or may not occur in the same read call]
// FIXME -- this is quite crude -- just to illustrate
for (idx = 0; idx < rlen; ++idx) {
curchr = buf[idx];
buf_has_GP = ((lastchr == 'G') && (curchr == 'P'));
if (buf_has_GP)
break;
lastchr = curchr;
}
if (buf_has_GP)
break;
}
if (buf_has_GP)
break;
timout = 1000;
#if 0
sleep(1);
#endif
}
close(fd);
fclose(fout);
exit(0);
}
void
signal_handler_IO()
{
FILE *csv;
FILE *ff;
char buff[1024];
ff = fopen("/dev/ttyUSB0", "r");
// int n = read(fd, &buff, sizeof(buff));
fgets(buff, sizeof(buff), ff);
char *token = strtok(buff, ",");
csv = fopen("csvfile.csv", "w");
while (token != NULL) {
fprintf(csv, "%s\n", token);
token = strtok(NULL, ",");
}
sleep(0.2);
fclose(csv);
}
UPDATE:
Thank you so much for spend your time for me. I compiled it without error. Unfortunately, I get nothing from output and empty file.
This may have been because of the simple/crude EOF string detection code. I think it could have stopped prematurely. I've added more robust checking.
I've also added debug printing (if -d is given).
Because I don't have access to a real ttyUSB device, I've added test code using a PTY [pseudo TTY]. To use this, put the sample NMEA data into a file (e.g. input.txt) and add -pinput.txt to the arguments.
This was how I was able to debug the general program flow.
I've turned off any unnecessary fcntl options.
If, after trying this, you still have issues, you may wish to test your device interface with a terminal program (e.g. minicom) to verify that the remote device is, indeed, sending data.
If minicom produces output, but your program doesn't, you may have to modify some of the termios options.
Some usbtty/uart devices need RTS/CTS [I've actually used such a device for work]. minicom has a config option to deal with this.
In the program [although I suspect it's off by default], you may need to disable RTS/CTS hardware so that the port doesn't get hung up. And/or ensure that XON/XOFF flow control is disabled.
Or, the remote device needs RTS/CTS support [you have to somehow force the remote device to see CTS high]. Although unlikely, this might have to be done in the cable itself.
Anyway, here's the updated code:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <errno.h>
#include <termios.h>
#if 1
#include <poll.h>
#include <pty.h>
#include <sys/wait.h>
#include <time.h>
#endif
#ifndef RAWOUT
#define RAWOUT 1
#endif
void signal_handler_IO(); /* definition of signal handler */
const char *ttydev = "/dev/ttyUSB0";
int fd;
int opt_d; // 1=debug print
char *opt_pty; // PTY input file
int ptypid;
#define PTYSLP 1
FILE *fout = NULL;
struct termios termAttr;
struct sigaction saio;
struct pollfd fdpoll;
int linelen;
char linebuf[1000];
#define SHOWPOLL(_msk) \
if (events & _msk) \
bp += sprintf(bp,"/" #_msk)
typedef long long tsc_t;
tsc_t
tscget(void)
{
struct timespec ts;
tsc_t tsc;
static tsc_t tsczero = 0;
clock_gettime(CLOCK_REALTIME,&ts);
tsc = ts.tv_sec;
tsc *= 1000000000;
tsc += ts.tv_nsec;
if (tsczero == 0)
tsczero = tsc;
tsc -= tsczero;
return tsc;
}
double
tscsec(tsc_t tsc)
{
double sec;
sec = tsc;
sec /= 1e9;
return sec;
}
void
tscprt(void)
{
tsc_t tsc;
tsc = tscget();
printf("%.9f ",tscsec(tsc));
}
#define dbgprt(_fmt...) \
do { \
if (! opt_d) \
break; \
int sverr = errno; \
tscprt(); \
printf(_fmt); \
errno = sverr; \
} while (0)
// dopty -- generate pseudo TTY test device
void
dopty(void)
{
int fdm;
int fdtxt;
int rlen;
int wlen;
int off;
char buf[1000];
#if 0
fdm = open("/dev/pts/ptmx",O_RDWR | O_NDELAY);
#else
fdm = getpt();
#endif
if (fdm < 0) {
perror("dopty/open");
exit(1);
}
dbgprt("dopty: GETPT fdm=%d\n",fdm);
ttydev = ptsname(fdm);
dbgprt("dopty: PTSNAME ttydev=%s\n",ttydev);
grantpt(fdm);
unlockpt(fdm);
dbgprt("dopty: INPUT opt_pty=%s\n",opt_pty);
do {
ptypid = fork();
if (ptypid != 0) {
close(fdm);
break;
}
// open sample test data file
fdtxt = open(opt_pty,O_RDONLY);
if (fdtxt < 0) {
perror("dopty/open");
exit(1);
}
sleep(PTYSLP);
while (1) {
rlen = read(fdtxt,buf,sizeof(buf));
if (rlen <= 0)
break;
dbgprt("dopty: READ rlen=%d\n",rlen);
for (off = 0; off < rlen; off += wlen) {
wlen = rlen - off;
wlen = write(fdm,&buf[off],wlen);
dbgprt("dopty: WRITE wlen=%d\n",wlen);
}
}
sleep(PTYSLP);
dbgprt("dopty: CLOSE\n");
close(fdtxt);
close(fdm);
sleep(PTYSLP);
dbgprt("dopty: EXIT\n");
exit(0);
} while (0);
}
char *
showpoll(short events)
{
char *bp;
static char buf[1000];
bp = buf;
bp += sprintf(bp,"%4.4X",events);
SHOWPOLL(POLLIN);
SHOWPOLL(POLLPRI);
SHOWPOLL(POLLOUT);
SHOWPOLL(POLLRDHUP);
SHOWPOLL(POLLERR);
SHOWPOLL(POLLHUP);
return buf;
}
// lineadd -- add character to line buffer
void
lineadd(int chr)
{
char *bp;
char buf[10];
if (opt_d) {
bp = buf;
*bp = 0;
if ((chr >= 0x20) && (chr <= 0x7E))
bp += sprintf(bp," '%c'",chr);
dbgprt("showchr: CHR chr=%2.2X%s\n",chr,buf);
}
linebuf[linelen++] = chr;
linebuf[linelen] = 0;
}
// eoftst -- decide if current line is the last line
int
eoftst(void)
{
static char *eofstr = "$GP\n";
static int eoflen = 0;
int stopflg = 0;
if (eoflen == 0)
eoflen = strlen(eofstr);
stopflg = ((linelen == eoflen) && (memcmp(linebuf,eofstr,eoflen) == 0));
dbgprt("eoftst: %s\n",stopflg ? "STOP" : "CONT");
return stopflg;
}
int
main(int argc, char **argv)
{
int timout;
int buf_has_eof = 0;
int curchr;
int err;
int rlen;
int idx;
char buf[1000];
--argc;
++argv;
setlinebuf(stdout);
setlinebuf(stderr);
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'd':
opt_d = ! opt_d;
break;
case 'p':
opt_pty = (*cp != 0) ? cp : "input.txt";
break;
}
}
do {
// create test device
if (opt_pty != NULL) {
dopty();
break;
}
if (argc > 0) {
ttydev = *argv;
--argc;
++argv;
}
} while (0);
dbgprt("main: TTYDEV ttydev=%s\n",ttydev);
fd = open(ttydev, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror("open_port: Unable to open port\n");
exit(1);
}
#if 0
saio.sa_handler = signal_handler_IO;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO, &saio, NULL);
#endif
// not needed unless doing signal handler
#if 0
fcntl(fd, F_SETFL, FNDELAY);
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, O_ASYNC);
#endif
#if 1
tcgetattr(fd, &termAttr);
#endif
#if 1
cfsetispeed(&termAttr, B9600);
cfsetospeed(&termAttr, B9600);
#endif
// force immediate return from device read if no chars available
#if 1
dbgprt("main: CC VMIN=%d VTIME=%d\n",
termAttr.c_cc[VMIN],termAttr.c_cc[VTIME]);
termAttr.c_cc[VMIN] = 0;
termAttr.c_cc[VTIME] = 0;
#endif
termAttr.c_cflag &= ~PARENB;
termAttr.c_cflag &= ~CSTOPB;
termAttr.c_cflag &= ~CSIZE;
termAttr.c_cflag |= CS8;
termAttr.c_cflag |= (CLOCAL | CREAD);
// FIXME -- you may need to handle this
#if 1
termAttr.c_cflag &= ~CRTSCTS;
#else
termAttr.c_cflag |= CRTSCTS;
#endif
termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
termAttr.c_oflag &= ~OPOST;
#if 1
tcsetattr(fd, TCSANOW, &termAttr);
#endif
printf("UART1 configured....\n");
// open output file
fout = fopen("csvfile.csv","w");
if (fout == NULL) {
perror("main/fopen");
exit(1);
}
fdpoll.fd = fd;
fdpoll.events = POLLIN;
fdpoll.revents = 0;
// set initial timeout of 10 seconds
timout = 10000;
// NOTE: iter is just for testing to prevent infinite looping if failure to
// read or match the EOF string
for (int iter = 1; iter < 10; ++iter) {
dbgprt("main: POLL iter=%d events=%s timout=%d\n",
iter,showpoll(fdpoll.events),timout);
err = poll(&fdpoll,1,timout);
dbgprt("main: POLL revents=%s err=%d\n",showpoll(fdpoll.revents),err);
// timeout
if (err == 0)
break;
// error
if (err < 0) {
fprintf(stderr,"error -- %s\n",strerror(errno));
break;
}
// err will always be _one_ because poll's second arg is 1
// process all data in current chunk
while (1) {
rlen = read(fd,buf,sizeof(buf));
dbgprt("main: READ iter=%d rlen=%d\n",iter,rlen);
if (rlen <= 0)
break;
// send data to output file
#if RAWOUT
fwrite(buf,1,rlen,fout);
#endif
// need to check buf looking for last line (e.g. $GP)
// to know when to stop
// since read is _not_ line oriented we have to check for G followed
// by P [and they may or may not occur in the same read call]
// FIXME -- this is quite crude -- just to illustrate
for (idx = 0; idx < rlen; ++idx) {
curchr = buf[idx];
// add to line buffer
lineadd(curchr);
// wait for newline
if (curchr != '\n')
continue;
// decide if this is the last line of the current NMEA message
buf_has_eof = eoftst();
#if (! RAWOUT)
// do processing on line buffer ...
#endif
// reset line buffer index/length for next line
linelen = 0;
if (buf_has_eof)
break;
}
if (buf_has_eof)
break;
}
if (buf_has_eof)
break;
// set 1 second timeout for subsequent reads
timout = 1000;
#if 0
sleep(1);
#endif
}
close(fd);
fclose(fout);
// reap any child processes [only if doing PTY mode]
while (opt_pty != NULL) {
pid_t pid = wait(NULL);
dbgprt("main: WAIT pid=%d\n",pid);
if (pid <= 0)
break;
}
exit(0);
}
void
signal_handler_IO()
{
FILE *csv;
FILE *ff;
char buff[1024];
ff = fopen("/dev/ttyUSB0", "r");
// int n = read(fd, &buff, sizeof(buff));
fgets(buff, sizeof(buff), ff);
char *token = strtok(buff, ",");
csv = fopen("csvfile.csv", "w");
while (token != NULL) {
fprintf(csv, "%s\n", token);
token = strtok(NULL, ",");
}
sleep(0.2);
fclose(csv);
}

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.

C - Weird termios behaviour when calling read() function Linux

I encountered problem when reading ELM327 chip over serial port.
This is my code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h
#include <sys/ioctl.h>
#define DEVICE "/dev/ttyUSB0"
#define SPEED B38400
int main()
{
struct termios old_stdio; //save the current port settings
int tty_fd; //file descriptor for serial port
int retval, res, n, res2, read1, wri;
char buf[255];
char buf2[255];
tty_fd = open(DEVICE, O_RDWR | O_NOCTTY | O_NDELAY );
if(tty_fd < 0)
{
perror(DEVICE);
exit(-1);
}
printf("Init 1 complete.\n");
tcflush(tty_fd, TCIOFLUSH);
fcntl(tty_fd, F_SETFL, 0);
if(tcgetattr(tty_fd, &old_stdio) != 0)
{
perror(DEVICE);
exit(-1);
}
printf("Init 2 complete.\n");
struct termios newtio;
if(tcgetattr(tty_fd, &newtio) != 0)
{
perror(DEVICE);
exit(-1);
}
cfsetospeed(&newtio, B38400);
cfsetispeed(&newtio, B38400);
printf("Init 3 complete.\n");
newtio.c_cflag |= CLOCAL | CREAD | HUPCL;
newtio.c_iflag |= ICRNL | IGNPAR;
newtio.c_lflag &= !(ICANON | ISIG);
newtio.c_lflag |= ECHOK | ECHOE | ECHOKE;
newtio.c_oflag |= ONLCR;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
if(tcsetattr(tty_fd, TCSANOW, &newtio) != 0)
{
perror(DEVICE);
exit(-1);
}
printf("Init 4 complete.\n");
for(n = 55; n > 0; n--)
{
printf("Please enter a command: ");
(void)fgets(buf2, 255, stdin);
printf("Ok.input was %s ",&buf2);
(void)write(tty_fd, buf2, strlen(buf2));
(void)write(tty_fd, "\r", 1);
printf("Ok. Waiting for reply\n");
res = read(tty_fd, &buf, 255);
printf("Read:%d %s \n",res, &buf);
}
//restore the original port settings
tcsetattr(tty_fd, TCSANOW, &old_stdio);
close(tty_fd);
return EXIT_SUCCESS; //return all good
}
I can write to device but after writing atz read function isn't reading anything. But after hitting enter \n one more time it prints output in terminal.
Init 1 complete.
Init 2 complete.
Init 3 complete.
Init 4 complete.
Please enter a command: atz
Ok.input was atz
Ok. Waiting for reply
Read:0
Please enter a command:
Ok.input was
Ok. Waiting for reply
Read:20 atz
ELM327 v1.5
>�
Please enter a command:
Dose anyone have idea why is this happening??

Receive from UART on Pi

I'm new here.
I'm trying receive data from UART which is from Xbee.
The sample code below is allow me to check the API frame.
But what I need is make these to be a useful array.
How should i make this into a complete array?
The result shows what i need but they are independent.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> //Used for UART
#include <fcntl.h> //Used for UART
#include <termios.h> //Used for UART
int main(int argc, char const *argv[])
{
//-------------------------
//----- SETUP USART 0 -----
//-------------------------
//At bootup, pins 8 and 10 are already set to UART0_TXD, UART0_RXD (ie the alt0 function) respectively
int uart0_filestream = -1;
//OPEN THE UART
//The flags (defined in fcntl.h):
// Access modes (use 1 of these):
// O_RDONLY - Open for reading only.
// O_RDWR - Open for reading and writing.
// O_WRONLY - Open for writing only.
//
// O_NDELAY / O_NONBLOCK (same function) - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status
// if there is no input immediately available (instead of blocking). Likewise, write requests can also return
// immediately with a failure status if the output can't be written immediately.
//
// O_NOCTTY - When set and path identifies a terminal device, open() shall not cause the terminal device to become the controlling terminal for the process.
uart0_filestream = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode
if (uart0_filestream == -1)
{
//ERROR - CAN'T OPEN SERIAL PORT
printf("Error - Unable to open UART. Ensure it is not in use by another application\n");
}
struct termios options;
tcgetattr(uart0_filestream, &options);
options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; //<Set baud rate
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
tcflush(uart0_filestream, TCIFLUSH);
tcsetattr(uart0_filestream, TCSANOW, &options);
//----- TX BYTES -----
unsigned char tx_buffer[20];
unsigned char *p_tx_buffer;
p_tx_buffer = &tx_buffer[0];
*p_tx_buffer++ = 'H';
*p_tx_buffer++ = 'e';
*p_tx_buffer++ = 'l';
*p_tx_buffer++ = 'l';
*p_tx_buffer++ = 'o';
if (uart0_filestream != -1)
{
int count = write(uart0_filestream, &tx_buffer[0], (p_tx_buffer - &tx_buffer[0]));
//Filestream, bytes to write, number of bytes to write
if (count < 0)
{
printf("UART TX error\n");
}
}
char r[100];
while(1){
//----- CHECK FOR ANY RX BYTES -----
if (uart0_filestream != -1)
{
//printf("OK");
// Read up to 255 characters from the port if they are there
unsigned char rx_buffer[256];
int rx_length = read(uart0_filestream, (void*)rx_buffer, 255); //Filestream, buffer to store in, number of bytes to read (max)
if (rx_length < 0)
{
//An error occured (will occur if there are no bytes)
}
else if (rx_length == 0)
{
//No data waiting
}
else
{
//Bytes received
rx_buffer[rx_length] = '\0';
printf("%i bytes read : %s\n", rx_length, rx_buffer);
//printf("%d\n",rx_buffer[0]);
}
}
}
return 0;
}

How to convert char to int and then display it in hex format

I am making a program in c in which I am getting data from serial device and I am storing it in buffer char receivebuffer[100] . when I display the contents of receivebuffer, the output shows � (Is this an ASCII format) . But the expected output is in hex format. How can i convert it in hex?
I also want to know that if I convert buffer to int, will the output be same. Please tell me how can I convert char buffer to int also?
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int set_interface_attribs (int fd, int speed, int parity)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
printf("error %d from tcgetattr\n\n", errno);
printf("Error Opening the device\n\n");
exit(0);
//error_message ("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)
{
printf("error %d from tcsetattr\n\n", errno);
printf("Error Opening the device\n\n");
exit(0);
//error_message ("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)
{
printf("error\n\n");
printf("Error Opening the device\n\n");
exit(0);
//error_message ("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)
printf("Error Opening the device\n\n");
//error_message ("error %d setting term attributes", errno);
}
int main()
{
char *portname = "/dev/ttyUSB0";
int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0)
{
//error_message ("error %d opening %s: %s", errno, portname, strerror (errno));
printf("error");
}
set_interface_attribs (fd, B9600, 0); // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (fd, 0); // set no blocking
// send 7 character greeting
usleep ((7 + 25) * 100); // sleep enough to transmit the 7 plus
while(1)
{
char receivebuffer [100];
read (fd, receivebuffer, sizeof receivebuffer); // read up to 100 characters if ready to read
printf("value of buffer is %s\n\n", receivebuffer);
return 0;
}
}
You need to store the count of received bytes somewhere and use that in a for loop try it like this
char receivebuffer[100];
int count;
int i;
count = read (fd, receivebuffer, sizeof receivebuffer); // read up to 100 characters if ready to read
for (i = 0 ; i < count ; ++i)
{
printf("0x%02X ", receivebuffer[i]);
if ((i + 1) % 8 == 0)
printf("\n");
}
this if ((i + 1) % 8 == 0) is just to print 8 bytes in a row, you can change or remove it, it helps inspecting the data though.
You should replace this:
printf("value of buffer is %s\n\n", receivebuffer);
with:
for (int tmpfoo = 0; receivebuffer[tmpfoo] != '\0'; tmpfoo++)
{
printf("value of buffer is %X\n\n", (int)receivebuffer[tmpfoo]);
}
If you want it just to be followed HEXvalue by HEXvalue.
You don't need to convert it. To display char as a hexadecimal number, use %hhx formatting in printf-group of functions.

Resources