Linux 将串口映射为TCP服务+无线热点(树莓派)

起因

最近在做一个Android控制APP,本来想使用蓝牙BLE的串口透传模块,但是因为项目中使用的串口数据包比较长,通信比较频繁,买了个透传模块一次多大(MTU)只有20个字节,也不支持修改,而且写入过程需要有Thread.sleep(23),就很蛋疼了,数据量一大通信延迟特别严重,而且控制的设备对实时性要求较高,这个方案显然不能满足要求,放弃。

手里刚好有一块树莓派3B+,放着吃灰,想着是不是可以用树莓派建WIFI热点,把串口用网络转发出来。树莓派自带的串口,网上说有些小问题,再有项目中需要控制多个串口,树莓派最多自带2个,不走弯路直接上usb转串口,插到rspi的USB上,试了几个常用模块,都可以识别,美滋滋。插上之后在/dev/ttyUSB*上,可以用echo 123 > /dev/ttyUSB0去尝试发送。

第一步将串口映射为TCP服务,双向转发。

首先当然是想自己写代码转发,网上搜了下,嗯~~,好像都是简单demo,要写成产品级别的估计要下点功夫。看有没有现成的工具,学过几天信安,转发当然是nc(netcat),奈何好像脚本挺负载的,最后在国外某论坛上找到了一个一条命令搞定的好玩意,socat,完美解决问题

socat TCP-LISTEN:4161,fork,reuseaddr FILE:/dev/ttyUSB0,b57600,raw,echo=0”
#4161是TCP服务的端口号
#/dev/ttyUSB0就是对应的USB转的串口
#最后加echo=0,不加的话树莓派会给串口回写,也就是外部设备向串口写什么,串口会原样向串口返回同样的数据

测试使用sscom串口调试工具使用网络“TCPClient”连接测试,通信顺畅,测试一小时连接正常,大问题解决

第二步将TCP服务设为自启动,不需要也不可能每次都要人工启动。

确定串口设备的名称

首先要解决的问题是usb转串口每次插入时设备名不确定的问题,由于是多个串口,不能确定是哪一个,这就要求设置usb串口名称固定

/etc/udev/rules.d/99-input.rules(也有可能不一样),中添加一行:

SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523",ATTRS{serial}=="123456", SYMLINK+="userilaMoto"

这些信息可以使用命令 udevadmin info -a -n /dev/ttyUSB0 中查找idVendor,idProduct,serial,可以添加更多只要能区分出不同设备即可

最后SYMLINK就是设置完成后在/dev中要出现的名称,当然不能与现有的重名。

可是有些串口设备没有serial号,呵呵,如果买了几个相同的usb转串口根本没法区分。

最后在/dev/serial目录下有两个目录 by-id 和 by-path,在by-path下不管是什么模块插上只要物理USB端口一样,名字就不会变,这就方便多了,哈哈。

串口名称形如/dev/serial/by-path/platform-3f980000.usb-0:1.2:0-port0

设置编写服务脚本

在/etc/init.d/下建立一个脚本文件取名netserial,名字可以自己起

在/etc/init.d/下有很多服务脚本,可以参考编写,注意下面脚本中echo和log*mes使用service或systemctl命令都不会显示,不写无妨,只是在自己测试脚本的时候比较方便而已

/etc/init.d/netserial

#!/bin/sh
#
### BEGIN INIT INFO
# Provides:          netserial
# Required-Start:    
# Required-Stop:     
# Should-Start: 
# Should-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: serial port via network
# Description:
### END INIT INFO

PATH=/sbin:/bin:/usr/sbin:/usr/bin
. /lib/lsb/init-functions

