Делаем iBeacon и Eddystone Beacon «на коленке»

от автора


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 | Srart — 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

Примечание: домена .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) 

Компилим, заливаем, смотрим результат.

На этом всё, до новых встречь. Теперь уже на просторах хабра.

ссылка на оригинал статьи http://habrahabr.ru/post/273865/