一、源码准备
1、 在barrier_breaker/package/utils文件夹下新建ttl_client文件
2、 barrier_breaker/package/utils/ttl_client文件夹下的Makefile文件如下
# # Copyright (C) 2009 OpenWrt.org # # This is free software, licensed under the GNU General Public License v2. # See /LICENSE for more information. # include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/kernel.mk PKG_NAME:=ttl_client PKG_RELEASE:=1 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) include $(INCLUDE_DIR)/package.mk define Package/ttl_client SECTION:=utils CATEGORY:=Utilities TITLE:=serial to tcp DEPENDS:=+libuci +libpthread endef define Package/ttl_client/description A client of tcp to serial or serial to tcp endef define Build/Prepare mkdir -p $(PKG_BUILD_DIR) $(CP) ./src/* $(PKG_BUILD_DIR)/ endef define Build/Configure endef define Build/Compile $(MAKE) -C $(PKG_BUILD_DIR) CC="$(TARGET_CC)" CFLAGS="$(TARGET_CFLAGS) -Wall -I$(LINUX_DIR)/user_headers/include" LDFLAGS="$(TARGET_LDFLAGS)" endef define Package/ttl_client/install $(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_DIR) $(1)/etc/config $(INSTALL_BIN) $(PKG_BUILD_DIR)/ttl_client $(1)/usr/sbin/ endef $(eval $(call BuildPackage,ttl_client))
3、 新建src文件夹,barrier_breaker/package/utils/ttl_client/src的Makefile文件如下
CC = gcc CFLAGS = -Wall OBJS = ttl_client.o all: ttl_client %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< ttl_client: $(OBJS) $(CC) -o $@ $(OBJS) -luci -lpthread clean: rm -f rbcfg *.o
4、barrier_breaker/package/utils/ttl_client/src的ttl_client.c文件如下
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> #include <errno.h> #include <strings.h> #include <time.h> #include <arpa/inet.h> #include <pthread.h> #define SER_MAXLINE 128 #define SOCK_MAXLINE 136//为了应对缓存一出,多分配一个字节 struct argument { int fd; int sockfd; }; //为了保证用户输入的波特率是个正确的值,所以需要这两个数组验证,对于设置波特率时候,前面要加个B int speed_arr[] = { B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300, B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300, }; int name_arr[] = {115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300, 115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300, }; /*----------------------------------------------------------------------------- 函数名: set_speed 参数: int fd ,int speed 返回值: void 描述: 设置fd表述符的串口波特率 *-----------------------------------------------------------------------------*/ void set_speed(int fd ,int speed) { struct termios opt; int i; int status; tcgetattr(fd,&opt); for(i = 0;i < sizeof(speed_arr)/sizeof(int);i++) { if(speed == name_arr[i]) //找到标准的波特率与用户一致 { tcflush(fd,TCIOFLUSH); //清除IO输入和输出缓存 cfsetispeed(&opt,speed_arr[i]); //设置串口输入波特率 cfsetospeed(&opt,speed_arr[i]); //设置串口输出波特率 status = tcsetattr(fd,TCSANOW,&opt); //将属性设置到opt的数据结构中,并且立即生效 if(status != 0) perror("tcsetattr fd:"); //设置失败 return ; } tcflush(fd,TCIOFLUSH); //每次清除IO缓存 } } /*----------------------------------------------------------------------------- 函数名: set_parity 参数: int fd 返回值: int 描述: 设置fd表述符的奇偶校验 *-----------------------------------------------------------------------------*/ int set_parity(int fd) { struct termios opt; if(tcgetattr(fd,&opt) != 0) //或许原先的配置信息 { perror("Get opt in parity error:"); return -1; } /*通过设置opt数据结构,来配置相关功能,以下为八个数据位,不使能奇偶校验*/ opt.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); opt.c_oflag &= ~OPOST; opt.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); opt.c_cflag &= ~(CSIZE | PARENB); opt.c_cflag |= CS8; tcflush(fd,TCIFLUSH); //清空输入缓存 if(tcsetattr(fd,TCSANOW,&opt) != 0) { perror("set attr parity error:"); return -1; } return 0; } /*----------------------------------------------------------------------------- 函数名: serial_init 参数: char *dev_path,int speed,int is_block 返回值: 初始化成功返回打开的文件描述符 描述: 串口初始化,根据串口文件路径名,串口的速度,和串口是否阻塞,block为1表示阻塞 *-----------------------------------------------------------------------------*/ int serial_init(char *dev_path,int speed,int is_block) { int fd; int flag; flag = 0; flag |= O_RDWR; //设置为可读写的串口属性文件 if(is_block == 0) flag |=O_NONBLOCK; //若为0则表示以非阻塞方式打开 fd = open(dev_path,flag); //打开设备文件 if(fd < 0) { perror("Open device file err:"); close(fd); return -1; } /*打开设备文件后,下面开始设置波特率*/ set_speed(fd,speed); //考虑到波特率可能被单独设置,所以独立成函数 /*设置奇偶校验*/ if(set_parity(fd) != 0) { perror("set parity error:"); close(fd); //一定要关闭文件,否则文件一直为打开状态 return -1; } return fd; } /*----------------------------------------------------------------------------- 函数名: serial_send 参数: int fd,char *str,unsigned int len 返回值: 发送成功返回发送长度,否则返回小于0的值 描述: 向fd描述符的串口发送数据,长度为len,内容为str *-----------------------------------------------------------------------------*/ int serial_send(int fd,char *str,unsigned int len) { int ret; if(len > strlen(str)) //判断长度是否超过str的最大长度 len = strlen(str); ret = write(fd,str,len); if(ret < 0) { perror("serial send err:"); return -1; } return ret; } /*----------------------------------------------------------------------------- 函数名: serial_read 参数: int fd,char *str,unsigned int len,unsigned int timeout 返回值: 在规定的时间内读取数据,超时则退出,超时时间为ms级别 描述: 向fd描述符的串口接收数据,长度为len,存入str,timeout 为超时时间 *-----------------------------------------------------------------------------*/ int serial_read(int fd, char *str, unsigned int len, unsigned int timeout) { fd_set rfds; struct timeval tv; int ret; //每次读的结果 int sret; //select监控结果 int readlen = 0; //实际读到的字节数 char * ptr; ptr = str; //读指针,每次移动,因为实际读出的长度和传入参数可能存在差异 FD_ZERO(&rfds); //清除文件描述符集合 FD_SET(fd,&rfds); //将fd加入fds文件描述符,以待下面用select方法监听 /*传入的timeout是ms级别的单位,这里需要转换为struct timeval 结构的*/ tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout%1000)*1000; /*防止读数据长度超过缓冲区*/ //if(sizeof(&str) < len) // len = sizeof(str); /*开始读*/ while(readlen < len) { sret = select(fd+1,&rfds,NULL,NULL,&tv); //检测串口是否可读 if(sret == -1) //检测失败 { perror("select:"); break; } else if(sret > 0) //<SPAN style="WHITE-SPACE: pre"> </SPAN>//检测成功可读 { ret = read(fd,ptr,1); //第三个参数为请求读取的字节数 if(ret < 0) { perror("read err:"); break; } else if(ret == 0) break; readlen += ret; //更新读的长度 ptr += ret; //更新读的位置 } else //超时 sret == 0 ,没填满buf也退出循环 { printf("timeout! "); break; } } return readlen; } int socket_read(int fd,char *str,unsigned int len,unsigned int timeout) { fd_set fdsr; struct timeval tv; int readlen = 0; char * ptr; int ret; ptr = str; // initialize file descriptor set FD_ZERO(&fdsr);//每次循环都要清空 FD_SET(fd, &fdsr); tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout%1000)*1000; while(readlen < len){ ret = select(fd + 1, &fdsr, NULL, NULL, &tv); if (ret < 0) { perror("select"); break; } else if (ret == 0) { printf("timeout "); break; } //每次申请读取8个字节,但事实上是按发送端每次发送的字符串长度来确定的,如果长度小于8,则每次读取实际长度,如果大于8,则读取8字节。 //recv多少就从缓冲区中删除多少,剩下的数据可以在下次recv时得到 //即使子线程挂起,也一直有数据可以读,数据不丢失,真正的接收数据是协议来完成的,存放在s的接收缓冲中。 ret = recv(fd, ptr, 8, 0);//申请8个字节 if (ret <= 0) {//如果连接已中止,返回0。如果发生错误,返回-1 printf("client close "); close(fd); FD_CLR(fd, &fdsr); fd = 0; } else { readlen +=ret; ptr += ret; //printf("the ret length is:%d ",readlen); } } return readlen; } void *socket_thread(void *arg) { char sockbuf[SOCK_MAXLINE]; int sreadlen; int freadlen; struct argument thread_arg; thread_arg = *(struct argument *)arg; memset(sockbuf, 0, sizeof(sockbuf)); while(1) { sreadlen = socket_read(thread_arg.sockfd, sockbuf, SOCK_MAXLINE-8, 1000);//为了防止缓存溢出,少读取一个字节 printf("the sockbuf is:%s ", sockbuf);//打印出数据 printf("the sockbuf length is:%d ",sreadlen); freadlen = serial_send(thread_arg.fd,sockbuf,sreadlen); printf("send %d bytes! ",freadlen); memset(sockbuf, 0, sizeof(sockbuf)); usleep(1); } } int main(int argc, char** argv) { //串口变量定义 int fd; //int ret; //char str[]="hello linux serial!"; //字符串初始化 char serbuf[SER_MAXLINE]; int readlen; // socket变量定义 int sockfd; struct sockaddr_in servaddr; //多线程 pthread_t thread; int mret; struct argument arg; //判断输入是否正确输入参数 if( argc != 2) { printf("usage: ./client <ipaddress> "); exit(0); } //串口初始化 //fd = serial_init("/dev/ttyS0",115200,1); //fd = serial_init("/dev/ttyUSB0",115200,1); fd = serial_init("/dev/ttyATH0", 115200,1);if(fd < 0) { perror("serial init err:"); return -1; } memset(serbuf, 0, sizeof(serbuf)); //socket 初始化 if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("create socket error: %s(errno: %d) ", strerror(errno),errno); exit(0); } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(10001); if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) { printf("inet_pton error for %s ",argv[1]); exit(0); } if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { printf("connect error: %s(errno: %d) ",strerror(errno),errno); exit(0); } printf("send msg to server: "); //测试代码 //ret = serial_send(fd,str,22); //printf("send %d bytes! ",ret); //fgets(sendline, MAXLINE, stdin);//要处理 /*if( send(sockfd, str, strlen(str), 0) < 0) { printf("send msg error: %s(errno: %d) ", strerror(errno), errno); exit(0); }*/ //创建多线程,tcp转串口 arg.fd=fd; arg.sockfd=sockfd; mret = pthread_create(&thread, NULL, socket_thread, (void *)(long)&arg); if (mret != 0) { printf("Create thread failed "); exit(mret); } printf("Create thread "); //串口转tcp while(1) { readlen = serial_read(fd,serbuf,SER_MAXLINE,1000);//1s内如果数据没有装满buf,则读取完毕。 如果数据量大,则读取的速度也越快。 printf("the serbuf is :%s ",serbuf); printf("the serbuf length is :%d ",readlen); if( send(sockfd, serbuf, readlen, 0) < 0) { printf("send msg error: %s(errno: %d) ", strerror(errno), errno); exit(0); } memset(serbuf, 0, sizeof(serbuf)); usleep(1); } //退出 close(fd); close(sockfd); exit(0); }
二、编译
1、 进入barrier_breaker目录,执行make package/utils/ttl_client/compile V=s
三、配置
安装好ipk包及其依赖包后,
vi /etc/inittab
注释最后一行。