BtDog - Bluetooth дисплей на очки +++
Главная »Проекты »BtDog - Bluetooth дисплей на очки
BtDog - Bluetooth дисплей на очки

Описываемый в статье аппарат BtDog (Bluetooth Display On Glasses) предназначен для отображения графической информации на полупрозрачном экране, помещаемом перед глазом пользователя. Аппарат надевается на дужку очков, имеет автономное питание и поддерживает Bluetooth-связь с управляющим устройством. Это упрощенный вариант Google glasses.

Вид сверху:

Вид со стороны очков:

Вид слева:

Изображения:

Технические характеристики

  1. Дисплей: монохромный белый OLED 64x48, 0,66";
  2. Электропитание: встроенный Li-Ion аккумулятор емкостью 250 мАч;
  3. Время автономной работы: не менее 10 часов;
  4. Габаритные размеры корпуса: 128x31x30;
  5. Масса: 41 г.

Устройство

Аппарат собран с применением некоторых готовых модулей с AliExpress:

Электрическая схема:

Мозгом аппарата является Arduino Pro Mini (ATmega168P, 8Мгц) DD1. Управление питанием Bluetooth-модуля HC-05 (включение/выключение) осуществляется нажатием кнопки SA1. Питание модуля подается с шести пинов порта B (выводы 8-13 Arduino Pro Mini). OLED подключен к аппаратному I2C. Плата зарядки Li-Ion применена со встроенной защитой. SA2 включает/выключает питание аппарата.

Элементы SA1, R1, R2, C1, C2 размещаются на печатной плате размерами 18,4x26,7 мм:

Через круглые отверстия эта плата вплотную припаивается к Arduino Pro Mini:

В месте установки разъема подключения дисплея (BLS4-R) высота паек 0,5 мм. Разъем клеится на прокладку толщиной 0,5 мм и по бокам также фиксируется эпоксидным клеем. Соединения платы с Arduino Pro Mini и модулем HC-05 (с квадратных контактных площадок):

Модуль HC-05 устанавливается на двусторонний скотч на кроватку. Перед установкой модуля, необходимо его переименовать в "BTDOG-2017" и демонтировать с подложки.

Соединения:

Разъем PLS5 монтируется в штатные отверстия для программирования Arduino Pro Mini. Через этот разъем осуществляется и программирование и подача питания на аппарат.

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

Оптическая схема:

В качестве зеркала М1 применено зеркало от детской игрушки, у которого зеркальный слой нанесен на переднюю поверхность. В качестве линзы - обточенная в размер пластиковая линза от Google Cardboard исходным диаметром 35 мм. В качестве заготовки для полупрозрачного зеркала М2 использован прозрачный футляр от компакт-диска.

Корпус аппарата состоит из собственно корпуса-основания и верхней крышки, изготовленных методом 3D-печати. В основании выполнены отверстия под толкатель кнопки SA1, выключатель SA2 и microUSB-разъем платы зарядки. Компоновка:

Собранный электронный модуль крепится на основание внизу на двусторонний скотч:

Зеркало М1 и линза клеются цианакрилатом, полупрозрачное зеркало М2 съемное, крепится в пазы корпуса. OLED фиксируется в 4-х углах герметиком. Аккумулятор и плата зарядки крепятся на двусторонний скотч. Дополнительно, напротив microUSB-разъема приклеивается фиксирующий вкладыш. Питание на электронный модуль подается с четырехконтактного разъема, на два незадействованных контакта которого распаяны короткие отрезки проводов для улучшения надежности соединения.

Вид снизу на готовый аппарат:

Протокол обмена

Протокол обмена между управляющим устройством (контроллером) и аппаратом по каналу Bluetooth – односторонный, от контроллера к аппарату. Обмен осуществляется ASCII-пакетами, формат пакета:

#LKxxx, где
# – символ старта пакета;
L – длина пакета [один символ], не включая символ старта и сам символ длины;
K – команда [один символ] – буква латинского алфавита в верхнем регистре;
xxx – параметры команды (могут отсутствовать).

