Свой Sphero R2D2 я купил два года назад, это была крутая игрушка для таких фанатов Звездных войн, как я, и отличный друг для моего кота. как бы то ни было, через некоторое время я начал думать о том, чтобы сделать какой-нибудь проект по программированию с этим прекрасным кластером электроники! Я хотел найти способ написать скрипт для своего робота, но не нашел ничего хорошо задокументированного и поддерживаемого.

Единственное, что я знал о R2D2, это то, что он работает с использованием технологии BLE, и вы можете управлять им с помощью официального приложения Sphero Droids. После недолгих поисков я нашел только эту статью, хорошую отправную точку и некоторую документацию о протокольной связи. R2D2 двигается и танцует.

Вот почему я решил написать код на Javascript, чтобы узнать, как общаться с R2D2! В этой статье я покажу вам свой личный опыт реверс-инжиниринга этого дроида: вы можете использовать этот подход для любого BLE-устройства, которое хотите взломать.

TL;DR

Используя телефон Android, Wireshark и документацию по протоколу, я обнаружил, как приложение взаимодействует с R2D2, и написал драйвер с помощью Node: вы можете перейти в этот репозиторий и использовать код для связи с R2D2. Финальный результат в этом видео 📺

Настраивать

Для этого эксперимента необходимо:

  • Базовые знания протокола BLE (учебник для начинающих)
  • Компьютер с поддержкой BLE (у меня MacBook Pro)
  • Телефон Android (я использую старую Motorola с Android 6)
  • Дроид Sphero R2D2! (Амазон 📦)

Первое, что нужно сделать, это установить на ПК инструменты Wireshark и Android Developer:

  • Wireshark — анализатор сетевых протоколов, полезный для проверки сообщений Bluetooth, который можно скачать с официального сайта.
  • Инструменты Android Developer содержат исполняемый файл adb для связи с вашим телефоном Android с ПК, посетите официальный сайт для получения дополнительной информации.

На телефон Android установите приложение Sphero Droids и включите функцию Bluetooth HCI Spoofing в разделе Параметры разработчика.

Используя эту функцию, я могу получить файл со всеми пакетами связи Bluetooth, отправленными и полученными между устройствами.

Сбор данных

Теперь, когда BLE HCI Spoofing включен, откройте приложение Sphero Droids, подключите R2D2 и поиграйте с ним некоторое время.

После этого закройте приложение и загрузите файл, созданный на вашем диске, с помощью adb.

adb pull /sdcard/btsnoop_hci.log /dest/путь

Этот файл обычно сохраняется в папке /sdcard/btsnoop_hci.log и может быть открыт с помощью Wireshark.

Инспекция Wireshark

Это самая интересная часть проекта: открытие файла с помощью Wireshark открывает много полезной информации для реверс-инжиниринга дроида. Вот что я получил после первого сеанса: между Android-устройством (localhost) и дроидом (мой помечен адрес d7:1b:52:17:7b:d6) и после некоторой прокрутки появится первый запрос на запись!

Как вы можете видеть в инспекторе байтов, полезная нагрузка довольно красноречива: «usetheforce. ..группа”. Звучит отлично :)

Еще одна полезная информация — это UUID службы и UUID характеристики (дескриптор 0x0015), аннотируйте их, чтобы знать, куда отправлять «usetheforce. ..band» сообщение!

Теперь пришло время прочитать документацию, начиная со структуры пакета. Это структура пакета в протоколе Sphero:

Каждый пакет имеет байт SOP (начало пакета) и байт EOP (конец пакета), оба равны 0x8D и 0xD8, поэтому необходимо искать все эти пакеты, начиная с SOP и заканчивая EOP.

Другие интересные байты:

SEQ (порядковый номер): токен, используемый для связывания команд с ответами
DATA (данные сообщения): ноль или более байтов полезных данных
CHK (контрольная сумма): сумма всех байтов (исключая SOP и EOP) по модулю 256, бит-инверсия

Первый пакет, отправленный из приложения, выглядит следующим образом:

0x8D 0x0A 0x13 0x0D 0x00 0xD5 0xD8

Байт SEQ здесь равен 0x00 в соответствии со схемой структуры пакета: это первый пакет, который приложение отправляет дроиду! Назовем его Пакет инициализации.

Как видите, есть еще один UUID службы и еще один UUID характеристики (дескриптор 0x001c), которые будут получать следующие сообщения.

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

