1、串口轉以太ttl2tcp應用程序html
一、 新建文件夾ttl2tcpnode
該文件夾下的Makefilelinux
# # 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:=ttl2tcp PKG_RELEASE:=0.5.0 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) include $(INCLUDE_DIR)/package.mk define Package/ttl2tcp SECTION:=utils CATEGORY:=Utilities TITLE:=serial to tcp DEPENDS:=+libuci +libpthread endef define Package/ttl2tcp/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/ttl2tcp/install $(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_BIN) $(PKG_BUILD_DIR)/ttl2tcp $(1)/usr/sbin/ $(INSTALL_DIR) $(1)/etc/config $(INSTALL_DATA) ./files/$(PKG_NAME).config $(1)/etc/config/$(PKG_NAME) $(INSTALL_DIR) $(1)/etc/init.d $(INSTALL_BIN) ./files/$(PKG_NAME).init $(1)/etc/init.d/$(PKG_NAME) endef $(eval $(call BuildPackage,ttl2tcp))
二、 ttl2tcp\src\ttl2tcp.cios
/* * ttl2tcp * * tingpan<dktingpan@sina.cn> 2015-11-30 * * this is a client of serial translate to tcp or tcp translate to serial. * serial read overtime is 0.5s * every server read overtime is 0.5s. */ #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> #include <uci.h> #include <semaphore.h> #include <signal.h> #define SER_MAXLINE 128 #define CONF_MAXLINE 64 #define SEND_MAXLINE 64 #define RECV_MAXLINE 64 //爲了應對緩存溢出,多分配一個字節, //與請求的字節數有關,由於請求的是8個字節,而串口讀每次請求1個字節,因此能夠準確不超過緩衝區。 #define SOCK_MAXLINE 136 #define TTL2TCP_DEBUG 0 #if (TTL2TCP_DEBUG == 1) #define PRT(...) printf(__VA_ARGS__) #else #define PRT(...) #endif struct argument { int fd; //串口文件描述符 int sockfd; }; struct _options { unsigned int enabled; char name[10]; unsigned int baudrate; //unsigned int data; //unsigned int parity; //unsigned int stop; unsigned int open; unsigned int sendhex; char send[SEND_MAXLINE]; unsigned int recvhex; char recv[RECV_MAXLINE]; struct in_addr ipaddr; unsigned int port; }; struct _options opt; //pthread_mutex_t socket_lock; //互斥鎖 //爲了保證用戶輸入的波特率是個正確的值,因此須要這兩個數組驗證,對於設置波特率時候,前面要加個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 * 備註: 由於沒法發送含有0x00的字符串,因此將len相關注釋,有待改進 */ int serial_send(int fd,char *str,unsigned int len) { int ret; //if(len > strlen(str)) //判斷長度是否超過str的最大長度,以0x00爲結束標誌 //if(len > sizeof(str)) //判斷長度是否超過str的最大長度 //if(len > sizeof(str)) //len = sizeof(str); //printf("sizeof(str) is %d\n",sizeof(str)); //printf("strlen(str) is %d\n",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; //開始讀 while(readlen < len) { sret = select(fd+1,&rfds,NULL,NULL,&tv); //檢測串口是否可讀 if(sret == -1) //檢測失敗 { perror("serial_read 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也退出循環 { PRT("serial read timeout!\n"); break; } } return readlen; } /* * socket_read: 讀取tcp數據 * @fd: socket文件描述符 * @str:將讀到的數據存放在該地址 * @len:申請讀取的字符長度 * @timeout:超時時間,單位ms */ 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("socket_read select"); //自動添加冒號 sleep(1); break; //exit(-1); } else if (ret == 0) { PRT("socket read timeout\n"); sleep(1); break; } //每次申請讀取8個字節,但事實上是按發送端每次發送的字符串長度來肯定的,若是長度小於8,則每次讀取實際長度,若是大於8,則讀取8字節。 //recv多少就從緩衝區中刪除多少,剩下的數據能夠在下次recv時獲得 //即便子線程掛起,也一直有數據能夠讀,數據不丟失,真正的接收數據是協議來完成的,存放在s的接收緩衝中。 ret = recv(fd, ptr, 8, 0);//申請8個字節 if (ret <= 0) {//若是鏈接已停止,返回0。若是發生錯誤,返回-1 printf("client close\n"); close(fd); //能夠關閉文件描述符嗎,如何主線程和子線程的同步, 執行了該句,子線程的文件描述符仍是不變 FD_CLR(fd, &fdsr); fd = 0; //會改變文件描述符嗎 //sleep(1); //break;//要跳出循環,不然只要send沒發東西,就一直在這裏面。斷線了必定要從新鏈接,跳出了也沒用。 } else { readlen +=ret; ptr += ret; //printf("the ret length is:%d\n",readlen); } } return readlen; } /* * read_config: 讀取配置文件 * @popt: 配置信息保存的結構體 */ static int read_config(struct _options *popt) { static struct uci_context *ctx; struct uci_ptr ptr; char a[CONF_MAXLINE]; //int len = 0; ctx = uci_alloc_context(); //讀取設備名稱 if ((strncpy(a, "ttl2tcp.device.name", sizeof(a)) == NULL) || (uci_lookup_ptr(ctx, &ptr, a, true) != UCI_OK)) { printf("Read ttl2tcp.device.name failed! exit.\n"); exit(-1); } if (ptr.o) { strncpy(popt->name, ptr.o->v.string, sizeof(popt->name)); } else { printf("ttl2tcp.device.name Not found!\n"); } //讀取串口波特率 if ((strncpy(a, "ttl2tcp.device.baudrate", sizeof(a)) == NULL) || (uci_lookup_ptr(ctx, &ptr, a, true) != UCI_OK)) { printf("Read tttl2tcp.device.baudrate failed! exit.\n"); exit(-1); } if (ptr.o) { popt->baudrate = strtol(ptr.o->v.string, NULL, 10); } else { printf("ttl2tcp.device.baudrate Not found!\n"); } //Serial Probe //open if ((strncpy(a, "ttl2tcp.probe.open", sizeof(a)) == NULL) || (uci_lookup_ptr(ctx, &ptr, a, true) != UCI_OK)) { printf("Read ttl2tcp.probe.open failed! exit.\n"); exit(-1); } if (ptr.o) { popt->open = strtol(ptr.o->v.string, NULL, 10); } else { printf("ttl2tcp.probe.open Not found!\n"); } //sendhex if ((strncpy(a, "ttl2tcp.probe.sendhex", sizeof(a)) == NULL) || (uci_lookup_ptr(ctx, &ptr, a, true) != UCI_OK)) { printf("Read ttl2tcp.probe.sendhex failed! exit.\n"); exit(-1); } if (ptr.o) { popt->sendhex = strtol(ptr.o->v.string, NULL, 10); } else { printf("ttl2tcp.probe.sendhex Not found!\n"); } //send if ((strncpy(a, "ttl2tcp.probe.send", sizeof(a)) == NULL) || (uci_lookup_ptr(ctx, &ptr, a, true) != UCI_OK)) { printf("Read ttl2tcp.device.send failed! exit.\n"); exit(-1); } if (ptr.o) { //if (sizeof(popt->send) > SEND_MAXLINE) { //len = SEND_MAXLINE; //} else { //len = sizeof(popt->send); //} //strncpy(popt->send, ptr.o->v.string, len); strncpy(popt->send, ptr.o->v.string, sizeof(popt->send)); } else { printf("ttl2tcp.probe.send Not found!\n"); } //recvhex if ((strncpy(a, "ttl2tcp.probe.recvhex", sizeof(a)) == NULL) || (uci_lookup_ptr(ctx, &ptr, a, true) != UCI_OK)) { printf("Read ttl2tcp.probe.recvhex failed! exit.\n"); exit(-1); } if (ptr.o) { popt->recvhex = strtol(ptr.o->v.string, NULL, 10); } else { printf("ttl2tcp.probe.recvhex Not found!\n"); } //recv if ((strncpy(a, "ttl2tcp.probe.recv", sizeof(a)) == NULL) || (uci_lookup_ptr(ctx, &ptr, a, true) != UCI_OK)) { printf("Read ttl2tcp.device.recv failed! exit.\n"); exit(-1); } if (ptr.o) { strncpy(popt->recv, ptr.o->v.string, sizeof(popt->recv)); } else { printf("ttl2tcp.probe.send Not found!\n"); } //讀取ip地址 if (!snprintf(a,sizeof(a),"ttl2tcp.server.ipaddr") || (uci_lookup_ptr(ctx, &ptr, a, true) != UCI_OK)) { printf("Read ttl2tcp.server.ipaddr failed! exit.\n"); exit(-1); } if (ptr.o) { unsigned int laddr; laddr = inet_addr(ptr.o->v.string);//由於ipaddr爲網絡字節順序,大端字節序,而輸入的爲主機字節序 memcpy(&popt->ipaddr, &laddr, 4); } else { printf("ttl2tcp.server.ipaddr Not found!\n");//若是不存在對應的配置文件 } //讀取port if (!snprintf(a,sizeof(a),"ttl2tcp.server.port") || (uci_lookup_ptr(ctx, &ptr, a, true) != UCI_OK)) { printf("Read ttl2tcp.server.port failed! exit.\n"); exit(-1); } if (ptr.o) { popt->port = strtol(ptr.o->v.string, NULL, 10); } else { printf("ttl2tcp.server.port Not found!\n");//若是不存在對應的配置文件 } uci_free_context(ctx); return 0; } //tcp轉串口 /*void *socket_thread(void *arg) { char sockbuf[SOCK_MAXLINE]; int sreadlen = 0; int freadlen = 0; struct argument thread_arg; thread_arg = *(struct argument *)arg; memset(sockbuf, 0, sizeof(sockbuf)); while(1) { //pthread_mutex_lock(&socket_lock);//lock sreadlen = socket_read(thread_arg.sockfd, sockbuf, SOCK_MAXLINE-8, 50);//爲了防止緩存溢出,少讀取一個字節 //printf("thread sockfd is %d\n",thread_arg.sockfd); PRT("the sockbuf is:%s\n", sockbuf);//打印出數據 PRT("the sockbuf length is:%d\n",sreadlen); freadlen = serial_send(thread_arg.fd,sockbuf,sreadlen); //發送含有0x00的十六進制數據有問題,會看成結束符號 PRT("tcp to serial send %d bytes!\n",freadlen); memset(sockbuf, 0, sizeof(sockbuf)); //pthread_mutex_unlock(&socket_lock);//unlock usleep(1); } }*/ //str1和str2對比,比較兩個字符串或者兩組十六進制數是否同樣,str1的長度大於str2,將被忽略 int strhexcmp(char *str1,char *str2,int len) { int i; for (i = 0; i < len; i++) { if (*(str1+i) != *(str2+i)) return -1; } return 0; } //串口握手測試 void serial_probe(int fd) { char serbuf[SER_MAXLINE]; int readlen; char *beginstr; int beginstr_len = 0; //char beginstr_hex[]={0x45,0x46,0x00,0x0D,//頭部 // 0x00,0x00,0x01,//源,第三個爲類型,00無效;01主機;02基站;03爲CS;04節點 // 0x01,0x00,0x04,//目標,發送給節點,第三個爲類型, // 0x88,0x01,0x00 , //示開始接收數據 // 0x2E,0x20,0x0D,0x0A};//結束 char beginstr_hex[22] = {0}; int beginstr_hex_len = 0; char *ackstr; int ackstr_len = 0; char ackstr_hex[22] = {0}; int ackstr_hex_len = 0; int i; beginstr = opt.send; ackstr = opt.recv; //爲防止溢出,最大爲SEN_MAXLIN-1,數組最後一個爲'\0' if (strlen(beginstr) >= SEND_MAXLINE) { beginstr_len = SEND_MAXLINE -1; } else { beginstr_len = strlen(beginstr); } if (strlen(ackstr) >= RECV_MAXLINE) { ackstr_len = RECV_MAXLINE -1; } else { ackstr_len = strlen(ackstr); } PRT("beginstr is %d: %s\n", beginstr_len, beginstr); PRT("ackstr is %d: %s\n", ackstr_len, ackstr); if (opt.sendhex == 1) { //將字符串轉換成十六進制 beginstr_hex_len = (beginstr_len+1)/3; PRT("beginstr to hex len is %d\n",beginstr_hex_len); for (i = 0; i < beginstr_hex_len; i++) { beginstr_hex[i] = (char)strtol(&opt.send[3*i], NULL, 16); PRT("beginstr_hex[%d] is %hhx\n",i,beginstr_hex[i]); } beginstr = beginstr_hex; beginstr_len = beginstr_hex_len; PRT("beginstr to hex is success\n"); } if (opt.recvhex == 1) { //將字符串轉換成十六進制 ackstr_hex_len = (ackstr_len+1)/3; PRT("ackstr to hex len is %d\n",ackstr_hex_len); for (i = 0; i < ackstr_hex_len; i++) { ackstr_hex[i] = (char)strtol(&opt.recv[3*i], NULL, 16); PRT("ackstr_hex[%d] is %hhx\n",i,ackstr_hex[i]); } ackstr = ackstr_hex; ackstr_len = ackstr_hex_len; PRT("ackstr to hex is success\n"); } printf("serial probing ...\n"); while(1) { memset(serbuf, 0, sizeof(serbuf)); serial_send(fd, beginstr, beginstr_len); readlen = serial_read(fd,serbuf,SER_MAXLINE,1000); if (readlen > 0) { //PRT("the ack serbuf is %d: %s\n",readlen,serbuf); PRT("the ack serbuf is :\n"); for (i = 0; i < readlen; i++) { PRT("%hhx ",*(serbuf+i)); } PRT("\n"); //if( strcmp(serbuf,ackstr) == 0 ) if( strhexcmp(serbuf,ackstr,ackstr_len) == 0 ) { PRT("begin print data\n"); break; } } } } int main(int argc, char** argv) { //串口變量定義 int fd; char str[]="hello linux serial!"; //字符串初始化 char serbuf[SER_MAXLINE]; int readlen; char dev_path[20]; // socket變量定義 int sockfd; struct sockaddr_in servaddr; char sockbuf[SOCK_MAXLINE]; int sreadlen = 0; int freadlen = 0; //多線程 //pthread_t thread; //int mret; //struct argument arg; //讀取配置文件 memset(&opt,0 ,sizeof(struct _options)); read_config(&opt); //串口初始化 if (!snprintf(dev_path,sizeof(dev_path),"/dev/%s",opt.name) || (fd = serial_init(dev_path,opt.baudrate,1)) < 0) { perror("serial init err:"); return -1; } memset(serbuf, 0, sizeof(serbuf)); //socket始化 if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("create socket2 error: %s(errno: %d)\n", strerror(errno),errno); exit(-1); //return -1; } PRT("create sockfd is %d\n",sockfd); //memset(&servaddr, 0, sizeof(servaddr)); bzero(&servaddr, sizeof(struct sockaddr_in)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(opt.port); servaddr.sin_addr = opt.ipaddr;//與配置文件有關,注意配置文件要正確 PRT("socket init!\n"); while (-1 == connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr))) { printf("connect error: %s(errno: %d)\n",strerror(errno),errno); sleep(5); } PRT("send msg to server:\n"); signal(SIGPIPE, SIG_IGN); // prevent exiting when we send data to closed socket //socket發送 //if( send(sockfd, str, strlen(str), 0) < 0) //{ //printf("send msg error: %s(errno: %d)\n", strerror(errno), errno); //sleep(1); //} //鎖 //pthread_mutex_init(&socket_lock, NULL); //建立多線程 /*arg.fd=fd; arg.sockfd=sockfd; mret = pthread_create(&thread, NULL, socket_thread, (void *)(long)&arg); if (mret != 0) { printf("Create thread failed\n"); exit(mret); } printf("Create thread\n"); */ //發送消息告訴cc2530能夠打印出數據 if (opt.open != 1) { PRT("do not open serial probe!\n"); } else { PRT("open serial probe!\n"); serial_probe(fd); } printf("tcp to serial or serial to tcp start!\n"); while(1) { //tcp轉串口 memset(sockbuf, 0, sizeof(sockbuf)); sreadlen = socket_read(sockfd, sockbuf, SOCK_MAXLINE-8, 50);//爲了防止緩存溢出,少讀取一個字節 //printf("thread sockfd is %d\n",thread_arg.sockfd); PRT("the sockbuf is:%s\n", sockbuf);//打印出數據 PRT("the sockbuf length is:%d\n",sreadlen); freadlen = serial_send(fd,sockbuf,sreadlen); //發送含有0x00的十六進制數據有問題,會看成結束符號 PRT("tcp to serial send %d bytes!\n",freadlen); //串口轉tcp memset(serbuf, 0, sizeof(serbuf)); freadlen = serial_read(fd,serbuf,SER_MAXLINE,50);//50ms內若是數據沒有裝滿buf,則讀取完畢。 若是數據量大,則讀取的速度也越快。 PRT("the serbuf is :%s\n",serbuf); PRT("the serbuf length is :%d\n",freadlen); if(send(sockfd, serbuf, freadlen, 0) < 0)//serbuf中有數據能夠發送纔會執行這條語句,沒數據就執行成功 { printf("serial to tcp send msg error: %s(errno: %d)\n", strerror(errno), errno);//服務端斷掉,則發送失敗。 //exit(0); //斷線重連 close(sockfd); sockfd = socket(AF_INET, SOCK_STREAM, 0);//從新建立一個套接子,但描述符可能重複 if (sockfd == -1) { printf("recreate socket Error!!! Error # is %d. Exit!\n", errno); exit(-1); } PRT("recreate sockfd is %d\n",sockfd); // connect to server //有兩種方式,一種是用if,子線程能夠close(fd) //另外一種用while,子線程也不能夠close(fd),貌似行不通 //if (-1 == connect(sockfd,(struct sockaddr *)(&servaddr), sizeof(servaddr))) while (-1 == connect(sockfd,(struct sockaddr *)(&servaddr), sizeof(servaddr))) { printf("sockfd reconnect error: %s(errno: %d)\n",strerror(errno),errno); sleep(5); } } } //退出 printf("ttl2tcp exit!\n"); close(fd); close(sockfd); exit(0); }
三、\ttl2tcp\src\Makefileapache
CC = gcc CFLAGS = -Wall OBJS = ttl2tcp.o all: ttl2tcp %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< ttl2tcp: $(OBJS) $(CC) -o $@ $(OBJS) -luci -lpthread clean: rm -f rbcfg *.o
四、 ttl2tcp\files\ttl2tcp.config編程
該文件爲關於串口和服務器地址及端口的配置文件,在openwrt下的/etc/config文件夾下的ttl2tcp數組
package ttl2tcp config serial device #option name ttyUSB0 option name ttyS1 #option name ttyATH0 option baudrate 115200 option data 8 option parity None option stop 1 option enabled 1 config serp probe option open 1 option sendhex 1 option send A2-E3-45-56-78-92 option recvhex 1 option recv A2-E3-45-56-78-9D-35-46-87 config server server option ipaddr 192.168.6.100 option port 10001
五、 ttl2tcp\files\ ttl2tcp.init緩存
該文件爲啓動腳本,用於編寫Luci時,點擊save & Apply以後可以調用到該腳本。服務器
#!/bin/sh /etc/rc.common START=101 start_service() { local section="$1" config_get_bool "enabled" "$section" "enabled" '0' [ "$enabled" -gt 0 ] || return 1 ttl2tcp & ttl2tcp_pid=$! echo "$ttl2tcp_pid" > /tmp/ttl2tcp_temp logger -s -t fqrouter ttl2tcp has started $ttl2tcp_pid. } start() { config_load ttl2tcp config_foreach start_service serial } stop() { read ttl2tcp_pid < /tmp/ttl2tcp_temp kill -9 $ttl2tcp_pid logger -s -t fqrouter ttl2tcp has stoped $ttl2tcp_pid. }
注意:網絡
(1)、start()中的「serial」對應的是配置文件ttl2tcp中的section(「節」)。
(2)、config_get_bool "enabled" "$section" "enabled" '0'
這句語句表示若是配置文件中沒有enabled這個option,那麼久默認表示enabled等於0。
六、 將該文件夾ttl2tcp整個拷貝至barrier_breaker/package/utils文件夾下。
2、Luci配置界面
Luci的整個工做過程大體是這樣的:當設置好配置文件後,點擊Save & Apply以後,就會調用/etc/config/ucitrack文件,在該文件中就能夠找到該配置文件對應的啓動腳本/etc/init.d/ttl2tcp。調用該啓動腳本的時候會先執行stop函數,接着執行start函數,保證關閉以前的進程,避免重複打開多個一樣進程。
一、 新建luci-ttl2tcp文件夾,該文件夾目錄下的Makefile文件以下,用於中文翻譯
PO = ttl2tcp include ../../build/config.mk include ../../build/module.mk
二、 luci-ttl2tcp\luasrc\controller\ ttl2tcp.lua
該文件爲註冊一個新的Luci模塊
--[[ LuCI - Lua Configuration Interface Copyright 2008 Steven Barth <steven@midlink.org> Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 $Id$ ]]-- module("luci.controller.ttl2tcp", package.seeall) function index() --entry({"admin", "services", "ttl_client"}, cbi("ttl_client"), _("serial service"),2) --位置順序有待考慮 entry({"admin", "services", "ttl2tcp"}, cbi("ttl2tcp"), _("Serial Service")) end
三、 luci-ttl2tcp\luasrc\model\cbi\ ttl2tcp.lua
該文件爲Luci具體頁面的實現
--[[ LuCI - Lua Configuration Interface Copyright 2015 tingpan<dktingpan@sina.cn> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. ]]-- local fs = require "nixio.fs" local sys = require "luci.sys" local uci = require "luci.model.uci".cursor() --字符串串接,不能用"\",由於這樣沒法表示空格 local m = Map("ttl2tcp", translate("Serial Service"), translate("This service use default serial configuration include eight data bits, one stop bit, no parity. " .. "It use tcp network protocol. The serial probe is used to send one string to serial slave device, when the slave " .. "device receive the string, it will return an ack and then begin to print data to serial. " .. "Pay attention to don't over 64 bytes in the send and the receive.")) local s = m:section( TypedSection, "serial", translate("Serial Configuration")) s.addremove = false s.anonymous = true --不顯示配置文件的名字 s:option(Flag, "enabled", translate("Enabled")) name = s:option(Value, "name", translate("Serial")) local device_suggestions = nixio.fs.glob("/dev/tty[A-Z]*") if device_suggestions then local node for node in device_suggestions do --name:value(node) name:value(string.sub(node,6,string.len(node))) end end baudrate = s:option(Value, "baudrate", translate("Baudrate")) baudrate:value("115200", translate("115200")) baudrate:value("57600", translate("57600")) baudrate:value("38400", translate("38400")) baudrate:value("19200", translate("19200")) baudrate:value("9600", translate("9600")) baudrate:value("4800", translate("4800")) baudrate:value("2400", translate("2400")) baudrate:value("1200", translate("1200")) baudrate:value("300", translate("300")) local s = m:section( TypedSection, "serp", translate("Serial Probe")) s.addremove = false s.anonymous = true --不顯示配置文件的名字 s:option(Flag, "open", translate("Enabled")) s:option(Flag, "sendhex", translate("Send in hex"), translate("if send in hex,the Send should input like this \"A2-E3-45-56-78-9D\".")) s:option(Value, "send", translate("Send")) s:option(Flag, "recvhex", translate("Receive in hex"), translate("if receive in hex,the Receive should input like this \"A2-E3-45-56-78-9D\".")) s:option(Value, "recv", translate("Receive")) local s = m:section( TypedSection, "server", translate("Servers Configuration")) s.addremove = false s.anonymous = true --不顯示配置文件的名字 --s.template = "cbi/tblsection" s:option(Value, "ipaddr", translate("IPv4 address")) s:option(Value, "port", translate("Port")) return m
四、 luci-ttl2tcp\root\etc\uci-defaults\ luci-ttl2tcp
該文件用於在openwrt中的/etc/config/ucitrack中加入新的關聯,只要配置文件變化了,即點擊了Save & Apply以後,就會執行/etc/init.d/ttl2tcp腳本文件。
#!/bin/sh uci -q batch <<-EOF >/dev/null delete ucitrack.@ttl2tcp[-1] add ucitrack ttl2tcp set ucitrack.@ttl2tcp[-1].init=ttl2tcp commit ucitrack EOF rm -f /tmp/luci-indexcache exit 0
一、 luci-ttl2tcp\ipkg\ postinst
五、 luci-ttl2tcp\ipkg\ postinst
#!/bin/sh [ -n "${IPKG_INSTROOT}" ] || { ( . /etc/uci-defaults/luci-ttl2tcp ) && rm -f /etc/uci-defaults/luci-ttl2tcp /etc/init.d/ttl2tcp enabled || /etc/init.d/ttl2tcp enable exit 0 }
六、 將整個文件夾拷貝至barrier_breaker/feeds/luci/applications文件夾下
七、 進入barrier_breaker/feeds/luci/po/templates,新建ttl2tcp.pot文件
msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" msgid "Serial Service" msgstr "" msgid "" "This service use default serial configuration include eight data bits, one stop bit, no parity. " "It use tcp network protocol. The serial probe is used to send one string to serial slave device, when the slave " "device receive the string, it will return an ack and then begin to print data to serial. " "Pay attention to don't over 64 bytes in the send and the receive." msgstr "" msgid "Serial Configuration" msgstr "" msgid "Serial" msgstr "" msgid "Baudrate" msgstr "" msgid "Serial Probe" msgstr "" msgid "Send in hex" msgstr "" msgid "if send in hex,the Send should input like this \"A2-E3-45-56-78-9D\"." msgstr "" msgid "Send" msgstr "" msgid "Receive in hex" msgstr "" msgid "if receive in hex,the Receive should input like this \"A2-E3-45-56-78-9D\"." msgstr "" msgid "Receive" msgstr "" msgid "Servers Configuration" msgstr ""
八、 進入barrier_breaker/feeds/luci/po/zh_CN文件夾,新建ttl2tcp.po
msgid "Serial Service" msgstr "串口服務" msgid "" "This service use default serial configuration include eight data bits, one stop bit, no parity. " "It use tcp network protocol. The serial probe is used to send one string to serial slave device, when the slave " "device receive the string, it will return an ack and then begin to print data to serial. " "Pay attention to don't over 64 bytes in the send and the receive." msgstr "" "這個服務使用默認的串口配置,包括8位數據位,1位中止位,沒有奇偶較驗位。這個服務使用的是tcp的網絡協議。串口探測功能是用於發送一串數據到串口從設備," "當從設備收到了這樣的一串數據,它將反饋回一串數據給主設備,而後從設備的串口才開始打印數據。注意,不管是發送仍是接收,數據長度不要超過64個字節。" msgid "Serial Configuration" msgstr "串口設置" msgid "Serial" msgstr "串口" msgid "Baudrate" msgstr "波特率" msgid "Serial Probe" msgstr "串口探測" msgid "Send in hex" msgstr "以十六進制發送" msgid "if send in hex,the Send should input like this \"A2-E3-45-56-78-9D\"." msgstr "若是以十六進制形式發送,必須輸入以下「A2-E3-45-56-78-9D」。" msgid "Send" msgstr "發送" msgid "Receive in hex" msgstr "以十六進制接收" msgid "if receive in hex,the Receive should input like this \"A2-E3-45-56-78-9D\"." msgstr "若是以十六進制形式接收,必須輸入以下「A2-E3-45-56-78-9D」。" msgid "Receive" msgstr "接收" msgid "Servers Configuration" msgstr "服務器設置"
這裏要注意兩點:
(1) 若是要翻譯的內容比較多,能夠寫成多行,但要句子結束的引號前面不能有空格;
(2) 含有引號的內容用轉義斜槓」\」,要和lua中的內容對應起來。
九、 在barrier_breaker/feeds/luci/contrib/package/luci-addons的Makefile文件的### Applications ###欄目下,約215行,添加
$(eval $(call application,ttl2tcp,LuCI Support for serial service,ttl2tcp))
3、編譯
一、 make menuconfig,在Utils菜單下選中ttl2tcp應用程序,在Luci菜單下的Application子菜單下選中luci-app-ttl2tcp軟件包;
二、 make V=s
三、 完成以後就能夠從新燒寫固件,或者單獨安裝ttl2tcp和luci-app-ttl2tcp軟件包。若是單獨安裝,會提示缺乏一些依賴包,只要根據提示安裝對應的依賴包就好。
2、說明
對應luci-ttl2tcp文件夾,若是在編譯了一次以後又更改了裏面的內容,此時要把更改後的內容拷貝至barrier_breaker/build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/luci-addons所在目錄。或者也能夠用make clean從新編譯。
參考:
Linux下串口通信 - Linux設備驅動編程總結_Linux編程_Linux公社-Linux系統門戶網站
非阻塞connect的實現 - The time is passing - ITeye技術網站
【Linux開發】Linux下的多線程編程 - gnuhpc - 博客園
請教Luci Save&Apply 如何工做的? - OPENWRT專版 - 恩山WIFI論壇 - Powered by Discuz!
源碼下載: