이 시각 주요 뉴스

여백

‘안드로이드 띵스’ 내부 구조와 프로그래밍 방법 (2)

이나리 기자l승인2017.05.26 15:05:10l수정2017.05.26 15:22

크게

작게

메일

인쇄

신고

[CCTV뉴스=이나리 기자] IoT 개발에서 가장 중요한 부분은 ‘연결성, 보안, 개발 환경’이다. 또 쉽게 개발할 수 있는 장점이 있어야 한다. 안드로이드 띵스(Android Things)의 장점은 안드로이드 운영체제와 같은 맥락의 운영체제이고, 자바를 기반으로 하는 시스템이다. 따라서 안드로이드 띵스는 자바(JAVA) 언어를 통해 IoT 하드웨어 장치를 만들 수 있고, 기존의 IoT 플랫폼 위에 안드로이드 띵스를 설치해 운영할 수 있는 것이 장점이다.

스마트폰 환경에서는 안드로이드 운영체제를 통해 플랫폼과 앱 개발 환경을 제공하고 있는 구글이 이제 IoT 환경용 플랫폼과 개발 환경을 제공하기 시작했다. 안드로이드 띵스의 장단점은 다음과 같다.

◇ 장점
- 기존 스마트폰 앱처럼 IoT 앱을 개발할 수 있다.
- JAVA 언어를 사용해 앱을 개발할 수 있다.
- 다양한 플랫폼을 지원하고 있다.
- 하드웨어에 대한 이해가 적더라도 IoT 프로그래밍을 할 수 있다.
- 운영체제, 네트워크에 대한 별도의 개발이 필요 없다.
- 자바로 프로그래밍 할 수 있기 때문에 이식성이 좋다.

◇ 단점
- 아직까지는 IoT 플랫폼 중 고가의 하드웨어 제품군만 지원한다.
- 아직까지는 많은 플랫폼에 적용이 되지 않고 있다.
- 경량화된 버전이 나오면(16비트 프로세서 지원) 확산 가능성이 있으나 아직까지는 미지수다.

안드로이드 띵스는 발표된지는 얼마 되지 않았고 안드로이드 운영체제와 같은 파괴력은 가지고 있지 않기 때문에 앞으로 어떻게 될지는 예측하기 어렵다. 다만 IoT의 새로운 방향으로 제시되고 있는 방법이라고 생각하면 될 것이다.

▲ [그림1] 안드로이드 띵스 관련 개발 지원 웹사이트 그림 


 ◇ 안드로이드 띵스(Android Things)의 개발 방법

안드로이드 띵스의 개발 방법은 기존 판매중인 IoT 모듈에 안드로이드 띵스 운영체제 이미지를 올린 후 사용하는 방법이다. 현재 지원하고 있는 하드웨어 플랫폼은 인텔 에디슨(Edison), 인텔 줄(Joule), NXP 피코(Pico) I.MX6UL, NXP 아르곤(Argon) I.MX6UL 등이 있다[그림2]. 더불어 라즈베리파이 3(Raspberry Pi 3) 하드웨어를 지원하고 있지만, 아직까지는 많은 하드웨어를 지원하고 있지는 않다.

안드로이드 띵스는 안드로이드 운영체제처럼 자바 언어를 지원하기 때문에 빠른 프로세서와 많은 용량의 메모리를 요구하고 있다. 이런 점이 개선돼 다양한 하드웨어를 지원할 수 있다면 안드로이드 띵스는 다양한 플랫폼에서 사용될 것으로 예상된댜.

안드로이드 띵스는 [그림2]에서 지원하는 하드웨어를 사용해 개발하거나 구글에서 BSP(Board Support Package)를 입수해 별도로 개발한 하드웨어에 구글 띵스(Google Things) 운영체제를 탑재시켜 개발하는 방법이 있다.
  

▲ [그림2] 안드로이드 띵스를 지원하는 하드웨어 


◇ 안드로이드 띵스(Android Things)의 구조

[그림3]은 안드로이드 띵스의 내부 구조를 보여 준다. 안드로이드 띵스는 안드로이드 운영체제와 마찬가지로 리눅스 커널위에 자바언어로 만든 응용 프로그램을 처리하는 각종 서비스로 구성됐다. 또 안드로이드 띵스는 안드로이드와 동일한 구조를 가지고 유사한 프레임워크를 사용하고 있기 때문에 기존의 안드로이드 앱 개발자도 쉽게 안드로이드 띵스 앱을 개발 수 있도록 지원하고 있다. 특히 안드로이드 띵스는 안드로이드와 달리 디스플레이를 탑재하지 않아도 동작할 수 있도록 만들어졌다.

