BLE to Ethernet Thin Gateway 만들기

2016. 11. 24. 14:042018년 이전 관심사/IoT

반응형

본 포스팅에서는 BLE(Bluetooth Low Energy)로 수집된 데이터를 Ethernet을 통해 Cloud Server에 전달하기 위한 Data Collector를 구현하는 방법에 대해 설명한다.

시스템 구성

본 시스템은 아래 그림과 같이 Data Sender, Data Collector, Cloud Server로 구성되어 있다. Data Sender는 Heart Rate(심장박동)과 Battery Level을 측정하여 Data Collector로 전달하는 역할을 한다.

Data Sender의 Software는 Nordic에서 제공하는 ble_app_hrs_s110 예제를 변경 없이 사용하였으며 측정되는 Heart Rate와 Battery Level은 가상으로 만든 자료 이다.

Data Collector는 Data Sender가 송신하는 데이터를 수집하고 수집된 데이터를 Cloud Service(본 예제에서는 dweet.io를 사용함)에 전달하는 역할을 한다. Data Collector의 Software는 Nordic에서 제공하는 ble_app_hrs_c_s120 예제를 기반으로 W5500을 사용하기 위한 코드와 Cloud Service에 데이터를 송신하는 코드를 추가 하였다.

Cloud Service는 dweet.io를 이용하였으며, dweet.io에 대한 자세한 내용은 아래 링크를 참고 하기 바란다.
http://dweet.io/

20160111_162525

하드웨어 구성

Data Sender

Data Collector

Debugging Log를 확인 하기 위한 UART to USB Module과 Ethernet을 사용하기 위한 WIZ550io를 아래 그림과 같이 연결한다.

20160111_182139

nRF51 DK Pin WIZ550io Pin
P0.01 J1.3 MOSI
P0.03 J1.4 MISO
P0.04 J1.5 SCLK
P0.02 J1.6 SCSn
VDD J1.7 VDD(3.3V)
GND J1.1 GND

소프트웨어 구성

Download nRF51 SDK

https://developer.nordicsemi.com/nRF5_SDK/nRF51_SDK_v10.x.x/
nRF51 SDK 개발환경 설정에 대한 자세한 내용은 링크를 참고 하기 바란다.

Github Repository에는 Data Sender와 Data Collector에 대한 Eclipse Project 및 소스 파일들이 공유 되어 있다. Github Repository에서 nRF51_SDK10.0.0_Example을 다운로드 한 후,해당 파일들을 아래 경로로 복사 한다.

ble_app_hrs : <SDK>/examples/ble_peripheral
ble_app_hrs_c_with_W5500 : <SDK>/examples/ble_central

Data Sender

(1) 개발 환경 설정

Import Existing Project

  • [import]-[Existing Projects into Workspace]
  • Select root directory
<SDK>/examples/ble_peripheral/ble_app_hrs/pca10028/s110/armgcc

(2) Upload SoftDevice

20160112_105834

(3) Upload Application

20160112_105849

Data Collector

(1) 개발 환경 설정

Import Existing Project

  • [import]-[Existing Projects into Workspace]
  • Select root directory
<SDK>/examples/ble_central/ble_app_hrs_c_with_W5500/pca10028/s120/armgcc

(2) Upload SoftDevice

20160112_105310

(3) Upload Application

20160112_105337

주요 코드 설명(Data Collector)

#define PUBLISH_INTERVAL                 APP_TIMER_TICKS(300, APP_TIMER_PRESCALER)

int main(void)
{
    bool erase_bonds;
    uint32_t err_code;

    // Initialize.
    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, NULL);

    buttons_leds_init(&erase_bonds);
    uart_init();
    printf("Heart rate collector example\r\n");

    spi0_master_init();
    user_ethernet_init();

    app_mailbox_create(&hr_data_mailbox);
    app_mailbox_create(&bas_data_mailbox);

    ble_stack_init();
    device_manager_init(erase_bonds);
    db_discovery_init();

    hrs_c_init();
    bas_c_init();


    // Start scanning for peripherals and initiate connection
    // with devices that advertise Heart Rate UUID.
    scan_start();

    err_code = app_timer_create(&m_publish_data_timer_id, APP_TIMER_MODE_REPEATED, publish_data_handler);
    APP_ERROR_CHECK(err_code);
    err_code = app_timer_start(m_publish_data_timer_id,PUBLISH_INTERVAL,NULL);
    APP_ERROR_CHECK(err_code);


    for (;; )
    {
        power_manage();
    }
}
#define HR_EVT_MAILBOX_QUEUE_SIZE     10
#define BAS_EVT_MAILBOX_QUEUE_SIZE    10

typedef struct
{
    uint16_t evt_data;
    uint16_t dummy_data;
}hrs_c_evt_data;

typedef struct
{
    uint16_t battery_level;
    uint16_t dummy_data;
}bas_c_evt_data;

APP_MAILBOX_DEF(hr_data_mailbox, HR_EVT_MAILBOX_QUEUE_SIZE, sizeof(hrs_c_evt_data));
APP_MAILBOX_DEF(bas_data_mailbox, BAS_EVT_MAILBOX_QUEUE_SIZE, sizeof(bas_c_evt_data));

