#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