▲ [그림3] 안드로이드 띵스의 내부 구조 

리눅스 커널(Linux Kernel) = 안드로이드 띵스의 주 운영체제, 프로세서와 메모리를 관리하고 응용 프로그램을 실행, 각종 하드웨어 장치를 위한 디바이스 드라이버를 탑재하고 있다.

HAL(Hardware Abstraction Layer) = 안드로이드 띵스에서 하드웨어를 다루기 위한 소프트웨어 레이어, 안드로이드 띵스 앱에서 하드웨어를 다루기 위한 각종 라이브러리와 처리 루틴.
네이티브 C/C++ 라이브러리(Native C/C++ Libraries) = C/C++ 언어를 지원하기 위한 각종 라이브러리 루틴과 함수들

띵스 서포트 라이브러리(Things Support Library) = 안드로이드 띵스에서 GPIO, PWM, I2C, SPI, UART 등을 처리하기 위한 Peripheral I/O API 등

구글 서비스(Google Services) = 구글 서비스를 지원하기 위한 함수와 처리 루틴

자바 API 프레임워크(JAVA API Framework) = JAVA 기능을 지원하기 위한 API

안드로이드 띵스를 지원하지 않는 패키지는 안드로이드와 달리 다음과 같은 패키지를 사용할 수 없다.

 CalendarContract
 ContactsContract
 DocumentsContract
 DownloadManager
 MediaStore
 Settings
 Telephony
 UserDictionary
 VoicemailContract 

 

안드로이드 띵스의 엑티비티는 다음과 같은 엑티비트를 통해 앱을 구성해야 한다. 여기서 주목해야 할 내용은 IOT_LAUNCHER이며, IOT_LAUNCHER가 안드로이드 띵스를 구동하기 위한 기본 엑티비티다.

<application
     android:label="@string/app_name">
     <activity android:name=".HomeActivity">
         <!-- Launch activity as default from Android Studio -->
         <intent-filter>
             <action android:name="android.intent.action.MAIN"/>
             <category android:name="android.intent.category.LAUNCHER"/>
         </intent-filter> 
<!-- Launch activity automatically on boot -->
         <intent-filter>
             <action android:name="android.intent.action.MAIN"/>
             <category android:name="android.intent.category.IOT_LAUNCHER"/>
             <category android:name="android.intent.category.DEFAULT"/>
         </intent-filter>
     </activity>
 </application>
 


안드로이드 띵스는 안드로이드와 유사한 서비스를 지원하고 있다. [그림4]에서 안드로이드 띵스에서 지원하는 서비스를 확인할 수 있다.

▲ [그림4] 안드로이드 띵스 지원과 비지원 서비스 


◇ 안드로이드 띵스를 이용한 앱 개발 방법
 안드로이드 띵스를 이용한 앱 개발 방법을 통해 어떻게 안드로이드 띵스를 개발하는지 살펴보겠다. [그림5]는 인텔의 에디슨 보드에 LED를 연결해 제어하는 구조다. 하드웨어 연결은 [그림5]와 같다. 구조는 1개의 LED, 1 버튼을 이용해 어떻게 제어하는지에 대해서 살펴보는 소스다.

▲ [그림5] 안드로이드 띵스의 하드웨어 연결 그림 

LED 제어 앱에 대한 manifest.xml 파일

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.example.androidthings.button">

    <application android:allowBackup="true" android:icon=
        "@android:drawable/sym_def_app_icon"
         android:label="@string/app_name">

        <uses-library android:name="com.google.android.things"/>

        <activity android:name=".ButtonActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>

            <!-- Launch activity automatically on boot -->
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.IOT_LAUNCHER"/>
                 <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
         </activity>
     </application>

</manifest>

BoardDefaults.java는 안드로이드 띵스의 보드를 설정하는 소스다.

package com.example.androidthings.button;

import android.os.Build;

import com.google.android.things.pio.PeripheralManagerService;

import java.util.List;

