检测并从“挂起"中恢复蓝牙python连接 [英] Detecting and recovering from "hanging" bluetooth python connection

查看:19
本文介绍了检测并从“挂起"中恢复蓝牙python连接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 RPi 内置模块和 Arduino 上的 HC-05 模块在 Raspberry Pi 3b+ 和 Arduino Mega 之间建立了蓝牙连接.双向交流就像一种魅力,一次几分钟,有时甚至几个小时.

I have a bluetooth connection between a Raspberry Pi 3b+ and an Arduino Mega, using the RPi built-in module, and an HC-05 module on the Arduino. Bi-directional communication works like a charm, for minutes to sometimes, hours at a time.

然后,在看似随机的时间,python 代码挂起,阻塞在 sock.recv() 函数上.我可以通过 ctrl-c 杀死它,然后重新启动它,它通常可以毫无问题地重新连接.

Then, at seemingly random times, the python code hangs, blocked on the sock.recv() function. I can kill it via ctrl-c, and restart it, and it usually reconnects without a problem.

我知道蓝牙有点挑剔,所以虽然任何关于如何使通信更健壮和延长运行时间直到挂起的建议都值得赞赏,但我更感兴趣的是如何检测这种挂起"并从中恢复.即:我只想终止连接并尝试从 Python 程序内部重新连接,而不是我必须自己看到这个并对其做出反应.

I know Bluetooth is a little finicky and so while any suggestions of how to make the communication more robust and extend the runtime-until-hanging are definitely appreciated, what I'm more interested in is how to detect this "hanging" and recover from it. i.e.: I want to just kill the connection and try to reconnect from within the Python program, rather than me having to see this myself and react to it.

这是我目前在 python 中所拥有的:

This is what I have so far in python:

#!/usr/bin/python3

import datetime
import socket
import sys
import time

import bluetooth

COMMAND_START_CHAR = '<'
COMMAND_END_CHAR = '>'
LOGFILE = 'bt.log'


def SearchForFullCommand(buffer):
  """Puts fully formed commands from buffer into a list, returning the remaining buffer.

  We expect commands to be demarcated by COMMAND_START_CHAR and COMMAND_END_CHAR.  The
  buffer may have zero or more such commands. This function finds all demarcated commands,
  strips off those demarcations, and returns the remaining buffer.  Any text that arrives
  before a COMMAND_START_CHAR is discarded.

  Args:
    buffer: string representing the text received so far.

  Returns:
    A 2-tuple, where the first element is the remaining buffer, and the second element
    is a potentially empty list of identified commands.
  """
  commands = []
  while COMMAND_END_CHAR in buffer:
    end_pos = buffer.find(COMMAND_END_CHAR)
    if COMMAND_START_CHAR in buffer[:end_pos]:
      start_pos = buffer.find(COMMAND_START_CHAR) + len(COMMAND_START_CHAR)
      commands.append(buffer[start_pos:end_pos])
    buffer = buffer[end_pos+len(COMMAND_END_CHAR):]
  return (buffer, commands)  # no command found


def Log(s):
  """Appends message s to the logfile."""
  with open(LOGFILE, 'a') as f:
    f.write('%s\n' % s)


def ConnectBluetooth(address, port):
  """Attempts to make one connection to the given address and port."""
  sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  try:
    sock.connect((address, port))
  except (bluetooth.btcommon.BluetoothError) as e:
    Log('Failed to connect: %s' % e)
    return None
  return sock


def ConnectBluetoothRetry(address, port, seconds):
  """Attempts to make connections for a number of seconds, exiting program on fail."""
  second = 0
  while second < seconds:
    sock = ConnectBluetooth(address, port)
    if sock:
      Log('Connected after %d seconds' % second)
      return sock
    time.sleep(1)
    second += 1
  Log('Failed to connect after %d seconds' % second)
  sys.exit()


def main():
  """Sends sequential numbers over bluetooth, and receives & parses anything sent."""
  sys.stderr = open(LOGFILE, 'a')

  start = time.time()
  timestring = datetime.datetime.fromtimestamp(start).strftime('%Y-%m-%d %H:%M:%S')
  Log('Started at %s' % timestring)

  bd_addr = '98:D3:11:FC:42:16'
  port = 1
  sock = ConnectBluetoothRetry(bd_addr, port, 10)

  buffer = ''
  x = 0

  while True:
    try:
      recv = sock.recv(1024)
    except (bluetooth.btcommon.BluetoothError) as e:
      Log('Failed to receive: %s' % e)
      sock.close()
      sock = ConnectBluetoothRetry(bd_addr, port, 10)
    Log('.. %s (len=%d) after running for %.3f hours' % (
        recv, len(recv), (time.time() - start) / 60**2))
    buffer += recv.decode()
    buffer, commands = SearchForFullCommand(buffer)
    if commands:
      for n, command in enumerate(commands):
        Log('Received full command #%d: %s' % (n, command))

    send = COMMAND_START_CHAR+str(x)+COMMAND_END_CHAR
    try:
      sock.send(send)
    except (bluetooth.btcommon.BluetoothError) as e:
      Log('Failed to send %s: %s' % (send, e))
      sock.close()
      sock = ConnectBluetoothRetry(bd_addr, port, 10)
    Log('Sent %s' % send)

    x += 1
    time.sleep(1)


