레이블이 bluetooth low energy인 게시물을 표시합니다. 모든 게시물 표시
레이블이 bluetooth low energy인 게시물을 표시합니다. 모든 게시물 표시

2016년 11월 14일 월요일

Simblee BLE module

아두이노 등으로 작품을 만들 때 가장 골치아픈 부분중에 하나가 블루투스 통신이다.  블루투스 통신  뿐  아니고 스마트폰으로 무언가를 제어하거나 센서 값을 화면에 표시하려면 아두이노쪽 코드 뿐 아니고 안드로이드 또는 iOS  앱을 따로 만들어 줘야만 한다.
하지만 simblee는 이 문제를 다른 방법으로 접근해 간단하게 해결했다.

디바이스 코드 뿐 아니고 스마트폰에 표시될 UI  관련 코드까지 아두이노 UI에서 한번애  다 작성할 수 있게 만들어 버렸다.  즉 스마트폰 화면에 표시할 UI를 simblee에서 직접 보내주고,  스마트폰에서 이벤트가 발생(버튼 클릭 등)하면 그 이벤트에 대한 처리 코드 역시 simblee에 들어있다.



스마트폰은 일종의 웹 브라우져같이  동작해  스마트폰에서 Simblee for mobile앱을 실행시켜 놓으면 웹서버(즉 여기서는 simblee)에서 웹페이지 내용(여기서는 UI  화면)을 받아 화면에 표시해주고 사용자가 버튼이나 링크를 클릭하는 등의 이벤트가 생기면 웹 서버(여기서는 simblee)에 그 이벤트를 전달해 정해진 동작을 처리하게 한다.

그래서 아두이노 스케치에는 기본적으로 setup(), loop() 이렇게 두개의 함수가 있어야만 하는데 simblee  앱에서는 setup(), loop() 외에 추가로 ui(), ui_event() 함수가 있어야 한다.  Simblee는  자신의 정보를 advertise하고 있다가 스마트폰의 Simblee  for  mobile  앱과 연결되면 ui()  자동으로 ui()  함수가 실행되게 된다.  ui()  함수에서 아래와 같이 스마트폰 화면에 표시될 ui  정보를 보내주게 된다.

void ui()
{
  SimbleeForMobile.beginScreen(WHITE,PORTRAIT);
  SimbleeForMobile.drawSwitch(135, 100);
  SimbleeForMobile.endScreen();
}

UI  정보가 스마트폰으로 전송되면 스마트폰 앱 화면에 다음과 같이 수신된 UI가 표시된다.


사용자가 스위치를 토글하면 이벤트가 발생되어 그 이벤트 정보가 simblee로 전달된다.  Simblee는 이벤트를 받으면 자동으로 ui_event()  함수를 호출해 이벤트에 대한 처리를 수행하게 된다.

#include <SimbleeForMobile.h>

uint_8 switch;

void setup()
{
  Serial.begin(9600);
  SimbleeForMobile.deviceName = "Switch";  // SimbleeForMobile  앱에서 'Switch'라는 이름으로 검색됨
  SimbleeForMobile.begin();
}

void loop()
{
    SimbleeForMobile.process();
}

void ui()
{
  SimbleeForMobile.beginScreen(WHITE, PORTRAIT);
  switch = SimbleeForMobile.drawSwitch(135,100);
  SimbleeForMobile.endScreen();
}


void ui_event(event_t &event)
{
  if (event.id == switch) {
    Serial.println(event.value);
  }
}

위의 코드를 Simblee에 다운로드 하고 스마트폰에서 SimbleeForMobile앱을 실행해 'switch'라는 이름의디바이스를 선택하면 위와 같은 화면이 나오고 스위치를 토글하면 아래와 같이 시리얼 포트로 값이 변화하는걸  볼 수 있다.


 좌표계는 아래  그림처럼 좌측 상단이 (0, 0)이 된다.


 Simblee에 연결되는 디바이스마다 화면 크기는 달라진다.  아이폰 5의 경우 폭은 320픽셀,  높이는 570픽셀이 된다.  각 디바이스의 화면 크기는 스마트폰에 연결되면 SimbleeForMobile.screenWidth 과 SimbleeForMobile.screenHeight 에 자동으로 값이 설정된다.




2016년 8월 11일 목요일

BBC micro:bit - 영국의 코딩교육용 보드

영국의 국영방송사인 BBC에서 만든 코딩 교육용 보드로 31개 기관과의 파트너쉽으로 영국의 모든 11~12세 어린이에게 무료로 제공되었다. 파트너에는 Microsoft, Lancaster University, Farnell Element14, Nordic Semiconductor, NXP Semiconductor, ARM Holdings, Barclays, Python Software Foundation, Bluetooth SIG 등 이름만 들어도 알만한 쟁쟁한 업체들이 망라되어 있다.

가로 5cm, 세로 4cm로 일반적인 명함의 절반 크기이지만 매우 다양한 기능을 가지고 있다.


ARM Cortex M0 기반인 Nordic Semiconductor의 nRF51822 프로세서를 사용해 256KB flash memory, 16KB SRAM 뿐 아니고 BLE로 통신이 가능하다. 즉 안드로이드, 아이폰과 연동해 센서값을 보내거나, 스마트폰에서 이 보드를 직접 제어가 가능하다. 또한 USB 2.0 OTG 컨트롤러를 가지고 있어 USB로 컴퓨터와도 바로 통신이 가능하다. 센서로는 MMA8652 3축 가속도 센서와 MAG3110 3축 지자기 센서(디지털 컴파스)가 장착되어 있다.

웨어러블이나 스탠드얼론 동작을 위해 배터리 커넥터를 가지고 있어 AAA 배터리 팩을 장착해 전원을 공급해 줄 수 있다.

그리고 보드 앞면에 두개의 푸쉬버튼과 25개의 LED(5x5 array)를 가지고 있어 다양한 방법으로 사용자 입/출력이 가능하다.

Pinout은 다음과 같다.


소프트웨어 개발에는 이 보드가 처음부터 어린이들의 코딩 교육용으로 만들어 졌기 때문에 JavaScript를 사용하는 CodeKingdoms, Microsoft의 Blockly기반의 Block Editor, TouchDevelop같이 GUI기반으로 마우스 drag-n-drop 만으로 쉽게 프로그램을 만들 수 있는 툴 뿐 아니고 기존의 프로그래밍 언어 방식으로 작성할 수 있는  MicroPYthon이 있다.

* Code Kingdoms

 * Touch Develop

 * Block Editor

* MicroPython

또한 mbed-compliant이기 때문에 임베디드 환경에 익숙한 사람들은 기존의 mbed환경에서 프로그램을 개발할 수도 있다.


상세한 내용은 micro:bit 프로젝트 홈페이지(https://www.microbit.co.uk/)를 참고하면 된다.

현재 영국에서만 판매되고 있는데 온라인으로 주문하는데 해외배송에는 아무 문제가 없고 가격은 micro:bit 보드만인 경우 10.83 파운드(현재 환율로 약 14500원 정도.....브렉시트 만세~)이다.

일반적으로 아두이노에 BLE모듈을 붙이거나 mbed 중에 BLE 지원 보드를 구입하는것보다 더 저렴하게  구입할 수 있는 BLE를 지원하는 프로세서 보드로 매력적이다.

mbed BLE Button example

BLE_Button은 BLE service 템플릿이다. 단순한 boolean 값에 대한 read-only characteristic을 처리한다. 입력 소스는 보드에 있는 푸쉬버튼이다. 버튼이 눌리거나 놓을 때 characteristic의 값을 바꿔 준다.

이 코드는 다음의 기능을 다룬다.

* Advertising 및 connection 설정
* Input characteristic 만들기 : read-only, boolean with notification
* Service class 구축 및 BLE 스택에 추가하기
* 서비스와 characteristic에 UUID 할당
* Characteristic의 값이 바뀔 때 notification을 푸쉬하기

Running the application

Requirements

이 샘플 어플리케이션은 스마트폰의 어떤 BLE 스캐너에서도 볼 수 있다. 폰에 스캐너 앱이 없으면 다음의 앱을 설치하면 된다

* nRF Master Control Panel for Android
* LightBlue for iPhone

하드웨어 요구사항은 main readme 를 참고하면 된다.

* 주의: 두개 이상의 mbed board를 가지고 있다면 (nrf51dk 또는 mkit) BLE_LED 와 BLE_LEDBlinker를 동시에 실행시킬 수 있다. 더 상세한 내용은 BLE_LEDBlinker 데모를 참고하면 된다.

Build Instructions

온라인 mbed 컴파일러에서 이 예제를 빌드하고 싶으면, 먼저 오른쪽의 'Import' 버튼을 사용해 예제를 import 해야 한다.

그 다음 빌드한 코드를 실행하려는 플랫폼을 선택해 준다. 플랫폼은 예를 들어 NRF51-DK 같이 BLE를 지원하는 플랫폼이거나 또는 다음의 목록에 있는 것 중 하나여야 한다.

BLE를 지원하는 플랫폼 목록

또는 예를 들어 K64F나 NUCLEO_F401RE같은 보드에 X-NUCLEO-IDB04A1를 추가하고 그 하드웨어에 적합한 BLE 드라이버를 포함하는 지원 라이브러리를 설치해 줘야만 한다.

일단 플랫폼을 선택하고 나면 예제를 컴파일 한 후 바이너리 파일을 보드에 넣어주면 된다.


Checking for success

주의: 아래의 스크린 캡춰는 안드로이드에서 nRF Master Control Panel 버젼 4.0.5를 사용해 얻은 것이다. 버젼이 다르거나 아이폰을 사용하는 경우 버튼의 위치나 화면 레이아웃이 다를 수 있다.

* 어플리케이션을 빌드한 후 바이너리 파일을 보드에 설치
* 스마트폰에서 BLE 스캐너 앱을 실행

 * 스캔을 시작


* 디바이스를 찾는다. 'Button'이라는 이름을 가지고 있어야 한다.


 * 디바이스와 connection을 설정한다.


* 디바이스의 service와 characteristic을 검색한다. *Button service*는 UUID '0xA000'을 가지고 있고 이 서비스는 UUID가 '0xA001'인 *Button state characteristic* 를 포함하고 있다. 사용하는 스캐너에 따라 비표준 16-bit UUID는 128-bit UUID로 표시될수도 있다. 만일 이런 경우라면 다음 포맷이 사용된다.
'0000XXXX-0000-1000-8000-00805F9B34FB' 에서 'XXXX' 부분에 16-bit UUID 값이 들어간다.


* button state characteristic의 notification을 등록한다. 그러면 버튼의 상태가 바뀔 때 마다 버튼의 새 상태를 포함한 notification을 자동으로 받게 된다.


* 보드의 버튼 1을 누르면 버튼의 상태가 업데이트 되고 스캐너로 notification을 보낸다. 버튼 characteristic 값의 새 상태는 0x01 이어야 한다.


* 보드의 버튼 1을 누른걸 떼면 버튼의 상태가 업데이트 되고 스캐너로 notification을 보낸다. 버튼 characteristic 값의 새 상태는 0x00 이어야 한다.


예제의 전체 소스코드는 아래 링크에서 BLE_Button 디렉토리를 보면 된다.

https://github.com/ARMmbed/mbed-os-example-ble

핵심 코드는 아래의 두 파일(ButtonService.h, main.coo)이다.

이 예제를 템플릿으로서 약간만 수정하면 동시에 여러개의 버튼 입력 또는 아날로그 센서값을 쉽게 받아올 수 있을 것이다.

ButtonService.h


/* mbed Microcontroller Library
 * Copyright (c) 2006-2013 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __BLE_BUTTON_SERVICE_H__
#define __BLE_BUTTON_SERVICE_H__

class ButtonService {
public:
    const static uint16_t BUTTON_SERVICE_UUID              = 0xA000;
    const static uint16_t BUTTON_STATE_CHARACTERISTIC_UUID = 0xA001;

    ButtonService(BLE &_ble, bool buttonPressedInitial) :
        ble(_ble), buttonState(BUTTON_STATE_CHARACTERISTIC_UUID, &buttonPressedInitial, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY)
    {
        GattCharacteristic *charTable[] = {&buttonState};
        GattService         buttonService(ButtonService::BUTTON_SERVICE_UUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
        ble.gattServer().addService(buttonService);
    }

    void updateButtonState(bool newState) {
        ble.gattServer().write(buttonState.getValueHandle(), (uint8_t *)&newState, sizeof(bool));
    }

private:
    BLE                              &ble;
    ReadOnlyGattCharacteristic<bool>  buttonState;
};

#endif /* #ifndef __BLE_BUTTON_SERVICE_H__ */

--------------------------------------------

main.cpp

/* mbed Microcontroller Library
 * Copyright (c) 2006-2013 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <mbed-events/events.h>

#include <mbed.h>
#include "ble/BLE.h"
#include "ble/Gap.h"
#include "ButtonService.h"

DigitalOut  led1(LED1, 1);
InterruptIn button(BLE_BUTTON_PIN_NAME);

static EventQueue eventQueue(
    /* event count */ 10 * /* event size */ 32
);

