serialoverip功能与ser2net相似,都能实现IP端口与串口间的数据转发,但不同点在于:
1. 官方发布的ser2net只支持TCP协议转发,增加UDP需要额外加补丁(详见:http://sourceforge.net/p/ser2net/patches/12/)。
2. serialoverip支持且只支持UDP协议转发,但也因此使得其程序非常小巧。后有我注释、替换变量名后的源代码,对此类软件的原理分析很有帮助。
在安装serialoverip后,尝试加入其启动命令到rc.local中,但发现开机并未成功启动serialoverip。几番分析,是由于执行rc.local中语句时OpenWrt的IP分配还未完成(即serialoverip无法绑定192.168.1.1端口),造成启动失败。
由于serialoverip源代码对此情况没有做考虑,所以笔者决定使用sleep来固定等待,修改了rc.local中命令:
sleep 10s && serialoverip -s 192.168.1.1 10002 -d /dev/ttyATH0 9600-8N1 &
先等待10秒再打开192.168.1.1:10002上UDP端口与/dev/ttyATH0串口的转发(加&让其后台运行)。
上例方法基本解决了serialoverip无法启动的问题,但是灵活性还是较低。苦苦搜索了sh命令规范后,本吊探索出一个更灵活的脚本(由于Linux虚拟机没装输入法,所以用英文注解了):
# Start data forwarding between UDP and Serial. # If an instance is already running, then exit. # The starting timeout is 30s. timeout=30 while [ $timeout -ne 0 ] do if [ `ps | grep serialoverip | wc -l` -eq 2 ] then break fi if [ `ifconfig br-lan | grep 192.168.1.1 | wc -l` -eq 1 ] then serialoverip -s 192.168.1.1 10002 -d /dev/ttyATH0 9600-8N1 & break fi let timeout=timeout-1 sleep 1s done
以上代码功能,检测是否已经完成IP分配,一旦完成分配则启动serialoverip并退出,否则每隔1秒再检测,直到30秒时间全部走完(当然你也可以改个更高的timeout初始值,不过我觉得30秒够长了)。
加到rc.local的exit 0之前就行。当然为了提高rc.local的可读性,还可以将以上代码连同exit 0写入到新的文件(比如udp2serial),放在/etc目录(或其他目录),之后在rc.local的exit 0前加入:
sh /etc/udp2serial
即当执行rc.local时会再去执行/etc下的udp2serial。由于这种方法最坏情况下会造成阻塞,故建议是放在exit 0之前,保证其他命令都执行后再处理(上面的例子均是放在exit 0前)。
以下是我使用的方案
/etc/rc.local文件末尾:
sh /etc/udp2serial exit 0
/etc/udp2serial文件:
# Start data forwarding between UDP and Serial. # If an instance is already running, then exit. # The starting timeout is 30s. timeout=30 while [ $timeout -ne 0 ] do if [ `ps | grep serialoverip | wc -l` -eq 2 ] then break fi if [ `ifconfig br-lan | grep 192.168.1.1 | wc -l` -eq 1 ] then serialoverip -s 192.168.1.1 10002 -d /dev/ttyATH0 9600-8N1 & break fi let timeout=timeout-1 sleep 1s done exit 0
欢迎跟我讨论更加有效的解决方法:)
我的邮箱:imjgz@qq.com
可以从http://sourceforge.net/projects/serialoverip/获取源代码。
这是serialoverip的源代码,我进行了变量替换和部分的注释(由于Linux虚拟机没装输入法,所以用英文注解了):
/* * —————————————————————————- * serialoverip * Utility for transport of serial interfaces over UDP/IP * Copyright (C) 2002 Stefan-Florin Nicola <sten@fx.ro> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place – Suite 330, Boston, MA 02111-1307, USA * */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <fcntl.h> #include <termios.h> #include <signal.h> #define MAXMESG 2048 char *programName; int source[2], sourceType[2]; // Display the help. void PrintHelp() { fprintf(stderr,"\ SerialOverIP version 1.0, Copyright (C) 2002 Stefan-Florin Nicola <sten@fx.ro> SerialOverIP comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under GNU General Public License. Usage: %s <source1> <source2> where <source1> and <source2> are one of the folowing: -s <IP> <port> UDP server on IP:port -c <IP> <port> UDP client for server IP:port -d <device> sss-dps local serial device sss is speed (50,..,230400) d is data bits (5,6,7,8) p is parity type (N,E,O) s is stop bits (1,2) ",programName); return; } // Set the serial terminal and return the result. int SetSerial(int source, struct termios *cfg, int baudRate, int dataBits, unsigned char parityType, int stopBits) { // Reset the serial terminal. cfmakeraw(cfg); // Set the serial baud rate. switch (baudRate) { case 50: { cfsetispeed(cfg, B50); cfsetospeed(cfg, B50); break; } case 75: { cfsetispeed(cfg, B75); cfsetospeed(cfg, B75); break; } case 110: { cfsetispeed(cfg, B110); cfsetospeed(cfg, B110); break; } case 134: { cfsetispeed(cfg, B134); cfsetospeed(cfg, B134); break; } case 150: { cfsetispeed(cfg, B150); cfsetospeed(cfg, B150); break; } case 200: { cfsetispeed(cfg, B200); cfsetospeed(cfg, B200); break; } case 300: { cfsetispeed(cfg, B300); cfsetospeed(cfg, B300); break; } case 600: { cfsetispeed(cfg, B600); cfsetospeed(cfg, B600); break; } case 1200: { cfsetispeed(cfg, B1200); cfsetospeed(cfg, B1200); break; } case 1800: { cfsetispeed(cfg, B1800); cfsetospeed(cfg, B1800); break; } case 2400: { cfsetispeed(cfg, B2400); cfsetospeed(cfg, B2400); break; } case 4800: { cfsetispeed(cfg, B4800); cfsetospeed(cfg, B4800); break; } case 9600: { cfsetispeed(cfg, B9600); cfsetospeed(cfg, B9600); break; } case 19200: { cfsetispeed(cfg, B19200); cfsetospeed(cfg, B19200); break; } case 38400: { cfsetispeed(cfg, B38400); cfsetospeed(cfg, B38400); break; } case 57600: { cfsetispeed(cfg, B57600); cfsetospeed(cfg, B57600); break; } case 115200: { cfsetispeed(cfg, B115200); cfsetospeed(cfg, B115200); break; } case 230400: { cfsetispeed(cfg, B230400); cfsetospeed(cfg, B230400); break; } } // Set the parity type. switch (parityType | 32) { case 'n': { cfg->c_cflag &= ~PARENB; break; } case 'e': { cfg->c_cflag |= PARENB; cfg->c_cflag &= ~PARODD; break; } case 'o': { cfg->c_cflag |= PARENB; cfg->c_cflag |= PARODD; break; } } cfg->c_cflag &= ~CSIZE; // Set the data bits. switch (dataBits) { case 5: { cfg->c_cflag |= CS5; break; } case 6: { cfg->c_cflag |= CS6; break; } case 7: { cfg->c_cflag |= CS7; break; } case 8: { cfg->c_cflag |= CS8; break; } } // Set the stop bits. if (stopBits == 1) cfg->c_cflag &= ~CSTOPB; else cfg->c_cflag |= CSTOPB; // Set the serial terminal and return. return tcsetattr(source, TCSANOW, cfg); } // Close serial terminal then exit the program. void ExitProgram(int x) { if (sourceType[0] & 2) { tcflush(source[0], TCIOFLUSH); close(source[0]); } if (sourceType[1] & 2) { tcflush(source[1], TCIOFLUSH); close(source[1]); } printf("%s exiting.\n", programName); exit(1); } int main(int argc, char **argv) { int i, n, w, clen[2], nonblock[2], baudRate, dataBits, stopBits; unsigned char c, buf[MAXMESG], *dataPointer, parityType; struct termios cfg; struct sockaddr_in addr[4][4]; struct sigaction newact, oldact; programName = argv[0]; if (argc != 7) { PrintHelp(); return 1; } for (i = 0; i < 2; i++) { sourceType[i] = 0; switch (argv[3 * i + 1][1]) { case 's': sourceType[i] = 1; case 'c': bzero((char *)&(addr[i][0]), sizeof(addr[i][0])); addr[i][0].sin_family = AF_INET; addr[i][0].sin_addr.s_addr = inet_addr(argv[3 * i + 2]); addr[i][0].sin_port = htons(atoi(argv[3 * i + 3])); bzero((char *)&(addr[i][1]), sizeof(addr[i][1])); addr[i][1].sin_family = AF_INET; addr[i][1].sin_addr.s_addr = 0; addr[i][1].sin_port = htons(0); if ((source[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { fprintf(stderr, "%s: can't open datagram socket", programName); return 3; } if (bind(source[i], (struct sockaddr *)&addr[i][!sourceType[i]], sizeof(addr[i][!sourceType[i]])) < 0) { fprintf(stderr, "%s: can't bind local address", programName); return 4; } break; case 'd': sourceType[i] = 2; if ((source[i] = open(argv[3 * i + 2], O_RDWR | O_NDELAY)) < 0) { fprintf(stderr, "%s: could not open device %s\n", programName, argv[3 * i + 2]); return -1; } n = sscanf(argv[3 * i + 3], "%d-%d%c%d", &baudRate, &dataBits, &parityType, &stopBits); if (n < 4) { fprintf(stderr, "%s: invalid argument %1d from %s\n", programName, read + 1, argv[3 * i + 3]); return 3; } if (SetSerial(source[i], &cfg, baudRate, dataBits, parityType, stopBits) < 0) { fprintf(stderr, "%s: could not initialize device %s\n", programName, argv[3 * i + 2]); return 7; } break; default: PrintHelp(); return 2; } clen[i] = sizeof(addr[i][1]); nonblock[i] = !(sourceType[i] & 1); } // Let ExitProgram function handle the SIGINT message(Generated by Ctrl+C). signal(SIGINT, ExitProgram); i = 0; // Keep data forwarding until SIGINT message occurred. while (1) { if (sourceType[i] & 2) n = read(source[i], buf, MAXMESG); else { n = recvfrom(source[i], buf, MAXMESG, nonblock[i] * MSG_DONTWAIT, (struct sockaddr *)&addr[i][sourceType[i]], &clen[i]); nonblock[i] = 1; } dataPointer = buf; while (n > 0) { if (sourceType[!i] & 2) w = write(source[!i], dataPointer, n); else w = sendto(source[!i], dataPointer, n, 0, (struct sockaddr *)&addr[!i][sourceType[!i]], clen[!i]); if (w > 0) { n -= w; dataPointer += w; } else { fprintf(stderr, "%s: write error\n", programName); break; } } i = !i; } return 0; }