static any_port = 50000;
uint8_t dweet_io_ip[4] = {54,172,56,193};
uint16_t dest_port = 80;
#define THING         "Ble2Eth"
#define HEART_RATE     "heart_rate"
#define BATTERY     "battery"
static void publish_data_handler(void * p_context)
{
    UNUSED_PARAMETER(p_context);
    uint8_t sn = 0;
    uint32_t ret;
    uint8_t eth_data[512];
    hrs_c_evt_data hr_data;
    bas_c_evt_data batt_data;

    // Connect to server
    if(app_mailbox_length_get(&hr_data_mailbox) || app_mailbox_length_get(&bas_data_mailbox))
    {
        if(getSn_SR(sn) != SOCK_CLOSED)
        {
            close(sn);
            disconnect(sn);
        }
        while(getSn_SR(sn) != SOCK_CLOSED);

        if( ret = socket(sn, Sn_MR_TCP, any_port++, 0x00) != sn )
        {
            APPL_LOG("[PUBLISH]: Socket open failed, reason %d\r\n", ret);
            return;
        }

        if( ret = connect(sn, dweet_io_ip, dest_port) != SOCK_OK )
        {
            APPL_LOG("[PUBLISH]: Socket Connection failed, reason %d\r\n", ret);
            return;
        }
    }
    else    return;

    // Publish collected data
    while( app_mailbox_length_get(&hr_data_mailbox) != 0)
    {
        app_mailbox_get(&hr_data_mailbox,&hr_data);
        sprintf(eth_data,"GET /dweet/for/%s?%s=%d HTTP/1.1\r\nHost: dweet. io\r\n\r\n",THING,HEART_RATE,hr_data.evt_data);
        send(sn,eth_data,strlen((uint8_t*)eth_data));
    }

    while( app_mailbox_length_get(&bas_data_mailbox) != 0)
    {
        app_mailbox_get(&bas_data_mailbox,&batt_data);
        sprintf(eth_data,"GET /dweet/for/%s?%s=%d HTTP/1.1\r\nHost: dweet. io\r\n\r\n",THING,BATTERY,batt_data.battery_level);
        send(sn,eth_data,strlen((uint8_t*)eth_data));
    }

    close(sn);
    disconnect(sn);
}

APP_TIMER_DEF(m_publish_data_timer_id);                                                /**< Publish data timer. */
/**@brief Heart Rate Collector Handler.
 */
static void hrs_c_evt_handler(ble_hrs_c_t * p_hrs_c, ble_hrs_c_evt_t * p_hrs_c_evt)
{
    uint32_t err_code;
    uint32_t test;
    hrs_c_evt_data hrs_c_data;

    switch (p_hrs_c_evt->evt_type)
    {
        case BLE_HRS_C_EVT_DISCOVERY_COMPLETE:

            // Heart rate service discovered. Enable notification of Heart Rate Measurement.
            err_code = ble_hrs_c_hrm_notif_enable(p_hrs_c);
            APP_ERROR_CHECK(err_code);

            printf("Heart rate service discovered \r\n");
            break;

        case BLE_HRS_C_EVT_HRM_NOTIFICATION:
        {
            APPL_LOG("[APPL]: HR Measurement received %d \r\n", p_hrs_c_evt->params.hrm.hr_value);
            hrs_c_data.evt_data = p_hrs_c_evt->params.hrm.hr_value;
            app_mailbox_put(&hr_data_mailbox,&hrs_c_data);
            break;
        }

        default:
            break;
    }
}


/**@brief Battery levelCollector Handler.
 */
static void bas_c_evt_handler(ble_bas_c_t * p_bas_c, ble_bas_c_evt_t * p_bas_c_evt)
{
    uint32_t err_code;
    bas_c_evt_data bas_data;

    switch (p_bas_c_evt->evt_type)
    {
        case BLE_BAS_C_EVT_DISCOVERY_COMPLETE:
            // Batttery service discovered. Enable notification of Battery Level.
            APPL_LOG("[APPL]: Battery Service discovered. \r\n");

            APPL_LOG("[APPL]: Reading battery level. \r\n");

            err_code = ble_bas_c_bl_read(p_bas_c);
            APP_ERROR_CHECK(err_code);


            APPL_LOG("[APPL]: Enabling Battery Level Notification. \r\n");
            err_code = ble_bas_c_bl_notif_enable(p_bas_c);
            APP_ERROR_CHECK(err_code);

            break;

        case BLE_BAS_C_EVT_BATT_NOTIFICATION:
        {
            APPL_LOG("[APPL]: Battery Level received %d %%\r\n", p_bas_c_evt->params.battery_level);
            bas_data.battery_level = p_bas_c_evt->params.battery_level;
            app_mailbox_put(&bas_data_mailbox,&bas_data);
            break;
        }

        case BLE_BAS_C_EVT_BATT_READ_RESP:
        {
            APPL_LOG("[APPL]: Battery Level Read as %d %%\r\n", p_bas_c_evt->params.battery_level);
            break;
        }

        default:
            break;
    }
}

Cloud Service(dweet.io)에서 데이터 확인하기

무료 버전의 dweet.io는 디바이스로 부터 수신한 데이터를 별도의 데이터베이스에 저장하지 않고 웹 브라우저에 출력하는 구조로 동작한다.
때문에 별도의 설정이나 Key 값이 없이도 아래 그림과 같은 데이터를 확인 할 수 있다.
아래와 같은 데이터를 확인하기 위한 URL은 https://dweet.io/follow/Ble2Eth 이다. https://dweet.io/follow/ 뒤에 위 코드에서 설정한 THING 주소를 기입하면 된다.

20160112_125136

dweet.io에 대한 자세한 설명은 링크를 참고 하기 바란다.

반응형