美文网首页raspberry pi
Linux 将串口映射为TCP服务+无线热点(树莓派)

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

作者: N2ED | 来源:发表于2019-08-12 20:12 被阅读0次

    起因

    最近在做一个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服务+无线热点(树莓派)

        本文链接:https://www.haomeiwen.com/subject/isepjctx.html