Все числа в пакете 16-тиричные в верхнем регистре. Список команд:

Команда Символ команды Параметры Описание
Системные
Стереть экран Erase display E  
Залить экран Fill display F  
Выключить обновление Block update B  
Включить обновление Unblock update U  
Инверсия экрана set Inverse display I xx Число; 0 – без инверсии; 1 – инверсия
Контрастность set contrAst A xx Число от 1 до 255
Поворот set rOtation O xx Число от 0 до 3
Параметры текста
Размер текста set teXt size X xx Число от 1 до 255
Цвет текста set Text color T xxyy xx – цвет текста; yy – цвет фона
Перенос строк set text Wrap W xx Число; 0 – выключить; 1 – включить
Позиция текста set cursor Position P xxyy xx – координата по оси X; yy – координата по оси Y
Графические
Цвет set line dYe (color) Y xx Число от 0 до 2
Точка draw Dot (pixel) D xxyy xx – координата по оси X; yy – координата по оси Y
Линия draw Line L x1y1x2y2 x1y1 – координаты начала; x2y2 – координаты конца
Прямоугольник draw Rectangle R xxyywwhh xx, yy – координаты ВЛУ; ww, hh – длина, высота
Залитый прямоугольник draw filled rectaNgle N xxyywwhh xx, yy – координаты ВЛУ; ww, hh – длина, высота
Окружность draw Circle C xxyyrr xx, yy – координаты центра; rr – радиус
Залитая окружность draw filled wHeel (circle) H xxyyrr xx, yy – координаты центра; rr – радиус
Текст draw String S ccc… Символы строки (максимум 14 символов)
Картинка draw imaGe (bitmap) G xxyywhbb… xx, yy – координаты ВЛУ; w, h – длина, высота; bb – байты изображения

Если планируется передача нескольких команд, перед их передачей необходимо передать команду "Выключить обновление", а после – команду "Включить обновление".

Программное обеспечение (Firmware)

Исходный код написан на C++ в Atmel Studio 7 с плагином Visual Micro (используется диалект Arduino). В качестве драйвера OLED использованая библиотека от Adafruit. Так как применен микроконтроллер с объемом ОЗУ 1 кБ, то, с целью его экономии:

  • Библиотека от Adafruit урезана;
  • Урезаны буферы в стандартных библиотеках:
    • "HardwareSerial.h" – до 32 байт вместо 64;
    • "twi.h" – до 24 байт вместо 32;
    • "Wire.h" – до 24 байт вместо 32.

Так как корректное внесение изменений в стандартные библиотеки занятие не простое, опишу этот процесс подробнее.

Внесение изменений в стандартные библиотеки Arduino

Сначала рассмотрим изменение "HardwareSerial.h".

Для этого идем в "папка установки Arduino IDE\hardware\arduino\avr\cores" и видим там папку "arduino". Копируем ее сюда же и переименовываем, например в "btdog_core". Так мы создали набор для нового ядра с названием "btdog_core". Теперь заходим в папку "btdog_core", находим в ней файл "HardwareSerial.h" и открываем текстовым редактором. Недалеко от начала файла видим определения буферов:

#if !defined(SERIAL_TX_BUFFER_SIZE)
#if ((RAMEND - RAMSTART) < 1023)
#define SERIAL_TX_BUFFER_SIZE 16
#else
#define SERIAL_TX_BUFFER_SIZE 64
#endif
#endif
#if !defined(SERIAL_RX_BUFFER_SIZE)
#if ((RAMEND - RAMSTART) < 1023)
#define SERIAL_RX_BUFFER_SIZE 16
#else
#define SERIAL_RX_BUFFER_SIZE 64
#endif
#endif

Уменьшаем размеры буферов приема и передачи и сохраняем файл:

#if !defined(SERIAL_TX_BUFFER_SIZE)
#if ((RAMEND - RAMSTART) < 1023)
#define SERIAL_TX_BUFFER_SIZE 16
#else
#define SERIAL_TX_BUFFER_SIZE 32
#endif
#endif
#if !defined(SERIAL_RX_BUFFER_SIZE)
#if ((RAMEND - RAMSTART) < 1023)
#define SERIAL_RX_BUFFER_SIZE 16
#else
#define SERIAL_RX_BUFFER_SIZE 32
#endif
#endif

Теперь это новое ядро надо подключить к Arduino, для этого идем в "папка установки Arduino IDE\hardware\arduino\avr" и находим файл "boards.txt". Открываем его (для надежности перед редактированием можно сохранить его копию) и находим секцию для Arduino Pro Mini – она начинается со строки "pro.name=Arduino Pro or Pro Mini" (секции визуально разделены строками с решетками). Копируем всю эту секцию (т.е. до следующей строки с решетками) ниже. И начинаем ее редактировать. Для начала удаляем подсекции для "Arduino Pro or Pro Mini (5V, 16 MHz) w/ ATmega328" и "Arduino Pro or Pro Mini (3.3V, 8 MHz) w/ ATmega328", т.к. это относится к микроконтроллеру ATmega328, у которого памяти достаточно. Эти подсекции визуально отделяются так:

## Arduino Pro or Pro Mini (3.3V, 8 MHz) w/ ATmega328
## --------------------------------------------------

Оставляем только подсекции для контроллера ATmega168. Далее переименовываем идентификатор секции (возможно используемые определения неправильные, но все же логичные) – каждая строка начинается с "pro."; переименовываем это в "proBtDog.". Далее переименовываем подекции "proBtDog.menu.cpu.16MHzatmega168" и "proBtDog.menu.cpu.8MHzatmega168" добавлением в конце имени суффикса "BtDog". Теперь переименовываем всю секцию – было "pro.name=Arduino Pro or Pro Mini", стало "proBtDog.name=Arduino Pro Mini [BtDog]". Подошло время подключить новое ядро. Находим строку "proBtDog.build.core=arduino" и меняем ее на "proBtDog.build.core=btdog_core". Итоговая секция:

##############################################################

proBtDog.name=Arduino Pro Mini [BtDog]

proBtDog.upload.tool=avrdude
proBtDog.upload.protocol=arduino

proBtDog.bootloader.tool=avrdude
proBtDog.bootloader.unlock_bits=0x3F
proBtDog.bootloader.lock_bits=0x0F

proBtDog.build.board=AVR_PRO
proBtDog.build.core=btdog_core
proBtDog.build.variant=eightanaloginputs

## Arduino Pro Mini [BtDog] (5V, 16 MHz) w/ ATmega168
## -------------------------------------------------
proBtDog.menu.cpu.16MHzatmega168BtDog=ATmega168 (5V, 16 MHz)

proBtDog.menu.cpu.16MHzatmega168BtDog.upload.maximum_size=14336
proBtDog.menu.cpu.16MHzatmega168BtDog.upload.maximum_data_size=1024
proBtDog.menu.cpu.16MHzatmega168BtDog.upload.speed=19200

proBtDog.menu.cpu.16MHzatmega168BtDog.bootloader.low_fuses=0xff
proBtDog.menu.cpu.16MHzatmega168BtDog.bootloader.high_fuses=0xdd
proBtDog.menu.cpu.16MHzatmega168BtDog.bootloader.extended_fuses=0xF8
proBtDog.menu.cpu.16MHzatmega168BtDog.bootloader.file=atmega/ATmegaBOOT_168_diecimila.hex

proBtDog.menu.cpu.16MHzatmega168BtDog.build.mcu=atmega168
proBtDog.menu.cpu.16MHzatmega168BtDog.build.f_cpu=16000000L

## Arduino Pro Mini [BtDog] (3.3V, 8 MHz) w/ ATmega168
## --------------------------------------------------
proBtDog.menu.cpu.8MHzatmega168BtDog=ATmega168 (3.3V, 8 MHz)