const static char     DEVICE_NAME[] = "Button";
static const uint16_t uuid16_list[] = {ButtonService::BUTTON_SERVICE_UUID};

ButtonService *buttonServicePtr;

void buttonPressedCallback(void)
{
    eventQueue.post(Callback<void(bool)>(buttonServicePtr, &ButtonService::updateButtonState), true);
}

void buttonReleasedCallback(void)
{
    eventQueue.post(Callback<void(bool)>(buttonServicePtr, &ButtonService::updateButtonState), false);
}

void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
    BLE::Instance().gap().startAdvertising(); // restart advertising
}

void blinkCallback(void)
{
    led1 = !led1; /* Do blinky on LED1 to indicate system aliveness. */
}

void onBleInitError(BLE &ble, ble_error_t 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) {
        /* In case of error, forward the error handling to onBleInitError */
        onBleInitError(ble, error);
        return;
    }

    /* Ensure that it is the default instance of BLE */
    if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
        return;
    }

    ble.gap().onDisconnection(disconnectionCallback);

    button.fall(buttonPressedCallback);
    button.rise(buttonReleasedCallback);

    /* Setup primary service. */
    buttonServicePtr = new ButtonService(ble, false /* initial value for button pressed */);

    /* setup advertising */
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.gap().setAdvertisingInterval(1000); /* 1000ms. */
    ble.gap().startAdvertising();
}

void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
    BLE &ble = BLE::Instance();
    eventQueue.post(Callback<void()>(&ble, &BLE::processEvents));
}

int main()
{
    eventQueue.post_every(500, blinkCallback);

    BLE &ble = BLE::Instance();
    ble.onEventsToProcess(scheduleBleEventsProcessing);
    ble.init(bleInitComplete);

    while (true) {
        eventQueue.dispatch();
    }

    return 0;
}


mbed BLE LED example

이 예제는 폰의 앱에서 BLE를 통해 LED의 on/off를 제어하기 위해 characteristic을 읽고 쓰는 코드이다.

이 코드는 다음의 기능을 다룬다.

* Advertising 및 connection 설정
* 서비스와 characteristic에 UUID 할당
* Input characteristic 만들기 : read/write, boolean. 이 characteristic으로 LED를 제어함
* Service class 구축 및 BLE 스택에 추가하기

Running the application

Requirements

이 샘플 어플리케이션은 스마트폰의 어떤 BLE 스캐너에서도 볼 수 있다. 폰에 스캐너 앱이 없으면 다음의 앱을 설치하면 된다

* nRF Master Control Panel for Android
* LightBlue for iPhone

하드웨어 요구사항은 main readme 를 참고하면 된다.

* 주의: 두개 이상의 mbed board를 가지고 있다면 (nrf51dk 또는 mkit) BLE_LED 와 BLE_LEDBlinker를 동시에 실행시킬 수 있다. 더 상세한 내용은 BLE_LEDBlinker 데모를 참고하면 된다.

Build Instructions


온라인 mbed 컴파일러에서 이 예제를 빌드하고 싶으면, 먼저 오른쪽의 'Import' 버튼을 사용해 예제를 import 해야 한다.

그 다음 빌드한 코드를 실행하려는 플랫폼을 선택해 준다. 플랫폼은 예를 들어 NRF51-DK 같이 BLE를 지원하는 플랫폼이거나 또는 다음의 목록에 있는 것 중 하나여야 한다.

BLE를 지원하는 플랫폼 목록

또는 예를 들어 K64F나 NUCLEO_F401RE같은 보드에 X-NUCLEO-IDB04A1를 추가하고 그 하드웨어에 적합한 BLE 드라이버를 포함하는 지원 라이브러리를 설치해 줘야만 한다.

일단 플랫폼을 선택하고 나면 예제를 컴파일 한 후 바이너리 파일을 보드에 넣어주면 된다.

Checking for success

주의: 아래의 스크린 캡춰는 안드로이드에서 nRF Master Control Panel 버젼 4.0.5를 사용해 얻은 것이다. 버젼이 다르거나 아이폰을 사용하는 경우 버튼의 위치나 화면 레이아웃이 다를 수 있다.

* 어플리케이션을 빌드한 후 바이너리 파일을 보드에 설치
* 스마트폰에서 BLE 스캐너 앱을 실행

* 스캔을 시작

* 디바이스를 찾는다. 'LED'라는 이름을 가지고 있어야 한다.

* 디바이스와 connection을 설정한다.

* 디바이스의 service와 characteristic을 검색한다. *LED service*는 UUID '0xA000'을 가지고 있고 이 서비스는 UUID가 '0xA001'인 *LED state characteristic* 를 포함하고 있다.

* *LED state* characteristic의 write 패널을 연다.

* 이 characteristic은 1 바이트 값을 허용한다.

* '0x01' : LED ON


* '0x00' : LED OFF

LED characteristic 값을 토글하면서 그 값에 따라 LED가 On/Off 되는걸 확인할 수 있다.

예제의 전체 소스코드는 아래 링크에서 BLE_LED 디렉토리를 보면 된다.

https://github.com/ARMmbed/mbed-os-example-ble

핵심 코드는 아래의 두 파일(LEDService.h, main.coo)이다.

이 예제를 템플릿으로 약간만 수정하면 동시에 여러개의 LED 또는 모터를 동시에 쉽게 제어할 수 있을 것이다.

LEDService.h


/* mbed Microcontroller Library
 * Copyright (c) 2006-2013 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __BLE_LED_SERVICE_H__
#define __BLE_LED_SERVICE_H__

class LEDService {
public:
    const static uint16_t LED_SERVICE_UUID              = 0xA000;
    const static uint16_t LED_STATE_CHARACTERISTIC_UUID = 0xA001;

    LEDService(BLEDevice &_ble, bool initialValueForLEDCharacteristic) :
        ble(_ble), ledState(LED_STATE_CHARACTERISTIC_UUID, &initialValueForLEDCharacteristic)
    {
        GattCharacteristic *charTable[] = {&ledState};
        GattService         ledService(LED_SERVICE_UUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
        ble.addService(ledService);
    }

    GattAttribute::Handle_t getValueHandle() const
    {
        return ledState.getValueHandle();
    }

private:
    BLEDevice                         &ble;
    ReadWriteGattCharacteristic<bool> ledState;
};

#endif /* #ifndef __BLE_LED_SERVICE_H__ */ 
 
------------------------------------------------------

