Blog Page 2

[WizFi250 심화과정]Cortex-M3에서 Wi-Fi 사용하기 강의자료

0

WizFi250(Wi-Fi Module)을 사용하여 Cortex-M3에서 Wi-Fi를 사용하기 위한 방법을 설명한다.

  • 개발 환경
    • IDE : Eclipse
    • Compiler : arm-none-eabi-gcc
    • Build tool : make

BLE to Ethernet Thin Gateway 만들기

0

본 포스팅에서는 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)
APP_TIMER_DEF(m_publish_data_timer_id);

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();
    }
}
  • 1~2 : Cloud Service에 Publish 하기 위한 Timer Interval 설정 ( 300ms ) 및 초기화
  • 16 : WIZ550io를 제어하기 위한 SPI Interface 초기화
  • 17 : Ethernet 초기화 ( IP 주소 설정 및 네트워크 설정 )
  • 19 ~ 20 : mailbox 초기화 ( HR Event와 BAS Event가 발생하면 해당 값을 mailbox로 보관 )
  • 22 ~ 24 : BLE Central Stack 초기화 BLE Scan을 위한 초기화 작업
  • 26 ~ 27 : HR(Heart rate)과 BAS(Battery) collector 초기화
  • 32 : BLE Peripheral Scan 및 Connection Scan
  • 34 ~ 37 : Publish Timer 생성 및 Start
#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"
  • 1 ~ 2  : Mailbox QUEUE SIZE 설정
  • 4 ~ 14: Heart rate & Battery Level 데이터 포맷 초기화
  • 16 ~ 17 : Mailbox 초기화
  • 19 ~ 24 : dweet.io에 데이터를 송신하기 위한 정보
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);
}
  • 300ms 마다 publish_data_handler가 수행되며 dweet.io에 TCP 연결 후 Battery Data 혹은 Heart Rate Data를 송신한다.
  • publish_data_handler가 수행되면서 보관되어 있는 mailbox의 데이터를 꺼내서 Ethernet으로 송신한다.