proBtDog.menu.cpu.8MHzatmega168BtDog.upload.maximum_size=14336
proBtDog.menu.cpu.8MHzatmega168BtDog.upload.maximum_data_size=1024
proBtDog.menu.cpu.8MHzatmega168BtDog.upload.speed=19200

proBtDog.menu.cpu.8MHzatmega168BtDog.bootloader.low_fuses=0xc6
proBtDog.menu.cpu.8MHzatmega168BtDog.bootloader.high_fuses=0xdd
proBtDog.menu.cpu.8MHzatmega168BtDog.bootloader.extended_fuses=0xF8
proBtDog.menu.cpu.8MHzatmega168BtDog.bootloader.file=atmega/ATmegaBOOT_168_pro_8MHz.hex

proBtDog.menu.cpu.8MHzatmega168BtDog.build.mcu=atmega168
proBtDog.menu.cpu.8MHzatmega168BtDog.build.f_cpu=8000000L

##############################################################

Сохраняем файл и проверяем, загрузив Arduino IDE (естественно, в Atmel Studio изменения также будут видны):

Все, теперь мы можем при необходимости вносить и другие изменения в файлы ядра в папке "btdog_core".

Теперь об изменениях в библиотеках поддержки I2C. Они лежат в "папка установки Arduino IDE\hardware\arduino\avr\libraries\Wire". Создаем в папке своего проекта папку "libs", копируем в нее папку "src" и переименовываем в "MyWire". Заходим в эту папку и всем, в т.ч. во вложенных папках файлам добавляем префикс "My" (или другой, главное – единообразно). Открываем "MyWire.h" и переименовываем класс "TwoWire" в "MyTwoWire", объект "Wire" в "MyWire". В дефайне однократного включения файла "TwoWire_h" заменяем на "MyTwoWire_h". Открываем "MyWire.cpp". Меняем "#include "utility/twi.h" на "#include "utility/mytwi.h" и "#include "MyWire.h" на "#include "MyWire.h". Далее переименовываем имя класса. В файле "mytwi.h" в дефайне однократного включения файла "twi_h" меняем на "mytwi_h". В файле "mytwi.c" подключение "#include "twi.h" изменяем на "#include "mytwi.h".

Измененные пользовательские библиотеки, которые в т.ч. могут использовать модифицированные системные также помещаем в папку "libs". Для обращения из главного файла проекта к такой пользовательской библиотеке, а из библиотеки к модифицированной системной необходимо правильно указать пути. Например, откорректированная библиотека Adafruit_SSD1306, лежащая в папке проекта "libs/Adafruit_SSD1306_Reduced" использует библиотеку "MyWire" включая ее так:

#include "../MyWire/MyWire.h"

Подключается библиотека "Adafruit_SSD1306_Reduced" в проект так:

#include "libs/Adafruit_SSD1306_Reduced/Adafruit_GFX_Reduced.h"
#include "libs/Adafruit_SSD1306_Reduced/Adafruit_SSD1306_Reduced.h"

Описанный способ позволяет не модицицировать оригиналы системных файлов и библиотек. Недостатком является необходимость ручного изменения некоторых файлов и необходимость при каждом открытии проекта в Atmel Studio выполнять команду "Project -> Show All Files" перед компиляцией.

Firmware BtDog

Для экономии ОЗУ не используется тип Boolean, вместо этого введена байтовая переменная flags, биты которой используются как флаги. Шесть пинов PORTB, использумые для включения/выключения питания Bluetooth-модуля управляются маской. Функции _hex2uchar и hex2uchar преобразуют 16-тиричное число в десятичное. Кнопка подтянута к напряжению питания и подключена к внешнему прерыванию INT1; обработчик прерывания по спаду сигнала выставляет флаг BT_SWITH, который контролируется в основном цикле. Здесь переключается флаг BT_STATE. Если он установлен включается Bluetooth-модуль, если сброшен – выключается. В основном цикле контролируется флаг BT_STATE. Если он установлен, принимаются данные из Bluetooth-модуля (если буфер UART не пустой). Если флаг BT_STATE сброшен, производится отрисовка на дисплее движущегося шарика.

