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

в 11:08, , рубрики: atmel, BLE, bluetooth low energy, btlc1000, cortex-m0, Eddystone, ibeacon, saml21, Блог компании Rainbow, программирование микроконтроллеров, Разработка для интернета вещей

Делаем iBeacon и Eddystone Beacon «на коленке» - 1
iBeacon и Eddystone — это сервисы Apple и Google соответственно, использующие BLE (Bluetooth Low Energy) для локального позиционирования внутри помещений. Базовый принцип у обоих сервисов одинаков, отличается лишь формат передаваемых данных. Маяк (передатчик) периодически, с интервалом от долей секунды до нескольких секунд, передаёт пакеты стандарта Bluetooth LE, которые содержат помимо заголовка дополнительную информацию. Технология не предназначена для точного определения положения в помещении, а лишь для фиксирования момента приближения на некоторое близкое расстояние к маяку.
Классический пример использования маяков — музеи. Приходя в музей, вы устанавливаете на смартфон специальное приложение и отправляетесь осматривать экспозицию. Приближаясь к экспонату (маяку) на некоторое расстояние, смартфон это фиксирует и выводит на экран экскурсионную информацию.
В продаже можно найти немало готовых маяков, но сегодня мы соберем прототип собственного маяка (как iBeacon, так и Eddystone) на микроконтроллере SAML21 и BLE модуле BTLC1000 от Atmel.

Железо

Bluetooth будем реализовывать на базе платы расширения ATBTLC1000-XPRO с данным модулем. В качестве хоста используем микроконтроллер ATSAML21J18B, установленный на отладочной плате ATSAML21-XPRO-B
Делаем iBeacon и Eddystone Beacon «на коленке» - 2

Генерируем пример для iBeacon

Для этого заходим на страницу Atmel | Start — Web конфигуратор кода для микроконтроллеров Atmel. В одной из наших прошлых статей мы уже писали об использовании этого инструмента. На главной странице нажимаем кнопку Browse All Examples и в открывшемся окне выбираем отладочную плату SAM L21 Xplained Pro, а в поиске набираем строку ibeacon. Выбираем пример BLE Simple-BTLC1000 и жмем кнопку Open Selected Project:
Делаем iBeacon и Eddystone Beacon «на коленке» - 3

Получившаяся по умолчанию конфигурация по-идее соответствует железу и менять тут ничего не нужно. На всякий случай, можно сравнить результат со скриншотом и жмем кнопку EXPORT PROJECT в правом верхнем углу экрана:
Делаем iBeacon и Eddystone Beacon «на коленке» - 4

Ставим галочку напротив пункта Makefile (standalone), переименовываем проект и жмем кнопку DOWNLOAD PACK:
Делаем iBeacon и Eddystone Beacon «на коленке» - 5

В результате скачивается файл с расширением *.atzip. В принципе это обычный архив, в котором хранится проект для Atmel Studio. Открываем файл (он должен быть ассоциирован со студией) и ждем пока запустится проект. В открывшемся диалоге меняем при желании имя проекта и путь к папке с проектом и жмем ОК:
Делаем iBeacon и Eddystone Beacon «на коленке» - 6

Ждем пока проект сформируется.

Пробный запуск

В принципе, пример «из коробки» уже работает как iBeacon. Пробуем скомпилировать проект кнопкой F7.
Если компиляция прошла успешно можно залить проект в микроконтроллер для первых тестов. Рекомендую предварительно запустить терминальную программу для мониторинга дебажной информации. Встроенный отладчик EDBG определяется в системе в том числе и как виртуальный COM-порт, к которому подключен один из SERCOM'ов микроконтроллера
Делаем iBeacon и Eddystone Beacon «на коленке» - 7

Запускаем свою любимую терминалку, ставим скорость 115200 и галочку DTR.

Жмем кнопку F5 для того чтобы залить прошивку. Если заливаете в первый раз, появится сообщение с просьбой выбрать используемый программатор:
Делаем iBeacon и Eddystone Beacon «на коленке» - 8

В диалоговом окне указываем наш отладчик:

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

Жмем еще раз F5 и ждем пока запрограммируется контроллер и начнет исполняться программа. Если всё сделано правильно, в терминалке должны увидеть следующую информацию:
Делаем iBeacon и Eddystone Beacon «на коленке» - 10

Теперь запускаем на телефоне любое приложение, работающее с маяками (например, iBeacon & Eddystone Scanner для Android) и мы должны увидеть нашу метку.
Делаем iBeacon и Eddystone Beacon «на коленке» - 11

Немного разберемся с форматом передаваемых данных. А он, по старой традиции 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 типа пакетов. Идентификатор типа передается в одном из байтов посылки:
Делаем iBeacon и Eddystone Beacon «на коленке» - 12

Мы будем передавать ссылку.
Формат записи самой ссылки можно подсмотреть на этой странице.

Формат передачи ссылки

URL Scheme Prefix
Делаем iBeacon и Eddystone Beacon «на коленке» - 13
Eddystone-URL HTTP URL encoding
Делаем iBeacon и Eddystone Beacon «на коленке» - 14

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

Компилим, заливаем, смотрим результат.
Делаем iBeacon и Eddystone Beacon «на коленке» - 15

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

Автор: Rainbow

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js