简体   繁体   English

如何使用 PySerial 重新连接蓝牙连接(算法问题)Raspberry Pi - 蓝牙设备

[英]How to reconnect a bluetooth connection with PySerial (algorithm question) Raspberry Pi - Bluetooth device

Goodday programmers,程序员们好,

I'm working on a connection between a Raspberry Pi 3B+ and several Bluetooth devices, max 5 per Pi at this moment.我正在处理 Raspberry Pi 3B+ 和多个蓝牙设备之间的连接,目前每个 Pi 最多 5 个。 I'm able to make the connection, do all the interaction with the device but struggle with reconnecting after the device lost connection for a longer period of time.我能够建立连接,与设备进行所有交互,但在设备失去连接较长时间后重新连接时遇到困难。 Please lend me your great minds and assist me in making a bulletproof script that always connects the devices effectively.请把你的智慧借给我,并帮助我制作一个始终有效连接设备的防弹脚本。

Some info:一些信息:

  • Pi 3B+ running Raspbian Buster运行 Raspbian Buster 的 Pi 3B+
  • Bluetooth devices are paired using bluetoothctl and bound to a rfcomm[x] in rc.local (sudo bind ...)蓝牙设备使用 bluetoothctl 配对并绑定到 rc.local 中的 rfcomm[x](sudo bind ...)
  • Python script uses PySerial to connect to the device, read and write using threading and queues. Python 脚本使用 PySerial 连接到设备,使用线程和队列进行读写。
  • All devices connect when starting the script启动脚本时所有设备都连接
  • Main script is started with .config/lxsession/LXDE-pi/autostart主脚本以 .config/lxsession/LXDE-pi/autostart 启动
  • Using PySerial to connect to Bluetooth devices results in a pretty long time to connect, about 2-3 seconds.使用 PySerial 连接蓝牙设备会导致连接时间很长,大约 2-3 秒。

There are 3 threads running per device, Watchdog, Read, Write.每个设备有 3 个线程在运行,Watchdog、Read、Write。 The Watchdog adds a dummy message to the write queue about every 5 seconds (which is send to the device by the write thread), if it reacts (with incorrect command response) I know it's alive.看门狗大约每 5 秒向写入队列添加一条虚拟消息(由写入线程发送到设备),如果它做出反应(命令响应不正确),我知道它是活着的。 If it doesn't react for about 30 seconds I know it's lost connection and I must try to reconnect.如果它在大约 30 秒内没有反​​应,我知道它失去了连接,我必须尝试重新连接。 I have a variable that indicates the connectionstatus (connectionAlive) and can be 0, 1 or 2. 0 is inactive, 1 is reconnecting, 2 is connected.我有一个指示连接状态 (connectionAlive) 的变量,可以是 0、1 或 2。0 处于非活动状态,1 正在重新连接,2 已连接。 Read and write threads are only active when connectionAlive > 0. The problem is that when the devices loses connection by going on standby or shutting off to charge the batteries.读取和写入线程仅在 connectionAlive > 0 时处于活动状态。问题在于,当设备因待机或关闭电池充电而失去连接时。 The read and write threads are pretty straight forward so I think the algorithm to reconnect is the problem.读写线程非常简单,所以我认为重新连接的算法是问题所在。 I have added the Watchdog code below:我在下面添加了看门狗代码:

def Watchdog(self):
        logging.info('Start watchdog thread for: {}'.format(self.tool_id))
        while(True):
            if self.connectionAlive == 0:
                self.ChangeAlive(1)
                time.sleep(0.2)
            if self.wq.qsize() < 3:
                self.wq.put(['ping', ''])
            time.sleep(0.5)
            #if last alive longer than 20 ticks
            if (time.time() - self.last_alive) > 30: #or self.errorQ.qsize() > 0
                last_tick = time.time() - self.last_alive
                #if it was alive, tell thread its dead.
                if self.connectionAlive == 2 and self.com != False:
                    logging.error('Tool {} is taking longer to respond'.format(self.tool_id))
                self.ChangeAlive(0)
                if self.com != False:
                    try:
                        self.com.close()
                        time.sleep(0.1)
                    except:
                        #com wasnt alive (never connected since thread started)
                        pass
                self.connectionTryCount += 1
                try:
                    self.com = serial.Serial(port=self.comport, baudrate=9600, timeout=5, write_timeout=5)
                    #time.sleep(3)
                except:
                    if self.connectionTryCount < 2:
                        logging.error('Tool {} is unable to connect!'.format(self.tool_id)) #com port not available
                        #time.sleep(5)
            else:
                if self.connectionTryCount > 0:
                    logging.info('Tool {} connected after {} tries'.format(self.tool_id, self.connectionTryCount))
                    self.connectionTryCount = 0
                if self.connectionAlive != 2:
                    self.ChangeAlive(2)
                while self.errorQ.qsize() > 0:
                    self.errorQ.get()
                    time.sleep(0.05)

            time.sleep(4)

I think there's a problem with the threads sleeping and somehow making the window to connect and read an 'alive' message too small to stay connected with the result that it disconnects too fast to try to reconnect.我认为线程睡眠存在问题,并且以某种方式使窗口连接并读取“活动”消息太小而无法保持连接,结果它断开连接太快而无法尝试重新连接。 The read thread has a sleep of 0.1 and the write thread a sleep of 0.05.读取线程的睡眠时间为 0.1,写入线程的睡眠时间为 0.05。

EDIT: The bluetooth devices are connected to a measuring device which needs parameters sent to it.编辑:蓝牙设备连接到需要向其发送参数的测量设备。 The measuring device can go standby while the bluetooth part is still connected, but that means the device is not alive and is not ready to receive parameters, the device has no build-in queue.测量设备可以在蓝牙部分仍然连接的情况下进入待机状态,但这意味着设备不处于活动状态并且没有准备好接收参数,设备没有内置队列。

Is my script poorly written or am I doomed from the start for using PySerial or a Pi at all?我的脚本写得不好还是我从一开始就注定要使用 PySerial 或 Pi?

Any input or advice is greatly appreciated!非常感谢任何输入或建议!

rfcomm is one of 8 tools that has been deprecated by the BlueZ developers so I would recommend moving away from having rfcomm in rc.local. rfcomm 是 BlueZ 开发人员不赞成使用8 个工具之一,因此我建议不要在 rc.local 中使用 rfcomm。

On Linux the Bluetooth serial connection can be made with the standard Python sockets library as explained in this blog: http://blog.kevindoran.co/bluetooth-programming-with-python-3/在 Linux 上,可以使用标准 Python 套接字库建立蓝牙串行连接,如本博客所述: http : //blog.kevindoran.co/bluetooth-programming-with-python-3/

There is also functionality within the Bluedot library that might be helpful. Bluedot 库中还有一些功能可能会有所帮助。 In addition to the documentation there is also some examples .除了文档之外,还有一些示例

Depending what your script is doing, you might want to look at using systemd to run your program at startup.根据您的脚本正在做什么,您可能需要考虑使用 systemd 在启动时运行您的程序。 I find the following blog a useful reference: https://blog.usedbytes.com/2019/11/run-at-startup-without-rc.local/我发现以下博客是一个有用的参考: https : //blog.usedbytes.com/2019/11/run-at-startup-without-rc.local/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM