一、串口转以太ttl2tcp应用程序
1、 新建文件夹ttl2tcp
该文件夹下的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:=ttl2tcp PKG_RELEASE:=1 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))
2、 ttl2tcp\src\ttl2tcp.c
/* * ttl2tcp * * tingpan<dktingpan@sina.cn> 2015-05-31 * * this is a client of serial translate to tcp or tcp translate to serial. * serial read overtime is 1s * every server read overtime is 0.5s,and the most server number is 3. */ #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> #define SER_MAXLINE 128 #define SOCK_MAXLINE 136//为了应对缓存溢出,多分配一个字节 #define SERVER_MAXNUM 3 struct argument { int fd; int sockfd[SERVER_MAXNUM]; }; unsigned char on_max; struct _options { char name[10]; unsigned int baudrate; //unsigned int data; //unsigned int parity; //unsigned int stop; unsigned int enabled; struct in_addr ipaddr[SERVER_MAXNUM]; unsigned int port[SERVER_MAXNUM]; }; 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 *-----------------------------------------------------------------------------*/ 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!\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("select"); //break; exit(-1); } else if (ret == 0) { printf("timeout\n"); 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; } 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[32]; char i; unsigned char server_num = 0; ctx = uci_alloc_context(); //读取设备名称 //if ((strcpy(a, "ttl2tcp.device.name") == NULL) if ((strncpy(a, "ttl2tcp.device.name", sizeof(a)) == NULL) //if (!snprintf(a,sizeof(a),"ttl2tcp.%s.baudrate",SERNAME) || (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) //if (!snprintf(a,sizeof(a),"ttl2tcp.%s.baudrate",SERNAME) || (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"); } //是否使能 //if (!snprintf(a,sizeof(a),"ttl2tcp.%s.enabled",SERNAME) if ((strncpy(a, "ttl2tcp.device.enabled", sizeof(a)) == NULL) || (uci_lookup_ptr(ctx, &ptr, a, true) != UCI_OK)) { printf("Read ttl2tcp.device.enabled failed! exit.\n"); exit(-1); } if (ptr.o) { popt->enabled = strtol(ptr.o->v.string, NULL, 10); } else { printf("ttl2tcp.device.enabled Not found!\n"); } for(i=0; i<SERVER_MAXNUM; i++){ //读取ip地址 //if ((strncpy(a, "ttl2tcp.@server[0].ipaddr",sizeof(a)) == NULL) if (!snprintf(a,sizeof(a),"ttl2tcp.@server[%d].ipaddr",i) || (uci_lookup_ptr(ctx, &ptr, a, true) != UCI_OK)) { printf("Read ttl2tcp.@server[%d].ipaddr failed! exit.\n",i); exit(-1); } if (ptr.o) { unsigned int laddr; laddr = inet_addr(ptr.o->v.string);//因为ipaddr为网络字节顺序,大端字节序,而输入的为主机字节序 memcpy(&popt->ipaddr[i], &laddr, 4); } else { printf("ttl2tcp.@server[%d].ipaddr Not found!\n",i); } //读取port //if ((strcpy(a, "ttl2tcp.@server[0].port") == NULL) if (!snprintf(a,sizeof(a),"ttl2tcp.@server[%d].port",i) || (uci_lookup_ptr(ctx, &ptr, a, true) != UCI_OK)) { printf("Read ttl2tcp.@server[%d].port failed! exit.\n",i); exit(-1); } if (ptr.o) { popt->port[i] = strtol(ptr.o->v.string, NULL, 10); server_num++; } else { printf("ttl2tcp.@server[%d].port Not found!\n",i); } } uci_free_context(ctx); return server_num; } /** * conn_nonb: 非阻塞connect * @sockfd: socket文件描述符 * @saptr:指向数据结构sockaddr的指针,其中包括目的端口和IP地址 * @salen:sockaddr的长度 * @nsec:超时时间,单位ms */ int conn_nonb(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec) { int flags, n, error, code; socklen_t len; fd_set wset; struct timeval tval; flags = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); error = 0; if ((n = connect(sockfd, saptr, salen)) == 0) { //马上连接成功,可能性小 printf("conn_nonb success!\n"); goto done; }else if (n < 0 && errno != EINPROGRESS){ //多次连接或服务端没开启,出错 ,第一次一般执行不到该处。 printf("conn_nonb error!\n"); return (-1); } /* Do whatever we want while the connect is taking place */ //连接建立已经启动,但是尚未完成 FD_ZERO(&wset); FD_SET(sockfd, &wset); tval.tv_sec = nsec; tval.tv_usec = 0; //printf("conn_nonb select start!\n"); if ((n = select(sockfd+1, NULL, &wset, //有连接要处理 NULL, nsec ? &tval : NULL)) == 0) { close(sockfd); /* timeout */ errno = ETIMEDOUT; printf("conn_nonb select timeout!\n"); return (-1); } if (FD_ISSET(sockfd, &wset)) { len = sizeof(error); code = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len); /* 如果发生错误,Solaris实现的getsockopt返回-1, * 把pending error设置给errno. Berkeley实现的 * getsockopt返回0, pending error返回给error. * 我们需要处理这两种情况 */ if (code < 0 || error) { close(sockfd); if (error) errno = error; printf("conn_nonb getsockopt error!\n"); return (-1); } } else { fprintf(stderr, "select error: sockfd not set"); exit(0); } done: fcntl(sockfd, F_SETFL, flags); /* restore file status flags */ return (0); } void *socket_thread(void *arg) { char sockbuf[SOCK_MAXLINE]; int sreadlen = 0; int freadlen = 0; unsigned char i; struct argument thread_arg; thread_arg = *(struct argument *)arg; memset(sockbuf, 0, sizeof(sockbuf)); while(1) { for (i=0; i<on_max;i++) { //pthread_mutex_lock(&socket_lock);//lock sreadlen = socket_read(thread_arg.sockfd[i], sockbuf, SOCK_MAXLINE-8, 500);//为了防止缓存溢出,少读取一个字节 printf("the sockbuf is:%s\n", sockbuf);//打印出数据 printf("the sockbuf length is:%d\n",sreadlen); freadlen = serial_send(thread_arg.fd,sockbuf,sreadlen); printf("send %d bytes!\n",freadlen); memset(sockbuf, 0, sizeof(sockbuf)); //pthread_mutex_unlock(&socket_lock);//unlock usleep(1); } } } 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[SERVER_MAXNUM];//SERVER_MAXNUM struct sockaddr_in servaddr[SERVER_MAXNUM];//SERVER_MAXNUM unsigned char i; unsigned char on_sockfd[SERVER_MAXNUM] = {0}; on_max = 0;//最大上线个数 //多线程 pthread_t thread; int mret; struct argument arg; //读取配置文件 unsigned char server_num;//服务器个数初始化 server_num = read_config(&opt); if (opt.enabled != 1) { printf("do not enable ttl2tcp!\n"); exit(-1); } //串口初始化 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始化 for (i=0; i<server_num; i++) { printf("socket init %d/%d\n",i,server_num); if( (sockfd[i] = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("create socket2 error: %s(errno: %d)\n", strerror(errno),errno); exit(0); } memset(&servaddr[i], 0, sizeof(servaddr[i])); servaddr[i].sin_family = AF_INET; servaddr[i].sin_port = htons(opt.port[i]); servaddr[i].sin_addr = opt.ipaddr[i];//与配置文件有关,注意配置文件要正确 //if( connect(sockfd[i], (struct sockaddr*)&servaddr[i], sizeof(servaddr[i])) < 0) //非阻塞连接10s,如果前一个sockfd没有connect成功,则下次将建立一样的文件描述符号 if( conn_nonb(sockfd[i], (struct sockaddr*)&servaddr[i], sizeof(servaddr[i]),10) < 0) { printf("connect error: %s(errno: %d)\n",strerror(errno),errno); continue; //exit(0); } on_sockfd[on_max++] = i; printf("send msg to server[%d]: %d\n",i,on_max-1);//on_max-1为上线客户端的新编号 //socket发送 if( send(sockfd[i], str, strlen(str), 0) < 0) { printf("send msg error: %s(errno: %d)\n", strerror(errno), errno); exit(0); } } //如果没有一个服务器上线 if (on_max == 0) { exit(0); } //锁 //pthread_mutex_init(&socket_lock, NULL); //创建多线程 arg.fd=fd; for (i=0; i<on_max;i++) { arg.sockfd[i]=sockfd[on_sockfd[i]]; } mret = pthread_create(&thread, NULL, socket_thread, (void *)(long)&arg); if (mret != 0) { printf("Create thread failed\n"); exit(mret); } printf("Create thread\n"); //串口转tcp while(1) { readlen = serial_read(fd,serbuf,SER_MAXLINE,1000);//1s内如果数据没有装满buf,则读取完毕。 如果数据量大,则读取的速度也越快。 printf("the serbuf is :%s\n",serbuf); printf("the serbuf length is :%d\n",readlen); for (i=0; i<on_max;i++) { printf("sockfd[%d]:%d\n",i,sockfd[on_sockfd[i]]); if( send(sockfd[on_sockfd[i]], serbuf, readlen, 0) < 0)//serbuf中有数据可以发送才会执行这条语句 { printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);//服务端断掉,则发送失败。 exit(0); } } memset(serbuf, 0, sizeof(serbuf)); sleep(1); } //退出 close(fd); for (i=0; i<server_num;i++) close(sockfd[i]); exit(0); }
3、\ttl2tcp\src\Makefile
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
4、 ttl2tcp\files\ttl2tcp.config
该文件为关于串口和服务器地址及端口的配置文件,在openwrt下的/etc/config文件夹下的ttl2tcp
package ttl2tcp config serial device #option name ttyUSB0 #option name ttyS0 option name ttyATH0 option baudrate 115200 option data 8 option parity None option stop 1 option enabled 1 config server option ipaddr 172.16.1.165 option port 10001 config server option ipaddr 172.16.1.139 option port 10001 config server option ipaddr 172.16.1.235 option port 10001
5、 ttl2tcp\files\ ttl2tcp.init
该文件为启动脚本,用于编写Luci时,点击save & Apply之后能够调用到该脚本。
#!/bin/sh /etc/rc.common START=99 start() { logger -s -t fqrouter ttl2tcp has started0. sleep 2 ttl2tcp & ttl2tcp_pid=$! echo "$ttl2tcp_pid" > /tmp/ttl2tcp_temp logger -s -t fqrouter ttl2tcp has started $ttl2tcp_pid. } stop() { read ttl2tcp_pid < /tmp/ttl2tcp_temp logger -s -t fqrouter ttl2tcp has stoped0 $ttl2tcp_pid. kill -9 $ttl2tcp_pid sleep 1 logger -s -t fqrouter ttl2tcp has stoped. }
6、 将该文件夹ttl2tcp整个拷贝至barrier_breaker/package/utils文件夹下。
二、Luci配置界面
Luci的整个工作过程大致是这样的:当设置好配置文件后,点击Save & Apply之后,就会调用/etc/config/ucitrack文件,在该文件中就可以找到该配置文件对应的启动脚本/etc/init.d/ttl2tcp。调用该启动脚本的时候会先执行stop函数,接着执行start函数,保证关闭之前的进程,避免重复打开多个同样进程。
1、 新建luci-ttl2tcp文件夹,该文件夹目录下的Makefile文件如下,用于中文翻译
PO = ttl2tcp include ../../build/config.mk include ../../build/module.mk
2、 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
3、 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")) 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, "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
4、 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
5、 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 }
6、 将整个文件夹拷贝至barrier_breaker/feeds/luci/applications文件夹下
7、 进入barrier_breaker/feeds/luci/po/templates,新建ttl2tcp.pot文件
msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" msgid "Serial Service" msgstr "" msgid "Serial" msgstr "" msgid "Baudrate" msgstr "" msgid "Servers Configuration" msgstr ""
8、 进入barrier_breaker/feeds/luci/po/zh_CN文件夹,新建ttl2tcp.po
msgid "Serial Service" msgstr "串口服务" msgid "Serial" msgstr "串口" msgid "Baudrate" msgstr "波特率" msgid "Servers Configuration" msgstr "服务器配置"
9、 在barrier_breaker/feeds/luci/contrib/package/luci-addons的Makefile文件的### Applications ###栏目下,约215行,添加
$(eval $(call application,ttl2tcp,LuCI Support for serial service,ttl2tcp))
三、编译
1、 make menuconfig,在Utils菜单下选中ttl2tcp应用程序,在Luci菜单下的Application子菜单下选中luci-app-ttl2tcp软件包;
2、 make V=s
3、 完成之后就可以重新烧写固件,或者单独安装ttl2tcp和luci-app-ttl2tcp软件包。如果单独安装,会提示缺少一些依赖包,只要根据提示安装对应的依赖包就好。
四、说明
对应luci-ttl2tcp文件夹,如果在编译了一次之后又更改了里面的内容,此时要把更改后的内容拷贝至barrier_breaker/build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/luci-addons所在目录。或者也可以用make clean重新编译。
五、使用
vi /etc/inittab
修改如下:
::sysinit:/etc/init.d/rcS S boot ::shutdown:/etc/init.d/rcS K shutdown #::askconsole:/bin/ash --login
参考:
Linux下串口通讯 - Linux设备驱动编程总结_Linux编程_Linux公社-Linux系统门户网站
非阻塞connect的实现 - The time is passing - ITeye技术网站
【Linux开发】Linux下的多线程编程 - gnuhpc - 博客园
请教Luci Save&Apply 如何工作的? - OPENWRT专版 - 恩山WIFI论坛 - Powered by Discuz!
代码下载: