串行端口环回/双工testing,在Bash或C? (过程替代)

我有一个串行设备设置为回送(这意味着它只会回显它收到的任何字符),我想测量有效的吞吐速度。 为此,我希望可以利用time

 time bash -c '...' 

哪里“ ... ”将是我可以运行的一些命令。

现在,第一个问题是我想用2000000 bps的设备,所以我不能使用ttylog或屏幕 (他们似乎只能达到115200 bps)。 但是,使用/dev/ttyUSB0作为文件(使用文件redirect和cat )似乎工作正常:

 # initialize serial port stty 2000000 -ixon icanon </dev/ttyUSB0 # check settings stty -a -F /dev/ttyUSB0 # in one terminal - read from serial port while (true) do cat -A /dev/ttyUSB0 ; done # in other terminal - write to serial port echo "1234567890" > /dev/ttyUSB0 # back to first terminal, I now have: # $ while (true) do cat -A /dev/ttyUSB0 ; done # 1234567890$ # ... 

现在,我想要做类似的事情 – 我想将一个文件捕获到一个串行端口,并将串行端口读回 – 但是从一个terminal命令(所以我可以用它作为time参数)。

我认为我可以使用Bash过程replace,让“书写”和“阅读”部分以“并行”的方式进行 – 如果我使用命名pipe道进行尝试,它将起作用:

 # mkfifo my.pipe # same as below: $ mknod my.pipe p $ comm <(echo -e "test\ntest\ntest\n" > my.pipe) <(cat my.pipe) test test test comm: file 2 is not in sorted order 

在那里,我没有使用任何其他的目的,而是(有点)将两个进程合并成一个单一的命令(我想我可以使用echo来代替)。

不幸的是,这个技巧似乎并不适用于串行端口,因为当我尝试它时,我有时会得到:

 $ comm <(echo "1234567890" > /dev/ttyUSB0) <(while (true) do cat -A /dev/ttyUSB0 ; done) cat: /dev/ttyUSB0: Invalid argument 

但是,通常我只是得不到任何输出。 这告诉我:或者不能控制哪个进程首先启动,所以cat可能会在端口准备好之前开始读取(但是,在上面的第一个例子中,这似乎不是问题)。 或者在Linux / Bash中,你不能同时读取和写入串行端口,所以在读写似乎同时发生的那些时刻将出现“ Invalid argument ”。

所以我的问题是:

  • 有没有一种方法可以做到这一点(只是将一个文件转换为串行端口configuration为环回;读回来,看看需要多长时间)只有在Bash,而不诉诸写一个专门的C程序?
  • 如果我需要一个专用的C程序,那么我可以在网上使用哪些源代码示例?

任何回应非常感谢,

干杯!

编辑:我知道上面写的while循环不会退出; 该命令行是用于初步testing,我用Ctrl-C中断它。 ( 我原则上可以用类似timeout -9 0.1 bash -c 'while (true) do echo AA ; done'方法来中断它timeout -9 0.1 bash -c 'while (true) do echo AA ; done' ,但是这会打败time的目的,然后:)

在那里的原因是,暂时从设备上通过cat读取立即退出; 有时候,我已经设置了这个设备,所以当cat发出时,它实际上会阻塞并等待input的数据。 但我还不能确定发生了什么事情(部分原因是我正在寻找一种从命令行进行testing的方法)。

如果我没有使用的while ,我想像时间,我会使用像这样的东西:

time bash -c 'comm <(echo "1234567890" > /dev/ttyUSB0) <(cat -A /dev/ttyUSB0)'

…但是为了这个工作,有点,假定cat -A /dev/ttyUSB0先启动并阻止; 然后echo写入串口(并退出); 然后cat -A输出从串口读取的任何内容,然后退出。 ( 而且我也不确定串行端口是否可以这样做,也不知道cat可以像这样任意阻塞和退出 )。

确切的方法真的没关系; 如果可能的话,我只是想避免编写我自己的C程序来做这种testing – 这就是为什么我的主要兴趣是如果它是可能的运行这样的“全双工testing”使用基本的Bash / Linux(即coreutils ); ( 如果没有,如果有一个现成的代码,我可以用这样的东西 )。

编辑2:也可能有关:

  • Bash中的并行进程 – Ubuntu论坛

Solutions Collecting From Web of "串行端口环回/双工testing,在Bash或C? (过程替代)"

那么,这里就是一个部分的答案 – 虽然有关使用bash的问题仍然是开放的。 我试着在一些C代码解决方案中看起来有点儿 – 看起来,这也不是微不足道的! 🙂

首先,让我们看看这种情况可能不起作用 – 下面是“ 写入和读取之间:串行端口 – C ”的示例:

 // from: between write and read:serial port. - C - http://www.daniweb.com/forums/thread286634.html // gcc -o sertest -Wall -g sertest.c #include <stdio.h> #include <sys/types.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <termios.h> int main(int argc, char *argv[]) { char line[1024]; int chkin; char input[1024]; char msg[1024]; char serport[24]; // argv[1] - serial port // argv[2] - file or echo sprintf(serport, "%s", argv[1]); int file= open(serport, O_RDWR | O_NOCTTY | O_NDELAY); if (file == 0) { sprintf(msg, "open_port: Unable to open %s.\n", serport); perror(msg); } else fcntl(file, F_SETFL, FNDELAY); //fcntl(file, F_SETFL, 0); while (1) { printf("enter input data:\n"); scanf("%s",&input[0]); chkin=write(file,input,sizeof input); if (chkin<0) { printf("cannot write to port\n"); } //chkin=read(file,line,sizeof line); while ((chkin=read(file,line,sizeof line))>=0) { if (chkin<0) { printf("cannot read from port\n"); } else { printf("bytes: %d, line=%s\n",chkin, line); } } /*CODE TO EXIT THE LOOP GOES HERE*/ if (input[0] == 'q') break; } close(file); return 0; } 

上述代码的问题在于,它没有明确地初始化字符(“原始”)操作的串口; 所以根据之前设置端口的方式,会话可能如下所示:

 $ ./sertest /dev/ttyUSB0 enter input data: t1 enter input data: t2 enter input data: t3 enter input data: ^C 

换句话说,输入数据没有回音。 但是,如果串口设置正确,我们可以得到如下的会话:

 $ ./sertest /dev/ttyUSB0 enter input data: t1 enter input data: t2 bytes: 127, line=t1 enter input data: t3 bytes: 127, line=t2 enter input data: t4 bytes: 127, line=t3 enter input data: ^C 