main.cpp

 
/* mbed Microcontroller Library
 * Copyright (c) 2006-2013 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <mbed-events/events.h>
#include <mbed.h>
#include "ble/BLE.h"
#include "LEDService.h"

DigitalOut alivenessLED(LED1, 0);
DigitalOut actuatedLED(LED2, 0);

const static char     DEVICE_NAME[] = "LED";
static const uint16_t uuid16_list[] = {LEDService::LED_SERVICE_UUID};

static EventQueue eventQueue(
    /* event count */ 10 * /* event size */ 32
);

LEDService *ledServicePtr;

void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
    (void) params;
    BLE::Instance().gap().startAdvertising();
}

void blinkCallback(void)
{
    alivenessLED = !alivenessLED; /* Do blinky on LED1 to indicate system aliveness. */
}

/**
 * This callback allows the LEDService to receive updates to the ledState Characteristic.
 *
 * @param[in] params
 *     Information about the characterisitc being updated.
 */
void onDataWrittenCallback(const GattWriteCallbackParams *params) {
    if ((params->handle == ledServicePtr->getValueHandle()) && (params->len == 1)) {
        actuatedLED = *(params->data);
    }
}

/**
 * This function is called when the ble initialization process has failled
 */
void onBleInitError(BLE &ble, ble_error_t error)
{
    /* Initialization error handling should go here */
}

/**
 * Callback triggered when the ble initialization process has finished
 */
void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
{
    BLE&        ble   = params->ble;
    ble_error_t error = params->error;

    if (error != BLE_ERROR_NONE) {
        /* In case of error, forward the error handling to onBleInitError */
        onBleInitError(ble, error);
        return;
    }

    /* Ensure that it is the default instance of BLE */
    if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
        return;
    }

    ble.gap().onDisconnection(disconnectionCallback);
    ble.gattServer().onDataWritten(onDataWrittenCallback);

    bool initialValueForLEDCharacteristic = false;
    ledServicePtr = new LEDService(ble, initialValueForLEDCharacteristic);

    /* setup advertising */
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.gap().setAdvertisingInterval(1000); /* 1000ms. */
    ble.gap().startAdvertising();
}

void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
    BLE &ble = BLE::Instance();
    eventQueue.post(Callback<void()>(&ble, &BLE::processEvents));
}

int main()
{
    eventQueue.post_every(500, blinkCallback);

    BLE &ble = BLE::Instance();
    ble.onEventsToProcess(scheduleBleEventsProcessing);
    ble.init(bleInitComplete);

    while (true) {
        eventQueue.dispatch();
    }

    return 0;
} 
 


2016년 5월 12일 목요일

라즈베리 파이를 비콘으로 사용하기 (Using Raspberry Pi as a Beacon)

독립적으로 비콘 모듈을 사용할 수도 있지만 이미 라즈베리 파이를 가지고 있다면 별도의 비콘 모듈 없이 라즈베리 파이를 비콘으로 설정해 사용할 수 있다.


라즈베리 파이 3의 경우 무선랜/블루투스가 기본 내장되어 있어 별도의 추가 하드웨어가 필요 없어 더 경제적이다. 라즈베리 파이 1/2의 경우는 USB타입의 블루투스 동글을 추가해 주면 된다.


 비콘은 특별한 것이 아니고 BLE(Bluetooth Low Energy)의 advertising packet 안에 옵션 필드인 Manufacturer Specific Data 에 특정 값을 집어넣어 주기적으로 advertising 하는 것이다. 그러므로 이 필드에 들어갈 값을 알아야 한다.

필드의 구조는 다음과 같다.

  • ID (uint8_t) : 이 값은 항상 0x02
  • Data Len (uint8_t) : payload 나머지 부분의 길이 → 0x15 (21 bytes)
  • 128-bit UUID (uint8_t[16]) : 128-bit 식별자
  • Major (uint16_t) : Major value
  • Minor (uint16_t) : Minor value
  • TX Power (uint8_t) : RSSI 기반으로 거리를 추정하기 위해 사용됨
 예를 들어 다음은 유효한 iBeacon payload의 값이다.


블루투스 표준에 의하면 Company Identifier가 Manufacturer Specific Data 필드 앞에 와야만 한다. 여기서 사용할 Apple의 company identifier는 0x004C 이다.

라즈베리 파이가 비콘 데이터를 보내게 하려면 몇가지 툴을 설치해 줘야만 한다. 여기서 핵심은 Bluez(리눅스 블루투스 스택)이고 부가적으로 libusb와 몇개의 helper library가 필요하다.

순서대로 필요한 툴들을 설치, bluez를 다운받아 컴파일 및 설치 해 주면 된다.

pi@raspberrypi ~ $ sudo apt-get install libusb-dev libdbus-1-dev libglib2.0-dev libudev-dev libical-dev libreadline-def

pi@raspberrypi ~ $ sudo mkdir bluez
pi@raspberrypi ~ $ cd bluez
pi@raspberrypi ~ $ sudo wget www.kernel.org/pub/linux/bluetooth/bluez-5.11.tar.xz
 
pi@raspberrypi ~ $ sudo unzx bluez-5.11.tar.xz 
pi@raspberrypi ~ $ sudo tar xvf bluez-5.11.tar
pi@raspberrypi ~ $ cd bluez-5.11
pi@raspberrypi ~ $ sudo ./configure --disable-systemd
pi@raspberrypi ~ $ sudo make
pi@raspberrypi ~ $ sudo make install



설치가 완료되었으면 블루투스 동글을 꼽고 재부팅 시켜주면 된다.

재부팅이 완료되면 먼저 시스템에 설치된 디바이스 목록을 확인한다.


pi@raspberrypi ~ $ hciconfig
hci0:  Type: BR/EDR    Bus: USB
       BD Address: 00:1A:7D:DA:71:12 ACL MTU: 310:10 SCO MTU: 64:8
       DOWN
       RX bytes: 495 acl:0 sco:0 events:21 errors: 0
       TX bytes: 89 acl:0 sco:0 commands: 21 errors: 0
pi@raspberrypi ~ $

만일 모든것이 정상적으로 설정되어 있으면 위와 같이 hci 디바이스가 보이게 된다. 이제 다음 명령으로 디바이스를 활성화 시켜 준다. 또한 advertising을 할 때 문제를 일으킬 수도 있으므로 device scanning은 꺼 준다.

pi@raspberrypi ~ $ sudo hciconfig hci0 up 
pi@raspberrypi ~ $ sudo hciconfig hci0 leadv3
pi@raspberrypi ~ $ sudo hciconfig hci0 noscan

이제 다시 hciconfig 명령을 수행해 보면 'DOWN' 대신 'UP  RUNNING'이 보여야 한다.