start(){
    pcount=$(ps -aux | grep TCP-LISTEN:5961 | grep socat | wc -l)
    if [ $pcount -eq 0 ] 
    then 
        nohup socat -lu -d -d -d TCP-LISTEN:5961,fork,reuseaddr FILE:'/dev/serial/by-path/platform-3f980000.usb-usb-0\:1.3\:1.0-port0',b57600,raw,echo=0 > /var/logs/socat5961.log &
        log_success_msg "\033[32msocat TCP:5961 <----> RightTop USB Serial Port@57600 Started  \033[0m"
    else
        log_warning_msg "\033[35msocat TCP:5961 <----> RightTop USB Serial Port@57600 has Started last time  \033[0m"
    fi

    pcount=$(ps -aux | grep TCP-LISTEN:5962 | grep socat | wc -l)
    if [ $pcount -eq 0 ] 
    then 
        nohup socat -lu -d -d -d TCP-LISTEN:5962,fork,reuseaddr FILE:'/dev/serial/by-path/platform-3f980000.usb-usb-0\:1.2\:1.0-port0',b38400,raw,echo=0 > /var/logs/socat5962.log &
        log_success_msg "\033[32msocat TCP:5962 <----> RightBottom USB Serial Port@38400 Started  \033[0m"
    else 
        log_warning_msg "\033[35msocat TCP:5962 <----> RightBottom USB Serial Port@38400 has Started last time  \033[0m"
    fi

    pcount=$(ps -aux | grep TCP-LISTEN:5963| grep socat | wc -l)
    if [ $pcount -eq 0 ] 
    then 
        nohup socat -lu -d -d -d TCP-LISTEN:5963,fork,reuseaddr FILE:'/dev/serial/by-path/platform-3f980000.usb-usb-0\:1.1.3\:1.0-port0',b9600,raw,echo=0 > /var/logs/socat5963.log &
        log_success_msg "\033[32msocat TCP:5963 <----> LeftBottom USB Serial Port@9600  Started \033[0m"
    else
        log_warning_msg "\033[35msocat TCP:5963 <----> LeftBottom USB Serial Port@9600  has Started last time \033[0m"
    fi
}

stop(){
    pcount=$(ps -aux | grep TCP-LISTEN:5961 | grep socat | wc -l)
    if [ $pcount -gt 0 ] 
    then 
        ps -aux | grep TCP-LISTEN:5961 | grep socat | awk '{print $2}' | xargs kill
        log_success_msg "\033[31msocat TCP:5961 <----> RightTop USB Serial Port Stop\033[0m" 
    fi

    pcount=$(ps -aux | grep TCP-LISTEN:5962| grep socat | wc -l)
    if [ $pcount -gt 0 ] 
    then 
        ps -aux | grep TCP-LISTEN:5962| grep socat | awk '{print $2}' | xargs kill
        log_success_msg "\033[31msocat TCP:5962 <----> RightBottom USB Serial Port Stop\033[0m" 
    fi

    pcount=$(ps -aux | grep TCP-LISTEN:5963 | grep socat | wc -l)
    if [ $pcount -gt 0 ] 
    then 
        ps -aux | grep TCP-LISTEN:5963 | grep socat | awk '{print $2}' | xargs kill
        log_success_msg "\033[31msocat TCP:5963 <----> LeftBottom USB Serial Port Stop\033[0m" 
    fi
    
}

status(){
    pcount=$(ps -aux | grep TCP-LISTEN:5961 | grep socat | wc -l)
    if [ $pcount -gt 0 ] 
    then 
        log_progress_msg "\033[32msocat TCP:5961 <----> RightTop USB Serial Port @57600 active\033[0m \n"
    else 
        echo "\033[31msocat TCP:5961 <----> RightTop USB Serial Port @57600 inactive\033[0m \n"
        #cat /var/logs/socat5961.log  | grep  -v transferred | tail -n 20 
    fi
    log_progress_msg "see log in /var/logs/socat5961.log \n"

    pcount=$(ps -aux | grep TCP-LISTEN:5962| grep socat | wc -l)
    if [ $pcount -gt 0 ] 
    then 
        echo "\033[32msocat TCP:5962 <----> RightBottom USB Serial Port @38400  active\033[0m"
    else 
        echo "\033[31msocat TCP:5962 <----> RightBottom USB Serial Port @38400 inactive\033[0m"
    fi
    log_progress_msg "see log in /var/logs/socat5962.log \n"

    pcount=$(ps -aux | grep TCP-LISTEN:5963 | grep socat | wc -l)
    if [ $pcount -gt 0 ] 
    then 
        echo "\033[32msocat TCP:5963 <----> LeftBottom USB Serial Port @9600 active\033[0m"
    else 
        echo "\033[31msocat TCP:5963 <----> LeftBottom USB Serial Port @9600 inactive\033[0m"
    fi
    log_progress_msg "see log in /var/logs/socat5963.log \n"
}

case "$1" in
start)
    start
        ;;
stop)
    stop
    ;;
restart)
    stop
    start
        ;;
