Reading from Serial Port in linux using C language - c

I am new to serail programing in Linux (Fedora 12) using C Language. I have a simple device, it is supposed to get 3 bytes command in hex (wirte) like {0x02,0x03,0x0D} and return 4 bytes response.
First I wrote a simple java program on windows, and I get the correct response.. as and when,I switch to linux, I can't read from serial com port neither using java nor C language.
I tried using libraries like rs232 .. but still the problem remains.
I can open "/dev/ttyS0", and write on it .. (none of them returns any Error), but read is not possible ..
If I use canonical mode, the program blocks on reading until i kill the program.. If use non-canonical mode, with VMIN=0 and VTIME=5, read function returns whith -107725432 bytes for example ..
(I have tried reading and writing byte by byte or all at the same time .. no difference ..)
rs232.c
#include "rs232.h"
int Cport[6],
error;
struct termios new_port_settings,
old_port_settings[6];
char comports[6][16]={"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2","/dev/ttyS3","/dev/ttyS4","/dev/ttyS5"};
int RS232_OpenComport(int comport_number, int baudrate, const char *mode)
{
int baudr,
status;
if((comport_number>5)||(comport_number<0))
{
printf("illegal comport number\n");
return(1);
}
switch(baudrate)
{
case 2400 : baudr = B2400;
break;
case 4800 : baudr = B4800;
break;
case 9600 : baudr = B9600;
break;
case 19200 : baudr = B19200;
break;
default : printf("invalid baudrate\n");
return(1);
break;
}
int cbits=CS8,
cpar=0,
ipar=IGNPAR,
bstop=0;
if(strlen(mode) != 3)
{
printf("invalid mode \"%s\"\n", mode);
return(1);
}
switch(mode[0])
{
case '8': cbits = CS8;
break;
case '7': cbits = CS7;
break;
case '6': cbits = CS6;
break;
case '5': cbits = CS5;
break;
default : printf("invalid number of data-bits '%c'\n", mode[0]);
return(1);
break;
}
switch(mode[1])
{
case 'N':
case 'n': cpar = 0;
ipar = IGNPAR;
break;
case 'E':
case 'e': cpar = PARENB;
ipar = INPCK;
break;
case 'O':
case 'o': cpar = (PARENB | PARODD);
ipar = INPCK;
break;
default : printf("invalid parity '%c'\n", mode[1]);
return(1);
break;
}
switch(mode[2])
{
case '1': bstop = 0;
break;
case '2': bstop = CSTOPB;
break;
default : printf("invalid number of stop bits '%c'\n", mode[2]);
return(1);
break;
}
Cport[comport_number] = open(comports[comport_number], O_RDWR | O_NOCTTY);
if(Cport[comport_number]==-1)
{
perror("unable to open comport ");
return(1);
}
/* lock access so that another process can't also use the port */
if(flock(Cport[comport_number], LOCK_EX | LOCK_NB) != 0)
{
close(Cport[comport_number]);
perror("Another process has locked the comport.");
return(1);
}
error = tcgetattr(Cport[comport_number], old_port_settings + comport_number);
if(error==-1)
{
close(Cport[comport_number]);
perror("unable to read portsettings ");
return(1);
}
memset(&new_port_settings, 0, sizeof(new_port_settings)); /* clear the new struct */
new_port_settings.c_cflag = cbits | cpar | bstop | CLOCAL | CREAD;
new_port_settings.c_iflag = ipar;
new_port_settings.c_oflag = 0;
new_port_settings.c_lflag = 0;
new_port_settings.c_cc[VMIN] = 0; /* block untill n bytes are received */
new_port_settings.c_cc[VTIME] = 5; /* block untill a timer expires (n * 100 mSec.) */
cfsetispeed(&new_port_settings, baudr);
cfsetospeed(&new_port_settings, baudr);
error = tcsetattr(Cport[comport_number], TCSANOW, &new_port_settings);
if(error==-1)
{
close(Cport[comport_number]);
perror("unable to adjust portsettings ");
return(1);
}
if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1)
{
perror("unable to get portstatus");
return(1);
}
status |= TIOCM_DTR; /* turn on DTR */
status |= TIOCM_RTS; /* turn on RTS */
if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1)
{
perror("unable to set portstatus");
return(1);
}
return(0);
}
int RS232_PollComport(int comport_number, unsigned char *buf, int size)
{
int n;
n = read(Cport[comport_number], buf, size);
return(n);
}
int RS232_SendBuf(int comport_number, unsigned char *buf, int size)
{
return(write(Cport[comport_number], buf, size));
}
void RS232_CloseComport(int comport_number)
{
int status;
if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1)
{
perror("unable to get portstatus");
}
status &= ~TIOCM_DTR; /* turn off DTR */
status &= ~TIOCM_RTS; /* turn off RTS */
if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1)
{
perror("unable to set portstatus");
}
tcsetattr(Cport[comport_number], TCSANOW, old_port_settings + comport_number);
close(Cport[comport_number]);
flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */
}
void RS232_flushRXTX(int comport_number)
{
tcflush(Cport[comport_number], TCIOFLUSH);
}
and
main.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "rs232.h"
int main()
{
int cport_nr=0, bdrate=9600; /* 9600 baud */
char mode[]={'8','N','1',0},
str[512];
unsigned char buf[6000];
memset(buf, '\0' , 6000);
int buf_SIZE=sizeof(buf);
if(RS232_OpenComport(cport_nr, bdrate, mode))
{
printf("Can not open comport\n");
return(0);
}
unsigned char wr_buff[5];
memset(wr_buff, '\0', 5);
wr_buff[0] = 0x02;
wr_buff[1] = 0x01;
wr_buff[2] = 0x0D;
int cnt = RS232_SendBuf(cport_nr, wr_buff, 3);
printf("Number of bytes that has been written: %d\n",cnt);
if (cnt <= 0 )
return(-1);
cnt =0 ;
usleep(100000);
printf("Start Reading ... \n");
int i = 0;
do {
cnt = RS232_PollComport(cport_nr,(buf+i), 1);
i++;}
while(cnt>0);
printf ("%d bytes have been read\n");
RS232_CloseComport(cport_nr);
return (1);
}
I am really confused .. I tried almost every samples on the Internet .. An
y Idea Please?!
I have traced the program, using strace ..
...
write(3,"\2\3\r",3) = 3
fstat64(1 , {st_mode=S_IFREG| 0755, st_size =0,..}) = 0
mmap2(NULL,4096,PROT_READ|PROT_WRITE, MAP_PRIVATE| MAP_ANONYMOUS,-1,0) = 0Xb78b4000
nanosleep({0, 100000000}, NULL) = 0
read(3,"",1) = 0
.....
Can the problem be related to fedora12?
P.S. : if I haven't got response in windows, I was sure that its a problem with the device.

Solved ...
it was a mistake of me ...
In fact, every thing was ok,
the problem was in the way I print number of bytes have been read.. I had forgotten to put "cnt" in respect of %d ...
printf ("%d bytes have been read\n"); --> printf ("%d bytes have been read\n",cnt);
also i should have read only 4 bytes based on the protocol of communication with my device ..

Related

Empty packet on SOCK_SEQPACKET Unix Socket