Программное обеспечение (Software)

Исходный код написан на Java в Android Studio 1 (затем в Android Studio 3). В "AndroidManifest.xml" разрешена работа с Bluetooth:

    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.BLUETOOTH"/>

Класс BluetoothService реализует методы подключения и связи и отдает свое состояние хэндлеру BtHandler в MainActivity. В случаях, если при старте приложения пользователь откажется от включения Bluetooth, или во время работы приложения его выключит – приложение закрывается. Передача команды осуществляется методом sendMessage, которому передается сформированное сообщение в виде строки. Этот метод преобразует строку в массив байт (в кодировке cp_1251) и отдает его методу write службы BluetoothService. Сообщение в соответствии с протоколом формируется в методе doAction, вызываемом при выборе пользователем необходимого действия в AlertDialog, вызываемом при нажатии кнопки btnRun – "Пуск". Список действий повторяет команды протокола и добавляет следующие:

  • Сообщение;
  • Часы цифровые;
  • Часы аналоговые.

Действие "Сообщение" стирает экран и выводит сообщение в позицию (0,0). Действия часов выводят набор команд, рисующих часы. Это набор 1 раз в секунду выводится в методе secTimerTask, для чего заведен secTimer. В начале набора передается команда "Block update", после передачи всех графических команд, передается "Unblock update". Значения часов и минут для цифровых часов показываются с размером шрифта 2, что требует определенного времени для отрисовки, поэтому после передачи этой команды, основной поток приложения приостанавливается на 18 мс. методом sleep в функции delay.

Интерфейс

При включении питания аппарат работает в автономном режиме. В течение 2-х секунд демонстрирует заставку, затем – движущийся шарик:

Однократное нажатие на кнопку переводит аппарат в связной режим, сигнализируя об этом на индикаторе:

Теперь можно подключиться к аппарату (имя "BTDOG-2017") и управлять им. Примеры некоторых графических возможностей:

Интерфейс приложения состоит из одной Activity.

При старте приложения, если Bluetooth не включен, будет предложено его включить. В этот момент приложение еще не соединено с аппаратом и кнопка "Пуск" не активна:

Для соединения необходимо нажать кнопку с символом Bluetooth. Если это первое соединение, будет выдан запрос на подключение. Необходимо выбрать устройство "BTDOG-2017" и стандартный пароль 1234. После этого произойдет соединение, надпись "не соединено" сменится на "соединено с BTDOG-2017", а кнопка "Пуск" станет активной. Отправка команд осуществляется по нажатию кнопки "Пуск", и выборе в появившемся диалоге соответствующей команды:

Для выбора цвета текста или фона, типа примитива (точка, линия, прямоугольник, окружность), картинки нажимайте кнопки "Выбор".

Если в полях ввода цифровых данных стереть текущее значение, появляются подсказки:

Недостатки

  • Не универсальное крепление на очки;
  • Задняя часть корпуса упирается в ухо;
  • Выступающее за габарит полупрозрачное зеркало неудобное при транспортировании (приходится снимать);
  • Сужение поля зрения справа;
  • Низкая контрастность изображения – хорошо различается только на темном фоне;
  • Слишком большое полупрозрачное зеркало, в котором отражаются окружающие предметы снизу;
  • Низкая скорость обновления экрана – до нескольких кадров в секунду;
  • Код написан на диалекте Arduino;
  • Односторонний протокол обмена без контроля целостности.

К статье прилагаются файлы:


Внимание! Запрещается воспроизведение данной статьи или ее части без согласования с автором. Если вы желаете разместить эту статью на своем сайте или издать в печатном виде, свяжитесь с автором.
Автор статьи: Вершинин И.В.

+