main()

运行良好时,python 日志文件如下所示:

When working well, the python log file looks like this:

.. b'646>' (len=4) after running for 0.843 hours
Received full command #0: 646
Sent <2526>
.. b'<647>' (len=5) after running for 0.843 hours
Received full command #0: 647
Sent <2527>
.. b'<' (len=1) after running for 0.844 hours
Sent <2528>
.. b'648>' (len=4) after running for 0.844 hours

然后,注意到它停止工作,我杀死了它,然后重新启动它:

Then, noticing it had stopped working, I killed it, and then restarted it:

KeyboardInterrupt
Started at 2020-05-03 11:15:07
Failed to connect: [Errno 16] Device or resource busy
Failed to connect: [Errno 16] Device or resource busy
Failed to connect: [Errno 16] Device or resource busy
Failed to connect: [Errno 16] Device or resource busy
Failed to connect: [Errno 16] Device or resource busy
Failed to connect: [Errno 16] Device or resource busy
Failed to connect: [Errno 16] Device or resource busy
Failed to connect: [Errno 16] Device or resource busy
Failed to connect: [Errno 16] Device or resource busy
Failed to connect: [Errno 16] Device or resource busy
Failed to connect after 10 seconds

我再试一次:

Started at 2020-05-03 11:15:42
Failed to connect: [Errno 112] Host is down
Failed to connect: [Errno 112] Host is down
Failed to connect: [Errno 112] Host is down
Connected after 3 seconds
.. b'1146><1147><1148><1149><1150><1151><1152><1153><1154><1155><1156><1157><1158><1159><1160><1161><1162><1163><1164><1165><1166><1' (len=127) after running for 0.005 hours
Received full command #0: 1147
Received full command #1: 1148
Received full command #2: 1149

...它又运行了一两个小时,然后再次挂起.我没有在物理上移动发送者或接收者——它们彼此相距一英尺——所以它不是范围.尽管我尝试断开 Arduino 的连接,然后重新启动它,但它们确实在仍在运行的 Python 进程中重新连接没有问题.

... and it runs for another hour or two, before hanging again. I'm not physically moving either sender or receiver - they're within a foot of each other - so it's not range. Though I have tried disconnecting the Arduino, and repowering it, and they do reconnect without a problem within the still-running Python process.

Arduino 代码,虽然我认为它不相关,但在这里:

The Arduino code, though I don't think its as relevant, is here:

long n = 1;

void setup() 
{
    Serial.begin(9600);

    // HC-05 default serial speed for communcation mode is 9600
    Serial1.begin(9600);  
}

void loop() 
{
    Serial1.print("<");
    Serial1.print(n);
    Serial1.print(">");
    if(Serial1.available() > 0){ // Checks whether data is comming from the serial port
      Serial.println(Serial1.readString());} // Reads the data from the serial port
    delay(1000);
    n++;
}

感谢您的任何帮助或建议!

Thanks for any help or suggestions!

推荐答案

尽管几天的方法不同,但我的套接字连接无法持续超过几个小时 &努力;我只是陷入了套接字兔子洞,因为我无法弄清楚如何通过蓝牙使用 pySerialTransfer,甚至只是串行.

I could not get the socket connection to last more than a few hours despite a few days of different approaches & efforts; I only went down the socket rabbit hole because I was unable to figure out how to use pySerialTransfer, or even just serial, over bluetooth.

因此,回到串行方法,经过更多的努力和调查,我最终能够使该方法在 Raspberry Pi 和 Raspberry Pi 之间建立稳定的(此时超过 1 天)蓝牙连接Arduino 上的 HC-05.

So, going back to the serial approach, after a bit more effort and investigation, I was ultimately able to get that approach working for a stable (over 1 day at this point) bluetooth connection between Raspberry Pi & HC-05 on Arduino.

代码在这里.

这篇关于检测并从“挂起"中恢复蓝牙python连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