/**@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;
    }
}
  • 20 ~ 26 : Heart Rate Notification Event가 발생하면 mailbox에 data를 보관
  • 59 ~ 65 : Battery Notification Event가 발생하면 mailbox에 data를 보관
  • 위 코드에서 mailbox에 보관하는 data는 publish_data_handler가 수행되면서 꺼내서 Ethernet으로 송신한다.

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

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

20160112_125136

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

nRF51 SDK Eclipse 개발환경 구축

0

Hardware

nRF51 Development Kit board(PCA10028)

20160108_150259

GNU tools for ARM embedded Processors

(1) 다운로드 GCC ARM Embedded
https://launchpad.net/gcc-arm-embedded/+download

(2) Install GCC ARM Embedded
설치 옵션에서 path 등록 해줘야 함.

Default Path : C:\Program Files (x86)\GNU Tools ARM Embedded

GNUWin32 and CoreUtils

(1) GnuWin32 – Core Utility http://gnuwin32.sourceforge.net/packages/coreutils.htm
(2) GnuWin32 – GNU Make utility http://gnuwin32.sourceforge.net/packages/make.htm

nRF51 SDK 10.0.0

(1) nRF51 SDK 다운로드 https://developer.nordicsemi.com/nRF5_SDK/nRF51_SDK_v10.x.x/

SDK를 다운로드 받은 후, 아래 경로로 이동하면 3개의 Makefile이 존재 한다. 각자의 환경에 맞는 파일을 수정하면 된다. ( Makefile.posix : For Linux or OSX User, Makefile.windows : For Windows User)

<SDK>/components/toolchain/gcc

 

Text Editor를 이용하여 Makefile.windows 파일을 열고,GNU_INSTALL_ROOT와 Version을 수정한다.

ifeq ($(findstring 86, $(ProgramFiles)), )
    PROGFILES := C:/Program Files
else
    PROGFILES := C:/Program Files (x86)
endif

GNU_INSTALL_ROOT := $(PROGFILES)/GNU Tools ARM Embedded/4.9 2015q3
GNU_VERSION := 4.9.3
GNU_PREFIX := arm-none-eabi

Compile nRF51 Sample Example

<SDK>\examples\peripheral\blinky\pca10028\blank\armgcc

 

CMD 창에서 위 경로로 이동한 다음 make 명령을 수행하면 아래와 같은 로그가 나오며, _build 폴더에 해당 Binary가 생성되는 것을 확인 할 수 있다.

E:\test\nRF51_SDK_10.0.0\examples\peripheral\blinky\pca10028\blank\armgcc>make
rm -rf _build
echo  makefile
makefile
mkdir _build
Compiling file: system_nrf51.c
Compiling file: main.c
Compiling file: nrf_delay.c
Compiling file: gcc_startup_nrf51.s
Linking target: nrf51422_xxac.out
make[1]: Entering directory `E:/test/nRF51_SDK_10.0.0/examples/peripheral/blinky/pca10028/blank/armgcc'
Preparing: nrf51422_xxac.bin
Preparing: nrf51422_xxac.hex

   text    data     bss     dec     hex filename
   1004     104      28    1136     470 _build/nrf51422_xxac.out

make[1]: Leaving directory `E:/test/nRF51_SDK_10.0.0/examples/peripheral/blinky/pca10028/blank/armgcc'

E:\test\nRF51_SDK_10.0.0\examples\peripheral\blinky\pca10028\blank\armgcc>

 

nRF-tools/J-link

Download Link for nRF-tools(Windows,Linux)

Optional: erase target if not already blank

<SDK>\examples\peripheral\blinky\pca10028\blank\armgcc>nrfjprog --family nRF51 -e
Erasing code and UICR flash areas.
Applying system reset.

 

Load FW image to target

<SDK>\examples\peripheral\blinky\pca10028\blank\armgcc>nrfjprog --family nRF51 --program _build/nrf51422_xxac.hex
Parsing hex file.
Reading flash area to program to guarantee it is erased.
Checking that the area to write is not protected.
Programing device.

 

Reset and Run

E:\test\nRF51_SDK_10.0.0\examples\peripheral\blinky\pca10028\blank\armgcc>nrfjprog --family nRF51 -r
Applying system reset.
Run.

Eclipse 설정

(1) Download Mars C/C++ developers package

(2) Install GNU ARM Eclipse Plug-in

  • Start Eclipse
  • Click help and select install new software
  • Add http://gnuarmeclipse.sourceforge.net/updates to the list of repositories.
  • Install Cross compiler, J-link Debugging and Packs. The other components are optional

20151223_131131

20151223_131157

20151223_131209

(3) Configure environment

[windows]-[Preferences]-[C/C++]-[Build]-[Global Tools path]

Build tools folder: C:\Program Files (x86)\GnuWin32\bin
Toolchain folder : C:/Program Files (x86)/GNU Tools ARM Embedded/4.9 2015q3/bin

20151223_132030

20151223_132212

(4) Configuration Packages

20151223_154230

20151223_155100

20151223_155245

20151223_155954

참고 자료

http://gnuarmeclipse.github.io/plugins/packs-manager/

Create a new Eclipse Project

(1) Eclipse 실행 후, [file]->[new Makefile project with existing code] 선택

20151223_132945

(2) Project Name과 Existing Code Location 지정 후, Finish 버튼 클릭
(Exsting Code Location : \examples\peripheral\blinky\pca10028\s110\armgcc)

20151223_133028

(3) Makefile을 열고 C_SOURCE_FILES에 추가 되어 있는 파일들을 Eclipse의 Project Explorer에 추가 한다.

[NEW]-[Folder]-[Advanced]-[Virtual Folder] 체크 후, Folder name에 Application 이름으로 Virtual Folder 생성

20160107_092650

생성된 Virtual Folder 선택 -> [Import]-[File System] 선택

20160107_094800

원하는 main.c가 있는 경로 설정 -> main.c 선택 -> Create links in workspace 선택

20160107_095148

20160107_095627

nRF_Driver 이름의 Virtual Folder 생성-> nRF_Driver 선택-> [Import]-[File System] 선택 후 nRF51_SDK_10.0.0\components\drivers_nrf 폴더 등록

20160107_101037

(4) makefile을 열고 CFLAGS의 옵션을 -O3에서 -O0 -g3로 변경한다.

#flags common to all targets
CFLAGS  = -DNRF51
CFLAGS += -DBOARD_PCA10028
CFLAGS += -DBSP_DEFINES_ONLY
CFLAGS += -mcpu=cortex-m0
CFLAGS += -mthumb -mabi=aapcs --std=gnu99
CFLAGS += -Wall -Werror -O0 -g3
CFLAGS += -mfloat-abi=soft
# keep every function in separate section. This will allow linker to dump unused functions
CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing
CFLAGS += -fno-builtin --short-enums

(5) Create Make Target 해당 프로젝트를 컴파일하기 위해서 Make Target을 생성해야 한다. 생성 규칙은 Makefile에 기술되어 있으며, flash_softdevice는 NRF51에 Bluetooth Stack을 업로드하는데 사용되며 flash nrf51422_xxac_s110은 User Application을 업로드 하기 위해 사용된다.

20160107_102313

Builder Setting 변경

[Properties for project]-[C/C++ Build]-[Builder Settings]-[Build command]에 make VERBOSE=1로 변경

20160202_094831

CDT GCC Build Output Parser

아래 그림과 같이 Compiler command pattern을 (gcc)|([gc]\+\+)|(clang) 에서 (.*gcc)|(.*[gc]\+\+)로 변경하면 컴파일한 결과들을 Eclipse에서 Parsing해서 Debugging 시 함수 구현부분을 추적 할 수 있다.

20160202_095323

Reference

https://devzone.nordicsemi.com/tutorials/7/development-with-gcc-and-eclipse/ http://redbearlab.com/nrf51822-sdk/

3. OpenHAB Mosquito Broker 연동

0

본 포스팅에서는 라즈베리파이에 MQTT Broker를 설치하고, 이를 OpenHAB과 연동하기 위한 방법에 대해 설명한다.

MQTT

MQTT는 경량의 Publish/Subscribe(Pub/Sub) 메시징 프로토콜이다. M2M(machine-to-machine)와 IoT(Internet of things)에서의 사용을 목적으로 만들었다. 이를 위해서 낮은 전력, 낮은 대역폭 환경에서도 사용할 수 있도록 설계됐다. MQTT는 저전력, 신뢰할 수 없는 네트워크, No TCP/IP 기반에서 운용할 수 있다는 장점이 있다. 소형기기의 제어와 센서정보 수집에 유리하다. 이런 특징들로 특히 IoT 영역에서 주목받고 있다.

MQTT Broker

MQTT 서버라고 하지 않고 중개인(브로커)라고 하는 이유는 MQTT가 발행인과 구독자가 메시지를 주고 받을 수 있도록 다리를 놔주는 역할만을 하기 때문이다. 다른 기능들은 중계를 도와주는 부가 기능일 뿐이다. 다양한 종류의 브로커들이 있는데, 여기에서 확인하자.

Mosquitto MQTT

모스키토(Mosquitto)는 (2014년 6월 현재)MQTT 프로토콜 3.1을 구현한 BSD 라이센스 기반의 오픈소스 메시지 브로커다. 본 포스팅에서는 Mosquitto MQTT를 이용해서 OpenHAB과 연동할 예정이다.

MQTT와 MQTT Broker에 대한 자세한 설명은 Joinc:MQTT소개를 참고하기 바란다.

Mosquitto Repository 추가

라즈베리파이에서 Mosquitto Broker를 사용하기 위해서는 Repository Package Signing Key를 추가 해야 한다. 아래와 같은 명령을 수행하면 Signing Key를 다운로드 한 후, apt에 Mosquitto Repository를 등록하는 과정을 수행한다.

(1) Repository Package Signing Key Download

$ cd ~
$ wget http://repo.mosquitto.org/debian/mosquitto-repo.gpg.key
$ sudo apt-key add mosquitto-repo.gpg.key

(2) Mosquitto Repository Package 등록

라즈비안-Jessie용 Mosquitto Package를 /etc/apt/sources.list.d/에 다운로드 한 후, (3)을 실행 한다.

$ cd /etc/apt/sources.list.d/
$ sudo wget http://repo.mosquitto.org/debian/mosquitto-jessie.list

내가 사용하고 있는 Raspbian OS는 Jessie 버전이므로 위와 같은 명령을 사용하였고, Wheezy 버전을 사용하는 사람은 아래와 같은 명령을 이용하면 된다.

$ sudo wget http://repo.mosquitto.org/debian/mosquitto-wheezy.list

(3) Mosquitto Broker 설치

$ sudo apt-get update
$ sudo apt-cache search mosquitto
$ sudo apt-get install mosquitto mosquitto-clients

참고: Mosquitto Debian repository

sudo: apt-add-repository: command not found Error시 발생 시 해결법

라즈비안OS에서 apt-add-repository 명령을 수행하하면 Command not found Error가 발생하는 경우가 있다. 이런경우, 아래와 같이 software-properties-common과 python-software-properies를 설치하면 해결된다.

$ sudo apt-get install software-properties-common python-software-properties

Mosquitto 실행 및 Subscribe/Publish Test

앞 과정에서 Mosquitto Broker를 설치 하였으니, Broker가 정상적으로 동작하는지 확인하기 위한 방법을 설명한다. (1) 명령을 수행해서 Mosquitto를 실행한 다음, Putty 창(Putty2)을 하나 더 열고 mosquitto_sub -d -t hello/world 를 수행한다.

위 명령을 수행하면 mosquitto는 hello/world라는 Topic으로 수신되는 모든 메시지를 출력하게 된다.

나머지 Putty창(Putty1)에서, mosquitto_pub -d -t hello/world -m “Hello from MQTT”를 실행하면 Putty2에서 Hello from MQTT를 수신하는 것을 확인 할 수 있다.

참고로 -d 옵션은 debug, -t 옵션은 topic, -m 옵션은 message이다.

(1) Mosquitto 실행

$ sudo /etc/init.d/mosquitto start

20151216_140905

(2) MQTT Subscribe & Publish Test

$ mosquitto_sub -d -t hello/world
$ mosquitto_pub -d -t hello/world -m “Hello from MQTT”

위 명령을 복사해서 사용하는 경우, 터미널에서 “를 제대로 인식하지 못해 명령이 수행되지 않는 경우가 있다. 이런 경우, 키보드로 타이핑해서 수행하길 바란다.

20151216_141210

MQTT Binding을 위한 OpenHAB 설정

(1) openhab.cfg 생성

$ sudo cp /etc/openhab/configurations/openhab_default.cfg /etc/openhab/configurations/openhab.cfg

(2) openhab.cfg에 MQTT Broker 정의

$ sudo vi /etc/openhab/configurations/openhab.cfg

약 423라인과 427라인에 아래 구문을 추가 한다.

mqtt:broker.url=tcp://192.168.0.100:1883
mqtt:broker.clientId=openhab

아래 명령은 openHAB에 sitemap을 추가하기 위한 명령이다. sitemap이란,OpenHAB의 Element들을 만들기 위해 사용된다.(Element는 OpenHAB의 Item을 제어하기 위해 사용됨)

$ sudo vi /etc/openhab/configurations/sitemaps/kaizen.sitemap
sitemap kaizen label="Main Menu"
{
   Frame label="MQTT"{
   Switch item=mqttsw1 label="MQTT Switch 1"
   Switch item=mqttsw2 label="MQTT Switch 2"
   Switch item=mqttsw3 label="MQTT Switch 3"
   Text item=Office_Temp
   Text item=Office_Humidity
   }
}
$ sudo vi /etc/openhab/configurations/items/kaizen.items
Group All
Switch mqttsw1 "Switch 1" (all) {mqtt=">[broker:/wiznet/sw1:command:on:1],>[broker:/wiznet/sw1:command:off:0]"}
Switch mqttsw2 "Switch 2" (all) {mqtt=">[broker:/wiznet/sw2:command:on:1],>[broker:/wiznet/sw2:command:off:0]"}
Switch mqttsw3 "Switch 3" (all) {mqtt=">[broker:/wiznet/sw3:command:on:1],>[broker:/wiznet/sw3:command:off:0]"}

Number Office_Temp "Temperature [%.1f °C]" <temperature>(all) {mqtt="<[broker:/wiznet/temperature:state:default]"}
Number Office_Humidity "Humidity [%.1f °C]" <humidity>(all) {mqtt="<[broker:/wiznet/humidity:state:default]"}
$ sudo /etc/init.d/openhab restart

위 과정을 모두 수행 한 후, PC에서 http://192.168.0.100:8080/openhab.app?sitemap=kaizen 주소로 접속하면 아래와 같은 화면을 볼 수 있다. 위 과정을 통해 라즈베리파이에 OpenHAB을 구동하고 원하는 메뉴 버튼을 만들 수 있다. 본 예제에서는 원격지에 있는 Device의 LED를 제어하기 위한 Switch 1/2/3과 원격지에 있는 Device에서 검출한 온/습도 값을 모니터링 하기 위한 Temperature/Humidity 메뉴를 만들었다.

20151229_161607

Remote Device 만들기

1.Material

(1) WIZwiki-W7500 wizwiki-w7500_main

Features

  • WIZnet W7500
    • ARM Cortex-M0 Core
    • 48MHz, 128KB Flash
    • 16KB RAM
    • 32KB RAM for TCP/IP – Can be extended to system RAM
    • Hardwired TCP/IP Core
    • MII Interface
    • ADC (8)
    • GPIO (53)
    • SWD (Serial Wire Debug)
    • Timer/PWM
    • UART (3)
    • SPI (2)
    • I2C (2)
  • External Ethernet PHY
    • IC+ IP101GA
    • Single Port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver
    • Auto MDI/MDIX function
    • Supports MDC and MDIO to communicate with the MAC

(2) Sensor Shield

TB2wSGHaFXXXXcLXXXXXXXXXXXX_!!33841454

2. mbed Code

아래 주소를 이용하면 W7500 기반의 MQTT Client 예제 코드를 다운로드 할 수 있다.
Download(openHAB_mqtt_W7500)
다운로드 한 예제의 main 코드는 아래와 같으며 크게 MQTT 초기화, MQTT Subscribe, MQTT Publish로 나눌 수 있다. 

#include "mbed.h"
#include "DHT.h"
#include "MQTTEthernet.h"
#include "MQTTClient.h"

#define ECHO_SERVER_PORT   7

#include "mbed.h"

int arrivedcount = 0;
DigitalOut sw1(D9);
DigitalOut sw2(D10);
DigitalOut sw3(D11);


void sw1_messageArrived(MQTT::MessageData& md)
{
    MQTT::Message &message = md.message;
    printf("Message arrived: qos %d, retained %d, dup %d, packetid %d\r\n", message.qos, message.retained, message.dup, message.id);

    if( strncmp((char*)message.payload,"1",message.payloadlen) == 0 ){
        sw1 = 1;
    } else{
        sw1 = 0;
    }
}

void sw2_messageArrived(MQTT::MessageData& md)
{
    MQTT::Message &message = md.message;
    printf("Message arrived: qos %d, retained %d, dup %d, packetid %d\r\n", message.qos, message.retained, message.dup, message.id);
    if( strncmp((char*)message.payload,"1",message.payloadlen) == 0 ){
        sw2 = 1;
    } else{
        sw2 = 0;
    }
}

void sw3_messageArrived(MQTT::MessageData& md)
{
    MQTT::Message &message = md.message;
    printf("Message arrived: qos %d, retained %d, dup %d, packetid %d\r\n", message.qos, message.retained, message.dup, message.id);
    if( strncmp((char*)message.payload,"1",message.payloadlen) == 0 ){
        sw3 = 1;
    } else{
        sw3 = 0;
    }
}

void baud(int baudrate) {
    Serial s(USBTX, USBRX);
    s.baud(baudrate);
}

int main (void)
{
    DHT sensor(D4, DHT11);
    sw1 = 0; sw2 = 0; sw3 = 0;
    baud(115200);
    printf("Wait a second...\r\n");

    MQTTEthernet ipstack = MQTTEthernet();
    printf("IP Address : %s\r\n",ipstack.getEth().getIPAddress());
    MQTT::Client<MQTTEthernet, Countdown> client = MQTT::Client<MQTTEthernet, Countdown>(ipstack);

    char* hostname = "192.168.0.100";
    int port = 1883;

    int rc = ipstack.connect(hostname, port);
    if (rc != 0)
    {
        printf("rc from TCP connect is %d\n", rc);
        return 0;
    }
    printf("TCP connection is successful\r\n");

    char MQTTClientID[30];

    MQTTPacket_connectData data = MQTTPacket_connectData_initializer;       
    data.MQTTVersion = 3;
    sprintf(MQTTClientID,"WIZwiki-W7500-client-%d",rand()%1000);
    data.clientID.cstring = MQTTClientID;
    data.username.cstring = "";
    data.password.cstring = "";  

    if ((rc = client.connect(data)) != 0)
    {
        printf("rc from MQTT connect is %d\r\n", rc);
        return 0;
    }
    printf("MQTT connection is successful\r\n");


    char *sw1 = "/wiznet/sw1";
    if ((rc = client.subscribe(sw1, MQTT::QOS1, sw1_messageArrived)) != 0)
    {
        printf("rc from MQTT subscribe is %d\r\n", rc);
        return 0;
    }
    printf("Added subscription for sw1\r\n");

    char *sw2 = "/wiznet/sw2";
    if ((rc = client.subscribe(sw2, MQTT::QOS1, sw2_messageArrived)) != 0)
    {
        printf("rc from MQTT subscribe is %d\r\n", rc);
        return 0;
    }
    printf("Added subscription for sw2\r\n");

    char *sw3 = "/wiznet/sw3";
    if ((rc = client.subscribe(sw3, MQTT::QOS1, sw3_messageArrived)) != 0)
    {
        printf("rc from MQTT subscribe is %d\r\n", rc);
        return 0;
    }
    printf("Added subscription for sw3\r\n");

    MQTT::Message message;
    char buf[100];
    int error = 0;
    float hum = 0.0f, temp = 0.0f;
    char i = 0;
    while (true)
    {
        if(i > 100) i = 0;
        error = sensor.readData();
        if (0 == error) {
            hum = sensor.ReadHumidity();
            temp = sensor.ReadTemperature(CELCIUS);
        }
        message.qos = MQTT::QOS1;
        message.retained = false;
        message.dup = false;

        sprintf(buf, "%3.1f", hum);
        message.payload = (void*)buf;
        message.payloadlen = strlen(buf);
        rc = client.publish("/wiznet/humidity",message);
        printf("publish humidity data %s\r\n",(char*)message.payload);

        sprintf(buf, "%3.1f", temp);
        message.payload = (void*)buf;
        message.payloadlen = strlen(buf);
        rc = client.publish("/wiznet/temperature", message);
        printf("publish temperature data %s\r\n",(char*)message.payload);

        client.yield(60000);
    }
}

3. Code 설명

    MQTTEthernet ipstack = MQTTEthernet();
    printf("IP Address : %s\r\n",ipstack.getEth().getIPAddress());
    MQTT::Client<MQTTEthernet, Countdown> client = MQTT::Client<MQTTEthernet, Countdown>(ipstack);
 
    char* hostname = "192.168.0.100";
    int port = 1883;
 
    int rc = ipstack.connect(hostname, port);
    if (rc != 0)
    {
        printf("rc from TCP connect is %d\n", rc);
        return 0;
    }
    printf("TCP connection is successful\r\n");
 
    char MQTTClientID[30];
 
    MQTTPacket_connectData data = MQTTPacket_connectData_initializer;       
    data.MQTTVersion = 3;
    sprintf(MQTTClientID,"WIZwiki-W7500-client-%d",rand()%1000);
    data.clientID.cstring = MQTTClientID;
    data.username.cstring = "";
    data.password.cstring = "";  
 
    if ((rc = client.connect(data)) != 0)
    {
        printf("rc from MQTT connect is %d\r\n", rc);
        return 0;
    }
    printf("MQTT connection is successful\r\n");

위는 W7500에서 MQTT를 사용하기 위한 코드이다.

  • 1 : W7500의 Ethernet을 사용하기 위한 초기화
  • 3 : MQTT Client 초기화 ( 1번에서 생성한 Ethernet Interface를 파라미터로 사용 )
  • 5~6 : MQTT Broker의 IP주소와 Port 정보를 입력
    • 앞에서 설정한 MQTT Broker(Mosquitto)의 정보 사용
  • 18~30 : MQTT 설정 및 Broker에 접속
    • 주의 : MQTT ClientID는 중복되면 안됨. 중복 되면 Broker에서 무시 혹은 거절 할 수 있음
    char *sw1 = "/wiznet/sw1";
    if ((rc = client.subscribe(sw1, MQTT::QOS1, sw1_messageArrived)) != 0)
    {
        printf("rc from MQTT subscribe is %d\r\n", rc);
        return 0;
    }
    printf("Added subscription for sw1\r\n");
 
    char *sw2 = "/wiznet/sw2";
    if ((rc = client.subscribe(sw2, MQTT::QOS1, sw2_messageArrived)) != 0)
    {
        printf("rc from MQTT subscribe is %d\r\n", rc);
        return 0;
    }
    printf("Added subscription for sw2\r\n");
 
    char *sw3 = "/wiznet/sw3";
    if ((rc = client.subscribe(sw3, MQTT::QOS1, sw3_messageArrived)) != 0)
    {
        printf("rc from MQTT subscribe is %d\r\n", rc);
        return 0;
    }
    printf("Added subscription for sw3\r\n");

위는 W7500에서 MQTT Message를 Subscribe 하기 위한 코드이다.

  • 1~7 : /wiznet/sw1 으로 들어오는 Message를 구독함
    • /wiznet/sw1 message를 수신하면 sw1_messageArrived 함수를 호출
  • 9~15 : /wiznet/sw2 으로 들어오는 Message를 구독함
    • /wiznet/sw2 message를 수신하면 sw2_messageArrived 함수를 호출
  • 17~23 : /wiznet/sw3으로 들어오는 Message를 구독함
    • /wiznet/sw3 message를 수신하면 sw3_messageArrived 함수를 호출

sw1/sw2/sw3_messageArrived 함수는 아래와 같이 특정 LED를 On/Off 하는 기능을 수행한다.

void sw1_messageArrived(MQTT::MessageData& md)
{
    MQTT::Message &message = md.message;
    printf("Message arrived: qos %d, retained %d, dup %d, packetid %d\r\n", message.qos, message.retained, message.dup, message.id);
 
    if( strncmp((char*)message.payload,"1",message.payloadlen) == 0 ){
        sw1 = 1;
    } else{
        sw1 = 0;
    }
}
 
void sw2_messageArrived(MQTT::MessageData& md)
{
    MQTT::Message &message = md.message;
    printf("Message arrived: qos %d, retained %d, dup %d, packetid %d\r\n", message.qos, message.retained, message.dup, message.id);
    if( strncmp((char*)message.payload,"1",message.payloadlen) == 0 ){
        sw2 = 1;
    } else{
        sw2 = 0;
    }
}
 
void sw3_messageArrived(MQTT::MessageData& md)
{
    MQTT::Message &message = md.message;
    printf("Message arrived: qos %d, retained %d, dup %d, packetid %d\r\n", message.qos, message.retained, message.dup, message.id);
    if( strncmp((char*)message.payload,"1",message.payloadlen) == 0 ){
        sw3 = 1;
    } else{
        sw3 = 0;
    }
}
    MQTT::Message message;
    char buf[100];
    int error = 0;
    float hum = 0.0f, temp = 0.0f;
    char i = 0;
    while (true)
    {
        if(i > 100) i = 0;
        error = sensor.readData();
        if (0 == error) {
            hum = sensor.ReadHumidity();
            temp = sensor.ReadTemperature(CELCIUS);
        }
        message.qos = MQTT::QOS1;
        message.retained = false;
        message.dup = false;
 
        sprintf(buf, "%3.1f", hum);
        message.payload = (void*)buf;
        message.payloadlen = strlen(buf);
        rc = client.publish("/wiznet/humidity",message);
        printf("publish humidity data %s\r\n",(char*)message.payload);
 
        sprintf(buf, "%3.1f", temp);
        message.payload = (void*)buf;
        message.payloadlen = strlen(buf);
        rc = client.publish("/wiznet/temperature", message);
        printf("publish temperature data %s\r\n",(char*)message.payload);
 
        client.yield(60000);
    }

위 코드는 W7500이 측정한 온도/습도 정보를 MQTT Publish 하기 위한 코드이다.

  • 18~22 : /wiznet/humidity/xxxx 라는 포맷으로 Publish
  • 24~28 : /wiznet/temperature/xxx 라는 포맷으로 Publish

앞에서 설정한 MQTT Broker(Mosquitto)에서는 위에서 Publish 하는 Topic(/wiznet/humidity 등)을 모니터링하고 있다. MQTT Broker에서 해당 Topic를 수신하면 OpenHAB에 Message를 전달하며 OpenHAB에서는 전달 받은 메시지를 이용하여 Webpage로 보여 주는 구조로 동작한다.

4. 실행 후 LogMessage

20151229_161639

기타

sitemap에 정의할 수 있는 Element들은 아래와 같다.

Element
Colorpicker Allows the user to choose a color from a color wheel.
Chart Adds a time-series chart object for displaying logged data.
Frame Area with either various other sitemap elements or further nested frames
Group Renders all elements of a given group defined in an items definiton file
Image Renders an image
List
Selection Gives access to a new page where the user can choose among values defined in the mappings parameter.
Setpoint Shows a value and allows the user to modify it. Optionally, it is possible to specify maximum and minimum allowed values, as well as a step.
Slider Renders a slider
Switch Renders a switch item
Text Renders a text element
Video Displays a video
Webview

Sitemap에 대한 자세한 설명은 아래 주소를 참고 하기 바란다.

https://github.com/openhab/openhab/wiki/Explanation-of-Sitemaps

Sitemap이 정의되었으면, Sitemap에서 사용하는 Item들을 정의해야 한다. 여기에서 Item들이란, 어떤 이벤트로 인해 정보를 Read 하거나 Write 할 수 있는 Object를 의미하며 Itemtype과 Item에 대한 Command Type들은 아래와 같다.

Itemtype Description Command Types
Call Telephone call by origin and destination Call
Color Color information (RGB) OnOff, IncreaseDecrease, Percent, HSB
Contact Item storing status of e.g. door/window contacts OpenClosed
DateTime Stores date and time (see NTP binding for details)
Dimmer Item carrying a percentage value for dimmers OnOff, IncreaseDecrease, Percent
Group Item to nest other items / collect them in groups
Location GPS related information by latitude, longitude and altitude Point
Number Stores values in number format Decimal
Rollershutter Typically used for blinds UpDown,StopMove,Percent
String Stores texts String
Switch Typically used for lights OnOff

Item을 사용하기 위한 Example과 자세한 내용은 아래 주소를 참고 하기 바란다.

https://github.com/openhab/openhab/wiki/Explanation-of-items

아래는 Sitemap에서 만들어진 Swich와 Number Item을 Mqtt를 통해 데이터를 송/수신 하는 예제이다. ( mqttsw1은 데이터를 송신, Office_temp는 수신하는 역할을 한다.)

Topic : /wiznet/

Field : sw1, temperature

Item Command Query Topic Field
mqttsw1 /wiznet/sw1/1 /wiznet sw1
/wiznet/sw1/0
Office_temp /wiznet/temperature/38.5 /wiznet temperature
/wiznet/temperature/40.0

2.라즈베리파이 OpenHAB 환경설정

0

본 포스팅에서는 라즈베리파이에 OpenHAB을 설치하고 이를 실행하는 방법에 대해 설명한다.

OpenHAB이란?

OpenHAB(Open Automation Bus)는 Kai Kreuzer가 2010년부터 개발한 오픈 소스 홈 자동화 서버이다. 이 프로그램은 Equinox(Eclipse PDE) OSGi(Open Service Gateway initiative) 프레임워크 상에서 자바로 구현되어 있으며, 현재까지 많은 개발자들이 참여하여 개발되고 있다. OpenHAB은 The Thing System과 마찬가지로 KNX, Z-Wave, Insteon, Arduino, Ethernet, MQTT 등 수많은 다양한 사물인터넷 기기와 프로토콜을 지원하여 동적으로 바인딩할 수 있도록 설계되었다. 간단히, 가정에 있는 각종 전자 제품을 물리적 연결(ardware connection such as serial, ethernet, and wifi)이나 소프트웨어(software protocol such as XMPP, MQTT, and REST api)에 제약없이 연결되어 서비스할 수 있도록 설계된 Home Gateway라 생각하면 된다. 자세한 내용은 OpenHAB 사이트를 참초 하기 바란다.

20151224_154526

 

OpenHAB을 이용한 프로젝트들이 궁금하다면 아래 경로를 참고 하기 바란다. http://midnightcow.tistory.com/entry/Home-Automation-with-OpenHAB http://www.openhab.org/

OpenHAB 설치

먼저, OpenHAB 및 관련 Addon을 다운로드 받기 위한 저장소를 등록해야 하며, OpenHAB을 설치 하기 위한 과정은 아래와 같다.

(1)과 같이 vi 편집기를 이용하여 openhab.list 파일을 열고 (2) 내용을 입력 한 후, (3)명령을 수행하면 OpenHAB을 설치 할 수 있다.

(1) Add the OpenHab Repository

$ sudo vi /etc/apt/sources.list.d/openhab.list

(2) Insert

deb http://repository-openhab.forge.cloudbees.com/release/1.6.2/apt-repo/ /

(3) Install Open HAB and addon

$ sudo apt-get update
$ sudo apt-get install openhab-runtime openhab-addon-binding-mqtt openhab-addon-action-mail openhab-addon-binding-bluetooth openhab-addon-binding-serial openhab-addon-binding-weather openhab-addon-persistence-rrd4j

아래 그림과 같은 로그가 출력되면 정상적으로 설치가 완료 된 것이다.

20151216_130836

OpenHAB Demo Configuration

위 과정을 마치면 라즈베리파이에 OpenHAB이 설치가 되었을 것이다. 설치된 OpenHAB이 정상적으로 동작하는지 확인해야 하는데, 처음부터 무엇을 해야 하는지 막막할 것이다. 이런 경우를 대비하여 OpenHAB에서는 Demo Configuration을 제공한다.

Demo Configuration을 적용하기 위한 방법은 아래와 같다. (1)과 같이 Demo Configuration을 위한 파일들을 다운로드 한 후, addons 폴더를 /usr/share/openhab/addons 폴더로, configurations 폴더를 /etc/openhab/configurations 폴더로 복사한다. 그런 다음 (3)과 같이 OpenHAB을 실행 혹은 재실행 하면 Demo Configuration이 적용된 OpenHAB을 구동 할 수 있다.

(1) Download OpenHAB Demo Configuration

$ cd ~
$ mkdir openHAB_Demo
$ cd openHAB_Demo
$ wget https://github.com/openhab/openhab/releases/download/v1.6.2/distribution-1.6.2-demo-configuration.zip

(2) Demo Configuration 적용

$ unzip distribution-1.6.2-demo-configuration.zip 
$ sudo cp -rf addons/ /usr/share/openhab/ 
$ sudo cp -rf configurations/ /etc/openhab/
$ sudo cp /etc/openhab/configurations/openhab_default.cfg /etc/openhab/configurations/openhab.cfg

(3) OpenHAB 실행

$ sudo /etc/init.d/openhab start

(4) Demo Sitemap 접속

ifconfig 명령을 이용하여 라즈베리파이의 IP 주소를 확인 한다.

$ ifconfig

20151216_144502

같은 네트워크 안에 있는 PC에서 아래 주소를 입력하면 openHAB의 Demo 화면을 볼 수 있다.

http://192.168.0.100:8080/openhab.app?sitemap=demo

20151216_144316

Troubleshooting

OpenHAB이 실행안되는 현상 발생시

sudo journalctrl -xn 명령을 사용해서 실행이 안되는 상태의 로그를 확인 한다. 로그 확인 결과, 아래와 같이 /usr/bin/java(No such file or directory)가 나오면 java가 설치되어 있지 않다는 의미로 java를 설치하면 문제는 해결 된다.

$ sudo journalctl -xn
-- Logs begin at Thu 2015-12-17 23:37:03 UTC, end at Thu 2015-12-17 23:53:47 UTC. --
Dec 17 23:51:46 raspberrypi openhab[1341]: Starting openHAB server: openhabstart-stop-daemon: unable to stat /usr/bin/java (No such file or directory
Dec 17 23:51:46 raspberrypi openhab[1341]: failed!
Dec 17 23:51:46 raspberrypi systemd[1]: openhab.service: control process exited, code=exited status=1
Dec 17 23:51:46 raspberrypi systemd[1]: Failed to start LSB: openHAB server.
-- Subject: Unit openhab.service has failed

아래 명령을 수행하면 java를 설치 할 수 있다.

$ sudo apt-get update && sudo apt-get install oracle-java8-jdk

1. 라즈베리파이 설치 및 SSH 설정

0

본 포스팅에서는 라즈비안 OS를 라즈베리파이2에 설치하고 라즈베리파이2를 원격에서 제어 및 파일 관리를 하기 위한 과정을 설명한다.

라즈비안 OS 다운로드

라즈베리파이 이지지 다운로드에 접속하면 라즈베리파이2에 사용하기 위한 OS 이미지를 다운로드 할 수 있다. NOOBS와 RASPBIAN이 공식적으로 지원하는 버전이며, 3rd Patry로 Ubuntu 및 Windows 10을 지원하는 것을 확인 할 수 있다.

20151215_154527

본 포스팅에서는 RASPBIAN JESSIE OS를 사용할 예정이므로 아래 그림과 같이 JESSIE를 다운로드 버튼을 클릭 한다. 본 실습에서는 JESSIE LITE를 사용하였다. 4G SD Card에서는 JESSIE FULL 버전을 사용하기에는 용량이 조금 부족하다.

RASPBINA DOWNLOAD SITE (RASPBIAN JESSIE FULL ) (RASPBIAN JESSIE LITE )

20151215_154757

SD 카드에 라즈비안 OS 이미지 만들기

라즈베리파이2는 SD Card를 파일 시스템으로 하여 구동되며, SD Card에 OS 이미지를 만들기 위해서는 별도의 툴이 필요하다.

윈도우에서는 Win32diskmanager를 이용하면 어렵지 않게 이미지를 만들 수 있다.

다운로드 한 툴을 실행한 후, 앞에서 받은 라즈비안 OS와 SD Card의 경로를 입력하고 Write 버튼을 클릭하면 끝이다.

20151014_100558

이미지 생성이 완료되면, 라즈베리파이에 SD 카드를 장착하고 전원을 인가하면 초기 설정은 끝이다.

라즈베리파이 개발 환경 구축

라즈베리파이 기반에서 개발하기 위한 환경은 크게 3가지로 나눌 수 있다.

  1. HDMI Monitor를 이용하는 방법
  2. USB to Serial을 이용하는 방법
  3. SSH를 이용하는 방법

위 개발 환경에 대한 자세한 설명은 링크를 참고 하기 바란다. ( 단, 링크된 블로그에서는 라즈베리 파이1 기반으로 설명되어 있으니 유의 하기 바람. )

본 포스팅에서는 2번 방법을 사용하여 PC에서 라즈베리파이를 제어할 생각이다. 먼저 2번 방법을 이용하기 위해서는 USB to Serial이 필요하다. ( USB to Serial이 없다면 1번 방법을 이용하여 개발 하기 바란다. )

아래 그림을 보면 06(GND),08(GPIO14),10(GPIO15)가 Serial Monitor용 핀으로 사용되는 것을 알 수 있다.

GPIO_Pi2

RP2_Pinout

image

아래 그림과 같이 라즈베리파이의 GND와 USB to Serial의 GND를 연결하고, 8(GPIO14)을 USB to Serial의 RX 핀에 10번 핀을 USB to Serial의 TX핀에 연결한 후 전원을 인가하면 라즈베리가 부팅되는 로그 메시지를 시리얼 터미널에서 확인 할 수 있다.

20151215_161617

라즈비안의 초기 아이디와 비밀번호는 아래와 같다. 아이디 : pi 비밀번호 : raspberry

20151215_161821

vim 설치

$ sudo apt-get install vim

Filesystem 확장

pi@raspberrypi:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root       1.3G  967M  226M  82% /
devtmpfs        459M     0  459M   0% /dev
tmpfs           463M     0  463M   0% /dev/shm
tmpfs           463M  6.2M  457M   2% /run
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           463M     0  463M   0% /sys/fs/cgroup
/dev/mmcblk0p1   60M   20M   41M  34% /boot
pi@raspberrypi:~$

위와 같이 df 명령을 이용하면 라즈베리파이의 현재 사용하고 있는 Filesystem의 용량을 확인 할 수 있다. 근데 아래 결과를 보면 내 SD Card는 4G인데 사용 할 수 있는 총 사이즈가 1.4G 밖에 안된다. raspi-confi를 이용하면 Filesystem을 확장하기 위한 기능을 제공하는데 이를 이용하면 문제는 해결된다. Filesystem를 확장하기 위한 방법은 아래와 같다.

$ sudo raspi-config

위 명령을 실행한 후, 1 Expand Filesystem -> OK를 선택하면 Filesystem Size가 확장된다.

20151218_083248

20151218_083546

pi@raspberrypi:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root       3.6G  967M  2.5G  28% /
devtmpfs        459M     0  459M   0% /dev
tmpfs           463M     0  463M   0% /dev/shm
tmpfs           463M  6.2M  457M   2% /run
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           463M     0  463M   0% /sys/fs/cgroup
/dev/mmcblk0p1   60M   20M   41M  34% /boot
pi@raspberrypi:~$

SSH를 통한 라즈베리파이 원격제어

이미 앞 섹션에서 USB to Serial을 이용하여 라즈베리파이를 원격제어 했지만,다음 챕터 부터는 Ethernet을 이용하여 원격제어 하려고 한다. (USB to Serial이 없는 경우를 대비) Ethernet을 이용한 원격제어 방법은 Telnet, SSH 등 다양한 방법이 있지만, 본 포스팅에서는 SSH Server를 구동하여 라즈베리파이를 원격제어 하기 위한 방법을 설명한다.

SSH란

SSH는 Secure Shell의 약자로, 네트워크 상의 다른 컴퓨터에 로그인하거나 원격 시스템에서 명령을 실행하고 다른 시스템으로 파일을 복사 할 수 있도록 해주는 응용 프로그램 또는 그 프로토콜을 가리킨다. 기존의 rsh, rlogin, 텔넷 등을 대체하기 위해 설계되었으며, 강력한 인증 방법 및 안전하지 못한 네트워크에서 안전하게 통신을 할 수 있는 기능을 제공한다. 기본적으로는 22번 포트를 사용한다.

(자세한 내용은 위키백과 참고)

SSH Server Enable

다른 OS 이미지의 경우 SSH Server가 설치되어 있지 않아 설치 부터 해야 하는 경우도 있지만, 라즈비안의 경우 SSH Server가 이미 설치되어 있고 이를 설정하기 위한 Command도 제공하고 있다.

pi@raspberrypi:~$ sudo raspi-config

위 명령을 입력 한 후, 9(Advanced Options) -> A4 SSH -> Enable 을 선택하면 SSH Server를 구동 할 수 있다.

20151215_171049

20151215_171209

20151215_171221

라즈베리파이의 IP 주소 확인

pi@raspberrypi:~$ sudo ifconfig
pi@raspberrypi:~$ sudo ifconfig
eth0      Link encap:Ethernet  HWaddr b8:27:eb:ee:bc:e8  
          inet addr:192.168.0.100  Bcast:192.168.0.255  Mask:255.255.255.0
          inet6 addr: fe80::400:765c:d4b4:f6af/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:7178 errors:0 dropped:0 overruns:0 frame:0
          TX packets:452 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:438189 (427.9 KiB)  TX bytes:47107 (46.0 KiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:200 errors:0 dropped:0 overruns:0 frame:0
          TX packets:200 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:16656 (16.2 KiB)  TX bytes:16656 (16.2 KiB)

pi@raspberrypi:~$

PuTTY를 이용한 원격 접속

(1) 아래 링크에서 PuTTY를 다운로드 받아 실행한다. http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html

20151215_185012

(2) Host Name에 라즈베리파이의 IP 주소를 입력하고 Open 버튼을 누른다.

20151215_185209

(3) 아래와 같이 로그인 화면이 나오면 접속 성공 ( Default ID: pi / Default Password : raspberry )

20151215_185514

SFTP를 이용한 파일 시스템 관리

WinSCP는 Windows용 SFTP 및 FTP 클라이언트 프로그램이고 오픈소스 프리웨어이며 아래 경로에서 다운로드 할 수 있다. WinSCP 다운로드

WinSCP를 다운로드 하고 설치 한 후, 아래 그림과 같이 호스트 이름과 사용자 계정/비밀번호를 입력하면 라즈베리파이의 파일 시스템을 관리 할 수 있다.

20151215_190813

20151215_190722

위 항목들을 모두 정상적으로 설정하면, 이제 부터 라즈베리파이를 원격에서 접속하여 제어 및 파일들을 관리 할 수 있다.

[Electric Skateboard]Wi-Fi Receiver and Controller

0

Hardware Materials

Skateboard Materials

34″ Cruiser Longboard Deck

Trucks

90mm Wheels

Bearings 8mm

Materials for electric skateboard

Motor

Motor Mount

44T Drive Wheel Pulley Adapter

12S 120A ESC

TorqueBoards ESC Programming Card

12S UBEC](http://diyelectricskateboard.com/product/12s-ubec/) On/Off Anti-Spark Power Switch

For more detailed information, refer to this URL. http://www.life4iot.com/2015/10/30/electric-skateboard1-materials/?lang=en

Materials for Wi-Fi Receiver

WIZwiki-W7500ECO

wizwiki-w7500eco3d0degtop

WizFi250

wizfi250-h_1_small

Hardware Configuration for Motor control

Already you know,PWM is need to operate servo motor. So I made PWM signal using WIzwiki-W7500ECO board and this picture is configuration for motor control.

You need ESC(for controlling speed of motor) and UBEC(for input 5V to WIZwiki-W7500ECO).

20151029_14325720151029_212112

If you refer to this Link, you can get more detail information and download source code for motor test.

Hardware Configuration for Wi-Fi Receiver

Wi-Fi receiver is device for communicating control data with skateboard. I made Wi-Fi receiver using WizFi250 which is Wi-Fi module by WIZnet. This picture is configuration for Wi-Fi receiver.

20151029_143029

I am not hardware engineer so I couldn’t handle hardware well and this hardware may be showing some lack. But it’s operation is very well. In this device,it has 3 output pin and 2 input pin. Input pins are for getting voltage from battery and Output pins are for controlling motor.

20151030_082315

Wi-Fi Receiver Firmware

Source Code

Firmware of Wi-Fi receiver is operated as belows. If smartphone push “Speed Up” button, Wi-Fi receiver will increase motor speed and It it push “Speed Down” button, Wi-Fi receiver will decrease motor speed about 5 percentage.

20151030_084225

In exception situation like disconnect Wi-Fi or socket, Wi-Fi receiver will stop motor as below. But even if motor is stopped, skateboard can’t stop because it has acceleration.

20151030_085505

You can download source code for Wi-Fi receiver at this Link

#include "mbed.h"
#include "Servo.h"
#include "WizFi250Interface.h"

#define SECURE WizFi250::SEC_WPA2_MIXED
#define SSID "Kaizen_Skate"
#define PASS "qwertyuiop"

#define SERVER_PORT    5000
#define STOP_MOTOR     50


#if defined(TARGET_WIZwiki_W7500P)
    WizFi250Interface wizfi250(PC_2,PC_3,PC_13,PC_14,PC_12,NC,115200);
    Serial pc(USBTX, USBRX);
    Servo motor_speed(PC_8);      // it used to percentage value ( 0 ~ 1 )
    DigitalOut green_led(LED2);    
    DigitalOut red_led(LED1);
    DigitalOut blue_led(LED3);
#endif

#define START_MSG       "START"
#define END_MSG         "END\n"
#define CONTROL_UP      "UPXX"
#define CONTROL_DOWN    "DOWN"
#define CONTROL_CRUISER "CRUI"
#define CONTROL_STOP    "STOP"

#define STATUS_CMD_OK   "OKXX"
#define STATUS_CMD_FAIL "FAIL"

#define CONTROL_MSG_SIZE    19

// Control Pkt Format : START,CONTROL,SPEED_VALUE,END
//                      START,UPXX,500,END
// Status Pkt Format : START,CONTROL,SPEED_VALUE,STATUS,END

struct control_pkt{
    char start_msg[6];
    char control_msg[5];
    char speed_msg[4];
    char end_msg[4];
};
void parse(char buffer[], int *j, char *string);

int main() {    
    int recv_cnt,j;
    volatile float speed_value = 0.5;
    char recv_control_msg[100];
    char status_msg[100];
    control_pkt control;

    pc.baud(115200);
    green_led = 1; red_led = 1; blue_led = 1;

    wizfi250.init();
    wizfi250.setAntMode(WizFi250::PCB);
    wizfi250.setAddress("192.168.100.2","255.255.255.0","192.168.100.1");
    if ( wizfi250.connect(SECURE, SSID, PASS, WizFi250::WM_AP))
    {
        red_led = 0;
        return -1;
    }
    printf("IP Address is %s\r\n", wizfi250.getIPAddress());
    green_led = 0; red_led = 0; blue_led = 0;

    TCPSocketServer server;
    TCPSocketConnection client;

    while(true)
    {
        if( client.is_connected() == false )
        {
            green_led = 1; red_led = 1; blue_led = 0;            
            client.close();
            server.close();

            if(speed_value == 0.5)
            {
                server.bind(SERVER_PORT);
                server.listen();

                printf("\nWait for new connection...\r\n");
                server.accept(client);
                client.set_blocking(false, 1500);
            }
            else if(speed_value <= 0.4)
            {
                printf("Speed decrease +\r\n");

                speed_value = 0.5;
                motor_speed = speed_value;
            }
            else
            {
                printf("Speed decrease -\r\n");
                speed_value = 0.5;  motor_speed = speed_value;
            }
        }
        else
        {
            motor_speed = speed_value;

            green_led = 0; red_led = 1; blue_led = 1;
            //recv_cnt = client.receive_all((char*)recv_control_msg, sizeof(control)-1);
            recv_cnt = client.receive_all((char*)recv_control_msg, sizeof(control));

            j=0;
            parse(recv_control_msg, &j, control.start_msg);
            parse(recv_control_msg, &j, control.control_msg);
            parse(recv_control_msg, &j, control.speed_msg);
            parse(recv_control_msg, &j, control.end_msg);

            if(recv_cnt > 0)
            {
                if( (strncmp(control.start_msg,START_MSG,sizeof(control.start_msg)) != 0) || 
                    (strncmp(control.end_msg,END_MSG,sizeof(control.end_msg)) != 0) )
                {
                    printf("TEST Error\r\n");
                    sprintf(status_msg,"%s,%03d,%s,%s\n",START_MSG,int(speed_value*100),STATUS_CMD_FAIL,END_MSG);
                    client.send_all((char*)status_msg,strlen(status_msg));
                    continue;
                }

                if( strncmp(control.control_msg,CONTROL_UP,sizeof(control.control_msg)) == 0 )
                {
                    speed_value += 0.05; 
                    motor_speed = speed_value;
                    printf("TEST UP %f\r\n",speed_value); 
                }
                else if( strncmp(control.control_msg,CONTROL_DOWN,sizeof(control.control_msg)) == 0)
                {
                    speed_value -= 0.05; 
                    motor_speed = speed_value;
                    printf("TEST DOWN %f\r\n",speed_value);
                }
                else if( strncmp(control.control_msg,CONTROL_CRUISER,sizeof(control.control_msg)) == 0)
                {
                    printf("TEST CRUISER\r\n"); 
                    speed_value = (float)(atoi(control.speed_msg)) / 100;
                    motor_speed = speed_value;
                }
                else if( strncmp(control.control_msg,CONTROL_STOP,sizeof(control.control_msg)) == 0)
                {
                    printf("TEST STOP\r\n"); 
                    speed_value = 0.5;
                    motor_speed = speed_value;
                }
                else
                {
                    printf("TEST Error 1-2\r\n");
                    sprintf(status_msg,"%s,%03d,%s,%s\n",START_MSG,int(speed_value*100),STATUS_CMD_FAIL,END_MSG);
                    client.send_all((char*)status_msg,strlen(status_msg));
                    continue;
                }

                sprintf(status_msg,"%s,%03d,%s,%s\n",START_MSG,int(speed_value*100),STATUS_CMD_OK,END_MSG);
                client.send_all((char*)status_msg,strlen(status_msg));
            }
            else
            {
                sprintf(status_msg,"%s,%03d,%s,%s\n",START_MSG,int(speed_value*100),STATUS_CMD_OK,END_MSG);
                client.send_all((char*)status_msg,strlen(status_msg));
            }
        }
    }// while
}

void parse(char buffer[], int *j, char *string) {
//extracts next location string data item from buffer
    int i=0;
    for (i=0; i<=strlen(buffer); i++) {  //TOTAL SIZE OF RETURNED DATA
        if ((buffer[*j+i] == ',')||(buffer[*j+i] == '\0' )) { //IF comma or end of string
            //comma is the string field delimiter
            string[i]=0; //SETS END OF SRTRING TO 0
            *j=*j+i+1; //UPDATES to 1 after comma seperated value
            break;
        } else string[i]=buffer[*j+i]; //Keep adding to the string
    }
}

 

Andorid Application

Screenshot_2015-10-29-16-20-25

Source Code

https://github.com/kaizen8501/WiFi_Skateboard

Demo Video

[Electric Skateboard]#2.DC Motor Test

0

DC Motor Test

Hardware Configuration for Motor Control

Already you know,PWM is need to operate DC motor. So I made PWM signal using WIzwiki-W7500ECO board and this picture is configuration for motor control.

You need ESC(for controlling speed of motor) and UBEC(for input 5V to WIZwiki-W7500ECO).

20151029_14325720151029_212112

Test Software

I write simply code to test DC motor that is operated which PWM value in the mbed environment. As a result, If WIZwiki-W7500ECO has PWM value more than 0.5, DC motor goes forward and If it has less than 0.5, DC motor goes backward.

0.4 ~ 0.55 of PWM values are stop value.

#include "mbed.h"
#include "Servo.h"

int main() {    
    uint8_t ch;
    volatile float speed_value = 0.5;

    Servo myservo(PC_8);
    Serial pc(USBTX,USBRX);

    pc.baud(115200);
    wait(1);
    pc.printf("TEST Start\r\n");

    while(1)
    {
        pc.printf("speed : %f\r\n",speed_value);
        myservo = speed_value;

        ch = pc.getc();

        if(ch == 'u')
        {
            speed_value += 0.001;
        }
        else if(ch == 'd')
        {
            speed_value -= 0.001;
        }

        else if(ch == 's')
        {
            myservo = 0.5;
            wait(0.5);

            speed_value = 0.41;
            myservo = speed_value;
            wait(5);

            speed_value = 0.5;
            myservo = speed_value;
        }
    }
}

You can download this source code at this Link

Demo Video

[Electric Skateboard]#1. Materials

0

Hardware Materials

Skateboard Materials

There are materials for basic skateboard. I want to use skateboard for riding so I purchase 90mm wheels and 34″ longboard deck. For more information to purchase these materials, refer to these links.

34″ Cruiser Longboard Deck

30-inch-Electric-Longboard-325x127

Trucks

20151029_091514

90mm Wheels

90mm-longboard-wheels-epower-neon-green

Bearings 8mm

longboard-bearings-322x215

Materials for electric skateboard

Motor

There are specification of this motor. ( 50mm RC Brushless Motor, 2200 Watts / 2.95HP Motor, 200KV, 80Amp, 50mm x 65mm, 8mm shaft )

Electric-Skateboard-Motor2

Motor Mount

50mm-motor-mount

44T Drive Wheel Pulley Adapter

44T-Electric-Skateboard-Drive-Wheel-Pulley16t-htd5-motor-pulley-322x215

12S 120A ESC

TorqueBoards-12S-120A-ESC-OPTO1

TorqueBoards ESC Programming Card

torqueboards-esc-programming-card

12S UBEC

12s-ubec-322x215

On/Off Anti-Spark Power Switch

antispark-highvoltage-blue-led-switch

Assemble materials

It is so hard to me because I didn’t have experience about RC. But you can do it more than me if you refer to these pictures.

Assemble wheel adapter

20151023_11572020151023_115744

Attache wheels and trucks to deck

20151021_20493120151021_20491020151021_20492420151029_134552

[Electric Skateboard]#2. DC Motor Test

0

Mortor Test

Hardware 구성

다들 아시겠지만, DC Motor는 MCU의 PWM 파형을 이용하여 구동한다. PWM 파형을 제어하기 위해 Cortex-M0 계열의 WIZwiki-W7500ECO 보드를 이용하였으며, 하드웨어 구성은 아래 그림과 같다.

20151029_143257

20151029_212112

Test Software

Software는 mbed 환경에서 구현하였다. DC Motor를 제어하기 위해, mbed에서는 Servo 라이브러리를 제공하며 사용방법은 아래 주소를 참고 하기 바란다. (Servo Library)

내 스케이트 보드에 장착되어 있는 보드의 모터에 어떤 값을 입력하였을 때, 전진하고 후진하는지 알아 보기 위해 아래와 같은 테스트 코드를 작성하였다. mbed Servo는 0 ~ 1 사이의 값으로 PWM 파형을 생성하며, 스케이트 보드에 장착된 모터는 0.4 ~ 0.55 값에서 중립 상태를 보인다.

#include "mbed.h"
#include "Servo.h"

int main() {    
    uint8_t ch;
    volatile float speed_value = 0.5;

    Servo myservo(PC_8);
    Serial pc(USBTX,USBRX);

    pc.baud(115200);
    wait(1);
    pc.printf("TEST Start\r\n");

    while(1)
    {
        pc.printf("speed : %f\r\n",speed_value);
        myservo = speed_value;

        ch = pc.getc();

        if(ch == 'u')
        {
            speed_value += 0.001;
        }
        else if(ch == 'd')
        {
            speed_value -= 0.001;
        }

        else if(ch == 's')
        {
            myservo = 0.5;
            wait(0.5);

            speed_value = 0.41;
            myservo = speed_value;
            wait(5);

            speed_value = 0.5;
            myservo = speed_value;
        }
    }
}

위 코드는 https://developer.mbed.org/users/kaizen/code/Servo_HelloWorld/에서 확인 및 다운로드 할 수 있다.

Demo Video