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

Мир будет казаться чрезвычайно сложным и его положения (законы) не будут нормально перевариваться нашим мозгом, если мы будем ему скармливать тщательно перемешанные котлеты с мухами.

Поэтому давайте начнем отделять одно от другого. Через десяток минут вы поймете, на сколько бывают вкусными котлеты, когда они отдельно от мух.

Но прежде чем двинуться дальше, мне нужно заранее вас оповестить. Модули HC-03/05 не обладают потрясающей скоростью передачи. Хотя про Bluetooth официально заявлено, что эта технология поддерживает скорость обмена до 721 кбита/с (то есть около 70 кбайт/с), но модули HC-03/05 к сожалению с трудом могут предавать не более 20 кБайт/с.

Причём, эти 20 кбайт/с — это «толщина» канала для обоих направлений — обмен-то двусторонний (дуплексный). То есть, если мы передаем данные только в одном направлении, то максимальная скорость 20 кБайт/с. А если передача осуществляется в обоих направлениях, то скорость падает до 7-10 кБайт/с. Впрочем, если обратный трафик незначительный, то скорость прямой передачи будет снижена тоже незначительно. Я не проверял, но думаю, что снизится как раз на «толщину» обратного потока.

В общем, это обстоятельство нужно иметь ввиду и не строить особых надежд на большие скорости.

Итак приступим к сортировке мух и котлет.

Во первых, надо заметить, что обмен между двумя устройствами (двумя хостами) может происходить как в текстовом формате, так и в двоичном. Смешивать один формат с другим считается дурным тоном.

Текстовый формат отличается тем, что передача осуществляется только в символьном виде — буквы, цифры, знаки препинания и плюс как исключение — некоторые непечатные символы, например, «возврат каретки», «перевод строки», «Esc» и некоторые другие. Непечатные символы нужны не для отображения символов, а для передачи действий. Например, символ «звонок» (код 0x07) вызывает воспроизведение звукового сигнала.

Двоичный формат предполагает передачу байтов которые имеют значения от 0x00 по 0xFF включительно. Иначе говоря, при двоичной передаче нет никакого «особого» байта для указания каких-то иных действий — все байты имеют одинаковый статус.

Передавать информацию по-байтно (по одной букве или по одному байту) не очень разумно. Передачи обычно осуществляют блоками.

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

Если речь идет о текстовом формате, то для обозначения границ блока удобно использовать символ новой строки ‘\n’. таким образом, передача будет осуществляться по-строчно. Каждая строка должна заканчиваться символом ‘\n’. Это мы с вами уже видели при работе с модулем в режиме конфигурации (AT-команды).

Правда, разные изделия выставляют разные требования. Одни требуют окончания строк на символ ‘\n’ (Linux), другие на символ ‘\r’ (Mac), третьим подавай оба сразу и только в такой последовательности — «\r\n» (Windows). Поэтому у программ, которые работают с текстом, существуют настройки — чем должны оканчиваться строки.

Согласно вышесказанному мы не будем делать универсальную программу на все случаи жизни. Вместо этого, мы напишем две программы — одна будет предназначена для обмена текстовыми сообщениями, которые в реальном времени набираются на клавиатуре. А другая программы будет заниматься вопросами передачи двоичных данных, которые как правило представляют собой содержимое файлов. В том числе и текстовых. А для Линукса — ещё и файлы устройств.

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

Единственное о чём нужно позаботиться в нашей программе — это учесть, что размер входного буфера у модуля HC-03/05 не превышает 8 кБайт. И если мы будем «заливать» в модуль информацию со скоростью более чем 20 кБайт/с, то буфер перегрузиться и мы в лучшем случае потеряем информацию. В худшем — канал радио-обмена повиснет и нам придется передёргивать питание модулей.

В общем, если сообщение, которое нужно отправить, получилось слишком длинное, мы будем отправлять его по кусочкам. В остальном программа мало чем отличается от программы-конфигуратора:

#!/usr/bin/env python
#coding: utf-8

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

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


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):
    answer = ""                      # Заготовка для новой строки
    # Бесконечный цикл
    while True:
      byte = str(self.ser.read())    # Читаем один байт
      if len(byte) == 1:
        answer += byte               # Составляем строку
        
        # Вывод ответа на консоль произведём только тогда, когда строка будет
        # сформирована полностью
        if len(answer) >= 1:         
          if answer[-1:] == "\n":    # Окончание строки
            print answer[:-1]        # Выводим строку на консоль без окончания
            answer = ""              # Заготовка для новой строки          

  '''
  Отправка текстового сообщения на другой комп через Bluetooth
  '''
  def write(self, msg):
    while len(msg) > 128:
      self.ser.write(msg[:128])  # Отправляем кусочками по 128 байт
      msg = msg[128:]            # Оставшаяся часть сообщения
      time.sleep(0.010)          # Ограничение из-за скорости радио-канала
      
    self.ser.write(msg)          # Последний кусок сообщения


################################################################################
if __name__ == "__main__":

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

И как всегда напомню, прогу лучше забирать не отсюда, а клонированием всего проекта с github-а:

$ git clone https://github.com/zhevak/bluetooth

В результате нескольких дней экспериментов удалось выяснить, что если отправлять-принимать информационные блоки один раз в секунду, то максимальный размер блока должен быть не более примерно 7 тысяч байт. Если блок будет иметь размер 8 тысяч байт, то иногда радиообмен будет проходить успешно, иногда нет. Иногда можно переслать и принять до десятка таких блоков, иногда даже на пересылке первого блока всё зависает. При размере блока 7000 байт передача-прием происходят вполне надёжно. Более точную границу я не пытался выискивать. Просто не вижу в этом никакого смысла. Граница не чёткая, сегодня это может быть одна цифра, а завтра — другая.

Если обмен блоками осуществлять с периодом 0.1 с, то можно поднять размер блока примерно до 2 тысяч байт. Мне даже какое-то время удавалось осуществлять обмен по 2100 байт. Если посчитать скорость обмена, то это составит около 21 кБайт/с. Причём стоит заметить, что это скорость для дуплексного обмена.

Я ранее утверждал, что «толщина» канала связи для обоих направлений суммарно составляет около 20 кБайт/с. Но согласно только-что полученным данным, «толщина» канала составляет аж 42 кБайта. Ну, видимо, какое-то непродолжительное время можно итак работать.

А вот если передавать информацию раз в секунду, то суммарная толщина канала будет около 14 кБайт/с. В общем, всё очень неоднозначно.

Но самое поганое это то, что у модуля хоть и присутствуют контакты RTS и CTC, но они не работают. Поэтому нельзя никак узнать — можно в модуль записывать очередной байт или нельзя. Это какой-то крайне непрофессиональный подход к делу! Для ардуинщиков и так сойдет, но для ответственных применений эти модули я бы не рекомендовал к использованию.

Конечно, можно установить скорость обмена с модулем такую, которая будет заведомо меньше скорости обмена по радиоканалу. Разумеется, это не даст полной гарантии, но хоть как-то убережет вас от однозначного поражения. Иначе говоря, сделайте самым узким местом не радиоканал, который вы не контролируете, а UART.

Допустим минимальная скорость при ежесекундных посылках составляет примерно 7 кБайт/с. Таким образом скорость UART-а не должна превышать 70 кБод. Если мы будем посылать информационные пакеты 10 раз в секунду, то скорость UART можно поднять до 115200 Бод. Если предполагается только симплекс (без обратного канала), то можно попытать счастья на 230400 Бод.

Если ж вы проектируете обмен с модулем на более высоких скоростях UART, то вам придется позаботиться, о то как снизить реальную скорость обмена. Например, программно вставлять паузы или передавать информационные блоки по таймеру. В общем, предстоит та ещё веселуха.

Скорость UART, равная 115200 бод, может сохраняться до периодов отправки-приема до 50-100 пакетов в секунду. (Разумеется, размер пакета должен пропорционально быть уменьшен!) Начиная с периода отправки пакетов 10 мс и меньше, скорость радио-обмена опять начинает падать.

Ну, в общем, резюме такое — так или иначе, наиболее оптимальное количество пакетов в секунду получается примерно от 10 до 100. При этом реальная скорость радиообмена получается около 20 кБайт/с суммарно на оба направления.

В следующей части мы поиграемся с пересылкой двоичной информации.

Реклама

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

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

Логотип WordPress.com

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

Фотография Twitter

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

Фотография Facebook

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

Google+ photo

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

Connecting to %s