pi@raspberrypi ~ $ hciconfig
hci0:  Type: BR/EDR    Bus: USB
       BD Address: 00:1A:7D:DA:71:12 ACL MTU: 310:10 SCO MTU: 64:8
       UP RUNNING
       RX bytes: 1008 acl:0 sco:0 events:45 errors: 0
       TX bytes: 236 acl:0 sco:0 commands: 45 errors: 0
pi@raspberrypi ~ $

이제 마지막으로 해 줘야 할 일은 advertising data에 beacon 값을 넣어주는 것이다. 다음과 같이 명령을 입력해 준다. (좀 길지만 한 줄에 다 입력해 준다.)


pi@raspberrypi ~ $ sudo tools/hcitool -i hci0 cmd 0x08 0x0008 1E 02 01 1A 1A 
FF 4C 00 02 15 E2 0A 39 F4 73 F5 4B C4 A1 2F 17 D1 AD 07 A9 61 00 00 00 00 C8 00

위의 값들 중에서 FF는 Manufacturer Specific Data의 시작을 알려 주고, 4C 00은 Apple의 Company ID(0x004C), 그리고 나머지 부분부터 C8까지가 beacon의 payload이다.

이제 라즈베리 파이는 비콘으로 동작하게 된다. 확인해 보려면 iOS7 이후 버젼을 OS를 사용하는 아이폰/아이패드/아이팟 터치 또는 안드로이드 폰에 beacon toolkit 또는 유사한 앱을 설치해 실행하면 된다.

앱을 시작해 listen 모드로 들어가면 아래와 유사한 화면을 볼 수 있을 것이다.



이제 동작하는건 확인했지만 라즈베리 파이를 부팅할 때 마다 매번 위의 명령을 입력하기 힘드니 스크립트를 사용하는것이 편하다.

pi@raspberrypi ~ $ cat /home/pi/beacon.sh

#!/bin/bash
# ref: http://www.theregister.co.uk/Print/2013/11/29/feature_diy_apple_ibeacons/
set -x
# inquiry local bluetooth device
#hcitool dev
export BLUETOOTH_DEVICE=hci0
#sudo hcitool -i hcix cmd <OGF> <OCF> <No. Significant Data Octets> <iBeacon Prefix> <UUID> <Major> <Minor> <Tx Power> <Placeholder Octets>

#OGF = Operation Group Field = Bluetooth Command Group = 0x08
#OCF = Operation Command Field = HCI_LE_Set_Advertising_Data = 0x0008
#No. Significant Data Octets (Max of 31) = 1E (Decimal 30)
#iBeacon Prefix (Always Fixed) = 02 01 1A 1A FF 4C 00 02 15

export OGF="0x08"
export OCF="0x0008"
export IBEACONPROFIX="1E 02 01 1A 1A FF 4C 00 02 15"

#uuidgen  could gerenate uuid
export UUID="4a 4e ce 60 7e b0 11 e4 b4 a9 08 00 20 0c 9a 66"

export MAJOR="00 02"
export MINOR="00 01"
export POWER="C5 00"


# initialize device
sudo hciconfig $BLUETOOTH_DEVICE up
# disable advertising
sudo hciconfig $BLUETOOTH_DEVICE noleadv
# stop the dongle looking for other Bluetooth devices
sudo hciconfig $BLUETOOTH_DEVICE noscan

sudo hciconfig $BLUETOOTH_DEVICE pscan


sudo hciconfig $BLUETOOTH_DEVICE leadv

# advertise
sudo hcitool -i $BLUETOOTH_DEVICE cmd 0x08 0x0008 $IBEACONPROFIX $UUID $MAJOR $MINOR $POWER
sudo hcitool -i $BLUETOOTH_DEVICE cmd 0x08 0x0006 A0 00 A0 00 00 00 00 00 00 00 00 00 00 07 00
sudo hcitool -i $BLUETOOTH_DEVICE cmd 0x08 0x000a 01

echo "complete"


pi@raspberrypi ~ $

/home/pi/beacon.sh 라는 파일을 만들어 위의 파란색 부분의 내용을 넣어주면 된다. 위의 스크립트 중에 hcitool cmd 0x08 0x0006 a0 00 a0 00 .. 에서 첫번째/두번째 바이트 (0xa0 0x00)은 minimum interval, 세번째/네번째 바이트(0xa0 0x00)은 maximum interval이다. 0xa0 0x00은 0x00a0 (big endian)이 되므로 10진수로는 160이 된다. BLE에서 시간 granularity는 0.625ms이므로 최소/최대 인터벌은 모두 160 * 0.625ms = 100ms가 된다. 즉 매 0.1초마다 한번씩 비콘 신호를 advertising하게 된다.

파일을 만들었으면 chmod 명령으로 실행 퍼미션을 추가해 준다.

pi@raspberrypi ~ $ chmod +x beacon.sh

이제부터는 ./beacon.sh 만 입력해 주면 라즈베리 파이가 비콘으로 동작한다.

pi@raspberrypi ~ $ ./beacon.sh
+ export BLUETOOTH_DEVICE=hci0
+ BLUETOOTH_DEVICE=hci0
+ export OGF=0x08
+ OGF=0x08
+ export OCF=0x0008
+ OCF=0x0008
+ export 'IBEACONPROFIX=1E 02 01 1A 1A FF 4C 00 02 15'
+ IBEACONPROFIX='1E 02 01 1A 1A FF 4C 00 02 15'
+ export 'UUID=4a 4e ce 60 7e b0 11 e4 b4 a9 08 00 20 0c 9a 66'
+ UUID='4a 4e ce 60 7e b0 11 e4 b4 a9 08 00 20 0c 9a 66'
+ export 'MAJOR=00 01'
+ MAJOR='00 01'
+ export 'MINOR=00 00'
+ MINOR='00 00'
+ export 'POWER=C5 00'
+ POWER='C5 00'
+ sudo hciconfig hci0 up
+ sudo hciconfig hci0 noleadv
+ sudo hciconfig hci0 noscan
+ sudo hciconfig hci0 pscan
+ sudo hciconfig hci0 leadv
+ sudo hcitool -i hci0 cmd 0x08 0x0008 1E 02 01 1A 1A FF 4C 00 02 15 4a 4e ce 60 7e b0 11 e4 b4 a9 08 00 20 0c 9a 66 00 01 00 00 C5 00
< HCI Command: ogf 0x08, ocf 0x0008, plen 32
  1E 02 01 1A 1A FF 4C 00 02 15 4A 4E CE 60 7E B0 11 E4 B4 A9
  08 00 20 0C 9A 66 00 01 00 00 C5 00
> HCI Event: 0x0e plen 4
  01 08 20 00