status)
        status
        ;;
*)
        log_failure_msg "Usage: /etc/init.d/netserial {start|stop|restart|status}"
    exit 1
        ;;
esac

设置开机启动

cd /etc/init.d
#脚本权限
chmod 775 netserial
#安装启动脚本,96是启动序号,因为需要使用网络,和usb硬件,设置到靠后,多个串口可以分别安装
update-rc.d netserial defaults 96
#卸载启动脚本,先写到这里,以免后期需要卸载
# update-rc.d -f netserial remove

# 同样可以使用 service或者systemctl start/stop/restart/status 控制服务 

无线热点

由于不可能每次都要手动在设备上连接无线网络,需要无线网络自动连接,有两种方案可选:

  1. 手机/平板创建热点,树莓派自动连接,缺点,移动设备热点不会自动开启,长时间不连接会关闭,如果热点名字改了密码变了,就再连接不上了,而且支持特定设备。

  2. 树莓派创建热点,移动设备自动连接,缺点实现有点难度,但使用方便。

3.使用自动化工具完成 [更新]
主要有两种一个是create_ap,另一个是 raspap-webgui
推荐使用raspap-webgui一个命令自动安装,还带web管理端,非常方便。
使用方法参见其github,我就不多嘴了。https://github.com/billz/raspap-webgui

wget -q https://git.io/voEUQ -O /tmp/raspap && bash /tmp/raspap

更新:以下方式太麻烦(不推荐)

下面采用法案2搭建热点,就当给自己做笔记,一面以后再填坑。

以下内容大部分参考 Simpreative文章 https://www.jianshu.com/p/0ecd8b734204

使用hostapd dnsmasq 搭建

sudo apt-get install dnsmasq hostapd

# 修改 /etc/dhcpcd.conf
interface wlan0
static ip_address=192.168.0.1/24

# 创建 sudo nano /etc/hostapd/hostapd.conf
interface=wlan0
driver=nl80211
ssid=RaspberryPi
hw_mode=g
channel=7
wmm_enabled=0
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=12345678
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP

# 修改/etc/default/hostapd
DAEMON_CONF="/etc/hostapd/hostapd.conf"

#配置DNSMASQ
sudo mv /etc/dnsmasq.conf /etc/bak_dnsmasq.conf

#重新创建 /etc/dnsmasq.conf,并写入
interface=wlan0
dhcp-range=192.168.0.2,192.168.0.20,255.255.255.0,24h

# 还有net转发,当然这里不涉及数据转发问题

到此为止好像结束了,但是hostapd 起不来,查看状态,提示

 Loaded: masked (/dev/null; bad)

需要使用命令"unmask"

sudo systemctl unmask hostapd.service
sudo systemctl enable hostapd.service

但是开机之后还是没有无线热点,但是 service hostapd status 提示启动了呀,并且重启服务就可以正常连接,好坑呀。

最后查看状态有一个日志提示

random: Cannot readfrom /dev/random: Try again
random: Only 0/20bytes of strong random data available from /dev/random
random: Not enoughentropy pool available for secure operations

表示随机数不够,查了一下是因为/dev/random 生成速度太慢,改成/dev/urandom 就好了
网上有说是将random 备份然后软链接urandom->random,但是每次重启后就没有了...

没办法只有源码改造了

#下载源码
wget http://w1.fi/releases/hostapd-2.9.tar.gz
tar -xzvf hostapd-2.6.tar.gz
#配置文件
cd hostapd-2.6/hostapd
cp defconfig .config
#修改源码
hostapd-2.6/src/crypto/random.c :(不止1处)
由    fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
改为  fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK);
#安装依赖,使用工具搜索缺失的依赖
sudo apt install apt-file
apt-file search xx/xx/xx.h

#编译,如果出现错误安装对应的依赖
cd hostapd-2.9/hostapd
make
#将编译好的hostapd覆盖原来的文件
sudo cp hostapd /usr/sbin/
sudo cp hostapd_cli /usr/sbin/

嗯....,提示信息是没了,但是还是不能开机启动,手启可以,没办法了,最后在netserial脚本上加了一句

start(){
...
#暂停10秒
sleep 10
#重启hostapd服务
service hostapd restart
}

不算完美,功能实现了,不想折腾了...

你可能感兴趣的:(Linux 将串口映射为TCP服务+无线热点(树莓派))