I'm playing with SOCK_SEQPACKET type on Unix sockets.
The code I'm using for reading is the classic
ssize_t recv_size = recv(sd, buffer, sizeof(buffer), 0);
if (recv_size < 0) {
handle_error("recv", errno);
} else if (recv_size > 0) {
handle_packet(buffer, recv_size);
} else {
// recv_size == 0 => peer closed socket.
handle_end_of_stream();
}
While this works just fine, I noticed it is not able to distinguish between a socket closure and a message of size 0. In other words, if on the other end I issue a sequence of calls like this:
send(sd, "hello", strlen("hello"), 0);
send(sd, "", 0, 0);
send(sd, "world", strlen("world"), 0);
…the reader will only receive "hello" and react to the second message with a socket closure, missing the "world" message entirely.
I was wondering if there's some way to disambiguate between the two situations.
What if you make some sort of "confirmation" function on both ends. For example instead of handle_end_of_stream, make something like this:
->send(xx, "UNIQUE_MESSAGE", strlen("UNIQUE_MESSAGE"), 0);
< you receive "UNIQUE_RESPONSE" if connection is still up, if you don't, you know for sure that the other end is closed. Just filter out some sort of "UNIQUE_MESSAGE" and "UNIQUE_RESPONSE" inf your "confirmation" function.
As I mentioned in a comment, zero-length seqpackets (as well as zero-length datagrams) can behave oddly, typically mistaken for disconnects; and for that reason, I definitely recommend against using zero-length seqpackets or datagrams for any purpose.
To illustrate the main problem, and explore the details, I created two test programs. First is receive.c, which listens on an Unix domain seqpacket socket, accepts one connection, and describes what it receives:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <signal.h>
#include <string.h>
#include <poll.h>
#include <time.h>
#include <stdio.h>
#include <errno.h>
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
done = 1;
}
static int install_done(int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
static inline unsigned int digit(const int c)
{
switch (c) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'A': case 'a': return 10;
case 'B': case 'b': return 11;
case 'C': case 'c': return 12;
case 'D': case 'd': return 13;
case 'E': case 'e': return 14;
case 'F': case 'f': return 15;
default: return 16;
}
}
static inline unsigned int octbyte(const char *src)
{
if (src) {
const unsigned int o0 = digit(src[0]);
if (o0 < 4) {
const unsigned int o1 = digit(src[1]);
if (o1 < 8) {
const unsigned int o2 = digit(src[2]);
if (o2 < 8)
return o0*64 + o1*8 + o2;
}
}
}
return 256;
}
static inline unsigned int hexbyte(const char *src)
{
if (src) {
const unsigned int hi = digit(src[0]);
if (hi < 16) {
const unsigned int lo = digit(src[1]);
if (lo < 16)
return 16*hi + lo;
}
}
return 256;
}
size_t set_unix_path(const char *src, struct sockaddr_un *addr)
{
char *dst = addr->sun_path;
char *const end = addr->sun_path + sizeof (addr->sun_path) - 1;
unsigned int byte;
if (!src || !addr)
return 0;
memset(addr, 0, sizeof *addr);
addr->sun_family = AF_UNIX;
while (*src && dst < end)
if (*src == '\\')
switch (*(++src)) {
case '0':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else {
*(dst++) = '\0';
src++;
}
break;
case '1':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case '2':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case '3':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case 'x':
byte = hexbyte(src + 1);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case 'a': *(dst++) = '\a'; src++; break;
case 'b': *(dst++) = '\b'; src++; break;
case 't': *(dst++) = '\t'; src++; break;
case 'n': *(dst++) = '\n'; src++; break;
case 'v': *(dst++) = '\v'; src++; break;
case 'f': *(dst++) = '\f'; src++; break;
case 'r': *(dst++) = '\r'; src++; break;
case '\\': *(dst++) = '\\'; src++; break;
default: *(dst++) = '\\';
}
else
*(dst++) = *(src++);
*(dst++) = '\0';
return (size_t)(dst - (char *)addr);
}
int main(int argc, char *argv[])
{
struct sockaddr_un addr, conn;
socklen_t addrlen, connlen;
int socketfd, connfd;
if (argc != 2 || !argv[1][0] || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s SOCKET_PATH\n", argv[0]);
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
if (install_done(SIGINT) ||
install_done(SIGHUP) ||
install_done(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
socketfd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (socketfd == -1) {
fprintf(stderr, "Cannot create an Unix domain seqpacket socket: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
addrlen = set_unix_path(argv[1], &addr);
if (bind(socketfd, (const struct sockaddr *)&addr, addrlen) == -1) {
fprintf(stderr, "Cannot bind to %s: %s.\n", argv[1], strerror(errno));
close(socketfd);
return EXIT_FAILURE;
}
if (listen(socketfd, 1) == -1) {
fprintf(stderr, "Cannot listen for incoming connections: %s.\n", strerror(errno));
close(socketfd);
return EXIT_FAILURE;
}
memset(&conn, 0, sizeof conn);
connlen = sizeof conn;
connfd = accept(socketfd, (struct sockaddr *)&conn, &connlen);
if (connfd == -1) {
close(socketfd);
fprintf(stderr, "Canceled.\n");
return EXIT_SUCCESS;
}
if (connlen > 0)
fprintf(stderr, "Connected, peer address size is %d.\n", (int)connlen);
else
fprintf(stderr, "Connected; no peer address.\n");
while (!done) {
char buffer[65536];
ssize_t n;
int r;
n = recv(connfd, buffer, sizeof buffer, 0);
if (n > 0)
fprintf(stderr, "Received %zd bytes.\n", n);
else
if (n == 0) {
struct pollfd fds[1];
fds[0].fd = connfd;
fds[0].events = 0;
fds[0].revents = 0;
r = poll(fds, 1, 0);
if (r > 0 && (fds[0].revents & POLLHUP)) {
fprintf(stderr, "Disconnected (revents = %d).\n", fds[0].revents);
break;
} else
if (r > 0)
fprintf(stderr, "recv() == 0, poll() == %d, revents == %d\n", r, fds[0].revents);
else
if (r == 0)
fprintf(stderr, "Received a zero-byte seqpacket.\n");
else
fprintf(stderr, "recv() == 0, poll() == %d, revents == %d\n", r, fds[0].revents);
}
}
close(connfd);
close(socketfd);
return EXIT_SUCCESS;
}
You can compile the above using e.g. gcc -Wall -O2 receive.c -o receive. To run, give it the Unix domain address to listen on. In Linux, you can use the abstract namespace by prepending \0 to the address; for example, by running ./receive '\0example' . Otherwise, the socket address will be visible in the filesystem, and you'll need to remove it (as if it was a file, using rm) before running ./receive again with the same socket address.
We also need an utility to send seqpackets. The following send.c is very similar (reuses much of the same code). You specify the Unix domain address to connect to, and one or more seqpacket lengths. You can also specify delays in milliseconds (just prepend a -; i.e., negative integers are delays in milliseconds):
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <signal.h>
#include <string.h>
#include <poll.h>
#include <time.h>
#include <stdio.h>
#include <errno.h>
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
done = 1;
}
static int install_done(int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
static inline unsigned int digit(const int c)
{
switch (c) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'A': case 'a': return 10;
case 'B': case 'b': return 11;
case 'C': case 'c': return 12;
case 'D': case 'd': return 13;
case 'E': case 'e': return 14;
case 'F': case 'f': return 15;
default: return 16;
}
}
static inline unsigned int octbyte(const char *src)
{
if (src) {
const unsigned int o0 = digit(src[0]);
if (o0 < 4) {
const unsigned int o1 = digit(src[1]);
if (o1 < 8) {
const unsigned int o2 = digit(src[2]);
if (o2 < 8)
return o0*64 + o1*8 + o2;
}
}
}
return 256;
}
static inline unsigned int hexbyte(const char *src)
{
if (src) {
const unsigned int hi = digit(src[0]);
if (hi < 16) {
const unsigned int lo = digit(src[1]);
if (lo < 16)
return 16*hi + lo;
}
}
return 256;
}
size_t set_unix_path(const char *src, struct sockaddr_un *addr)
{
char *dst = addr->sun_path;
char *const end = addr->sun_path + sizeof (addr->sun_path) - 1;
unsigned int byte;
if (!src || !addr)
return 0;
memset(addr, 0, sizeof *addr);
addr->sun_family = AF_UNIX;
while (*src && dst < end)
if (*src == '\\')
switch (*(++src)) {
case '0':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else {
*(dst++) = '\0';
src++;
}
break;
case '1':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case '2':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case '3':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case 'x':
byte = hexbyte(src + 1);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case 'a': *(dst++) = '\a'; src++; break;
case 'b': *(dst++) = '\b'; src++; break;
case 't': *(dst++) = '\t'; src++; break;
case 'n': *(dst++) = '\n'; src++; break;
case 'v': *(dst++) = '\v'; src++; break;
case 'f': *(dst++) = '\f'; src++; break;
case 'r': *(dst++) = '\r'; src++; break;
case '\\': *(dst++) = '\\'; src++; break;
default: *(dst++) = '\\';
}
else
*(dst++) = *(src++);
*(dst++) = '\0';
return (size_t)(dst - (char *)addr);
}
static inline long sleep_ms(const long ms)
{
struct timespec t;
if (ms > 0) {
t.tv_sec = ms / 1000;
t.tv_nsec = (ms % 1000) * 1000000;
if (nanosleep(&t, &t) == -1 && errno == EINTR)
return 1000 * (unsigned long)(t.tv_sec)
+ (unsigned long)(t.tv_nsec / 1000000);
return 0;
} else
return ms;
}
static int parse_long(const char *src, long *dst)
{
const char *end = src;
long val;
if (!src || !*src)
return errno = EINVAL;
errno = 0;
val = strtol(src, (char **)&end, 0);
if (errno)
return errno;
if (end == src)
return errno = EINVAL;
while (*end == '\t' || *end == '\n' || *end == '\v' ||
*end == '\f' || *end == '\r' || *end == ' ')
end++;
if (*end)
return errno = EINVAL;
if (dst)
*dst = val;
return 0;
}
int main(int argc, char *argv[])
{
char buffer[65536];
struct sockaddr_un conn;
socklen_t connlen;
int connfd, arg;
ssize_t n;
long val, left;
if (argc < 3 || !argv[1][0] || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s SOCKET_PATH [ LEN | -MS ] ...\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "All arguments except the first one, SOCKET_PATH, are integers.\n");
fprintf(stderr, "A positive integer causes a seqpacket of that length to be sent,\n");
fprintf(stderr, "a negative value causes a delay (magnitude in milliseconds).\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
if (install_done(SIGINT) ||
install_done(SIGHUP) ||
install_done(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* Fill buffer with some data. Anything works. */
{
size_t i = sizeof buffer;
while (i-->0)
buffer[i] = (i*i) ^ i;
}
connfd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (connfd == -1) {
fprintf(stderr, "Cannot create an Unix domain seqpacket socket: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
connlen = set_unix_path(argv[1], &conn);
if (connect(connfd, (const struct sockaddr *)&conn, connlen) == -1) {
fprintf(stderr, "Cannot connect to %s: %s.\n", argv[1], strerror(errno));
close(connfd);
return EXIT_FAILURE;
}
/* To avoid output affecting the timing, fully buffer stdout. */
setvbuf(stdout, NULL, _IOFBF, 65536);
for (arg = 2; arg < argc; arg++)
if (parse_long(argv[arg], &val)) {
fprintf(stderr, "%s: Not an integer.\n", argv[arg]);
close(connfd);
return EXIT_FAILURE;
} else
if (val > (long)sizeof buffer) {
fprintf(stderr, "%s: Seqpacket size too large. Current limit is %zu.\n", argv[arg], sizeof buffer);
close(connfd);
return EXIT_FAILURE;
} else
if (val >= 0) {
n = send(connfd, buffer, (size_t)val, 0);
if (n == (ssize_t)val)
printf("Sent %ld-byte seqpacket successfully.\n", val);
else
if (n != (ssize_t)val && n >= 0)
fprintf(stderr, "Sent %zd bytes of a %ld-byte seqpacket.\n", n, val);
else
if (n < -1) {
fprintf(stderr, "C library bug: send() returned %zd.\n", n);
close(connfd);
return EXIT_FAILURE;
} else
if (n == -1) {
fprintf(stderr, "Send failed: %s.\n", strerror(errno));
close(connfd);
return EXIT_FAILURE;
}
} else {
left = sleep_ms(-val);
if (left)
fprintf(stderr, "Slept %ld milliseconds (out of %ld ms).\n", -val-left, -val);
else
printf("Slept %ld milliseconds.\n", -val);
}
if (close(connfd) == -1) {
fprintf(stderr, "Error closing connection: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
printf("All done, connection closed.\n");
fflush(stdout);
return EXIT_SUCCESS;
}
Compile this using e.g. gcc -Wall -O2 send.c -o send .
For testing, I recommend you use two terminal windows. Run send in one, and receive in the other. For simplicity, I'll show the corresponding commands and outputs side-by-side. The machine this runs on is a Core i5 7200U laptop (HP EliteBook 830), running Ubuntu 16.04.4 LTS, 64-bit Linux kernel version 4.15.0-24-generic, and binaried compiled using GCC-5.4.0 20160609 (5.4.0-6ubuntu1~16.04.10) and the abovementioned commands (gcc -Wall -O2).
When we use a small delay before the final send, everything seems to work just fine:
$ ./send '\0example' 1 0 3 0 0 -1 6 │ $ ./receive '\0example'
│ Connected, peer address size is 2.
Sent 1-byte seqpacket successfully. │ Received 1 bytes.
Sent 0-byte seqpacket successfully. │ Received a zero-byte seqpacket.
Sent 3-byte seqpacket successfully. │ Received 3 bytes.
Sent 0-byte seqpacket successfully. │ Received a zero-byte seqpacket.
Sent 0-byte seqpacket successfully. │ Received a zero-byte seqpacket.
Slept 1 milliseconds. │
Sent 6-byte seqpacket successfully. │ Received 6 bytes.
All done, connection closed. │ Disconnected (revents = 16).
However, when the sender sends the final few seqpackets (starting with a zero-length one) without any delays in between, I observe this:
$ ./send '\0example' 1 0 3 0 0 6 │ ./receive '\0example'
│ Connected, peer address size is 2.
Sent 1-byte seqpacket successfully. │ Received 1 bytes.
Sent 0-byte seqpacket successfully. │ Received a zero-byte seqpacket.
Sent 3-byte seqpacket successfully. │ Received 3 bytes.
Sent 0-byte seqpacket successfully. │
Sent 0-byte seqpacket successfully. │
Sent 6-byte seqpacket successfully. │
All done, connection closed. │ Disconnected (revents = 16).
See how the two zero-byte seqpackets and the 6-byte seqpacket are missed (because poll() returned revents == POLLHUP. (POLLHUP == 0x0010 == 16, so there were no other flags set either time.)
I am personally not sure if this is a bug or not. In my opinion, it is just and indication that using zero-length seqpackets is problematic, and should be avoided.
(The peer address length is 2 above, because the sender does not bind to any address, and therefore uses an unnamed Unix domain socket address (as described in the man unix man page). I don't think it is important, but I left it in just in case.)
There was a discussion on one possible solution to the problem at hand, via MSG_EOR (since recvmsg() should add MSG_EOR to the msg_flags field in the msghdr structure; and since it "should be set for all seqpackets", even zero-length ones, it would be a reliable way to detect zero-length seqpackets from end-of-input/read-side shutdown/disconnect) in the Linux Kernel Mailing List (and linux-netdev list) in May 2007. (The archived thread at Marc.info is here)
However, in Linux Unix domain seqpacket sockets, the MSG_EOR is not set nor passed, according to the initial poster, Sam Kumar. The discussion did not lead anywhere; as I read it, nobody was sure what the expected behaviour even should be.
Looking at the Linux kernel changelog for Unix domain sockets, there have been no related changes since that thread either (as of 23 July 2018).
The above programs were written in one sitting, without review; so, they could easily have bugs or thinkos in them. If you notice any, or obtain very different results (but do note timing-based effects are sometimes hard to replicate), do let me know in a comment, so I can check, and fix if necessary.

Trying to write AT commands to USB to OBDii cable that uses FT232R to read ECU data (ISO 9141-2)

I am trying to read data from an ECU using the ISO 9141-2 protocol. The cable I'm using is OBD2 to USB, using a FT232R chip.
The programs that I am running are in C.
When I write commands to the serial port (ttyUSB0) it writes to it fine but when it reads back the received data it just returns the same data. Instead of returning the data from the ECU, I'm not sure on why it is doing this.
Any help is great, thanks.
Example code - Tried with setting the baud rate etc but no luck either.
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
char *port = "/dev/ttyUSB0";
char receivedData[100];
int n, fd;
fd = open(port, O_RDWR | O_NOCTTY);
if(fd > 0){
n = write(fd, "AT I\r\n", 10);
read(fd, receivedData, 10);
printf("%s\n", receivedData);
close(fd);
}
else{
printf("failed to open device\n");
}
return 0;
}
Even though this is not a direct answer to the question, this amount of code
does not fit in the comment section.
I use these functions to initialize the serial lines:
#include <stdio.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
struct baud_map {
int baud;
speed_t speed;
};
struct baud_map baudmap[] = {
{ 50 , B50 },
{ 75 , B75 },
{ 110 , B110 },
{ 134 , B134 },
{ 150 , B150 },
{ 200 , B200 },
{ 300 , B300 },
{ 600 , B600 },
{ 1200 , B1200 },
{ 1800 , B1800 },
{ 2400 , B2400 },
{ 4800 , B4800 },
{ 9600 , B9600 },
{ 19200 , B19200 },
{ 38400 , B38400 },
{ 57600 , B57600 },
{ 115200 , B115200 },
{ 230400 , B230400 },
{ 0, 0 }
};
int dbits_map[] = { 0, 0, 0, 0, 0, CS5, CS6, CS7, CS8 };
enum parity_t {
PARITY_NO_PARITY,
PARITY_ODD,
PARITY_EVEN
};
int baud_to_speed(int baud, speed_t *speed)
{
if(speed == NULL)
return 0;
struct baud_map *map = baudmap;
while(map->baud)
{
if(map->baud == baud)
{
*speed = map->speed;
return 1;
}
map++;
}
return 0;
}
/*
* tty: "/dev/ttyUSB0"
* baud: baudrate, for example 9600
* parity: see enum parity_t
* stop_bits: 1 or 2
* data_bits: [5-8]
*
* return the fd on success, -1 on failure
*/
int openSerial_long(const char *tty, int baud, enum parity_t parity, int stop_bits, int data_bits)
{
int fd;
speed_t speed;
if(baud_to_speed(baud, &speed) == 0)
{
fprintf(stderr, "Invalid baudrate %d\n", baud);
return 0;
}
fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
if(fd == -1)
{
fprintf(stderr, "Could not open %s as a tty: %s\n", tty, strerror(errno));
return -1;
}
struct termios termios;
if(tcgetattr(fd, &termios) == -1)
{
fprintf(stderr, "Could not get tty attributes from %s: %s\n", tty, strerror(errno));
close(fd);
return -1;
}
// setting common values
termios.c_iflag &= ~ICRNL; // do not translate \r into \n
termios.c_oflag &= ~OPOST; // do not map \n to \r\n
termios.c_cflag |= (CREAD | CLOCAL); // enable receiver & ignore model ctrl lines
termios.c_lflag |= (ISIG | ICANON); // enable signals and noncanonical mode
termios.c_lflag &= ~ECHO; // disable echo
cfsetispeed(&termios, speed);
cfsetospeed(&termios, speed);
switch(parity)
{
case PARITY_NO_PARITY:
termios.c_cflag &= ~PARENB;
break;
case PARITY_ODD:
termios.c_cflag |= PARENB;
termios.c_cflag |= PARODD;
break;
case PARITY_EVEN:
termios.c_cflag |= PARENB;
termios.c_cflag &= ~PARODD;
break;
default:
fprintf(stderr, "invalid parity\n");
break;
}
if(stop_bits == 1)
termios.c_cflag &= ~CSTOPB;
else if(stop_bits == 2)
termios.c_cflag |= CSTOPB;
else
fprintf(stderr, "Invalid stop bit\n");
int bits;
switch(data_bits)
{
case 5:
case 6:
case 7:
case 8:
bits = dbits_map[data_bits];
break;
default:
bits = -1;
}
if(bits != -1)
{
termios.c_cflag &= ~CSIZE;
termios.c_cflag |= bits;
} else
fprintf(stderr, "Invalid data size\n");
if(tcsetattr(fd, TCSANOW, &termios) == -1)
{
fprintf(stderr, "Could not get tty attributes from %s: %s\n", tty, strerror(errno));
close(fd);
return -1;
}
return fd;
}
/**
* tty: "/dev/ttyUSB0"
* baud: baudrate, for example 9600
* mode: a string like 8N1 where
* the first character is the number of data bits (range from 5-8)
* the second character is N (no parity), O (odd), E (even)
* the third character is the number of stop bits (1 or 2)
*/
int openSerial(const char *tty, int baud, const char *mode)
{
if(tty == NULL || mode == NULL)
return -1;
if(strlen(mode) != 3)
{
fprintf(stderr, "invalid mode\n");
return -1;
}
int stop_bits = mode[2];
if(stop_bits != '1' && stop_bits != '2')
{
fprintf(stderr, "Invalid stop bits\n");
return -1;
}
stop_bits -= '0';
enum parity_t parity;
switch(mode[1])
{
case 'n':
case 'N':
parity = PARITY_NO_PARITY;
break;
case 'o':
case 'O':
parity = PARITY_ODD;
break;
case 'e':
case 'E':
parity = PARITY_EVEN;
break;
default:
fprintf(stderr, "Invalid parity\n");
return -1;
}
int data_bits = mode[0] - '0';
if(data_bits < 5 || data_bits > 8)
{
fprintf(stderr, "invalid data bits\n");
return -1;
}
return openSerial_long(tty, baud, parity, stop_bits, data_bits);
}
The most common mode is "8N1" (8 bit data, no parity, 1 stop bit) and I open
the serial line with
int fd = open("/dev/ttyUSB0", 9600, "8N1");
if(fd == -1)
{
fprintf(stderr, "Error, could not initialize serial line\n");
exit(EXIT_FAILURE);
}
I have no idea which serial settings you have to use, look it up in the manual.
Also be aware that stuff like this
n = write(fd, "AT I\r\n", 10);
is wrong, it's undefined behvaiour because write is reading beyon the bound
of the string literal. It would be better to do:
const char *cmd = "AT I\r\n";
n = write(fd, cmd, strlen(cmd));
then you would write the correct amount of data.
Like I said in the comments, I've made a google search on ISO 9141-2 and I could
not find any reliable information on whether it uses AT commands. So please
check the manual of the OBDII chip you are trying to read.
Wikipedia says
ISO 9141-2. This protocol has an asynchronous serial data rate of 10.4 kbit/s. It is somewhat similar to RS-232; however, the signal levels are different, and communications happens on a single, bidirectional line without additional handshake signals. ISO 9141-2 is primarily used in Chrysler, European, and Asian vehicles.
It seems to me that this protocol is only similar to RS232, perhaps you need
another converter (other than FT232R) which supports this baudrate.
This page also states that the baudrate is 10400 bits/s, which is not
supported by default. I've found this post which also suggests that you
should use two converters, one for the communication between OBD2 and converter
at 10.4 kbit/s and one between the converter and your PC at a standard baudrate
like 19.2 kbits/s.
Alternatively you could try to set a custom baudrate as explained here and
here. I've never had to use custom baudrates, so I don't know if this
works.
Okay so first off: I don't know C. However, you did n = (whatever to send the AT command) and for the read data you receive, then print. Why not attach the result of the read into a variable and print that? Or, you could use bash based commands to communicate with the serial port like "minicom" and I know there are others like that.
A couple other notes, as I have been working with OBD2 a lot lately: AT commands go to the reader, not the ECU. You can use minicom to see when you reset the adapter (ATZ) the ECU does nothing. However, send a 03 (or whatever mode is ECU CODE RESET) and it will clear your codes.
For more info on headers and things like that for multiple ECUs see https://mechanics.stackexchange.com/questions/22982/received-frames-from-vehicles-with-multiple-ecu-chips
One last note: don't forget you have 2 different baud rates - one for your USB serial port to FT232R chip and one from said chip to the ECU. With ELM327 the latter is done via a AT command to change the proto. At any rate, use the FT232R baud rate coming from the computer, and test in minicom if necessary. Hope this helped!
It's perfectly normal for an OBD2 adapter to send you back the very same command you have sent. This is called echo mode and can be turned off by sending ATE0\r as the first command. If you read more of the answer, you should see the result of your command coming after the echoed request.
Note also that the AT commands are processed by the OBD2 without sending data to any ECUs, only the PIDs (commands composed out of digits) will be sent over the bus.

writing and reading data to and from a serial port (to an intel galileo) windows c

I'm building a cnc machine for a school project, it consists of an Intel galileo with a shield that controls stepper motors, this is controlled by a program on a windows machine(windows 7), what the program basically does is read a text file containing gcode and then sends it line by line to the Galileo, the Galileo then takes that line breaks it down into coordinates and instructions, moves the spindle to where it needs to go and when the instruction is finished it sends a message back to the computer through serial telling it that its finished, the computer then sends the next line of code and the process is repeated.
so far I can read the gcode file and send it line by line (using a keypress to send each line) to the galileo and everything works fine. my problem is reading from the galileo I have tried a few methods with no joy. this is what I think is closest to what I should be doing. I wont post the whole source because I think it will be just to much to read through, ive posted the main() which has all the setup functions and the function that is supposed to read the serial.
/************************************************************************************************************
application for controling galileo or arduino cnc machine
by brendan scullion
10/11/2014
**********************************************************************************************************/
#include <string.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <ctype.h>
#include <conio.h>
#include "functions.h"
int main()
{
system("COLOR 1F");
// Declare variables and structures
unsigned char text_to_send[MAX_PATH];
unsigned char digits[MAX_PATH];
int baudrate = 19200;
int dev_num = 50;
char dev_name[MAX_PATH];
HANDLE hSerial;
HANDLE screen = GetStdHandle(STD_OUTPUT_HANDLE);
DCB dcbSerialParams = {0};
COMMTIMEOUTS timeouts = {0};
printf("Searching serial ports...\n");
while(dev_num >= 0)
{
printf("\r ");
printf("\rTrying COM%d...", dev_num);
sprintf(dev_name, "\\\\.\\COM%d", dev_num);
hSerial = CreateFile(
dev_name,
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if (hSerial == INVALID_HANDLE_VALUE) dev_num--;
else break;
}
if (dev_num < 0)
{
printf("No serial port available\n");
return 1;
}
printf("OK\n");
// Set device parameters (38400 baud, 1 start bit,
// 1 stop bit, no parity)
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
if (GetCommState(hSerial, &dcbSerialParams) == 0)
{
printf("Error getting device state\n");
CloseHandle(hSerial);
return 1;
}
//dcbSerialParams.BaudRate = CBR_38400;
dcbSerialParams.BaudRate = baudrate;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
if(SetCommState(hSerial, &dcbSerialParams) == 0)
{
printf("Error setting device parameters\n");
CloseHandle(hSerial);
return 1;
}
// Set COM port timeout settings
timeouts.ReadIntervalTimeout = 1;
timeouts.ReadTotalTimeoutConstant = 1;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if(SetCommTimeouts(hSerial, &timeouts) == 0)
{
printf("Error setting timeouts\n");
CloseHandle(hSerial);
return 1;
}
char *cmd = NULL;
char *para1 = NULL;
char *para2 = NULL;
char *para3 = NULL;
char comPort[10];
float baudRate;
int keepGoing = 1;
unsigned char message[MAX_STRING_LENGHT];
//*********************************************************************************************************************
char cmdLine[200];
heading();
while(keepGoing == 1)
{
printf(">>");
gets(cmdLine);
cmd = strtok(cmdLine, " ");
if(cmd!=false)
{
if(cmd != NULL)
{
para1 = strtok(NULL, " ");
}
else if(para1 != NULL)
{
para2 = strtok(NULL, " ");
}
else if(para2 != NULL)
{
para3 = strtok(NULL, " ");
}
if(strcmp(cmd, "help")== 0)
{
help();
}
else if(strcmp(cmd, "comset")== 0)
{
setupComs(comPort, baudRate);
}
else if(strcmp(cmd, "getg")== 0)
{
getgcode(hSerial,text_to_send,dev_name);
}
else if(strcmp(cmd, "readserial")== 0)
{
read_serial(hSerial, message, dev_name);
}
else if(strcmp(cmd, "offset")==0)
{
getOffset(hSerial, text_to_send, dev_name);
}
else if(strcmp(cmd, "setup") == 0)
{
setup(hSerial, text_to_send, dev_name);
}
else if(strcmp(cmd, "exit") == 0)
{
keepGoing = 0;
}
else
{
printf("Unknown command!\n");
}
printf("\n");
}
}
// Close serial port
printf("Closing serial port...");
if (CloseHandle(hSerial) == 0)
{
printf("Error\n");
return 1;
}
printf("OK\n");
return 0;
}
and the function for reading
void read_serial(HANDLE hComm, HANDLE screen, char *message, char *devName )
{
char buffer[MAX_STRING_LENGHT];
unsigned char ch;
DWORD bytes_recieved = MAX_STRING_LENGHT, written = 0;
strcpy(buffer,""); //empty buffer
strcpy(buffer,"");
while(buffer!=NULL){ // wait untill serail message revieved
ReadFile(hComm, &buffer,sizeof(buffer), // read serial
&bytes_recieved, NULL );
if(bytes_recieved){ // if something to read
WriteFile(screen, buffer, bytes_recieved, &written, NULL);
strcpy(message, buffer);
printf("%s", message);
if(kbhit()){
ch = getch();
if(ch== 'q')
break;
}
}
}
}
i can post the entire source code if anybody wants to have a look at it
In read_serial, you can try replacing &buffer with buffer in the call to ReadFile. Name of a character array should be a pointer to the first element of the array.
What i would do is i would frame every message that goes from and to the Galileo board in a simple and useful protocol. For example i would use STX and ETX to frame and send messages from the Windows 7 via serial to the board, use ACK NAK to acknowledge the received packet and use SYN for signalling end of execution of a line on Galileo.
With the above framing you can construct receive and send functions where you can wait for specific characters (ETX,STX,SYN,ACK,etc) and exit once you have a full frame or an control character.

Controlling DTR and RTS pin of serial port in C on Windows platform

How to Control DTR and RTS pin of serial port in and on a windows platform? I want it to be bitbanged or operated by raising or lowering its voltage.
You'll need to use the EscapeCommFunction function, like so:
// winserial_io.cpp : Win32 test program to control RTS and DTS output lines
// Originator: Steven Woon
// Creation Date: 2007-12-15
#include "stdafx.h"
#include "windows.h"
#include <conio.h>
//#include "winbase.h"
int main(int argc, char* argv[])
{
HANDLE hComm;
char ch;
for (int i = 0; i < argc; i++)
printf("%s\n", argv[i]);
hComm = CreateFileA( argv[1],GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
NULL,
0);
if (hComm == INVALID_HANDLE_VALUE)
{
printf("Cannot open %s\n", argv[1]); //error occured alert user about error
return -1;
}
printf("Press the following keys:\n");
printf("1: Set DTR\n");
printf("2: Clear DTR\n");
printf("3: Set RTS\n");
printf("4: Clear RTS\n");
printf("q: End Program\n");
do
{
ch = _getch();
switch (ch)
{
case '1': if (EscapeCommFunction(hComm,SETDTR) == 0)
printf ("Error Setting DTR\n");
break;
case '2': if (EscapeCommFunction(hComm,CLRDTR) == 0)
printf ("Error Clearing DTR\n");
break;
case '3': if (EscapeCommFunction(hComm,SETRTS) == 0)
printf ("Error Setting CTS\n");
break;
case '4': if (EscapeCommFunction(hComm,CLRRTS) == 0)
printf ("Error Clearing CTS\n");
break;
}
} while (ch != 'q');
return 0;
}

Simple, small C program for testing serial bandwidth

I have two laptops with a serial port. How do I test the actual bandwidth of the serial port between the two machines using a simple, small C program?
In reality, I need to do this on an embedded Linux system which is why the utility must be a small, simple C program (because the embedded environment only has limited library support meaning it doesn't have python, perl, or any other fancy libraries).
I started this as a new question because I didn't want to take this question off topic: Serial port loopback test
That question was regarding testing the bandwidth of a serial port in loopback mode, so that you don't have to plug in an actual serial cable. The author (sdaau) put a lot of time into creating a multi-threaded serial bandwidth test program to answer his own question. I then used his simple C program and extended it to be used between two different physical machines connected with a serial cable.
It is necessary to start the "remote" side which will wait for the "initiator" side (the local side) to send a go byte, in which both will proceed to transfer data asynchronously. The program (which sdaau calls writeread.c) spawns 2 threads: one which writes data and the other which reads data. In this way, you are fully utilizing the serial port. You can pass in a datafile as a command-line argument.
As an example, here is the "remote" side:
./writeread /dev/ttyUSB0 115200 ./datafile.dat 3> output
As an example, here is the "local" (or initiator) side:
./writeread /dev/ttyUSB0 115200 ./datafile.dat -I 3> output
Note that I had some trouble redirecting the output on the remote side, meaning that 3> output didn't really work. I'm not sure whats going on with that, but my local side worked fine. Also, note the remote side's output timing is skewed because it has a timer running while it is waiting for the initiator. This means you should only trust the bandwidth printout from the local initiator side (see the original question for output results details).
Since both sides are sending the same datafile in this example, you should be able to compare the "output" file with the datafile:
diff output datafile.dat
Complie the code with:
gcc -c -Wall writeread.c
gcc writeread.o -lpthread -o writeread
Here is the modified writeread.c code:
/*
writeread.c - based on writeread.cpp
[SOLVED] Serial Programming, Write-Read Issue - http://www.linuxquestions.org/questions/programming-9/serial-programming-write-read-issue-822980/
build with: gcc -o writeread -lpthread -Wall -g writeread.c
*/
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/time.h>
#include <pthread.h>
#include "writeread.h"
int serport_fd;
//POSIX Threads Programming - https://computing.llnl.gov/tutorials/pthreads/#PassingArguments
struct write_thread_data{
int fd;
char* comm; //string to send
int bytesToSend;
int writtenBytes;
int iator; // Initiator. 0 = False, 1 = True
};
void usage(char **argv)
{
fprintf(stdout, "Usage:\n");
fprintf(stdout, "%s port baudrate file/string [-I]\n", argv[0]);
fprintf(stdout, " The -I is for initiator. Run on the remote side which "
"will wait, then start locally with -I which will initiate "
"the test.\n");
fprintf(stdout, "Examples:\n");
fprintf(stdout, "%s /dev/ttyUSB0 115200 /path/to/somefile.txt\n", argv[0]);
fprintf(stdout, "%s /dev/ttyUSB0 115200 \"some text test\"\n", argv[0]);
}
// POSIX threads explained - http://www.ibm.com/developerworks/library/l-posix1.html
// instead of writeport
void *write_thread_function(void *arg) {
int go = 0;
int lastBytesWritten;
struct write_thread_data *my_data;
my_data = (struct write_thread_data *) arg;
fprintf(stdout, "write_thread_function spawned\n");
// Are we the initiator?
if (my_data->iator == 1) {
// We are the initiator, send the start command
go = 0xde;
write(my_data->fd, &go, 1);
} else {
// We wait for the initiator to send us the start command
fprintf(stdout, "Waiting for initiator (start other end with -I)...\n");
read(my_data->fd, &go, 1);
if (go == 0xde) {
fprintf(stdout, "Go!\n");
} else {
fprintf(stdout, "Error: Did not receive start command [0x%x]\n", go);
return NULL;
}
}
my_data->writtenBytes = 0;
while(my_data->writtenBytes < my_data->bytesToSend)
{
lastBytesWritten = write( my_data->fd, my_data->comm + my_data->writtenBytes, my_data->bytesToSend - my_data->writtenBytes );
my_data->writtenBytes += lastBytesWritten;
if ( lastBytesWritten < 0 )
{
fprintf(stdout, "write failed!\n");
return 0;
}
fprintf(stderr, " write: %d - %d\n", lastBytesWritten, my_data->writtenBytes);
}
return NULL; //pthread_exit(NULL)
}
int main( int argc, char **argv )
{
if( argc < 4 ) {
usage(argv);
return 1;
}
char *serport;
char *serspeed;
speed_t serspeed_t;
char *serfstr;
int serf_fd; // if < 0, then serfstr is a string
int sentBytes;
int readChars;
int recdBytes, totlBytes;
char* sResp;
char* sRespTotal;
struct timeval timeStart, timeEnd, timeDelta;
float deltasec, expectBps, measReadBps, measWriteBps;
struct write_thread_data wrdata;
pthread_t myWriteThread;
/* Re: connecting alternative output stream to terminal -
* http://coding.derkeiler.com/Archive/C_CPP/comp.lang.c/2009-01/msg01616.html
* send read output to file descriptor 3 if open,
* else just send to stdout
*/
FILE *stdalt;
if(dup2(3, 3) == -1) {
fprintf(stdout, "stdalt not opened; ");
stdalt = fopen("/dev/tty", "w");
} else {
fprintf(stdout, "stdalt opened; ");
stdalt = fdopen(3, "w");
}
fprintf(stdout, "Alternative file descriptor: %d\n", fileno(stdalt));
// Get the PORT name
serport = argv[1];
fprintf(stdout, "Opening port %s;\n", serport);
// Get the baudrate
serspeed = argv[2];
serspeed_t = string_to_baud(serspeed);
fprintf(stdout, "Got speed %s (%d/0x%x);\n", serspeed, serspeed_t, serspeed_t);
//Get file or command;
serfstr = argv[3];
// Are we the initiator?
if (argc == 5 &&
strncmp(argv[4], "-I", 3) == 0 )
{
wrdata.iator = 1; // Initiator. 0 = False, 1 = True
} else {
wrdata.iator = 0; // Initiator. 0 = False, 1 = True
}
serf_fd = open( serfstr, O_RDONLY );
fprintf(stdout, "Got file/string '%s'; ", serfstr);
if (serf_fd < 0) {
wrdata.bytesToSend = strlen(serfstr);
wrdata.comm = serfstr; //pointer already defined
fprintf(stdout, "interpreting as string (%d).\n", wrdata.bytesToSend);
} else {
struct stat st;
stat(serfstr, &st);
wrdata.bytesToSend = st.st_size;
wrdata.comm = (char *)calloc(wrdata.bytesToSend, sizeof(char));
read(serf_fd, wrdata.comm, wrdata.bytesToSend);
fprintf(stdout, "opened as file (%d).\n", wrdata.bytesToSend);
}
sResp = (char *)calloc(wrdata.bytesToSend, sizeof(char));
sRespTotal = (char *)calloc(wrdata.bytesToSend, sizeof(char));
// Open and Initialise port
serport_fd = open( serport, O_RDWR | O_NOCTTY | O_NONBLOCK );
if ( serport_fd < 0 ) { perror(serport); return 1; }
initport( serport_fd, serspeed_t );
wrdata.fd = serport_fd;
sentBytes = 0; recdBytes = 0;
gettimeofday( &timeStart, NULL );
// start the thread for writing..
if ( pthread_create( &myWriteThread, NULL, write_thread_function, (void *) &wrdata) ) {
printf("error creating thread.");
abort();
}
// run read loop
while ( recdBytes < wrdata.bytesToSend )
{
while ( wait_flag == TRUE );
if ( (readChars = read( serport_fd, sResp, wrdata.bytesToSend)) >= 0 )
{
//~ fprintf(stdout, "InVAL: (%d) %s\n", readChars, sResp);
// binary safe - add sResp chunk to sRespTotal
memmove(sRespTotal+recdBytes, sResp+0, readChars*sizeof(char));
/* // text safe, but not binary:
sResp[readChars] = '\0';
fprintf(stdalt, "%s", sResp);
*/
recdBytes += readChars;
} else {
if ( errno == EAGAIN )
{
fprintf(stdout, "SERIAL EAGAIN ERROR\n");
return 0;
}
else
{
fprintf(stdout, "SERIAL read error: %d = %s\n", errno , strerror(errno));
return 0;
}
}
fprintf(stderr, " read: %d\n", recdBytes);
wait_flag = TRUE; // was ==
//~ usleep(50000);
}
if ( pthread_join ( myWriteThread, NULL ) ) {
printf("error joining thread.");
abort();
}
gettimeofday( &timeEnd, NULL );
// binary safe - dump sRespTotal to stdalt
fwrite(sRespTotal, sizeof(char), recdBytes, stdalt);
// Close the open port
close( serport_fd );
if (!(serf_fd < 0)) {
close( serf_fd );
free(wrdata.comm);
}
free(sResp);
free(sRespTotal);
fprintf(stdout, "\n+++DONE+++\n");
sentBytes = wrdata.writtenBytes;
totlBytes = sentBytes + recdBytes;
timeval_subtract(&timeDelta, &timeEnd, &timeStart);
deltasec = timeDelta.tv_sec+timeDelta.tv_usec*1e-6;
expectBps = atoi(serspeed)/10.0f;
measWriteBps = sentBytes/deltasec;
measReadBps = recdBytes/deltasec;
fprintf(stdout, "Wrote: %d bytes; Read: %d bytes; Total: %d bytes. \n", sentBytes, recdBytes, totlBytes);
fprintf(stdout, "Start: %ld s %ld us; End: %ld s %ld us; Delta: %ld s %ld us. \n", timeStart.tv_sec, timeStart.tv_usec, timeEnd.tv_sec, timeEnd.tv_usec, timeDelta.tv_sec, timeDelta.tv_usec);
fprintf(stdout, "%s baud for 8N1 is %d Bps (bytes/sec).\n", serspeed, (int)expectBps);
fprintf(stdout, "Measured: write %.02f Bps (%.02f%%), read %.02f Bps (%.02f%%), total %.02f Bps.\n", measWriteBps, (measWriteBps/expectBps)*100, measReadBps, (measReadBps/expectBps)*100, totlBytes/deltasec);
return 0;
}
And here is the .h file which I have renamed from the original question to writeread.h:
/* writeread.h
(C) 2004-5 Captain http://www.captain.at
Helper functions for "ser"
Used for testing the PIC-MMC test-board
http://www.captain.at/electronic-index.php
*/
#include <stdio.h> /* Standard input/output definitions */
#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/signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#define TRUE 1
#define FALSE 0
int wait_flag = TRUE; // TRUE while no signal received
// Definition of Signal Handler
void DAQ_signal_handler_IO ( int status )
{
//~ fprintf(stdout, "received SIGIO signal %d.\n", status);
wait_flag = FALSE;
}
int writeport( int fd, char *comm )
{
int len = strlen( comm );
int n = write( fd, comm, len );
if ( n < 0 )
{
fprintf(stdout, "write failed!\n");
return 0;
}
return n;
}
int readport( int fd, char *resp, size_t nbyte )
{
int iIn = read( fd, resp, nbyte );
if ( iIn < 0 )
{
if ( errno == EAGAIN )
{
fprintf(stdout, "SERIAL EAGAIN ERROR\n");
return 0;
}
else
{
fprintf(stdout, "SERIAL read error: %d = %s\n", errno , strerror(errno));
return 0;
}
}
if ( resp[iIn-1] == '\r' )
resp[iIn-1] = '\0';
else
resp[iIn] = '\0';
return iIn;
}
int getbaud( int fd )
{
struct termios termAttr;
int inputSpeed = -1;
speed_t baudRate;
tcgetattr( fd, &termAttr );
// Get the input speed
baudRate = cfgetispeed( &termAttr );
switch ( baudRate )
{
case B0: inputSpeed = 0; break;
case B50: inputSpeed = 50; break;
case B110: inputSpeed = 110; break;
case B134: inputSpeed = 134; break;
case B150: inputSpeed = 150; break;
case B200: inputSpeed = 200; break;
case B300: inputSpeed = 300; break;
case B600: inputSpeed = 600; break;
case B1200: inputSpeed = 1200; break;
case B1800: inputSpeed = 1800; break;
case B2400: inputSpeed = 2400; break;
case B4800: inputSpeed = 4800; break;
case B9600: inputSpeed = 9600; break;
case B19200: inputSpeed = 19200; break;
case B38400: inputSpeed = 38400; break;
case B115200: inputSpeed = 115200; break;
case B2000000: inputSpeed = 2000000; break; //added
}
return inputSpeed;
}
/* ser.c
(C) 2004-5 Captain http://www.captain.at
Sends 3 characters (ABC) via the serial port (/dev/ttyS0) and reads
them back if they are returned from the PIC.
Used for testing the PIC-MMC test-board
http://www.captain.at/electronic-index.php
*/
int initport( int fd, speed_t baudRate )
{
struct termios options;
struct sigaction saio; // Definition of Signal action
// Install the signal handler before making the device asynchronous
saio.sa_handler = DAQ_signal_handler_IO;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction( SIGIO, &saio, NULL );
// Allow the process to receive SIGIO
fcntl( fd, F_SETOWN, getpid() );
// Make the file descriptor asynchronous (the manual page says only
// O_APPEND and O_NONBLOCK, will work with F_SETFL...)
fcntl( fd, F_SETFL, FASYNC );
//~ fcntl( fd, F_SETFL, FNDELAY); //doesn't work; //fcntl(file, F_SETFL, 0);
// Get the current options for the port...
tcgetattr( fd, &options );
/*
// Set port settings for canonical input processing
options.c_cflag = BAUDRATE | CRTSCTS | CLOCAL | CREAD;
options.c_iflag = IGNPAR | ICRNL;
//options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = ICANON;
//options.c_lflag = 0;
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 0;
*/
/* ADDED - else 'read' will not return, unless it sees LF '\n' !!!!
* From: Unix Programming Frequently Asked Questions - 3. Terminal I/O -
* http://www.steve.org.uk/Reference/Unix/faq_4.html
*/
/* Disable canonical mode, and set buffer size to 1 byte */
options.c_lflag &= (~ICANON);
options.c_cc[VTIME] = 0;
options.c_cc[VMIN] = 1;
// Set the baud rates to...
cfsetispeed( &options, baudRate );
cfsetospeed( &options, baudRate );
// Enable the receiver and set local mode...
options.c_cflag |= ( CLOCAL | CREAD );
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
// Flush the input & output...
tcflush( fd, TCIOFLUSH );
// Set the new options for the port...
tcsetattr( fd, TCSANOW, &options );
return 1;
}
/*
ripped from
http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/stty.c
*/
#define STREQ(a, b) (strcmp((a), (b)) == 0)
struct speed_map
{
const char *string; /* ASCII representation. */
speed_t speed; /* Internal form. */
unsigned long int value; /* Numeric value. */
};
static struct speed_map const speeds[] =
{
{"0", B0, 0},
{"50", B50, 50},
{"75", B75, 75},
{"110", B110, 110},
{"134", B134, 134},
{"134.5", B134, 134},
{"150", B150, 150},
{"200", B200, 200},
{"300", B300, 300},
{"600", B600, 600},
{"1200", B1200, 1200},
{"1800", B1800, 1800},
{"2400", B2400, 2400},
{"4800", B4800, 4800},
{"9600", B9600, 9600},
{"19200", B19200, 19200},
{"38400", B38400, 38400},
{"exta", B19200, 19200},
{"extb", B38400, 38400},
#ifdef B57600
{"57600", B57600, 57600},
#endif
#ifdef B115200
{"115200", B115200, 115200},
#endif
#ifdef B230400
{"230400", B230400, 230400},
#endif
#ifdef B460800
{"460800", B460800, 460800},
#endif
#ifdef B500000
{"500000", B500000, 500000},
#endif
#ifdef B576000
{"576000", B576000, 576000},
#endif
#ifdef B921600
{"921600", B921600, 921600},
#endif
#ifdef B1000000
{"1000000", B1000000, 1000000},
#endif
#ifdef B1152000
{"1152000", B1152000, 1152000},
#endif
#ifdef B1500000
{"1500000", B1500000, 1500000},
#endif
#ifdef B2000000
{"2000000", B2000000, 2000000},
#endif
#ifdef B2500000
{"2500000", B2500000, 2500000},
#endif
#ifdef B3000000
{"3000000", B3000000, 3000000},
#endif
#ifdef B3500000
{"3500000", B3500000, 3500000},
#endif
#ifdef B4000000
{"4000000", B4000000, 4000000},
#endif
{NULL, 0, 0}
};
static speed_t
string_to_baud (const char *arg)
{
int i;
for (i = 0; speeds[i].string != NULL; ++i)
if (STREQ (arg, speeds[i].string))
return speeds[i].speed;
return (speed_t) -1;
}
/* http://www.gnu.org/software/libtool/manual/libc/Elapsed-Time.html
Subtract the `struct timeval' values X and Y,
storing the result in RESULT.
Return 1 if the difference is negative, otherwise 0. */
int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y)
{
/* Perform the carry for the later subtraction by updating y. */
if (x->tv_usec < y->tv_usec) {
int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
y->tv_usec -= 1000000 * nsec;
y->tv_sec += nsec;
}
if (x->tv_usec - y->tv_usec > 1000000) {
int nsec = (x->tv_usec - y->tv_usec) / 1000000;
y->tv_usec += 1000000 * nsec;
y->tv_sec -= nsec;
}
/* Compute the time remaining to wait.
tv_usec is certainly positive. */
result->tv_sec = x->tv_sec - y->tv_sec;
result->tv_usec = x->tv_usec - y->tv_usec;
/* Return 1 if result is negative. */
return x->tv_sec < y->tv_sec;
}

Resources