+ sudo hcitool -i hci0 cmd 0x08 0x0006 A0 00 A0 00 00 00 00 00 00 00 00 00 00 07 00
< HCI Command: ogf 0x08, ocf 0x0006, plen 15
  A0 00 A0 00 00 00 00 00 00 00 00 00 00 07 00
> HCI Event: 0x0e plen 4
  01 06 20 0C
+ sudo hcitool -i hci0 cmd 0x08 0x000a 01
< HCI Command: ogf 0x08, ocf 0x000a, plen 1
  01
> HCI Event: 0x0e plen 4
  01 0A 20 0C
+ echo complete
complete 

pi@raspberrypi ~ $

이제 라즈베리 파이를 비콘으로 사용하려면 부팅될 때 마다 자동으로 위의 스크립트가 실행되도록 설정해 주면 된다.

라즈베리 파이에서 특정 프로그램이 부팅될 때 실행되게 하는 방법은 몇가지가 있지만 /etc/rc.local을 수정하는 방법 또는 cron을 사용하는 방법은 문제가 있다. 라즈베리 파이에서 블루투스가 활성화 된 이후에 위의 스크립트가 실행되어야만 하는데 두가지 방법은 스크립트가 먼저 실행되어 버리기 때문에 비콘으로 동작하지 못한다.

그러므로 systemd를 사용해 준다. 먼저 /lib/systemd/system 디렉토리에 두개의 파일을 만들어 준다. 각각 파일의 내용은 아래와 같다.

pi@raspberrypi ~ $ cat /lib/systemd/system/beacon.service
[Unit]
Description=Beacon service
After=bluetooth.target

[Service]
Type=forking
ExecStart=/home/pi/beacon.sh
ExecReload=/home/pi/beacon.sh

[Install]
WantedBy=bluetooth.target


pi@raspberrypi ~ $ cat /lib/systemd/system/beacon.target
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Beacon
Documentation=man:systemd.special(7)
StopWhenUnneeded=yes


pi@raspberrypi ~ $ sudo systemctl daemon-reload
pi@raspberrypi ~ $ sudo systemctl enable beacon.service
Synchronizing state for beacon.service with sysvinit using update-rc.d...
Executing /usr/sbin/update-rc.d beacon defaults
Executing /usr/sbin/update-rc.d beacon enable


pi@raspberrypi ~ $

이제 시스템을 리부팅 하면 항상 자동으로 비콘이 시작된다.





아이폰에서 Beecon 앱으로 확인해 본 결과이다. 스크립트에서 설정한 UUID(4a4e......9a66) , Major(2), Minor(1) 를 확인할 수 있다.






2015년 2월 5일 목요일

Blue Basic...CC2540/2541을 쉽게 사용하기

Bluetooth Low Energy (BLE)에서 가장 많이 사용되는 칩들중에 하나가 TI의 CC2540/2541이다. 이 SoC에는 BLE관련 회로 뿐 아니고 8051 코어도 같이 들어있어 단순히 BLE 통신용 모듈로서만 사용하는게 아니고 추가로 자신의 어플리케이션 코드를 집어넣어 별도의 추가 프로세서 없이 standalone으로 사용할수도 있다. 


개발을 위한 툴들도 거의 다 무료로 구할 수 있지만 문제는 개발을 위해 공부해야 하는 것이 상당히 많다는 것이다. 



그래서 이 CC2540/2541 환경에서 개발을 쉽게 할 수 있도록 나온것이 Blue Basic이다. Blue Basic은 CC2540/2541에서 실행되는 BASIC 인터프리터로 별도의 추가 툴 없이 쉽게 프로그램을 만들 수가 있다. 

기본적으로 BASIC 언어를 사용하지만 실제 프로그램을 만들려면 블루투스 스택 뿐 아니고 CC254x 칩의 하드웨어들(UART, SPI, I2C, 타이머 등)을 쉽게 사용할 수 있는게 필수적이다. 그래서 이 목적을 위해 BASIC언어를 약간 확장했고, 가능한 최대로 아두이노의 표기를 따르려고 노력했다. 예를들어 P0(0)을 입력으로 사용하려고 하면 아두이노에서 'pinMode(A0, INPUT)' 을 사용하는것 처럼 BASIC 명령 'PINMODE P(0) INPUT' 을 사용하면 된다. 블루투스 키워드는 쉽게 서비스를 만들어 다른 하드웨어에 쉽게 연결할 수 있게 되어 있다. 

간단한 서비스는 다음과 같이 만들 수 있다.

10 ADVERT GENERAL
20 ADVERT "25FB9E91-1616-448D-B5A3-F70A64BDA73A"
30 ADVERT END
40 GATT SERVICE "25FB9E91-1616-448D-B5A3-F70A64BDA73A"
50 GATT CHARACTERISTIC "D8ABBBE7-F10B-4EC3-B781-DBCBD2334400" "On/Off"
60 GATT READ WRITE A ONWRITE GOSUB 200
70 GATT END
80 PINMODE P0(0) OUTPUT
90 GOSUB 200
100 END
200 P0(0) = A
210 RETURN
BASIC을 처음 보는 사람이라고 해도 대강의 내용은 이해가 될 것이다. 물론 설명해야 할 부분은 많이 있지만 기본적으로 한개의 컨트롤을 on/off하는 서비스를 advertise하고, 이 서비스 값을 출력해 외부 장치를 제어한다. 예제 보드에서는 이 출력이 LED에 연결되어 있다.

Blue Basic을 사용하려면 CC254x를 사용한 BLE 모듈의 펌웨어를 교체해야 한다. 모듈은 aliexpress에서 $5~7정도에 구할 수 있고, 펌웨어를 업데이트 하기 위해서는 TI CC Debugger가 필요하다. 


일단 Blue Basic 펌웨어가 올라가면 그 이후는 PC/Mac/iPhone/Android용 콘솔 프로그램을 사용해 코드를 입력하면서 바로 실행시켜 볼 수가 있다.



Blue Basic을 이용해 iBeacon을 만드는 코드 예제이다.

10 // "A Simple iBeacon service"
20 DIM M(5)
30 // "Major (0-65535)"
40 M(0) = 0
41 M(1) = 2
50 // "Minor (0-65535)"
60 M(2) = 0
61 M(3) = 4
70 // "Power"
80 M(4) = 0xC8
90 ADVERT GENERAL
100 ADVERT CUSTOM "FF 4C 00 02 15" "74 27 8B DA B6 44 45 20 8F 0C 72 0E AF 05 99 35" M "00"
110 ADVERT END
120 // SET A NAME IN THE SCAN RESPONSE
130 SCAN NAME "iBeacon"
140 SCAN END
AUTORUN ON

