2016년 8월 14일 일요일

mbed OS상에서의 physical web과 web bluetooth (Physical Web and Web Bluetooth on mbed OS)

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에서 찾아볼 수 있다.













댓글 없음:

댓글 쓰기