Как приручить свой Bluetooth. Часть 8

Что лучше — ужасный конец или ужас без конца?

Пишем незатейливую программу, которая периодически отправляет пакеты по 400 байт каждые 100 мс. Простая арифметика с этими цифрами дает нам результат 4 кБайт/с. Другими словами —  скорость обмена информацией гарантировано меньшей той, на которой начинаются проблемы.

Прога называется looper.py и лежит на

$ git clone https://github.com/zhevak/bluetooth
#!/usr/bin/env python
#coding: utf-8

'''
Программа для обмена текстовыми сообщениями между двумя хостами (компьютерами).
Обмен осуществляется через модуль Bluetooth HC-05, подключённый к USB-порту.
'''

# Задайте правильные для вашего компа параметры:
PORT = "/dev/ttyUSB0"
#BAUD = 460800
BAUD = 921600


import threading
import serial
import time

################################################################################
'''
Класс дочернего потока.
Дочерний поток должен иметь атрибут self.daemon = True, иначе при завершении
главного потока он не завершиться и программа никогда не прекратит свое
выполнение
'''
class Bluetooth(threading.Thread):

  '''
  Обычный конструктор
  '''
  def __init__(self):
    threading.Thread.__init__(self)
    self.daemon = True
    
    try:
      self.ser = serial.Serial(PORT)
    except: # SerialException:
      print "Не могу открыть порт", PORT
      exit(0)
      
    self.ser.baudrate = BAUD
    self.ser.bytesize = serial.EIGHTBITS
    self.ser.parity   = serial.PARITY_NONE
    self.ser.stopbits = serial.STOPBITS_ONE
    self.ser.timeout  = None
  
  '''
  Функция потока.
  Эта функция не должна вызываться напрямую из главного потока.
  Запуск функции потока осуществляется вызовом метода start
  '''
  def run(self):
    # Бесконечный цикл
    while True:
      byte = str(self.ser.read())    # Читаем один байт
      if len(byte) == 1:
        print byte,

  '''
  Отправка текстового сообщения на другой комп через Bluetooth
  '''
  def write(self, msg):
    self.ser.write(msg)          # Последний кусок сообщения


################################################################################
def loop_test(bt):
  msg = "123456789_" * 40"  # 400 байт
  for i in xrange(100):
    bt.write(msg)
    time.sleep(0.1)
    
################################################################################
if __name__ == "__main__":

  bt = Bluetooth()
  bt.start()
  
  print "Наберите сообщение и нажмите Enter для его отправки на второй комп."
  print "Для выхода из программы наберите слово QUIT и нажмите Enter."
  while True:
    message = raw_input()
     
    if message.upper().strip() == "QUIT":
      break
    elif message.upper().strip() == "TEST":
      loop_test(bt)
    else:
      bt.write(message + "\n")

В опыте принимали участие два модуля, которые ради чистоты эксперимента были подключены к разным компам. Модули находились на разных столах. Расстояние между модулями составляло примерно 1.5-2 м.

IMG_0913

UART первого модуля был сконфигурирован на работу со скоростью 921600 Бод. Второй модуль был переведен в режим slave-loop. Таким образом, второй модуль принимал информацию от первого модуля и тут же отправлял её обратно. Делал он это без вмешательства второго компа.

К цепям RX и TX первого модуля также был подключен двухлучевой осциллограф для наблюдения скорости прохождения пакетов «по кругу». Я ожидал получить какую-нибудь вразумительную задержку между отправленным и принятым пакетом в диапазоне от 1 до 10 мс…

Однако, то, что я увидел, повергло меня уныние:

AZH-0306

Желтый луч — отправка пакета, лиловый — приём. По горизонтали в клетке 50 мс.

На осциллограмме хорошо видны пакеты, которые красиво идут через каждые 100 мс. (Дорогой Python — браво! Я был о тебе гораздо худшего мнения.)

А вот обратный прием тех же пакетов выглядит очень печально. Вместо одного чёткого пакета мы видим целый кластер из более мелких пакетов.

Кроме того, начало прихода кластера не подчиняется какому-то строгому закону. Кластер поступает со случайным запаздыванием, которое находится в диапазоне от 20 до 160 мс. Я, правда, не очень долго исследовал этот казус, потому скорее всего диапазон времени отклика будет ещё шире.