좀 더 상세한 설명은 github를 참조하면 된다.


2015년 1월 30일 금요일

HM-10 iBeacon 제작

이전 포스트(HM-10 Bluetooth 모듈을 iBeacon으로 사용하기)에서 소개한대로 CR2032 배터리를 사용해 소형 iBeacon 모듈을 제작해 보았다.



만능기판 위에 HM-10 모듈을 약간 두께가 있는 양면테입으로 붙여주고 뒷면에 배터리 홀더를 붙여 주었다. 위쪽 사진 우측 상단의 커넥터는 모듈에 명령을 주기 위해 USB-to-Serial 컨버터를 연결하기 위한 것이고 설정이 끝나면 케이블은 분리하고 모듈 자체적으로 동작하게 된다.


제작한 iBeacon의 크기 비교이다. 위의 사진처럼 500원짜리 동전보다 조금 큰 정도의 크기이다. 이렇게 만든 경우 배터리 한개로 대략 55일 정도 동작하게 된다.

커넥터를 연결하고 컴퓨터에서 시리얼 터미널 에뮬레이터를 동작시켜 명령을 내려준다. (통신 파라미터는 9600-N81)


이전 포스트에서 이야기 했던것처럼 명령어 입력이 끝난걸 엔터키로 확인하는게 아니고 타이밍을 가지고 하기 때문에 위와 같이 줄바꿈이 되지 않고 명령어, 응답이 계속 붙어 보이게 된다.

AT+VERR?HMSoft V533AT+RENEWOK+RENEWAT+RESETOK+RESETATOKAT+MARJ0x1234OK+Set:0x1234AT+MINO0xFA01OK+Set:0xFA01AT+ADVI5OK+Set:5AT+NAMEARSVIATOROK+Set:ARSVIATORAT+ADTY3OK+Set:3AT+IBEA1OK+Set:1AT+DELO2OK+DELO2AT+PWRM0OK+Set:0AT+RESETOK+RESET

쉽게 볼 수 있게 색깔로 구분해 놓은 것이다. 빨간색 볼드는 입력한 명령어, 파란색 이태릭체는 모듈의 응답이다. 

명령 입력이 끝났으면 이제 케이블을 분리해도 된다. 

실제로 정상 동작하는지 확인해 보겠다.



 아이폰에서 Locate 앱을 실행시켜 본 결과이다. 디폴트 UUID는 변경하지 않았으므로 Apple AirLocate로 보인다. 모듈에 설정해 준 대로의 Major 0x1234 (십진수로 4660), Minor 0xFA01 (십진수로 64001)를 확인할 수 있다.


Apple AirLocate.... 부분을 클릭하면 상세정보를 볼 수 있다. 


2015년 1월 28일 수요일

HM-10 Bluetooth 모듈을 iBeacon으로 사용하기

Aliexpress등에서 매우 저렴하게 구입할 수 있는 Bluetooth 4.0 Low Energy (BLE) 모듈인 HM-10을 iBeacon으로 사용할 수 있다. 물론 HM-10은 데이터 통신 또는 단독 센서, 디바이스 제어등의 용도로 사용할수도 있지만 여기서는 그것들에 대한 설명은 생략한다. 

HM-10은 TI의 CC2540 또는 CC2541 BLE SoC 기반의 Bluetooth 4.0 모듈이다. 개당 약$6~7 정도의 가격이다. 펌웨어 버젼은 2014년 9월 기준으로 펌웨어 버젼 531이 나와있다.



모듈은 위의 사진에서 볼 수 있는것처럼 매우 작다. 접촉패드 간격은 1.5mm이라 일반 브레드보드나 만능기판등에서 바로 사용하기는 힘들다. 전원에 연결하거나, 모듈을 iBeacon으로 설정하기 위해 PC에 연결된 USB-to-Serial 케이블에 납땜을 할 때 매우 주의해야만 한다. HM-10은 2.0~3.7V에서 동작하고 I/O핀은 3.3V tolerant이다.

Hardware Connections


모듈에 전원을 공급하기 위해 두가닥의 선이 필요하고, USB-to-Serial adapter를 통해 모듈을 설정하기 위해 임시로 3개의 선이 필요하다. 



위의 사진은 모듈이 3개의 NiMH 충전 배터리가 들어있는 배터리 팩(1.2V * 3 = 3.6V) 과 USB-to-Serial adapter에 연결되어 있는걸 보여준다. Arduino를 사용하고 있다면 아마도 이런 류의 adapter를 이미 가지고 있을것이고, 그렇지 않다면 약 $15정도로 구입할 수 있을 것이다. 만일 구입해야 한다면 3.3V/5V 점퍼나 선택 스위치가 있는 것으로 구입하길 권한다. 



HM-10은 평균적으로 약 0.18mA의 전류를 사용한다. 사진의 배터리팩은 약 1800mAh의 용량을 가지고 있으므로 약 10000시간(즉 1년 이상) 지속될 수 있다. 만일 매우 작은 iBeacon을 만든다면 240mAh 용량의 CR2032 코인셀을 사용할 수도 있고, 이 경우 약 55일정도 사용할 수 있다. 



위의 다이어그램은 케이브블을 어떻게 연결해야 하는지 보여준다. 배터리는 모듈의 12(+), 13(-)번 핀에 연결되어 있다. RS232 adapter의 RxD는 1번핀에, TxD는 2번핀에 연결한다. Adapter의 그라운드 핀은 모듈의 14번 핀에 연결한다. 

Using a Terminal Emulator to talk to the HM-10


하드웨어가 준비되었으면 HM-10이 동작하는지 확인할 수 있다. 배터리와 serial adapter가 HM-10에 연결되었으면 adapter의 USB케이블을 PC에 연결한다. 처음으로 adapter를 사용하는 것이면 adapter의 디바이스 드라이버를 설치하도록 요구받을 수도 있다. 대부분의 경우 OS가 자동으로 드라이버를 찾아준다. 

HM-10은 AT 명령셋을 사용하는데 매우 특이한 타이밍을 요구한다. 명령은 CR이나 LF로 끝나는것이 아니고, 명령이 완전히 다 입력된 다음의 매우 짧은 딜레이에 의존한다. 그러므로 arduino 소프트웨어의 시리얼 모니터를 이용하기를 권장한다. 이 글을 읽는 대다수는 arduino IDE가 이미 설치되어 있을것이라고 생각한다.



Tools 메뉴에서 Serial Port를 선택하고 거기에서 자신의 serial adapter에 해당하는 COM 포트를 선택한다. 대부분의 경우 가장 아래쪽의 COM포트이다. 위의 스크린샷에서는 COM3이다. 