@SuppressWarnings("WeakerAccess")
public class BoardDefaults {
    private static final String DEVICE_EDISON_ARDUINO = "edison_arduino";
    private static final String DEVICE_EDISON = "edison";
    private static final String DEVICE_JOULE = "joule";
    private static final String DEVICE_RPI3 = "rpi3";
    private static final String DEVICE_PICO = "imx6ul_pico";
    private static final String DEVICE_VVDN = "imx6ul_iopb";
    private static String sBoardVariant = "";

    /**
     * LED 포트에 대한 설정
     */
    public static String getGPIOForLED() {
        switch (getBoardVariant()) {
            case DEVICE_EDISON_ARDUINO:
                return "IO13";
            case DEVICE_EDISON:
                return "GP45";
            case DEVICE_JOULE:
                return "LED100";
            case DEVICE_RPI3:
                return "BCM6";
            case DEVICE_PICO:
                return "GPIO4_IO21";
            case DEVICE_VVDN:
                return "GPIO3_IO06";
            default:
                throw new IllegalStateException("Unknown Build.DEVICE " + Build.DEVICE);
        }
    }

    /**
     * 버튼에 대한 정의.
     */
    public static String getGPIOForButton() {
        switch (getBoardVariant()) {
            case DEVICE_EDISON_ARDUINO:
                return "IO12";
            case DEVICE_EDISON:
                return "GP44";
            case DEVICE_JOULE:
                return "FLASH_TRIGGER";
            case DEVICE_RPI3:
                return "BCM21";
            case DEVICE_PICO:
                return "GPIO4_IO20";
            case DEVICE_VVDN:
                return "GPIO3_IO01";
            default:
                throw new IllegalStateException("Unknown Build.DEVICE " + Build.DEVICE);
        }
    }

    private static String getBoardVariant() {
        if (!sBoardVariant.isEmpty()) {
            return sBoardVariant;
        }
        sBoardVariant = Build.DEVICE;
        // 현재 어떠한 보드 버전인지 확인
        if (sBoardVariant.equals(DEVICE_EDISON)) {
            PeripheralManagerService pioService = new PeripheralManagerService();
            List<String> gpioList = pioService.getGpioList();
            if (gpioList.size() != 0) {
                String pin = gpioList.get(0);
                if (pin.startsWith("IO")) {
                    sBoardVariant = DEVICE_EDISON_ARDUINO;
                }
            }
        }
        return sBoardVariant;
    }
}

ButtonActivity.java는 버튼을 누를 때 어떻게 처리하는 가에 대한 루틴 소스다.

package com.example.androidthings.button; 
import android.app.Activity;
 import android.os.Bundle;

import com.google.android.things.contrib.driver.button.Button;
 import com.google.android.things.contrib.driver.button.ButtonInputDriver;
 import com.google.android.things.pio.Gpio;
 import com.google.android.things.pio.PeripheralManagerService;
 import android.util.Log;
 import android.view.KeyEvent;

import java.io.IOException;

/**
  * 버튼 제어에 대한 소스
 */
public class ButtonActivity extends Activity {
     private static final String TAG = ButtonActivity.class.getSimpleName();

private Gpio mLedGpio;
     private ButtonInputDriver mButtonInputDriver;

@Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         Log.i(TAG, "Starting ButtonActivity");

PeripheralManagerService pioService = new PeripheralManagerService();
         try {
             Log.i(TAG, "Configuring GPIO pins");
             mLedGpio = pioService.openGpio(BoardDefaults.getGPIOForLED());
             mLedGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);

Log.i(TAG, "Registering button driver");
             // 버튼 제어 초기화
            mButtonInputDriver = new ButtonInputDriver(
                     BoardDefaults.getGPIOForButton(),
                     Button.LogicState.PRESSED_WHEN_LOW,
                     KeyEvent.KEYCODE_SPACE);
             mButtonInputDriver.register();
         } catch (IOException e) {
             Log.e(TAG, "Error configuring GPIO pins", e);
         }
     }
   
     // 버튼을 누를 때 처리하는 소스
    @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_SPACE) {
             // Turn on the LED
             setLedValue(true);
             return true;
         }
         return super.onKeyDown(keyCode, event);
     }
     // 버튼을 눌렀다 땔 때 처리하는 소스
    @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_SPACE) {
             // Turn off the LED
             setLedValue(false);
             return true;
         }

return super.onKeyUp(keyCode, event);
     }