0x8D 0x0A 0x13 0x01 0x20 0xC1 0xD8

Пришло время аннотировать сервисы, характеристики и сообщения (без SOP, EOP и прочих байтов) в каких-то константах.

Давайте напишем код

Окончательный сценарий составят:

  • функция для создания пакета
  • функция для подключения дроида R2D2
  • функциядля записи пакетов и ожидания ответа
  • функция отключения дроида

Создание пакета

Создать пакет очень просто, потому что это всего лишь массив байтов, начинающийся с байта SOP и заканчивающийся байтом EOP. Есть два байта, которые должны быть сгенерированы во время выполнения:

  • Байт SEQ: это просто переменная, инициализированная до 0x00 и увеличивающаяся на 1 каждый раз, когда создается пакет.
  • Байт CHK: согласно документации, байт CHK представляет собой сумму всех байтов (исключая SOP и EOP) по модулю 256, с инвертированным битом, очень легко сгенерировать:

Существуют и другие специальные байты, используемые для связи помимо SOP и EOP:

Когда в полезной нагрузке необходимы байты ESC, SOP или EOP, они кодируются в двухбайтовые управляющие последовательности следующим образом:

Это окончательный код для создания действительного пакета для R2D2:

Подключаем нашего дроида

В данном примере для подключения R2D2 к ПК по технологии BLE я использую Noble library. Я установил специальный форк, чтобы Noble работал на macOS Catalina.

пряжа добавить git://github.com/lzever/noble.git

С Noble действительно легко реализовать функцию, позволяющую получить основную характеристику, используемую для связи с дроидом.

Этот скрипт начинает сканировать все устройства вокруг и выбирает устройство с указанным адресом, получает службу подключения и отправляет usetheforce. ..band”(MSG_CONNECTION) к его характеристике (CONNECT_CHAR). После этого самое время получить «Основную характеристику» для отправки команд дроиду! Для этого лучше написать код для записи и чтения, потому что мне нужно дождаться некоторых ответов.

Запись пакетов и чтение ответов

Это основная часть эксперимента: создайте функцию для записи команд и… ждите, пока ответ будет прочитан! Когда приложение отправляет сообщение дроиду, оно получает один или несколько ответных пакетов, как видно из логов и/или из документации:

Ответы повторяют DID, CID и SEQ, чтобы помочь отправителю полностью идентифицировать соответствующий командный пакет.

Если байт ERR равен 0x00, при выполнении команды ошибки не произошло, иначе вам нужно проверить эту ссылку, чтобы узнать, что произошло.

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

Чтобы удовлетворить все эти случаи, конечная функция записи должна работать следующим образом:

  • Получает характерный объект, команду для выполнения, логическое значение, чтобы указать, должен ли он ждать другого ответа после эха и тайм-аута.
  • Отправляет команду на характеристику
  • Ожидает ответа, проверяет, есть ли какие-то ошибки, а затем разрешает промис (через некоторое время, если тайм-аут больше 0)

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

Поддерживаемые типы данных полезной нагрузки

Следуя тому же процессу, я попытался узнать, как повернуть верхнюю часть. Есть много похожих сообщений, чтобы сделать верхнюю ротацию, начиная с 0x0A 0x17 0x0F

Я попытался повернуть верхнюю часть почти на 90 градусов и получил 32-битную полезную нагрузку (0x42 0xb2 0x31 0x98, для простоты 0x42b23198) без значения, близкого к 90, поэтому… это число не должно быть представлено с помощью целое число. К счастью, в протоколе дроидов нет специального секретного кода, согласно документации есть и другие типы, поддерживаемые для данных полезной нагрузки.

32-битная полезная нагрузка 0x42b23198 очень похожа на число, закодированное с использованием IEEE754! Преобразовав это значение с помощью онлайн-инструмента, я получаю 89,09686.

Добавив простую функцию для преобразования градусов в шестнадцатеричные, это окончательный код для поворота вершины R2D2:

Пробовал сделать полный оборот волчка но не получается, дроид отвечает ошибкой 0x07 (параметр data недействителен).

В следующем эпизоде ​​я попытаюсь переместить R2D2, а пока вы можете проверить этот репозиторий, содержащий некоторые другие функции, такие как анимация и трансформация сошек/треноги.

В финальном эпизоде ​​я постараюсь написать настоящую библиотеку, переписав код (может, используя какую-то очередь?) и добавив поддержку других дроидов 🤖 Оставайтесь с нами.

Вот и все ребята 👋