Как повторно подключить соединение Bluetooth с помощью PySerial (вопрос алгоритма) Raspberry Pi — устройство Bluetooth

#python #multithreading #bluetooth #raspberry-pi #pyserial

#python #многопоточность #bluetooth #raspberry-pi #pyserial

Вопрос:

Программистам доброго дня,

Я работаю над соединением между Raspberry Pi 3B и несколькими устройствами Bluetooth, максимум 5 на Pi в данный момент. Я могу установить соединение, выполнять все взаимодействия с устройством, но не могу повторно подключиться после того, как устройство потеряло соединение на более длительный период времени. Пожалуйста, одолжите мне свои замечательные мысли и помогите мне в создании пуленепробиваемого скрипта, который всегда эффективно соединяет устройства.

Некоторая информация:

  • Pi 3B запускает Raspbian Buster
  • Устройства Bluetooth сопряжены с использованием bluetoothctl и привязаны к rfcomm [x] в rc.local (привязка sudo …)
  • Скрипт Python использует PySerial для подключения к устройству, чтения и записи с использованием потоков и очередей.
  • Все устройства подключаются при запуске скрипта
  • Основной скрипт запускается с помощью .config/lxsession/LXDE-pi / автозапуск
  • Использование PySerial для подключения к устройствам Bluetooth приводит к довольно длительному подключению, около 2-3 секунд.

На каждом устройстве запущено 3 потока: Watchdog, чтение, запись. Сторожевой таймер добавляет фиктивное сообщение в очередь записи примерно каждые 5 секунд (которое отправляется на устройство потоком записи), если оно реагирует (с неправильным ответом на команду) Я знаю, что оно живое. Если он не реагирует в течение примерно 30 секунд, я знаю, что соединение потеряно, и я должен попытаться восстановить соединение. У меня есть переменная, которая указывает connectionstatus (connectionAlive) и может быть 0, 1 или 2. 0 неактивен, 1 повторно подключается, 2 подключен. Потоки чтения и записи активны только тогда, когда значение connectionAlive > 0. Проблема в том, что когда устройства теряют соединение, переходя в режим ожидания или отключаясь для зарядки аккумуляторов. Потоки чтения и записи довольно просты, поэтому я думаю, что проблема в алгоритме повторного подключения. Я добавил код сторожевого таймера ниже:

 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)
  

Я думаю, что есть проблема со спящими потоками, и каким-то образом окно для подключения и чтения «живого» сообщения слишком мало, чтобы оставаться на связи, в результате чего оно отключается слишком быстро, чтобы попытаться повторно подключиться. Поток чтения находится в режиме ожидания 0,1, а поток записи — в режиме ожидания 0,05.

РЕДАКТИРОВАТЬ: устройства Bluetooth подключены к измерительному устройству, которому нужны параметры, отправленные на него. Измерительное устройство может перейти в режим ожидания, пока часть Bluetooth все еще подключена, но это означает, что устройство не работает и не готово к приему параметров, у устройства нет встроенной очереди.

Мой скрипт плохо написан или я с самого начала обречен на использование PySerial или Pi вообще?

Любой ввод или совет приветствуются!

Ответ №1:

rfcomm — один из 8 инструментов, который не рекомендуется разработчиками BlueZ, поэтому я бы рекомендовал отказаться от использования rfcomm в rc.local.

В Linux последовательное соединение Bluetooth может быть выполнено с помощью стандартной библиотеки Python sockets, как описано в этом блоге:http://blog.kevindoran.co/bluetooth-programming-with-python-3 /

В библиотеке Bluedot также есть функциональность, которая может оказаться полезной. В дополнение к документации также есть несколько примеров.

В зависимости от того, что делает ваш скрипт, вы можете захотеть посмотреть на использование systemd для запуска вашей программы при запуске. Я нахожу следующий блог полезной ссылкой:https://blog.usedbytes.com/2019/11/run-at-startup-without-rc.local /

Комментарии:

1. Спасибо за информацию. Для начала я собираюсь попробовать библиотеку Bluedot.

2. С помощью Bluedot скрипт улучшился и подключается быстрее, но после ночи неактивных устройств bt он не смог подключиться. При подключении устройства остаются подключенными весь день, поэтому сейчас я просто собираюсь перезагружать pi каждый день.

3. Вы можете найти дополнительную информацию об отладке с service bluetooth status или sudo btmon -t