/**
      * LED 처리 소스
     */
     private void setLedValue(boolean value) {
         try {
             mLedGpio.setValue(value);
         } catch (IOException e) {
             Log.e(TAG, "Error updating GPIO value", e);
         }
     }

@Override
     protected void onDestroy(){
         super.onDestroy();

if (mButtonInputDriver != null) {
             mButtonInputDriver.unregister();
             try {
                 mButtonInputDriver.close();
             } catch (IOException e) {
                 Log.e(TAG, "Error closing Button driver", e);
             } finally{
                 mButtonInputDriver = null;
             }
         }

if (mLedGpio != null) {
             try {
                 mLedGpio.close();
             } catch (IOException e) {
                 Log.e(TAG, "Error closing LED GPIO", e);
             } finally{
                 mLedGpio = null;
             }
             mLedGpio = null;
         }
     }
 }

앞의 세 소스에서 볼 수 있듯이 안드로이드 앱을 개발하는 것과 같은 방식으로 안드로이드 띵스 앱을 개발할 수 있다는 것을 확인할 수 있다.

안드로이드 띵스를 개발하는 방법은 우선 안드로이드 띵스를 지원하는 보드를 먼저 구해 안드로이드 띵스 운영체제 이미지를 탑재해 개발하는 것이다. 안드로이드 띵스를 지원하는 이미지는 안드로이드 개발자 사이트(https://developer.android.com/things/preview/download.html)에서 다운로드한 후 설치할 수 있다. [그림6]은 현재 지원하는 보드에 대한 지원 이미지에 대한 내용이다.

▲ [그림6] 안드로이드 띵스 보드에 따른 지원 이미지

안드로이드 띵스 이미지를 보드에 탑재하기 위해서는 우선 안드로이드 개발자 사이트에서 보드에 해당되는 운영체제(OS) 이미지를 다운로드해서 adb를 툴을 통해 설치하는 것이다.

우선 adb 툴을 이용해 보드를 fastboot 모드로 진입시킨다.

$ adb reboot bootloader 

Fastboot 모드로 정상적으로 진입했는지 확인한다.

$ fastboot devices
1b2f21d4e1fe0129    fastboot 

Fastboot를 이용해 안드로이드 띵스 이미지를 보드에 설치한다.

$ fastboot \
     flash gpt partition-table.img \
     flash u-boot u-boot-edison.bin \
     flash boot_a boot.img \
     flash boot_b boot.img \
     flash system_a system.img \
     flash system_b system.img \
     flash userdata userdata.img \
     erase misc \
     set_active a 
$ fastboot \
     flash gapps_a gapps.img \
     flash gapps_b gapps.img

$ fastboot \
     flash oem_a oem.img \
     flash oem_b oem.img

재부팅해서 다시 동작시킨다.

$ fastboot reboot 

지금까지 안드로이드 띵스의 내부 구조와 프로그래밍 방법에 대해 살펴봤다. 다음에는 안드로이드 띵스를 어떻게 세부적으로 프로그래밍하고 제어하는지 살펴보도록 하겠다.

글 : 라영호 테뷸라 대표이사
자료제공 : 테뷸라(www.tebular.com)

이나리 기자  narilee@epnc.co.kr
<저작권자 © CCTV뉴스, 무단 전재 및 재배포 금지>

이나리 기자의 다른기사 보기
기사 댓글
첫번째 댓글을 남겨주세요.
0 / 최대 400byte

숫자를 입력해주세요

욕설등 인신공격성 글은 삭제합니다.
여백
여백
여백
여백
매체소개공지사항보안자료실기사제보광고문의불편신고개인정보취급방침청소년보호정책이메일무단수집거부    [기사ㆍ기고 문의 : desk@cctvnews.co.kr]
(주)테크월드 08507 서울특별시 금천구 가산디지털1로 168, 1012-1호 (가산동, 우림라이온스밸리 C동)  |  제호: 씨씨티브이뉴스  |  발행일: 2009년 2월 19일
대표전화 : 02)2026-5700  |  팩스 : 02)2026-5701  |  이메일 : webmaster@techworld.co.kr  |  청소년보호책임자 : 박지성
통신판매업신고번호 : 제 2008-서울금천-0415 호  |  발행·편집인 : 박한식  |  인터넷신문등록번호 : 서울, 아 00607  |  등록일 : 2008.06.27
Copyright © 2020 CCTV뉴스. All rights reserved .