iBeacon и Eddystone — это сервисы Apple и Google соответственно, использующие BLE (Bluetooth Low Energy) для локального позиционирования внутри помещений. Базовый принцип у обоих сервисов одинаков, отличается лишь формат передаваемых данных. Маяк (передатчик) периодически, с интервалом от долей секунды до нескольких секунд, передаёт пакеты стандарта Bluetooth LE, которые содержат помимо заголовка дополнительную информацию. Технология не предназначена для точного определения положения в помещении, а лишь для фиксирования момента приближения на некоторое близкое расстояние к маяку.
Классический пример использования маяков — музеи. Приходя в музей, вы устанавливаете на смартфон специальное приложение и отправляетесь осматривать экспозицию. Приближаясь к экспонату (маяку) на некоторое расстояние, смартфон это фиксирует и выводит на экран экскурсионную информацию.
В продаже можно найти немало готовых маяков, но сегодня мы соберем прототип собственного маяка (как iBeacon, так и Eddystone) на микроконтроллере SAML21 и BLE модуле BTLC1000 от Atmel.
Железо
Bluetooth будем реализовывать на базе платы расширения ATBTLC1000-XPRO с данным модулем. В качестве хоста используем микроконтроллер ATSAML21J18B, установленный на отладочной плате ATSAML21-XPRO-B
Генерируем пример для iBeacon
Для этого заходим на страницу Atmel | Start — Web конфигуратор кода для микроконтроллеров Atmel. В одной из наших прошлых статей мы уже писали об использовании этого инструмента. На главной странице нажимаем кнопку Browse All Examples и в открывшемся окне выбираем отладочную плату SAM L21 Xplained Pro, а в поиске набираем строку ibeacon. Выбираем пример BLE Simple-BTLC1000 и жмем кнопку Open Selected Project:
Получившаяся по умолчанию конфигурация по-идее соответствует железу и менять тут ничего не нужно. На всякий случай, можно сравнить результат со скриншотом и жмем кнопку EXPORT PROJECT в правом верхнем углу экрана:
Ставим галочку напротив пункта Makefile (standalone), переименовываем проект и жмем кнопку DOWNLOAD PACK:
В результате скачивается файл с расширением *.atzip. В принципе это обычный архив, в котором хранится проект для Atmel Studio. Открываем файл (он должен быть ассоциирован со студией) и ждем пока запустится проект. В открывшемся диалоге меняем при желании имя проекта и путь к папке с проектом и жмем ОК:
Ждем пока проект сформируется.
Пробный запуск
В принципе, пример «из коробки» уже работает как iBeacon. Пробуем скомпилировать проект кнопкой F7.
Если компиляция прошла успешно можно залить проект в микроконтроллер для первых тестов. Рекомендую предварительно запустить терминальную программу для мониторинга дебажной информации. Встроенный отладчик EDBG определяется в системе в том числе и как виртуальный COM-порт, к которому подключен один из SERCOM'ов микроконтроллера
Запускаем свою любимую терминалку, ставим скорость 115200 и галочку DTR.
Жмем кнопку F5 для того чтобы залить прошивку. Если заливаете в первый раз, появится сообщение с просьбой выбрать используемый программатор:
В диалоговом окне указываем наш отладчик:
Жмем еще раз F5 и ждем пока запрограммируется контроллер и начнет исполняться программа. Если всё сделано правильно, в терминалке должны увидеть следующую информацию:
Теперь запускаем на телефоне любое приложение, работающее с маяками (например, iBeacon & Eddystone Scanner для Android) и мы должны увидеть нашу метку.
Немного разберемся с форматом передаваемых данных. А он, по старой традиции Apple, очень прост. Маяк из полезной нагрузки передает следующие данные:
UUID. 128-битный уникальный идентификатор группы маяков, определяющий их тип или принадлежность одной организации
Major. 16-битное беззнаковое значение, с помощью которого можно группировать маяки с одинаковым UUID
Minor. 16-битное беззнаковое значение, с помощью которого можно группировать маяки с одинаковым UUID и Major
Measured Power (уровень сигнала в 1 м от передатчика). 8-битное знаковое целое — значение индикации уровня принимаемого сигнала (RSSI), откалиброванное на расстоянии 1 м от приёмника, которое используется для определения близостимаяка к приёмнику (мобильному устройству). Измеряется в dBm.
В основном файле simple_btlc1000.c определен массив adv_data, в который и помещаются эти данные. Помимо этого, есть дополнительная служебная информация, расшифровку которой можно найти в документе Proximity Beacon
Specification на странице developer.apple.com/ibeacon.
static uint8_t adv_data[] =
{
0x1a, 0xff,
0x4c, 0x00, // Company ID
0x02, 0x15, // Beacon Type
// Proximity UUID
0x21, 0x8A, 0xF6, 0x52, 0x73, 0xE3, 0x40, 0xB3, 0xB4, 0x1C, 0x19, 0x53, 0x24, 0x2C, 0x72, 0xf4,
0x00, 0xbb, // Major
0x00, 0x45, // Minor
0xc5 // Measured Power
};
Основное волшебство происходит в функции beacon_init(), которая вызывается непосредственно перед главным циклом в main. В ней, используя API-функцию at_ble_adv_data_set мы сообщаем модулю какие именно данные мы хотим передавать (тот самый массив adv_data), а далее используя функцию at_ble_adv_start запускаем сам процесс передачи с указанием частоты их передачи (BEACON_ADV_INTERVAL).
Код функции beacon_init()
static void beacon_init(void)
{
static at_ble_handle_t service;
/* establish peripheral database */
if (at_ble_primary_service_define(&service_uuid, &service, NULL, 0, chars, 2) != AT_BLE_SUCCESS)
DBG_LOG("Failed to define the primary service");
/* set beacon advertisement data */
if(at_ble_adv_data_set(adv_data, sizeof(adv_data), scan_rsp_data, sizeof(scan_rsp_data)) != AT_BLE_SUCCESS)
DBG_LOG("BLE Beacon advertisement data set failed");
/* BLE start advertisement */
if(at_ble_adv_start(AT_BLE_ADV_TYPE_UNDIRECTED, AT_BLE_ADV_GEN_DISCOVERABLE, NULL, AT_BLE_ADV_FP_ANY,
BEACON_ADV_INTERVAL, BEACON_ADV_TIMEOUT, BEACON_ABSOLUTE_INTERVAL_ADV) != AT_BLE_SUCCESS)
{
DBG_LOG("BLE Beacon advertisement failed");
ble_device_disconnected_ind();
}
else
{
DBG_LOG("Advertisement started");
ble_device_connected_ind();
}
}
Переделываем проект под маяки Eddystone от Google
Для этого требуется сделать всего одну вещь — изменить передаваемый массив данных в функцию at_ble_adv_data_set. Заведем отдельный массив и заполним его. Про формат пакета можно прочитать тут. В отличии от iBeacon, Eddystone на данный момент может передавать 3 типа пакетов. Идентификатор типа передается в одном из байтов посылки:
Мы будем передавать ссылку.
Формат записи самой ссылки можно подсмотреть на этой странице.
Формат передачи ссылки
URL Scheme Prefix
Eddystone-URL HTTP URL encoding
Eddystone-URL HTTP URL encoding
Примечание: домена .ru в списке поддерживаемых нет. Мне очень хотелось, чтобы ссылка была живая, поэтому я напишу geektimes.com, т.к. с него есть редирект на geektimes.ru, а вот habrahabr.com аналогичным образом в домен .ru не перенаправляется.
В итоге получаем следующий пакет для Eddystone:
static uint8_t eddystone_data[] =
{
0x03,
0x03,
0xaa, 0xfe,
0x10, // длина
0x16,
0xaa, 0xfe, // 16-bit Eddystone UUID
0x10, // Frame Type: 0x10 для URL
0xf0, // TX Power
0x00, // URL Scheme: http://www.
'g', 'e', 'e', 'k', 't', 'i', 'm', 'e', 's',
0x00, // .com/
};
Не забываем указать в API новый пакет:
at_ble_adv_data_set(eddystone_data, sizeof(eddystone_data), scan_rsp_data, sizeof(scan_rsp_data)) != AT_BLE_SUCCESS)
Компилим, заливаем, смотрим результат.
На этом всё, до новых встреч. Теперь уже на просторах хабра.