…( 但是即使如此,这个sertest代码在输入字大于3个字符时失败。

最后,通过一些在线挖掘,我找到了“ (已解决)串行编程,写入读取问题 ”,它提供了writeread.cpp示例。 但是,对于这种逐字节的“双工”情况,即使这样也不够 – 即“ 串行编程HOWTO ”指出:“ 规范输入处理…是终端的正常处理模式…这意味着读取将只返回一个完整的输入行,一行默认以NL(ASCII LF)结尾… “; 因此我们不得不通过ICANON在我们的代码中将串行端口明确地设置为“非规范”(或“原始”)模式( 换句话说,仅仅通过open设置O_NONBLOCK是不够的 ) – 例如, “ 3.2如何从终端读取单个字符? – Unix编程常见问题 – 3.终端I / O ”。 一旦完成,调用writeread将“正确”设置serport示例(上面)的串行端口。

因此,我将一些writeread代码改回C,添加了所需的初始化内容,以及时间测量,发送字符串或文件的可能性以及额外的输出流( 用于将读取的串行数据“管道化”到单独的文件中 )。 下面的代码是writeread.cserial.h ,并且可以在下面的Bash会话中做类似的事情:

 $ ./writeread /dev/ttyUSB0 2000000 writeread.c 3>myout.txt stdalt opened; Alternative file descriptor: 3 Opening port /dev/ttyUSB0; Got speed 2000000 (4107/0x100b); Got file/string 'writeread.c'; opened as file (4182). +++DONE+++ Wrote: 4182 bytes; Read: 4182 bytes; Total: 8364 bytes. Start: 1284422340 s 443302 us; End: 1284422347 s 786999 us; Delta: 7 s 343697 us. 2000000 baud for 8N1 is 200000 Bps (bytes/sec). Measured: write 569.47 Bps, read 569.47 Bps, total 1138.94 Bps. $ diff writeread.c myout.txt $ ./writeread /dev/ttyUSB0 2000000 writeread.c 3>/dev/null stdalt opened; Alternative file descriptor: 3 Opening port /dev/ttyUSB0; Got speed 2000000 (4107/0x100b); Got file/string 'writeread.c'; opened as file (4182). +++DONE+++ Wrote: 4182 bytes; Read: 4182 bytes; Total: 8364 bytes. Start: 1284422380 s -461710 us; End: 1284422388 s 342977 us; Delta: 8 s 804687 us. 2000000 baud for 8N1 is 200000 Bps (bytes/sec). Measured: write 474.97 Bps, read 474.97 Bps, total 949.95 Bps. 

好:

  • 第一个惊喜 – 如果我正在写一个文件,速度会更快,而不是管道到/dev/null
  • 此外,获得约1000 BPS – 而设备显然是设置为200000 BPS!

在这一点上,我认为减速是因为writeread.c每个写入字节writeread.c ,我们等待一个标志被读取中断清除,然后再继续读取串行缓冲区。 可能的话,如果读写是单独的线程,那么读和写都可以尝试在单个readwrite调用中使用更大的字节块,那么带宽将会更好地被使用? ( 或者,也许中断处理程序在某种意义上的确起作用,就像一个“线程”并行运行一样 – 也许可以通过将所有读取相关函数移到中断处理程序来实现类似的功能

嗯 – 在这一点上,我非常愿意像writeread.c这样的现有代码的建议/链接,但多线程:)当然,对于任何其他可能的Linux工具,或者可能的Bash方法(尽管看起来Bash不会能够发挥这种控制…)

干杯!

writeread.c

 /* 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 -Wall -g writeread.c */ #include <stdio.h> #include <string.h> #include <stddef.h> #include <stdlib.h> #include <sys/time.h> #include "serial.h" int serport_fd; void usage(char **argv) { fprintf(stdout, "Usage:\n"); fprintf(stdout, "%s port baudrate file/string\n", argv[0]); 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]); } 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 bytesToSend; int sentBytes; char byteToSend[2]; int readChars; int recdBytes, totlBytes; char sResp[11]; struct timeval timeStart, timeEnd, timeDelta; float deltasec; /* 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]; serf_fd = open( serfstr, O_RDONLY ); fprintf(stdout, "Got file/string '%s'; ", serfstr); if (serf_fd < 0) { bytesToSend = strlen(serfstr); fprintf(stdout, "interpreting as string (%d).\n", bytesToSend); } else { struct stat st; stat(serfstr, &st); bytesToSend = st.st_size; fprintf(stdout, "opened as file (%d).\n", bytesToSend); } // 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 ); sentBytes = 0; recdBytes = 0; byteToSend[0]='x'; byteToSend[1]='\0'; gettimeofday( &timeStart, NULL ); // write / read loop - interleaved (ie will always write // one byte at a time, before 'emptying' the read buffer ) while ( sentBytes < bytesToSend ) { // read next byte from input... if (serf_fd < 0) { //interpreting as string byteToSend[0] = serfstr[sentBytes]; } else { //opened as file read( serf_fd, &byteToSend[0], 1 ); } if ( !writeport( serport_fd, byteToSend ) ) { fprintf(stdout, "write failed\n"); } //~ fprintf(stdout, "written:%s\n", byteToSend ); while ( wait_flag == TRUE ); if ( (readChars = readport( serport_fd, sResp, 10)) >= 0 ) { //~ fprintf(stdout, "InVAL: (%d) %s\n", readChars, sResp); recdBytes += readChars; fprintf(stdalt, "%s", sResp); } wait_flag = TRUE; // was == //~ usleep(50000); sentBytes++; } gettimeofday( &timeEnd, NULL ); // Close the open port close( serport_fd ); if (!(serf_fd < 0)) close( serf_fd ); fprintf(stdout, "\n+++DONE+++\n"); totlBytes = sentBytes + recdBytes; timeval_subtract(&timeDelta, &timeEnd, &timeStart); deltasec = timeDelta.tv_sec+timeDelta.tv_usec*1e-6; 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, atoi(serspeed)/10); fprintf(stdout, "Measured: write %.02f Bps, read %.02f Bps, total %.02f Bps.\n", sentBytes/deltasec, recdBytes/deltasec, totlBytes/deltasec); return 0; } 

serial.h

 /* serial.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 asynchronousous 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 asynchronousous (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; } 

那么,我设法把writeread.c一个使用pthread的线程版本( 代码如下 – 我不认为serial.h改变太多,反而在线程版本中没有太多serial.h )。 我也降低了速度到115200,现在我可以用下面的示例命令行会话中的设备确认这些测量:

 $ ./writeread /dev/ttyUSB0 115200 writeread.c 3>myout.txt stdalt opened; Alternative file descriptor: 3 Opening port /dev/ttyUSB0; Got speed 115200 (4098/0x1002); Got file/string 'writeread.c'; opened as file (6131). write_thread_function spawned write: 6131 read: 18 read: 64 read: 110 read: 156 read: 202 ... read: 6066 read: 6089 read: 6123 read: 6131 +++DONE+++ Wrote: 6131 bytes; Read: 6131 bytes; Total: 12262 bytes. Start: 1284462824 s 141104 us; End: 1284462824 s 682598 us; Delta: 0 s 541494 us. 115200 baud for 8N1 is 11520 Bps (bytes/sec). Measured: write 11322.38 Bps (98.28%), read 11322.38 Bps (98.28%), total 22644.76 Bps. $ diff writeread.c myout.txt $ 

那么测量结果现在可以报告预期波特率的99%,所以我想这意味着该程序的分析方面应该可以工作。 注意:

  • 对于这个设备, write是在一个单独的块中执行的( 如果需要的话PC应该能够处理数据包的排序 ),
  • read则以较小的块进行( 可能表明设备不等待整个块到达 – 相反,一旦收到足够的块,它就开始发回较小的块

那么,我想这是我最初需要的。 我也猜测它可能无法通过进程替换来安排catecho来执行,我们称之为“线程”,方式:)( 现在,我在2000000波特上做同样的事情,但是这表示该设备的编程有问题 )。

干杯!

writeread.c线程版本

 /* 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 "serial.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; }; void usage(char **argv) { fprintf(stdout, "Usage:\n"); fprintf(stdout, "%s port baudrate file/string\n", argv[0]); 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 lastBytesWritten; struct write_thread_data *my_data; my_data = (struct write_thread_data *) arg; fprintf(stdout, "write_thread_function spawned\n"); 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]; 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; }