그 다음에 Tools 메뉴에서 Serial Monitor를 실행한다. 아래 메뉴에서 “9600 Baud”와 “No line ending”을 선택한다. 이제 HM-10과 이야기 할 수 있다. “AT” 뒤에 엔터키를 타이핑 해 본다. HM-10이 “OK”라고 응답해야 한다. HM-10은 출력하는 내용 뒤에 LF로 끝나지 않기 때문에 모든 응답 메시지는 뒤에 계속 추가된다.



다음으로 HM-10의 펌웨어 버젼을 확인한다. “AT+VERR?”를 입력한다. 최근에 HM-10을 구입했으면 버젼은 526 또는 그 이후 버젼일것이다. 그렇다면 아래의 Firmware Update 부분은 건너 뛰고 iBeacon Configuration을 가도 된다.

Firmware update

펌웨어 업데이트가 필요하면, 먼저 JNHuaMao 사이트에서 적합한 버젼을 다운로드 받아야 한다. 자신이 가지고 있는 HM-10 모듈이 어떤 칩(2540인지 2541인지)을 사용하는가를 확인한다.  초기모듈은 CC2540을 사용했고 신형 모듈은 CC2541을 사용한다. (최근에 구입했다면 대부분은 CC2541을 사용했을 것이다.)


위의 링크에서 펌웨어를 다운받으면 된다. 다운받았으면 압축을 해제해준다. 최소 3개의 파일이 있을 것이다. readme.txt 파일에는 업데이트 하는 설명이 들어있다. .bin으로 끝나는 파일이 업데이트 할 실제 펌웨어이다. 그리고 .exe로 끝나는 실행파일은 펌웨어를 업데이트 해 주는 프로그램이다. 

1. Arduino 시리얼 모니터에서 ‘AT+SBLUP’를 입력해 준다. 그러면 ‘OK+SBLUP’라고 응답할 것이다. 이제 HM-10은 펌웨어 업데이트를 할 준비가 된 것이다.
2. 시리얼 모니터를 종료한다.
3. 펌웨어 업데이트 프로그램을 실행한다. 
4. COM 포트에 HM-10이 연결되어 있는 시리얼 포트 번호를 설정한다.
5. ‘…’ 버튼을 클릭해 다운로드 한 .bin 파일을 선택한다.
6. 마지막으로 ‘Load Image’ 버튼을 클릭한다.
7. 펌웨어 업데이트는 몇분이 걸린다. 업데이트 하는 도중에는 중단하면 안된다. 
8. Verification이 끝나면 ‘Download completed successfully’라는 메시지가 나올 것이다.



이제 업데이트가 끝났으니 펌웨어 업데이트 프로그램을 종료하면 된다.

iBeacon Configuration


HM-10을 iBeacon으로 설정하려면 몇가지 명령을 입력해 줘야 한다. Arduino Serial Monitor 프로그램을 사용해 명령을 입력해 준다. 아래 리스트에서 볼드로 되어 있는 부분이 실제 입력해줘야 하는 명령이고 그 뒷부분은 각 명령이 어떤 동작을 하는지에 대한 설명으로 입력하면 안된다. 각 명령은 “OK …”으로 ack 되어야 한다. 

1.  AT+RENEW       Factory default로 복구
2.  AT+RESET       Reboot
3.  AT             OK를 기다림
4.  AT+MARJ0X1234  Major no.를 0x1234로 설정
5.  AT+MINO0XFA01  Minor no.를 0xfa01로 설정
6.  AT+ADVI5       Advertising interval을 5(546.25 ms)로 설정
7.  AT+NAMEDOPEY   HM-10 모듈 이름을 DOPEY로 설정
8.  AT+ADTY3       non-connectable로 설정 (전원 절약)
9.  AT+IBEA1       iBeacon 모드 활성화
10. AT+DELO2       iBeacon broadcast-only로 설정 (전원 절약)
11. AT+PWRM0       Auto-sleep 활성화. 8에서 0.18mA로 전력소비를 줄여줌
12. AT+RESET       Reboot

위의 명령들을 다 입력했다면 HM-10은 아이폰이나 안드로이드에서 iBeacon으로 보여야 한다. 4~5단계에서 적절한 major와 minor no.를 선택할 수 있다. Major no.는 특정 범위(스토어 또는 빌딩)내에서 동일하고, minor no.가 iBeacon을 유일하게 식별한다. 위의 프로시져는 HM-10의 디폴트 UUID(표준 proximity UUID)는 변경하지 않는다. UUID를 변경하고 싶으면 AT+IBE0, AT+IBE1, AT+IBE2, AT+IBE3 명령을 사용할 수 있다. 16버이트 UUID는 4바이트 단위로 나뉘어 각각은 다른 명령으로 바꿀 수 있다. 아래 테이블은 UUID를 4개의 명령으로 각각 어느 부분을 변경할 수 있는지 보여준다. 



Removing the Programming Cable


HM-10이 정상적으로 iBeacon으로 동작하는게 확인되었으면 serial adapter에 납땜질 된 케이블을 제거하고, 전원 케이블만 연결해 놓으면 된다. 



배터리 팩과 모듈을 보호하기 위해 케이스에 집어넣어 준다.



Simple iBeacon Scan

아이폰이나 안드로이드 폰에서 iBeacon 스캔을 할 수 있는 앱을 실행시켜 본 결과이다. 위와 같이 정상 동작하고 있음을 확인할 수 있다. 단 iBeacon과의 거리는 RSSI값을 가지고 계산한 것이기 때문에 상대적인 거리이지 정확한 거리는 아님을 주의해야 한다. 

Using the HM-10 as an iBeacon Proximity Device


다음의 8개 UUID는 Apple AirLocate에 proximity device로 들어있다. AirLocate를 사용하는 iOS app과 호환성을 보장하기 위해 이 값을 사용한다.




Waking Up the HM-10


HM-10이 auto-sleep 모드로 설정되어 있으면 전원이 들어오거나 리부팅 될 때 빨리 sleep모드로 갈 수 있다. AT+PWRM0 명령을 보낸 뒤에 재시작하면 MH-10은 더이상 AT 명령에 응답하지 않는다. 디바이스를 깨우려면 80글자 이상의 랜덤 알파벳을 보내면 된다. 그러면 모듈이 깨어나 OK+WAKE라고 응답한다. 한동안 디바이스에 명령을 내려야 하면 잠들지 않게 하기 위해 먼저 AT+PWRM1 명령을 보내준다. 명령이 끝나면 전력소비를 줄이기 위해 AT+PWRM0을 보내면 된다. 


To be continued....   HM-10 iBeacon 제작