Eddystone은 구글이 만든 프로토콜로 BLE 디바이스가 URL을 메시지로 브로드캐스트 할 수 있게 해 준다. Eddystone에 관한 자세한 내용은 github를 보면 된다. 현재 스펙은 3가지 타입(UID, URL, telemetry)의 브로드캐스트 메시지를 정의하고 있다.
Eddystone 비콘의 마법은 스마트폰 쪽에서 이 브로드캐스트 메시지를 기다리고 있다가 메시지를 받으면경고 메시지를 표시하거나 특정 동작을 수행할 수 있다.
이 튜토리어에서는 라즈베리 파이를 eddystone beacon으로 사용하는 방법을 보여준다.
0x08 - OGF (Operaton Group Field) -> Bluetooth Command Group (0x08) 0x0008 - OCF (Operation Command Field) -> HCI_LE_Set_Advertising_Data (0x0008) 17 - Length (23 bytes) 02 - Length 01 - Flags Data type value 06 - Flags Data 03 - Length 03 - Complete list of 16-bit Service UUID data type value aa - 16-bit Eddystone UUID fe - 16-bit Eddystone UUID 0f - Length 16 - Service Data data type value aa - 16-bit Eddystone UUID fe - 16-bit Eddystone UUID 10 - Frame type -> URL 00 - TX Power (calibrated) 02 - URL Scheme 02 -> http:// 77 - 'w' 65 - 'e' 62 - 'b' 67 - 'g' 61 - 'a' 7a - 'z' 65 - 'e' 72 - 'r' 08 - 08 -> '.org' 00 - 00 - 00 - 00 - 00 - 00 - 00 - 00 - 위의 명령은 URL 'http://webgazer.org' 를 브로드캐스트 한다. 만일 다른 URL을 advertise하고 싶으면 아래 박스에 원하는 URL을 입력하면 그에 해당하는 명령을 자동으로 만들어 준다. 위의 명령을 라즈베리 파이에 입력해 주면 이제부터 라즈베리 파이가 Eddystone URL beacon으로 동작하게 된다. 아이폰에서 PhyWeb이나 안드로이드에서 크롬을 사용해 보면 링크가 뜨는걸 확인할 수 있다.
* 모든 설정이 끝났는데 안드로이드에서 비콘이 보이지 않는 경우 원인은 거의 99.9% 이상이 URL주소가 https:// 로 시작하지 않아서이다. 어떤 이유로...아마도 man in the middle attack에 대해 방어하기 위해 안드로이드의 크롬은 URL이 secure하기를 요구한다. 그러지 않으면 비콘이 발견되어도 트리거 되지 않는다.
IoT 개발자로서 마주치게 되는 가장 큰 의문은 '사용자가 어떻게 우리 디바이스를 사용할 수 있게 하지?' 이다. 최근 몇년간 이 문제를 해결하는 가장 일반적이고 표준적인 방법은 디바이스 전용 앱을 만드는 것이었다. 하지만 주변에 디바이스가 점점 많아지기 시작하면서 사용자들에게 '또 다른 전용 앱'을 설치하라고 하는게 점점 힘들어지고 있다. 물론 이건 사용자가 그곳에 당신의 디바이스가 있다는걸 알고 있다는 전제 하에서의 이야기이다...디바이스가 있는지 모른다면 저 질문 자체가 성립하지 않을것이다.
그러므로 주변에 있는 IoT 디바이스를 사용하게 하기 위한 더 편한 방법이 필요하게 된다. 단순히 디바이스를 조작하는 것 뿐 아니라 디바이스를 발견할 수 있어야만 한다. 이 두가지 문제를 해결하기 위해 구글은 안드로이드용 크롬 48부터 브라우져에 추가한 두가지 기능은 매우 흥미롭다.
먼저 Physical Web을 추가했는데 이 기능은 운영체제에 통합되어 주변의 블루투스 비콘을 보여준다. Eddystone-URL 프로토콜을 사용해 저가형 BLE 비콘을 가지고 URL을 브로드캐스트하는데 사용할 수 있게 해 주어 주변 디바이스와 상호작용을 할 수 있게 해 준다. 이것은 주차 미터의 결제 포털부터 새로운 장난감을 제어하는 웹 어플리케이션까지 다양한 곳에 적용할 수 있다. 이것만으로도 충분히 흥미롭지만 구글은 Web Bluetooth 지원도 추가해 웹 어플리케이션이 BLE 디바이스와 통신을 할 수 있게 해 주었다.
이 두 기능은 각각 그 자체로도 훌륭하지만, 두 기능을 통합하면 IoT 개발자에게 매우 유용한 기능을 제공해 준다.
1. 디바이스가 URL을 브로드캐스트 해서 주변에 있는 사용자가 디바이스를 발견할 수 있게 해 줌
2. 이 URL이 가르키는 곳에는 사용자가 디바이스를 컨트롤 할 수 있는 웹 앱이 있음
BOOM~! 이제 디바이스 발견 문제와 전용 앱 설치 문제를 해결했다. Physical world에서 디바이스를 발견하는것 부터 발견한 디바이스를 제어하는데 까지 몇초 내로 가능하다. 다음은 이 기능의 데모로 Parrot 드론을 발견하고 그것을 제어하는 비디오이다.
Eddystone beacons are non-connectable
하지만 임베디드 개발자 관점에서 코드를 작성하려고 하면 마주치게 되는 문제는 Eddystone 비콘도 '비콘'이라는 것이다. 비콘의 역할은 특정 위치에서 advertisement 패킷을 브로드캐스트 하는 것이다. 비콘 디바이스는 브로드캐스트만 수행하지 다른 디바이스가 비콘에 연결하는걸 허용하지 않는다. 그러므로 그 디바이스와 통신을 하기 위해 GATT로 연결할 수 없다. 문제를 더 복잡하게 만드는건 Eddystone 프로토콜에는 GATT 서비스 UUID 목록을 보내기 위한 공간이 없기 때문에 다른 어플리케이션이 디바이스가 가지고 있는 기능을 발견할 수 없다는 것이다. 이렇게 되면 사용성이 매우 제한되게 된다. 하지만 개발자로서 이 문제를 해결해 보자.
BLE examples repository에는 mbed OS에서 실행되는 훌륭한 Eddystone 라이브러리가 포함되어 있다. 최근 동료인 Andres Amaya Garcia가 이 라이브러리에 frame scheduling을 위한 몇가지 새로운 기능을 추가했다. 이 기능은 비콘이 URL과 telemetry 데이터를 둘 다 브로드캐스트 할 수 있도록 허용한다. 이는 한 비콘에서 서로 다른 advertisement 패키지를 보냄으로서 가능해졌다.
이 기능은 비콘이 Physical Web/Eddystone URL 비콘과, 연결할 수 있는(connectable) 블루투스 디바이스가 되길 바라는 우리들에게 딱 맞는 기능이다. 매 500ms마다 URL을 브로드캐스트 하고 그 500ms후에 일반 블루투스 프레임을 브로드캐스트 할 수 있다. 이 기능이 다음 라이브러리에 훌륭하게 포함되었다. 'eddystone' 폴더를 자신의 mbed OS 프로젝트 내의 source 폴더에 추가하고 나서 다음과 같이 라이브러리를 초기화 하면 된다.
#include "mbed-drivers/mbed.h"
#include "minar/minar.h"
#include "core-util/FunctionPointer.h"
#include "ble/BLE.h"
#include "eddystone/EddystoneService.h"
using namespace mbed::util;
// Eddystone URL
static const char defaultUrl[] = "https://control.me";
// Normal Beacon name
static char beaconName[] = "I'm a beacon!";
// Service UUIDs for the beacon
static uint16_t uuid16_list[] = { 0x8765 };
static const PowerLevels_t defaultAdvPowerLevels = {-47, -33, -21, -13};
static const PowerLevels_t radioPowerLevels = {-30, -16, -4, 4};
void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
BLE::Instance().gap().startAdvertising(); // restart advertising
}
void onBleInitError(BLE &ble, ble_error_t error)
{
(void)ble;
(void)error;
/* Initialization error handling should go here */
}
void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
{
BLE& ble = params->ble;
ble_error_t error = params->error;
if (error != BLE_ERROR_NONE) {
onBleInitError(ble, error);
return;
}
if (ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
return;
}
ble.gap().onDisconnection(disconnectionCallback);
// Set up Eddystone
auto eddyServicePtr = new EddystoneService(ble, defaultAdvPowerLevels, radioPowerLevels, 0);
eddyServicePtr->setURLData(defaultUrl);
// The name of the beacon and the service list
eddyServicePtr->setNormalFrameData(beaconName, strlen(beaconName), uuid16_list, sizeof(uuid16_list));
// Every 500 ms. Eddystone URL, then Normal frame
eddyServicePtr->setUIDFrameAdvertisingInterval(0);
eddyServicePtr->setTLMFrameAdvertisingInterval(0);
eddyServicePtr->setURLFrameAdvertisingInterval(500);
eddyServicePtr->setNormalFrameAdvertisingInterval(500);
eddyServicePtr->startBeaconService();
}
void app_start(int, char**) {
BLE::Instance().init(bleInitComplete);
}
Seeing it all come together
모든것이 동작하는지 확인하려면 안드로이드 6 버젼 이상을 사용하는 안드로이드 폰이 필요하고 그 폰에 Chrome Dev를 설치해야 한다. 설치한 후에 chrome://flags#enable-physical-web 과 chrome://flags#enable-web-bluetooth 두가지 기능을 활성화 시켜야 한다.
주소창에 'chrome://flags#enable-physical-web' 을 입력한다.
Enable the Physical Web 항목을 선택한다.
Enable the Physical Web 항목을 'Enabled'로 바꿔주면 된다.
주소창에 'chrome://flags#enable-web-bluetooth' 를 입력한다.
Web Bluetooth 항목을 'Enabled'로 바꿔주면 된다.
두 항목을 활성화 시켰으면 아래쪽의 'Relaunch Now'버튼을 눌러 크롬을 재시작 해 준다.
이제 drawer를 아래로 내리면 주변의 Physical Web 비콘과 URL 목록을 보여준다.
'Pair'를 터치하면 커넥션이 연결되고 characteristic을 읽거나 쓸 수 있게 된다.
Physical Web URL을 통해 연결할 때는 페어링 스탭은 더 이상 필요하지 않다고 생각하지만 아직까지는 이 스탭이 필요하다.
Conclusion
이제 가능성은 무한하다. 디바이스와의 적절한 BLE 커넥션을 가지고 있으니 네이티브 안드로이드 앱에서처럼 characteristic을 원하는 대로 읽고 쓸 수 있다. 다음 단계로 넘어갈 준비가 된 사람들을 위해 가속도 센서값을 블루투스를 통해 전송하고 복수의 BLE 디바이스에서의 움직임을 그래프로 그리는 웹 앱 코드를 준비 해 놓았다. 소스코드는 github에서 찾아볼 수 있다.