На осциллограмме видно, что кластеры принятых обратно пакетов ещё как-то можно сгруппировать в кучки. Но возникает вопрос — а что будет, если размер пакетов увеличить в два раза? Или во столько же раз уменьшить период? Мы уже не получим такой более-менее идентифицируемой кучки, а будет сплошное месиво из случайных микро-пакетков.

Возьму-ка тайм-аут, да немного пораскину мозгами…

 

UPDATE 15.12.2015-12:54

Я изменил схему.

IMG_0914

Теперь второй Bluetooth модуль снова работает в режиме slave и выдает принятые по радиоканалу пакеты на свой вывод TX. Осциллограф одним каналом подключен ко выводу RX первого модуля. Второй канал — подключен вы выводу TX второго модуля. Передача информации происходила только в направлении от первого модуля ко второму. Обратной передачи не было.

И вот, что получилось:

AZH-0307

Жёлтый луч — вывод RX первого модуля (передающего). Лиловый луч — вывод TX второго модуля (принимающего). В клетке 50 мс. Размер пакетов — 400 байт.

На снимке хорошо видно, что на выходе TX второго модуля всё равно присутствуют не цельные пакеты, а раздробленные кластеры маленьких пакетиков. Надо отметить, что степень «кучковатости» пакетиков больше, чем в первом случае (при передаче туда-обратно). Иначе говоря, при передаче пакета модули стремятся разбить его на более мелкие пакеты размазать их по времени.

Рассмотрим повнимательнее осциллограммы, попробуем понять логику разбития пакетов на более мелкие.

Здесь увеличен масштаб, в клетке 2 мс:

AZH-0308

Видно, что сначала приходит очень маленький пакетик, затем через 7.5 мс следуют более крупные.

Вопрос — почему на стороне передающего модуля на входе RX присутствует не цельный пакет, а тоже кластер из мелких пакетов? Ответ — дело в том, что передача пакета от компа к Bluetooth-модулю осуществляется через конвертер USB/UART.

USB пересылает информацию не цельным куском, а тоже дробит на пакеты. Поскольку скорость UART-а 921600 Бод, то получается, что принятый по USB пакет будет передан во вне (то есть из микросхемы конвертера) быстрее, чем придет следующий USB-пакетик. Таким образом, наш большой 400-байтовый пакет разбивается на более мелкие уже на стадии передачи его по USB.

Идеальный вариант передачи пакетов, не разваливая их на части, получается только при передаче через последовательный порт RS232. При USB-передачах пакеты хотя и «распиливаются», но тут хоть какая-то регулярность, да и паузы между пакетами небольшие — около 200 мкс. К тому же, если снизить скорость UART (до 115200 или 230400 Бод), то пакеты перестанут разваливаться и будут появляться на выходе конвертера единым куском.

Вот еще несколько осциллограмм что бы понять, что ни о какой регулярности получения пакетов по радиоканалу говорить нельзя:

AZH-0309 AZH-0310 AZH-0311 AZH-0312 AZH-0313

Видно, что полученные по радиоканалу пакеты «выплёвываются» Bluetooth-модулем как Бог на душу положит!

Хорошо. А как будет выглядеть картина при других параметрах передачи? Я изменил размер пакета и период. Ниже приведена осциллограмма для 240-байтных пакетов и периода равного 20 мс.

AZH-0314

Собственно, тут уже ни о каких кластерах микро-пакетов, представляющих собой единый исходный пакет говорить нельзя. Закройте ладонью жёлтый луч и попробуйте сформировать лиловые пакетики в группы. Даже если получилось мысленно оформить группы, то они (группы) ну никак не будут синхронизированы с исходными пакетами.

Подведём итоги.

Для передачи текстовой информации можно использовать пакетный режим. Границы пакетов удобно устанавливать по окончанию строк — по символу «\n».

Для передачи двоичных данных формировать пакеты на основе тайм-аутов (временных пауз между пакетами) довольно-таки проблематично. Если пакеты небольшие и передаются не чаще одного раза в секунду, то еще как-то можно их отличать друг от друга. Но если посылки осуществляются через каждые 0.1 секунду и чаще, а тем более если размеры пакетов вырастают до сотни и более байт, то нужен иной метод работы с пакетной информацией.

И такой метод существует.

Продолжение в следующем акте Марлезонского балета.

Антракт!

Advertisements

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s