반응형

이제부터는 본격적으로 Firmata를 분석해보고 사용해보도록 하자.

나의 개인적인 취향 상 분석을 먼저 면밀하게 한 후 사용을 하는 것 보다는 실제 동작하는 모습을

보면서 연관된 코드를 뜯어보는 것이 수월하기에 이번 포스팅 역시 그러한 방식으로 진행을 하겠다.

 

Firmata의 전체적인 구성은 호스트 PC 쪽의 Firmata Client와 아두이노쪽의 Firmata 스케치로 구성되어

있다. 물론 아두이노쪽에서 범용 Firmata 스케치가 아닌 Firmata 라이브러리를 사용한다면 구성이 조금

달라지겠지만 이 시리즈에서는 범용 Firmata 스케치를 사용한다는 전제로 포스팅을 하고 있다.

 

이러한 전제 하에 지난 번 포스팅의 가장 마지막에 실행했던 digital_output.py 소스를 가지고 간단하게

Firmata의 흐름과 관련 소스를 살펴보도록 하겠다.

 

다만 모든 소스를 세세하게 분석하는데는 많은 시간과 지식이 필요하므로 이번 포스팅에서는 우선 프로토콜이

어떻게 구현되는 지에 초점을 맞춰 살펴보도록 하자.

 

Firmata Client - pymata4

 

이전 포스팅에서도 언급한 바와 같이 Firmata Client는 다양한 언어로 구현이 가능하다. 그리고 이 시리즈

에서는 Python으로 된 Client를 사용하고 있으며 그 중에서도 자료가 가장 잘 정리되어있는 pymata4를

대상으로 한다.

 

pymata4의 핵심 소스는 3개의 파일로 구성이 되어있다.

 

  • pin_data.py : 아두이노의 pin mode를 설정하기 위한 각종 속성이 정의되어 있다.

  • private_constants.py : Firmata 프로토콜을 정의하는 여러가지 상수가 정의되어 있다.

  • pymata4.py : 가장 핵심적인 소스로 사용자들인 호출하여 사용할 API들이 구현되어있다.

 

단순히 Firmata를 이용하기 위해서는 pymata4.py에 있는 API를 어떻게 호출하는 지만 알면 된다. 이제

예제 코드인 digital_output.py로부터 시작하여 차근차근 알아보자.

 

digital_output.py 코드는 매우 단순하다 (코드에 집중하기 위해 원래 있던 주석은 모두 삭제하였다).

사전 이해를 위해 간단하게 코드에 주석을 붙였다. 

import sys
import time

from pymata4 import pymata4

# 사용할 디지털 핀 번호 설정. 기본 예제 코드에서는 6번을 사용한다.
DIGITAL_PIN = 6  # arduino pin number

# blink라는 함수를 구현하였다. 실제로 Firmata를 구동시키는 함수이다.
def blink(my_board, pin):
    # 가장 먼저 PIN 모드를 설정한다. 파라미터로 전달받은 핀(6번 핀)을 출력 모드로 설정한다. 
    my_board.set_pin_mode_digital_output(pin)

    # 1초 간격으로 6번 핀에 HIGH(1), LOW(0) 신호를 4회 반복하여 보낸다.
    # 이 6번 핀에 LED를 연결하여 1초 간격으로 LED가 켜지고 꺼지는 것을 4회 반복한다.
    # 직전 포스팅의 마지막 영상 참조.
    for x in range(4):
        print('ON')
	# 아두이노 보드의 6번 핀에 HIGH(1) 신호를 보낸다. LED가 연결된 경우 LED가 켜진다.
        my_board.digital_write(pin, 1)
        time.sleep(1)
        print('OFF')
	# 아두이노 보드의 6번 핀에 LOW(0) 신호를 보낸다. LED가 연결된 경우 LED가 꺼진다.
        my_board.digital_write(pin, 0)
        time.sleep(1)
    # 아두이노와의 연결을 끊는다.
    my_board.shutdown()

# 아두이노 보드의 인스턴스를 생성한다.
board = pymata4.Pymata4()
try:
    # blink 함수를 호출한다. 위에서 생성된 아두이노 보드 인스턴스와 사용할 핀 번호를 
    # 파라미터로 전달한다.
    blink(board, DIGITAL_PIN)
except KeyboardInterrupt:
    board.shutdown()
    sys.exit(0)

 

이제 위의 코드를 차근차근 살펴보자.


보드(아두이노) 인스턴스 생성하기

pymata4의 편리한 점 중 하나는 아두이노를 자동으로 찾아준다는 것이다. 아두이노와 호스트 PC간에 

정상적으로 연결만 되어있으면 단지 생성자 함수인 Pymata4()를 호출하는 것만으로 자동으로 

아두이노 보드를 찾아준다.

 

보드를 찾는 과정은 다음과 같은 로그로 확인할 수 있다.

 


/Volumes/Storage2/Firmata/FirmataTest/venv/bin/python /Volumes/Storage2/Firmata/FirmataTest/digital_write.py

pymata4: Version 1.10

 

Copyright (c) 2020 Alan Yorinks All Rights Reserved.

 

Opening all potential serial ports...

/dev/cu.wchusbserial1a1230

 

Waiting 4 seconds(arduino_wait) for Arduino devices to reset...

 

Searching for an Arduino configured with an arduino_instance = 1

Arduino compatible device found and connected to /dev/cu.wchusbserial1a1230

 

Retrieving Arduino Firmware ID...

Arduino Firmware ID: 1.1 FirmataExpress.ino

 

Retrieving analog map...

Auto-discovery complete. Found 22 Digital Pins and 8 Analog Pins


 

물론 명시적으로 필요한 파라미터를 전달하여 보드의 인스턴스를 생성할 수도 있다. 전달 가능한 파라미터는

다음과 같다(= 기호의 좌측은 파라미터 이름, 우측은 파라미터의 기본 값이다. Python은 파라미터 이름을 

사용하면 파라미터 순서와 관계없이 파라미터를 전달할 수 있으며 파라미터를 전달하지 않으면 = 우측의

기본 값이 사용된다).

 

  • com_port=None : 아두이노와 연결된 호스트PC의 포트. 위의 예에서는 /dev/cu.wchusbserial1a1230이다(매킨토시 PC에서 연결한 경우임).

  • baud_rate=115200 : 시리얼 통신 전송 속도

  • arduino_instance_id=1 : 아두이노 보드의 인스턴스 ID

  • arduino_wait=4 : 아두이노 리셋까지의 대기 시간

  • sleep_tune=0.000001 : 튜닝을 위한 파라미터로 일반적으로 수정하지 않는 것이 좋다.

  • shutdown_on_exception=True : RuntimeError Exception이나 KeyboardInterrupt exception이 발생한 경우 shutdown 함수를 호출할지의 여부

  • ip_address=None : StandardFirmataWifi 스케치를 사용하는 경우 Wi-Fi 기기의 IP 주소

  • ip_port=None : StandardFirmataWifi 스케치를 사용하는 경우 Wi-Fi 기기의 포트 번호. 보통 3030 사용

 

이와 같은 많은 파라미터들이 있으나 아무런 파라미터를 전달하지 않더라도 pymata4가 자동으로 연결된 

보드를 찾아 주는 것이다.

 

이 생성자 함수는 pymata4에서 사용할 전역 변수들을 초기화 하고 실제로 보드가 연결된 시리얼 포트를

찾는 것까지 상당히 긴 코드로 구현이 되어있어 자세한 설명은 생략하도록 하겠다. 다만 특별한 경우가 

아니라면  board = pymata4.Pymata4() 코드 한 줄로 아두이노를 연결하여 사용할 수 있다는 것만

알아두도록 하자.


blink 함수 분석 - 핀 모드 설정

Firmata를 이용하여 호스트 PC에서 아두이노의 핀을 이용하기 위해서는 먼저 아두이노 핀 모드를 설정해

주어야 한다. 그래서 가장 먼저 호출되는 API가 my_board.set_pin_mode_digital_output(pin)

이다. API 함수 이름으로 짐작할 수 있듯이 파라미터로 전달되는 pin(여기서는 6번 핀)을 디지털 출력으로 

설정하겠다는 의미이다. 그 과정을 조금 더 자세하게 살펴보자.

 

pymata4.py에서 set_pin_mode_digital_output 함수는 매우 단순하게 구현되어있다. 단지 전달

받은 파라미터인 pin에 private_constants.py에 정의된 상수인 OUPUT을 파라미터로 추가하여 private

함수인 _set_pin_mode 함수를 호출하는 것이 다이다. 전체 코드는 아래와 같다(원래의 주석은 삭제

했다).

 

def set_pin_mode_digital_output(self, pin_number):
        self._set_pin_mode(pin_number, PrivateConstants.OUTPUT)

 

_set_pin_mode 함수는 공통 함수이기 때문에 좀 더 복잡하게 구현이 되어있다. 하지만 우리는 Digital 

핀을 OUTPUT으로 사용할 것이므로 관련 부분만 집중해서 보도록 하자.

 

우선 전체 코드는 다음과 같다. 원래의 주석은 삭제를 하고 간단게 관련된 부분에만 별도의 주석을 달았다.

 

# Python에서 class의 멤버 함수들은 반드시 첫 번째 파라미터가 self여야 한다.
# 하지만 함수를 호출할 때는 self는 생략한다. 따라서 앞서 set_pin_mode_digital_output
# 함수에서 전달한 파라미터는 pin_number와 pin_state 2개이다. 나머지 2개의 파라미터는
# 전달받지 않았으므로 기본 값이 사용된다. 즉, callback은 None, differential은 1이다.
    def _set_pin_mode(self, pin_number, pin_state, callback=None,
                      differential=1):
# callback 파라미터는 전달받지 않았고 따라서 기본 값인 None을 사용하므로 아래 if문은
# 실행되지 않는다(None은 다른 언어의 null로 생각하면 된다).
        if callback:
            if pin_state == PrivateConstants.INPUT:
                self.digital_pins[pin_number].cb = callback
            elif pin_state == PrivateConstants.PULLUP:
                self.digital_pins[pin_number].cb = callback
                self.digital_pins[pin_number].pull_up = True
            elif pin_state == PrivateConstants.ANALOG:
                self.analog_pins[pin_number].cb = callback
                self.analog_pins[pin_number].differential = differential
            else:
                print('{} {}'.format('set_pin_mode: callback ignored for '
                                     'pin state:', pin_state))
#################################################################

# pin_state는 PrivateConstants.OUTPUT 값을 전달받았으므로 Pin_mode 역시
# PrivateConstants.OUTPUT 이다.
        pin_mode = pin_state

# pin_mode가 PrivateConstants.OUTPUT 이므로 아래 if문도 실행되지 않는다.
        if pin_mode == PrivateConstants.ANALOG:
            pin_number = pin_number + self.first_analog_pin
#################################################################

# _send_command 함수를 호출하여 아두이노로 통신하기 전에 마지막으로 Firmata 
# 프로토콜을 구현한다.
        command = [PrivateConstants.SET_PIN_MODE, pin_number, pin_mode]
        self._send_command(command)

# pin_mode가 PrivateConstants.OUTPUT 이므로 아래 if에서는 else절로 들어가
# 아무것도 실행하지 않고 끝낸다.
        if pin_state == PrivateConstants.INPUT or pin_state == PrivateConstants.PULLUP:
            self.enable_digital_reporting(pin_number)
        else:
            pass

 

 

위 소스 코드를 보면 대부분의 코드들은 파라미터 조건에 의해 실행되지 않거나 별다른 실행 없이 넘어가고

가장 핵심적인 부분인 Firmata 프로토콜을 구성하여 아두이노로 보내는 2줄만이 실행된다.

 

command = [PrivateConstants.SET_PIN_MODE, pin_number, pin_mode]
self._send_command(command)

 

Firmata 프로토콜 정의 문서(protocol/protocol.md at master · firmata/protocol · GitHub)

에서 보면 Message Type에 set pin mode(I/O) 라는 항목이 있고 이 항목은 다음과 같이 구성된다.

 

command MIDI channel
first byte second byte
0xF4   pin # (0~127) pin mode

 

 

그럼 이제 private_constants.py 파일을 열어 이 과정에서 사용된 2개의 상수인 OUTPUT과  SET_PIN_MODE

어떻게 정의되어 있는지 살펴보자.

 

SET_PIN_MODE = 0xF4  # set a pin to INPUT/OUTPUT/PWM/etc
...
OUTPUT = 0x01  # pin set as output

 

프로토콜로 정의된 것과 동일하게 command에 해당하는 SET_PIN_MODE0xF4 라는 값이 설정되어

있다. 나머지 2개의 파라미터는 범위 내에서 유동적으로 지정할 수 있으며 second byte에 해당하는 

OUTPUT은 카테고리 성격으로 자주 사용되는 값이므로 별도로 상수로 지정해 놓은 것이다.

 

마지막 단계의 _send_command함수도 단순한데, 전달받은 파라미터(프로토콜 포맷)를 byte 배열로 

바꾸어 serial port에 write(전송)하는 것이 끝이다. 전체 코드는 다음과 같다.

 

def _send_command(self, command):
        # 전달받은 파라미터(Firmata의 프로토콜 규격에 맞춘 데이터)를 바이트 배열로 바꾼다.
        send_message = bytes(command)

	# ip_address 전역변수가 지정되지 않은 경우 if 절 내의 내용을 실행한다.
        # 앞서 pymata4의 생성자 파라미터 설명에서 보았듯이 ip_address는 
        # StandardFirmataWifi라는 스케치에서만 사용되므로 여기서는 지정되지 않아
        # if 절이 실행되는 것이다.
        if not self.ip_address:
            try:
		# serial port에 write한다.
                result = self.serial_port.write(send_message)
            except SerialException:
                if self.shutdown_on_exception:
                    self.shutdown()
                raise RuntimeError('write fail in _send_command')
            return result
        else:
            self.sock.sendall(send_message)

 

정리

 

코드와 함께 정리하다보니 포스팅이 너무 길어진데다가 blink 함수에서 호출되는 digital_write API는 조금

복잡한 내용을 담고 있어서 다음 포스팅으로 넘기는 것이 좋을 것 같다.

 

다시 한번 강조하지만 전체 소스를 세세하게 파헤치는 것이 목표가 아니라 Firmata라는 프로토콜과 관련

라이브러리들이 어떤 흐름을 가지고 작동하는지를 아는 것이 목적인 만큼 관련 부분만 주목해서 파악하면

될 것 같다. 하지만 소프트웨어라는 것이 어찌보면 상당히 유기적이기도 한 만큼 생략으로 인해 혼란 스러운

부분도 많을 것이다. 이러한 부분들은 추후 기회를 봐서 더 상세히 다뤄보도록 하겠다.

 

그럼 다음 포스팅에서 digital_write API에 대한 내용으로 이어가겠다.

 

반응형

 

Firmata는 2가지 방식으로 사용이 가능하다.

 

그 한 가지 방법은 아두이노의 스케치에서 Firmata 라이브러리를 include한 후 라이브러리의 API를

활용하는방법이고, 다른 한 가지는 아두이노에는 범용 스케치를 업로드하고 모든 코딩을 클라이언트

쪽에서 하는 방법이다.

 

우선 두 번째 방법인 범용 Firmata 스케치를 아두이노에 업로드한 후 클라이언트에서만 코딩을 하는 방법을

알아보도록 하겠다. 이 방법은 아두이노 스케치의 C 프로그래밍에 익숙하지 않은 개발자들이 클라이언트

(호스트 PC)에서 본인에게 익숙한 언어로 개발을 할 수 있다는 장점이 있다.

 

본격적으로 Firmata의 소스 코드를 알아보기 전에 우선 간단하게 개발 환경을 먼저 정리하고 진행하고자

한다. 다만 이 글에서는 매킨토시 환경 하에서 Python Client를 선택하여 진행하는 만큼 Windows 사용자나

Python이 아닌 다른 언어를 사용하시는 분들은 다른 곳을 참조하셔서 환경을 구성하는 것이 좋을 것 같다.

 

환경 구성하기1 - 아두이노에 FirmataExpress 설치하기

 

Firmata 기반의 개발을 하기 위해서는 클라이언트(호스트 PC)에서는 Firmata Client 라이브러리를 설치

하여야 하며 아두이노에는 범용 Firmata 스케치를 업로드하는 것으로 끝난다.

 

누누이 말하듯이 Firmata Client 라이브러리는 다양한 언어로 구현이 되어있다. 각자에게 익숙한 언어로 된

라이브러리를 선택하면 되지만 역시 가장 심플하게 구현되어있는 것은 Python으로 구현된 라이브러리이다.

나역시 Java와 iOS의 Obj-c가 주로 사용하는 개발 언어이지만 코드를 분석하고 구현하는데는 오히려

Python이 더 접근하기 쉬웠고 소스 파일 구성도 매우 단순하여 부담이 없었다.

 

일반적으로 Python 라이브러리 중에는 pyFirmata, 아두이노 범용 스케치로는 StandardFirmata를 많이 

이용하는데 나는 자료가 좀 더 상세하게 정리되어있는 pymata4와 여기에 더 적합하다는 FirmataExpress를

사용하였다.

 

아두이노에서 기본적으로 제공하는 범용 Firmat 스케치 예제들이다.

 

사용하게 될 FirmataExpress는 기본 예제에 포함되어있지 않으므로 아두이노 스케치 IDE의 라이브러리 

관리 메뉴로 들어가 검색한 후 설치해야 한다(나는 이미 설치한 후라서 Installed로 표시되고 있다).

 

1. 툴 > 라이브러리 관리... 메뉴를 선택한다.

 

 

2. 라이브러리 매니저 창에서 Firmata로 검색을 한 후 FirmataExpress를 찾아 설치를 진행한다.

 

 

3. 설치가 완료되면 파일 > 예제 > 사용자 지정 라이브러리의 예제 항목에서 FirmataExpress를 찾을 수 있다.

 

 

라이브러리가 설치된 후 파일 > 예제 > 사용자 지정 라이브러리의 예제 항목에서 FirmataExpress를 찾아

열고 아두이노에 업로드하면 아두이쪽의 준비는 끝이다.

 

환경 구성하기 2 - Python 개발환경 구성하기

 

다음은 클라이언트 쪽인데…일단 Python의 설치 및 환경설정에 대한 내용은 그 자체만으로 한 번의 포스팅으로

끝날 내용이 아니므로 여기서는 생략을 하겠다. 일단 PC에 Python이 설치되어 있다는 전제하에 진행을 하겠다.

이 부분이 어려운 경우 앞서 말한대로 각자에게 익숙한 언어로 구현된 라이브러리를 이용하면 된다.

 

일단 나는 PyCharm을 이용하여 학습을 진행하였다. PyCharm에서 Virtualenv로 프로젝트를 생성하여 코딩

하는 법에 대해서만 간략하게 설명하도록 하겠다 (현재 맥미니를 사용 중이라 매킨토시 기준으로 작성한다).

 

1. Pycharm의 File 메뉴를 선택한 후 New Project를 선택한다.

 

2. Create Project 창에서 제일 처음 Location은 그냥 두고 Project Interpriter… 항목에서 New environment using Virtualenv를 선택한다. Location 항목에 프로젝트를 생성할 경로를 입력하고 Base Interpreter는 시스템에 설치된 Python 중 하나를 선택한다.

 

3. 프로젝트를 생성할 시의 옵션으로 기존 프로젝트에 붙여넣을지, 새 창으로 열지, 현재 윈도우에서 열지를 물어보는 창. New Window로 선택하면 무난함.

 

4. 프로젝트가 생성되면 아래와 같이 창이 열린다.

 

5. Firmata를 사용하기 위해 필요한 라이브러리를 추가하기 위해 Preferences... 메뉴를 연다.

 

6. Project <프로젝트 이름> 항목을 선택한 후 Project Interpreter를 선택한다. 기본으로 설치된 pip와 setup tool 라이브러리가 보인다. 필요한 라이브러리를 추가하기 위해 하단 좌측의 + 버튼을 클릭한다.

 

7. + 버튼을 누르면 열리는 Available Packages 창에서 pymata4로 검색을 한다. 검색된 목록에서 pymata4를 선택하고 좌측 하단의 Install Package 버튼을 클릭하여 설치한다. 설치 시 의존성에 필요한 패키지를 모두 설치한다.

 

8. 설치를 하고 난 후 아래 화면과 같이 pymata4와 pyserial 2개의 패키지가 추가로 설치된 것을 확인할 수 있다. Firmata가 기본적으로 Serial 통신을 하기 때문에 의존 패키지로 pyserial이 함께 설치된다.

 

9. 이제 다시 프로젝트 화면으로 돌아가 프로젝트 이름에 마우스 우클릭을 하여 New > Python File을 선택하여 소스 코드를 입력할 Python 파일을 만든다.

 

10. 파일명을 입력하고 OK 버튼을 클릭하여 파일을 생성한다.

 

11. 일단 빠른 테스트를 위해 생성된 Python 파일에는 pymata4의 예제 중 digital_output.py 파일을 그대로 카피하여 붙여넣기 하였다.

 

12. 코딩된 Python 파일을 실행하기 위하여 Run 메뉴의 Run…항목을 클릭한다.

 

13. 코드가 실행되면서 아래의 영상과 같이 LED에 1초 간격으로 4번 불이 들어오고 프로그램이 종료된다. 하단의 Run 창에는 아래 화면과 같이 로그가 찍힌다. Auto-discovery complete. Found 22 Digital Pins and 8 Analog Pins까지는 pymata4 라이브러리에서 찍는 로그로 아두이노가 연결된 serial 포트를 찾고 아두이노 및 아두이노에 업로드된 Firmata에 대한 정보를 가져오는 내용이 출력된다.



 

 

정리

 

오늘은 간단하게 개발 환경 구성과 간단한 예제를 하나 돌려보았다.

 

본문에서 설명한 것과 같이 아두이노는 FirmataExpress 스케치를 업로드한 이후 더이상 할 것이 없다. 

이제부터 모든 아두이노 제어는 호스트 PC에서 Python(물론 지원하는 타 언어를 이용해도 됨) 언어로

코딩하여 진행하게 된다.

 

다음 포스팅부터 pymata4와 FirmataExpress를 하나하나 뜯어보면서 어떻게 동작하는지에 대해 

살펴보도록 하자.

 

반응형

 

 

지난 시간 Firmata에 대해 포스팅한 후 많은 분들이 관심을 가져주시고 또한 의문을 제기해주셨다.

그중에 가장 중요한 것이 바로 다음의 두 가지였다.

 

  1.  왜 Firmata를 사용하는가?

  2. 왜 라즈베리파이와 아두이노를 연동하여 사용하는가?

 

이런 질문을 받고 나니 확실히 어떤 목적으로, 어떤 이점이 있기에 Firmata를 사용하고 라즈베리파이와

아두이노를 연동하려고 했는지 다시 한번 고민해보게 되었다. 아무리 작고 개인적인 프로젝트라 하더라도

역시나 대충, 어물쩡, 설렁설렁 하면 안되겠다고 새삼 깨달았다. 오늘은 위 두 질문에 대한 답을 찾는

것으로 시작을 해보고자 한다.

 

왜 Firmata를 사용하는가?

 

어찌보면 이 질문의 답은 명확하다. 대략 세 가지 정도로 추려보자면

 

1. 검증받은 프로토콜의 사용

지난 시간에도 언급했듯이 디지털 기기간에 통신을 하기 위해서는 프로토콜이라는 규약을 만들고 지킬 

필요가 있다. 하지만 프로토콜을 설계하고 구현한다는 것이 그리 만만한 작업은 아니다. 때문에 이미

검증된 MIDI Message Format을 이용하여 안정적으로 구현된 프로토콜을 사용할 수 있다는 것은

큰 이점이 될 것이다.

 

2. 다양한 기기 및 언어 이용

프로토콜이 명확하게 정의되어있기 때문에 이 프로토콜 구현만 제대로 한다면 어떤 언어로든 아두이노를

제어할 수 있다. Firmata Protocol git 페이지에 가보면 다양한 언어로 구현된 Firmata 클라이언트

링크를 볼 수 있다. 또한 시리얼 통신만 가능하다면 PC 뿐만 아니라 모바일 기기에서도 아두이노를 제어할 수

있다.

 

3. 사용의 용이성

Firmata Client 프로그램들은 이 프로토콜을 손쉽게 이용할 수 있도록 API가 잘 구현되어있다. 따라서

적절한 API를 가져다 쓰기만 한다면 웬만한 아두이노 컨트롤은 손쉽게 수행할 수 있다. 게다가 아두이노에는

Firmata 라이브러리만 올려두면 추가적인 프로그래밍이나 스케치 업로드등을 할 필요도 없어 오직 호스트

PC(혹은 모바일 기기)에서만 개발자에게 익숙한 클라이언트로 프로그래밍하여 제어할 수 있다.

 

이렇게 Firmata는 아두이노를 활용하는데 많은 이점을 주는 라이브러리라고 할 수 있다.

 

왜 라즈베리파이와 아두이노를 연동하여 사용하는가?

 

이 질문은 사실 많은 고민을 하게 만들었다. 라즈베리파이도 이미 충분한 수의 GPIO 핀이 있는데 굳이

아두이노를 연결해서 사용하는 이유는 무엇일까? 라즈베리파이와 아두이노 연동, 시리얼 통신 등으로

검색을 해보면 많은 자료들이 검색되지만 정작 왜 연동을 하는지 그 목적에 대해 설명한 자료는 거의

없다. 그저 기술적인 호기심 때문만은 아닐텐데…

 

하지만 내 개인 프로젝트에 국한했을 때는 이러한 구성에 의문을 가질 수 있지만 일반적인 상황에서, 또는

아두이노를 중심에 둔다면 이러한 구성이 충분히 합리적이라는 것을 알 수 있다.

 

1. 원격 센서 제어

호스트에서 원격으로 어떤 센서로 측정된 값을 가져와 연산을 하여 처리한다고 했을 때 당연히 원격 센서는

아두이노를 사용하는 것이 가격적인 측면에서 합리적일 것이다. 라즈베리파이는 호스트로서 수신된 데이터를

연산하기만 하면 된다.

 

2. 하드웨어적인 차이

원격이 아닌 같은 모듈 내에서 사용을 하더라도 라즈베리파이와 아두이노의 하드웨어적인 차이에 의해 함께

연동하여 사용할 수 있을 것이다. 가장 큰 차이라면 역시나 GPIO인데 아두이노에는 라즈베리파이에 없는

Analog PIN이 별도로 있으며, 작동 전압도 라즈베리파이가 3.3v 인 반면 아두이노의 경우 3.3v와 5v용을

선택하여 사용할 수 있고, 핀당 사용 가능한 전류도 라즈베리파이가 16mA인 반면 아두이노는 40mA까지

사용할 수 있다. 이러한 특성상 비록 라즈베리파이에 40여개의 GPIO가 있지만 아두이노를 연동해서 사용

하는 것이 더 나은 경우도 있을 것이다.

 

하드웨어에 대한 정보들은 간단하게 아래 내용을 참고하면 될 것이다.

 

The operating voltage of the GPIO pins is 3.3v with a maximum current draw of 16mA. This means that we can safely power one or two LEDs (Light Emitting Diodes) from a single GPIO pin, via a resistor. But for anything requiring more current, a DC motor for example, we will need to use external components to ensure that we do not damage the GPIO. 

-

출처 : https://www.tomshardware.com/reviews/raspberry-pi-gpio-pinout,6122.html

 

아두이노 우노의 사양

 

위의 내용을 감안해 본다면 분명히 라즈베리파이와 아두이노를 연동하여 사용해야 하는 것이 더 유용한 경우도 

있을 것이다. 

 

Firmata의 한계

 

하지만 100% 좋은 것이 어디 있으랴.

내 구미에 맞게 커스터마이징해야 하는 경우라면 Firmata는 결코 답이 아니다. 사실 모든 라이브러리나 

프레임워크가 다 동전의 양면이 아니던가? 쉽고 편하게 사용하기 위해 적용은 하지만 정작 중요하게 

커스터마이징해야 할 때 상당히 곤란한 상황에 직면하는 경우가 적지 않다.

 

지난 포스팅에서도 언급했듯이 나역시 PCA9685 PWM 서보모터 드라이버를 Firmata 상에서 사용하려고

했으나 이와 관련된 프로토콜은 정의되어있지 않기 때문에 대안을 찾기 위해 열심히 소스 코드를 뜯어보고 

있다…ㅠ.ㅠ 해결책에 한걸음 다가갔나 싶으면 비트 연산들이 줄줄이 나와 java 프로그래밍을 위주로 해온

나에게는 결코 쉽지 않은 작업이다…ㅠ.ㅠ

 

이 부분은 글을 이어 나가면서 극복해보도록 하겠다. 

 

정리

 

원래 이번 포스팅부터 코드 분석을 들어갈 예정이었으나 위의 질문들은 프로젝트를 수행하는데 있어서 분명

중요한 시사점이 있기 때문에 집고 넘어가는 것이 좋겠다고 생각했다. 그리고 이 글을 정리하기 위해 적잖이

검색을 하고 자료를 찾아보았지만 위와같은 근본적인 질문에 대한 명쾌한 답변은 찾기가 쉽지 않았다.

 

결국 몇몇 글들과 나름의 지식을 기반으로 글을 적기는 했지만 아직도 객관적인고 명확한 근거는 모자라지 

않나 하는 아쉬움이 있다. 이 부분은 앞으로의 숙제가 될 것 같다.

 

이제 예정한대로 다음 포스팅 부터는 Firmata Client와 라이브러리 그리고 예제 코드를 통해 Firmata에 

대해 조금 더 구체적으로 알아보도록 하겠다.

반응형

로봇을 제작하면서 전자부의 구성은 라즈베리파이 + 아두이노를 사용하기로 계획했다.

일단 사양과 활용도가 높은 라즈베리파이로 각종 데이터 분석과 추후 AI를 위한기능들을 구현하고

실제 구동부의 동작은 아두이노로 제어를 하는 구조다. 물론 요즘 새로 출시된 아두이노들은 웬만한

ML 처리는 가능하지만 아무래도 활용도라든지 관리 차원에서는 라즈베리파이가 조금 더 접근하기

쉬운 것은 사실이다.

 

게다가 일반적인 아두이노 호환 컨트롤러들은 워낙에 저가여서 모터나 센서 등을 조합해서 다양한

구동부를 만들고 이것을 라즈베리파이와 연동한다면 다양한 형태의 로봇이나 차량 혹은 드론을

만들 수 있을 것이라 생각했다. 그래서 프로젝트 이름도 MORS(MOdular Robot System)이라고

지었다.

 

계획은 세웠으니 이제 구현만 하면 되는데…라즈베리파이와 아두이노를 어떻게 연결하는 것이 좋을까?

아직 한번도 해본 적이 없는 구성이라 일단 열심히 검색을 해보았다.

 

일단 가장 기본적으로 알 수 있는 방법은 우리가 PC에서 아두이노 스케치를 개발하듯이 라즈베리파이를

호스트 PC로 만들고 거기서 스케치를 코딩하고 아두이노에 업로드하여 동작하도록 하는 것이다. 하지만

이 방식으로는 아두이노의 PIN에 대해 라즈베리파이가 직접 컨트롤 할 수 없다는 문제가 있다. 즉, 라즈베리

파이가 사용자의 신호를 받았을 때 그 신호를 실시간으로 아두이노에게 전달해야 하는데 이 방식으로는

그것이 불가능하다. 

 

사실상 라즈베리파이와 아두이노가 따로 노는 상황

 

그렇다면 라즈베리파이와 아두이노의 역할을 아예 분리하는 방식을 생각해볼 수도 있다. 라즈베리파이는

부하가 큰 데이터나 ML 처리의 연산만 담당하고 아두이노는 모터나 센서등의 동작을 담당하는 식이다.

하지만 이렇게 되면 사용자가 라즈베리파이와 아두이노를 이원화 하여 컨트롤해야 하고 또 다수의 아두이노

기반 구동부를 연결하는 경우 구조가 매우 복잡해진다(사실 첫 번째 경우와 동일한 케이스인데 다만

아두이노쪽에 사용자와 통신할 수 있는 모듈(ESP, 블루투스, nRF 등)을 추가하여 아두이노를 별도로

제어한다는 점이 다를 뿐이다).

 

쌍둥이를 키우는(?) 입장이랄까?

 

그러다 찾아낸 것이 바로 Firmata이다. 앞으로 얼마간 이 Firmata에 대해 알아보고자 한다.

 

바로 이거지~

 

이 글은 비록 개발자로 일하고 있지만 태생이 문돌이인 블로그 주인의 글이니 보다 정확한 자료를 원하시는

분들은 아래 링크로 이동하여 참고하시면 되겠다. 다만 영어의 압박은 감수하셔야…

 

Firmata 문서 Git : GitHub - firmata/protocol: Documentation of the Firmata protocol.

 

Firmata는 프로토콜이다.

 

프로토콜은 잘 아시다시피 하나의 규약이다. 통신을 할 때 지켜야 할 약속으로 우리가 가장 잘 알고있는

프로토콜은 바로 워키토키를 이용하여 통신을 할 때 자신이 할 말을 마쳤으니 상대방이 말하라고 하는

의미의 “오버”를 들 수 있겠다.

 

하지만 디지털 장치로 그 영역을 옮겨오면 그렇게 단순하지는 않다. 위의 예도 사람의 입장에서 적으니

단순하게 보일 뿐 실제 워키토키가 처리하는 것은 디지털화 된 음성 데이터를 두 대의 워키토키 사이에

전달하기 위해 채널을 확인해야 하고 데이터의 순서나 손실 여부를 체크해야 한다. 이 역시 중요한 부분만

언급했을 뿐 실제로는 더 많은 내용이 오고 갈 것이다.

 

아주 간단한 프로토콜

 

이렇게 디지털 기기에서 양자간에 데이터를 주고받기 위한 규칙을 프로토콜이라고 한다. 우리가 잘

아는 TCP/IP, SMTP, HTTP, FTP 등이 모두 프로토콜이며 각각의 목적에 따라 그 규칙이 모두 다르다.

지금 공부하고자 하는 Firmata 역시 이러한 프로토콜이며 Firmata의 목적은 호스트 컴퓨터와 마이크로

컨트롤러간에 직접 통신을 할 수 있도록 하기 위한 것이다.

 

Firmata는 midi message format을 기반으로 만들어졌다고 하는데 일단 midi message format이

무엇인지까지 파고드는 것은 범위가 너무 넓어지므로 관련 링크만 걸어두겠다.

 

Official MIDI Specifications : Specs

 

여기서 midi란 우리가 잘 알고 있는 디지털 악기가 맞다(Musical Instrument Digital Interface)

 

Firmata의 구성 - 소프트웨어 관점에서

 

Firmata는 호스트 PC쪽의 Firmata Client와 마이크로컨트롤러쪽의 Firmata 라이브러리로 구성

되어 있다. 

 

Firmata Client의 경우 웬만한 언어는 모두 사용 가능한데 대표적으로 Python, java, javascript로부터

iOS, Android까지 모두 사용 가능하다. 또한 각 언어로 구현된 클라이언트도 각각의 특성에 따라 여러

버전이 있어 필요에 따라 선택하여 사용 가능하다.

 

아두이노 라이브러리는 말 그대로 아두이노에서 사용 가능한 라이브러리 형태로 되어있으며 가장 기본적인

라이브러리는 아두이노 스케치의 예제에 포함이 되어있다. 파일 > 예제 > Firmata 항목으로 들어가면 

다양한 Firmata 목록을 볼 수 있는데 가장 기본적인 것은 StandardFirmata이다.

 

아두이노 스케치에서 Firmata 라이브러리 불러오기

 

아두이노에 Firmata 스케치를 업로드하고 클라이언트에서 API를 호출하면 Firmata 프로토콜을 통해

아두이노의 해당 API가 호출되는 식이다(자세한 것은 다음에 소스 분석을 통해 알아보겠다).

 

Firmata의 구성 - 프로토콜 관점에서

 

앞서도 이야기 했듯이 프로토콜이란 통신을 위해 지켜야 할 일종의 약속이다. 즉 FirmataClient에서

약속된 형태의 데이터를 전달해야 아두이노의 Firmara 라이브러리에서 데이터를 받아들이고 처리할 수

있다. 이러한 약속들은 아래 문서에 정의되어 있다.

 

Firmata protocol : protocol/protocol.md at master · firmata/protocol · GitHub

 

 

문돌이들의 경우 몇몇 개념이 헷갈릴 수 있으나 이 약속을 지키기 위한 대부분의 처리는 이미 API로 모두

구현이 되어있으며 다양한 샘플도 존재하니 그저 API를 호출해서 사용하면 된다. 조금 더 이해의 폭을

넓히자면 이 약속이라는 것이 그저 API 호출 시 넘겨주어야 할 파라미터를 정의하는 것이라 생각하면

조금 더 이해가 쉬울 것이다.

 

예를들어 위 링크된 문서의 Message Type 중 set digital pin value라는 항목을 살펴보자

테이블을 보면 우선 command 항목은 0xF5이다. 즉, 아두이노의 특정 디지털 핀에 값을 전달하기 

위해서는 0xF5라는 값으로 시작하는 데이터를 보내야 한다. 다음 열은 비어있고 세 번째 열에

1~127 사이의 핀 번호를 입력해야 하고 마지막으로 1 또는 0으로 해당 핀에 전달할 값을 지정해야

한다. 만일 9번 핀에 디지털 신호를 보내기 위해서는 0xF5, 9, 1 또는 0xF5, 9, 0으로 구성된 3개의

데이터를 보내면 되는데 command에 해당하는 값들은 상수로 지정하여 API에서 처리를 해주므로

실제 API를 호출할 때는 command를 제외한 9, 1 또는 9, 0만을 파라미터로 하여 다음과 같이

보내면 된다.

 

digital_pin_write(pin, 1) 
digital_pin_write(pin, 0)

 

이 중에서 특히 중요한 것이 Sysex Message Format인데 이 부분은 다음 포스팅에서 다뤄보기로 하겠다.

 

정리

 

사실 Firmata에 대해 굳이 이렇게까지 알지 않아도 대충 예제를 보면서 구현하는 것은 그리 어렵지 않다.

그런데 로봇을 만들면서 서보모터를 컨트롤하는데 I2C 기반으로 통신을 하는 PCA9685 PWM 서보모터

드라이버를 사용하려고 보니 아두이노에서 전용 라이브러리를 사용해야 하고 Firmata에서는 이러한 특정

라아브러리에 대한 message format까지 모두 지정해놓은 것은 아니라서 현재로서는 사용이 어렵다.

결국 어떻게 커스터마이징 좀 안될까 하고 조금 더 파고들어가 보기로 한 것이다.

 

물론 다른 방법으로는 I2C에 대한 message format은 정의가 되어있으므로 관련 API를 이용해서 제어하는

방법도 있을 것 같은데 그 또한 그리 만만하지는 않을 것 같아서 우선은 Firmata쪽을 보기로 하였다. 만일

이 시도가 실패한다면 아마도 I2C를 이용한 PCA9685 제어에 도전하게 될지도 모르겠다.

 

다음 포스팅에서는 Sysex Message Format에 대해 알아보고 본격적으로 API와 샘플코드에 대한 분석을

진행해보고자 한다. 

 

#블로그/로봇개발

반응형

 

OpenCV 학습을 위한 스테레오 카메라 테스트

다시금 4족 보행 로봇에 관심을 기울이게 되면서 기왕이면 OpenCV를 이용하여 Object Detection을 구현하고
낮은 수준의 자율주행 기능을 포함시키고 싶었다. 하지만 새로운 것을 공부해야 한다는 부담감 때문에 섣불리
손을 못대고 있다가 본능에 따라 한 번 들이대보기로 하였다.

 

아직은 정리할만큼도 학습을 못했기 때문에 실제 OpenCV에 대해서는 언급할 내용이 없고 오늘은 카메라에 대한
이야기나 조금 찌끄려보려고 한다.

 

USB Web CAM을 이용한 스테레오 카메라

사실 라즈베리파이에서 카메라 모듈을 2개 이상(4개까지) 연결하여 사용할 수 있는 모듈이 있다. 하지만 가격이
만만치 않다(6~7만원 선). 게다가 파이 카메라 모듈도 필요하고(그나마 V1.3 만 지원한다고 하니 파이 카메라
가격은 좀 저렴하게 구성할 수 있다), 어쨌든 대충 계산해도 한 10만원은 있어야 스테레오 카메라를 구성할 수
있을 것이다.

 

라즈베리파이에 4개까지 파이 카메라 V1.3을 연결할 수 있는 모듈

 

그래서 생각한 것이 저렴한 USB 웹캠 2개를 이용하면 되지 않을까 하는 생각이었다. 실제로 그렇게 구현한 예도
유튜브에 종종 보이고…그래서 일단 만원짜리 웹캠을 2개 샀다. 그리고 3D 프린터로 작은 홀더를 2개 만들어서
각각 등짝에 하나씩 붙여주고 8∅짜리 스테인레스 파이프로 연결하여 고정해주었다. 

 

 

그리고 라즈베리파이에 USB 웹캠을 연결을 하고 샘플 코드를 돌려보았다.
하지만…언제나 그렇듯이 유튜브에서 보여지는 것은 F1 레이싱카였지만 내가 돌리는 것은 애기들 태우는 전동차
수준…ㅠ.ㅠ 하드웨어 문제인지 뭔가 추가적인 코드가 더 필요한 것인지 영상이 엄청 끊기는데다가 2대의 카메라
영상이 거의 동기화가 되지 않았다…ㅠ.ㅠ

 

 

 

일단 바로 해볼 수 있는 해결 방법은 영상의 크기를 줄이는 방법! 그래서 코드에서 카메라 영상의 크기를 320X240
으로 줄이고 다시 시도해보았다. 역시나 사이즈가 작아지니 영상 끊김도 거의 없고 두 영상이 어느정도 동기화 되는
모습을 볼 수 있었다. 하지만 아직도 만족스러운 정도는 아니었다.

 

 

 

ELP Dual lense USB Camera module

계속해서 스테레오 카메라를 알아보고 있었는데 올 초부터 눈에 밟히는 녀석이 하나 있었다. 물론 전문적인 용도는
아니고 그냥 가볍게 사용해볼 수 있을만한 제품으로 보였다. 그럼에도 가격대는 역시 8만원대로 그리 만만한
녀석은 아니었다.

 

그리고…마침 로봇에 꽂혀 이것 저것 부품을 마련하는데 돈이 필요했던 나는 예전에 빅데이터와 클러스터링을
한번
 공부해보겠다고 어렵게 모은 맥미니 5대 중 3대를 팔아버리고 말았다…ㅠ.ㅠ 물론 가격은 구입 당시의 반토막
…ㅠ.ㅠ

 

그렇게 돈을 마련하고 제일 먼저 알리에서 이 제품을 구매했다. 그리고 생각보다 배송이 일찍되어 오늘 이렇게
라즈베리파이에 연결하여 확인을 해볼 수 있었다.

 

 

일단 하나의 모듈로 만들어진 제품이기 때문에 두 카메라로 입력된 영상이 하나의 영상의로 합쳐져 출력되었고
코드상에서도 하나의 VideoCapture 클래스만 사용하면 되었다. 영상의 동기화도 훌륭하고 기본으로 출력되는
320X240 정도의 크기(두 영상이 합쳐서 나오기 때문에 실제 가로는 640)에서는 영상 프레임도 상당히 좋은
수준을 보여줬다.

 

 

영상 사이즈를 640X480(전체 사이즈 1280X480)으로 출력했을 때도 영상이 끊기는 문제는 있었지만 적어도
프리징 되지는 않았고 동기화는 원활했다.

 

 

다만 사용하다보니 발열이 좀 있었고 하나의 영상의 입력되기 때문에 나중에 여러가지 처리를 하기 위해서는
영상을 둘로 나누는 코드가 필요할
것이다. 설마 이정도 코드 추가가 성능 저하를 일으키진 않겠지…-.-

 

정리

아직 이렇다할 작업 내용이 없어서 오늘은 2대의 웹캠을 이용한 것과 하나의 모듈로 구성된 2대의 카메라를 이용한
것의 영상 출력만을 비교해보았다. 앞으로 어느정도 학습이 진행이 되면 OpenCV에 대한 실습 내용을 위주로
블로그에 정리를 해보도록 하겠다.

  1. raspi 2020.05.06 13:41

    안녕하세요, 저희는 라즈베리파이에 웹캠 한개와 라즈베리파이모듈 한개씩을 각각 사용해서 스트리밍과 영상처리를 나누어서 할 계획을 가지고 있습니다. 혹시 이 작업이 가능할지 여쭤보고 싶네요. 감사합니다.

    • 마즈다 2020.05.06 15:17 신고

      우선 관심 가져주셔서 감사합니다.
      말씀하신 내용은 사실 저도 잘 아는 부분이 아니라서 정확한 답변을 드리긴 어렵지만 일단 OS에서 웹캠(USB)과 라즈베리파이 카메라 모듈(CSI 카메라 포트)는 서로 다른 주소로 인식할테니 각각의 카메라를 서로 다른 용도로 사용한다면 충분히 가능할 것으로 보입니다. 다만 저처럼 스테레오 카메라 구현이라면 두 개의 카메라가 동일한 스펙을 가진 것을 사용하는 것이 좋을 것 같구요.

      별 도움이 못되는 것 같아 죄송합니다^^;

반응형


Android Things Release Note 요약 : 4.0 ~ 4.1


지난 5월 14일에 정리한 것이 개발자 프리뷰 3.1까지였는데 그새 4.1까지 업데이트 되었다.
아무래도 짚고 넘어가야 할 것 같다…^^;


Developer Preview 4

릴리즈 날짜 : 2017년 5월
빌드 번호 : NIH40E
play service : 10.0.0


이 프리뷰 릴리즈는 지원되는 하드웨어에 대한 개발과 호환성 테스트를 하기 위해 Android Things를 이용하고자 
하는 개발자들과 얼리 어댑터들을 위한 것으로 이 프리뷰에 대한 아래의 일반 가이드라인에 주의하길 바란다.

  • 이 릴리즈는 지원되는 하드웨어에 대한 여러 지속적 이슈가 있으니 발견한 버그들은 리포트해주길 바란다.
  • 이 프리뷰에서는 API들의 모든 부분을 사용할 수는 없다. 사용 불가한 것으로 알려진 API들은 Known Issue 섹션에 문서화 되어있다.
  • 개발자 프리뷰 4는 Intel EdisonIntel JouleNXP i.MX7DNXP i.MX6UL, 그리고
    Raspberry Pi 3 개발 보드에서 사용 가능하다.

New in Preview 4


NXP i.MX7D support

Android Things는 이제 NXP® i.MX7D Pico 개발 플랫품을 지원한다. 이 기기에 대해 더 알고싶거나 성능을
확인하고 싶으면 developer kits 페이지를 참고하라.


Audio APIs

개발자들은 이제 Peripheral I/O를 이용하여 Inter-IC Sound (I2S)를 통해 디지털 오디오 장치를 연결할 수 
있으며 새로운 audio user-space 드라이버를 이용하여 미디어 프레임워크에 바인드할 수 있다.
보다 상세한 내용은 I2S와 audio drivers에 대한 새로운 API 가이드를 살펴보라.


Peripheral drivers

Peripheral I/O는 이제 PioDriverManager를 이용하여 런타임 시에 추가적인 인터페이스를 등록하는 것을
지원한다. 이를 통해 유닛 테스트를위한 스텁 인터페이스는 물론 주변 장치 버스 확장 장치를 등록 할 수 있다. 
자세한 내용은 reference documentation을 참조하라.


Known Issue

  • 시스템 전원 관리자는 현재 사용 불가능하다. 기기가 suspend 상태로 들어가지 않고 따라서 wake lock 기능이 필요치 않다(wake locks는 sleep 상태로 들어가거나 sleep 상태에서 깨우는 기능).
  • 부팅 시 dex를 미리 최적화하기 위해 Google Plat Service는 2~3분 정도의 시간을 필요로 한다. 이 프로세스가 완료되기 전에는 App을 설치할 수 없다.
  • 하드웨어 그래픽 가속 기능(OpenGL)은 현재 사용 불가능하다. 따라서 이러한 기능에 의존하는 API들(WebView같은) 역시 사용할 수 없다.


Peripheral I/O

  • 주변장치들은 close() 호출 후에 클리어 되거나 리셋되지 않는다. 출력은 그 장치들의 상태를 그대로 간직하고 있을 것이고 시리얼 포트에서는 이전에 버퍼링된 데이터들을 계속해서 보내려 할 것이다.
  • GPIO 핀들은 마지막으로 재부팅 된 이후에 엣지 트리거와 함께 이미 입력으로 활성화 된 경우 출력으로 사용할 수 없을 것이다.


User Driver

  • 사용자 센서들은 현재는 수동으로 해제할 수 없다. 이 센서들은 app 프로세스가 종료되면 자동으로 
    해제된다.
  • 사용자 센서로는 Continuous 및 On-change 센서만 지원 가능하다. One-shot과 special 리포팅 모드는 기대한대로 동작하지 않을 것이다.

Reporting modes란 (지난 프리뷰 번역에 언급했지만 포스팅이 나뉘었으므로 다시 설명함)

센서들은 다양한 방법으로 이벤트를 생성하는데 이를 Reporting mode라고 부른다. 각 센서들은 하나의 유형에 오직 하나의 reporting mode와 관계를 맺으며 모두 4개의 reporting mode가 있다.

  • Continuous : 배치 함수로 전달된 sampling_period_ns 파라미터에서 정의한 일정한 주기로 이벤트를 생성하며 가속도 센서나 자이로 센서가 여기에 포함된다.
  • On-change : 측정된 값에 변화가 있을 때만 이벤트가 발생한다. step counter, 근접 센서, 심박 센서 등이 여기에 포함된다.
  • One-shot : 이벤트가 감지되는 시점에서 HAL을 통해 단일 이벤트를 전송하고 센서 자신은 비활성화 된다. 중요 동작(Significant motion) 센서가 여기에 포함된다.
  • special : 센서별로 특정한 방식으로 이벤트를 발생시키며 자센한 것은 센서 타입 설명을 참조. 기울기 센서, Step detector 등이 여기에 포함된다.


인텔 Edison과 NXP Pico 관련 내용은 생략합니다.


Raspberry Pi

  • Audio : Wi-Fi와 Bluetooth가 모두 활성화 된 경우 Audio 품질 이슈가 발생한다.
  • Network : 인터넷 접속이 없더라도 이미 이더넷으로 네트워크에 연결되어있는 경우 Wi-Fi로 인터넷에 연결할 수 없다.
  • Camera : 하나 이상의 대상 출력 화면에 대해서는 새로운 CameraCaptureSession을 생성할 수 없다.
  • Camera : 어던 CameraCaptureSession에 대해서라도 최초의 요청에 대해서는 2개의 이미지가 queues에 쌓이게 된다. 이런 동작은 같은 session 내에서 연이어 발생하는 CaptureRequest가 이전 capture에서 버퍼링된 프레임을 리턴하는 문제를 야기한다.
  • I/O : BCM13/PWM1, BCM18/PWM0 공유 핀들은 이미 PWM 핀으로 활성화 된 경우 재부팅하기 전까지는 GPIO로 사용할 수 없다.
  • I/O : GPIO 핀인 BCM4, BCM5, 그리고 BCM6는 입력으로 사용할 경우 내부적으로 3.3V로 pulled-up(스위치가 Off 상태이면 전류가 흐르고 On 상태면 흐르지 않는 상태)된다.
  • Audio : 온보드 아날로그 오디오는 PWM과 동시에 사용할 수 없다.

Developer Preview 4.1

릴리즈 날짜 : 2017년 6월
빌드 번호 : NIH40K
play service : 11.0.0


개요는 Developer Preview 4와 동일하다.


New in Preview 2


NXP i.MX6UL Pico

NXP는 Preview 4.1 이상을 지원하는 Android 용 Pico 개발자 키트를 출시했다. 이전의 WandBoard kit
deprecated되고 차후 버전의 Android Things에서는 지원되지 않을 것이다.


Play Services for IoT

이번 릴리즈에는 IoT 기기를 겨냥한 별도의 Google Play Services가 포함되어 있다. 현재 버전에서의 Google
API 지원에 대해 더 배우고 싶다면 SDK overview페이지를 참고하라.


Known Issues

  • 시스템 전원 관리자는 현재 사용 불가능하다. 기기가 suspend 상태로 들어가지 않고 따라서 wake lock 기능이 필요치 않다(wake locks는 sleep 상태로 들어가거나 sleep 상태에서 깨우는 기능).
  • 부팅 시 dex를 미리 최적화하기 위해 Google Plat Service는 2~3분 정도의 시간을 필요로 한다. 이 프로세스가 완료되기 전에는 App을 설치할 수 없다.
  • 하드웨어 그래픽 가속 기능(OpenGL)은 현재 사용 불가능하다. 따라서 이러한 기능에 의존하는 API들(WebView같은) 역시 사용할 수 없다.


Peripheral I/O

  • 주변장치들은 close() 호출 후에 클리어 되거나 리셋되지 않는다. 출력은 그 장치들의 상태를 그대로 간직하고 있을 것이고 시리얼 포트에서는 이전에 버퍼링된 데이터들을 계속해서 보내려 할 것이다.
  • GPIO 핀들은 마지막으로 재부팅 된 이후에 엣지 트리거와 함께 이미 입력으로 활성화 된 경우 출력으로 사용할 수 없을 것이다.


User Driver

  • 사용자 센서들은 현재는 수동으로 해제할 수 없다. 이 센서들은 app 프로세스가 종료되면 자동으로 
    해제된다.
  • 사용자 센서로는 Continuous 및 On-change 센서만 지원 가능하다. One-shot과 special 리포팅 모드는 기대한대로 동작하지 않을 것이다.


인텔 Edison과 Joule 그리고 NXP Pico 관련 내용은 생략합니다.


Raspberry Pi

  • Audio : Wi-Fi와 Bluetooth가 모두 활성화 된 경우 Audio 품질 이슈가 발생한다.
  • Network : 인터넷 접속이 없더라도 이미 이더넷으로 네트워크에 연결되어있는 경우 Wi-Fi로 인터넷에 연결할 수 없다.
  • Camera : 하나 이상의 대상 출력 화면에 대해서는 새로운 CameraCaptureSession을 생성할 수 없다.
  • Camera : 어던 CameraCaptureSession에 대해서라도 최초의 요청에 대해서는 2개의 이미지가 queues에 쌓이게 된다. 이런 동작은 같은 session 내에서 연이어 발생하는 CaptureRequest가 이전 capture에서 버퍼링된 프레임을 리턴하는 문제를 야기한다.
  • I/O : BCM13/PWM1, BCM18/PWM0 공유 핀들은 이미 PWM 핀으로 활성화 된 경우 재부팅하기 전까지는 GPIO로 사용할 수 없다.
  • I/O : GPIO 핀인 BCM4, BCM5, 그리고 BCM6는 입력으로 사용할 경우 내부적으로 3.3V로 pulled-up(스위치가 Off 상태이면 전류가 흐르고 On 상태면 흐르지 않는 상태)된다.









반응형


Android Things : TensorFlow 예제


Android Things 홈페이지의 글만 번역을 하다가 너무 지루해서 예제 하나를 돌려보기로 했다.
기왕지사 돌리는 것, 조금은 있어보이는 것으로 돌려보자 하고 TensorFlow 예제를 돌려보기로 했다.
뭐, 내가 하는 일이 늘 그렇듯이 한번에 잘 되지는 않았다. 몇가지 실수와 실행 결과를 살펴보도록 하겠다.


TensorFlow 예제를 위한 준비


Android Thinsg의 TensorFlow 예제는 다음 링크에서 다운로드 받을 수 있다.


https://github.com/androidthings/sample-tensorflow-imageclassifier


이 예제는 대기 상태에서 LED가 점등되고 버튼을 누르면 카메라로 이미지를 촬영하여 촬영된 이미지를
TensorFlow의 모델로 전달하게 된다. 그러면 TensorFlow에서 이 이미지를 분석하여 그 결과를 logcat이나
모니터가 연결된 경우 모니터로 출력으 해주는 것이다. 스피커가 연결된 경우 결과 문자열을 읽어(text-to-speech)
스피커로도 출력을 해주나 이 부분은 성공하지 못했다.


그밖의 준비사항 및 회로 연결은 위 링크를 참고하시기 바란다.


기본적으로 라즈베리 파이3에 Android Things를 설치한 상태여야 하며 Android Studio에서 인식할 수 있도록
개발PC와 USB로 연결이 되어있어야 한다. 물론 TensorFlow 예제 실행을 위해 네트워크도 연결이 되어있어야 한다.
로그캣으로도 결과를 확인할 수 있다고는 하나 모니터가 연결된 경우 촬영된 이미지와 함께 결과 분석을 함께 볼 수
있어 더 좋다.


몇가지 실수


우선 회로를 구성할 때 사용한 빵판이 미니 빵판으로 전원부가 별도로 없는 빵판이었다. 그 생각을 미처 못하고 전원과
GND를 가로로 나란히 연결했다가 몽창 태워먹을 뻔했다…-.- 다행히 예전에 4족보행 로봇 만들 때 짧게 잘라놓은
기존 빵판의 전원부 조각이 있어서 이를 이용하여 전원을 연결하였더니 정상적으로 회로가 작동하였다.


다음으로 발생한 문제는 Android Studio를 통해 정상적으로 앱을 설치하였는데 대략 다음과 같은 오류가 발생을
하였다.

05-20 17:48:47.254 1771-1771/com.example.androidthings.button E/AndroidRuntime: FATAL EXCEPTION: main 
Process: com.example.androidthings.button, PID: 1771
java.lang.IllegalAccessError: Method 'void com.google.android.things.userdriver.InputDriver$Builder.<init>(int)' is inaccessible to class 'com.google.android.things.contrib.driver.button.ButtonInputDriver' (declaration of 'com.google.android.things.contrib.driver.button.ButtonInputDriver' appears in /data/app/com.example.androidthings.button-1/base.apk)
   at com.google.android.things.contrib.driver.button.ButtonInputDriver.build(ButtonInputDriver.java:98)
   at com.google.android.things.contrib.driver.button.ButtonInputDriver.register(ButtonInputDriver.java:82)
   at com.example.androidthings.button.ButtonActivity.onCreate(ButtonActivity.java:64)
   at android.app.Activity.performCreate(Activity.java:6662)
   at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2599)
   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
   at android.app.ActivityThread.-wrap12(ActivityThread.java)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:154)
   at android.app.ActivityThread.main(ActivityThread.java:6077)
   at java.lang.reflect.Method.invoke(Native Method)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)


이 문제는 아래 링크에서 확인할 수 있엇는데 결론은 Android Things Developer Preview의 최신 버전을 
사용하라는 것이었다(링크한 페이지에서는 DP4 버전이 언급되어있지만 현재 최종 버전은 DP4.1 이다).


https://github.com/androidthings/sample-button/issues/1


여기까지 다 조치를 하였고 이제 세 번째 문제에 봉착을 하게 된다. 바로 아래와 같은 오류가 발생한 것이다.

Android things Cannot capture image. Camera not initialized.


내용을 보면 알겠지만 카메라를 사용할 수 없다는 메시지였다. 확인해보니 카메라를 라즈베리파이에 연결하는데
뒤집어서 연결을 하였다. 접촉 핀이 있는 부분이 랜 포트 반대 방향으로 향해야 하는데 랜포트 방향으로
꽂아버렸던 것이다. 암튼 간단하게 바로 꽂아서 이 부분도 통과~


그리고 드디어 Run!!!


TesorFlow 샘플 실행


우선 처음 실행을 하게 되면 다음과 같이 2단계의 승인 과정을 거치게 된다.


모두 Allow를 선택하면 아래 이미지와 같은 상태로 stand by하게 된다. 이 상태에서 회로도에 있는 푸시 버튼을
클릭하게 되면 LED가 잠깐 꺼지고 카메라 촬영이 진행되며 모니터에 촬영된 이미지와 함께 분석 내용이 파란 
영역에 표시된다.


간단하게 책상 위에 이것 저것 놓고 촬영을 해보았는데 결과는 영 신통치 않다. 일단 카메라가 8MP로 해상도가 낮아
결과물의 품질도 좋지 않고 그래서 그런지 제대로 맞추는 것이 없다…-.- 이 예제의 소개에 보면 예제에 쓰인
TesorFlow의 학습이 Google의 시작 모델을 통해 이루어졌다는 것으로 봐서 초기 제품의 한계라고 봐야 할 것
같다.


가장 충격적인 사실은 내가 샤워 캡을 닮았다는 것이다…ㅠ.ㅠ


테스트 결과는 아래와 같다.


몇가지 팁


사실상 Android Things 플랫폼을 설치해보고 실제로 응용 앱은 처음 설치를 해보았다.
그러다보니 이 설치된 앱을 어떻게 종료하거나 실행시키는지 또는 어떻게 삭제하는지를 전혀 몰랐다.
그래서 간단하게 구글링한 결과를 정리한다.


  • 앱 종료 : 만일 키보드가 연결되어있다면 esc 키를 누르면 앱이 종료된다. 커맨드라인 상에서는 다음과 같이
    명령어를 입력한다.
    adb shell am force-stop <package-name>
    		
  • 앱 실행 : 커맨드라인에 다음과 같이 입력한다(TesorFlow 예제 기준).
    adb shell am start -n com.example.androidthings.imageclassifier/.ImageClassifierActivity
    		
  • 앱 삭제 : 커맨드라인에 다음과 같이 입력한다.
    adb uninstall <package name>
    		


일단 이정도만으로도 기본적인 사용은 가능하다.


정리


아직 소스는 분석해보지 않았고 그냥 라즈베리파이에 앱을 설치해서 기동해본 것 뿐이다. 개발자가 직접
학습을 진행할 수 있는지 등의 여부는 아직 확인을 못했다. 일단 이 첫 결과만 놓고 보면 아직은 갈 길이
멀다는 생각이 든다. 시간 나는대로 소스도 좀 들여다 봐야 할 것 같다. 그래도 심심풀이 땅콩으로는 꽤
괜찮았다.





반응형


Android Things Release Note 요약


아직 개발자 프리뷰 단계이지만 Android Thing가 어떻게 변화해 왔는지를 안다면 Android Things의 지향점을 
파악하는 데 조금은 도움이 될 것이라는 판단 하에 개발자 프리뷰1에서부터 현재의 개발자 프리뷰 3.1까지 각 버전별
릴리즈 노트를 살펴보고자 한다. 물론 Android Things 홈페이지 내용을 번역한 것이다…^^;


다 번역을 하고 보니 아직은 특별히 주목할 만한 것은 없으며 지원 기기의 추가, 아직 지원하지 않는 API들에 대한 지원 확대, 그리고 마지막 3.1 릴리즈에서 보여지는 구글 클라우드 서비스와의 연동 등이 주 내용으로 판단된다. 아직은 성장 단계로 조금 더 지켜봐야 할 것이다. 앞으로도 지속적으로 릴리즈 노트를 번역해보겠다.


Developer Preview 1

릴리즈 날짜 : 2016년 12월
빌드 번호 : NIF73/NIF74
play service : 10.0.0


이 프리뷰 릴리즈는 지원되는 하드웨어에 대한 개발과 호환성 테스트를 하기 위해 Android Things를 이용하고자 
하는 개발자들과 얼리 어댑터들을 위한 것으로 이 프리뷰에 대한 아래의 일반 가이드라인에 주의하길 바란다.


  • 이 릴리즈는 지원되는 하드웨어에 대한 여러 지속적 이슈가 있으니 발견한 버그들은 리포트해주길 바란다.
  • 이 프리뷰에서는 API들의 모든 부분을 사용할 수는 없다. 사용 불가한 것으로 알려진 API들은 Known Issue 섹션에 문서화 되어있다.
  • 개발자 프리뷰 1은 Intel EdisonNXP Pico, 그리고 Raspberry Pi 3 개발 보드에서 사용 가능하다.


◼︎Known Issue

  • 시스템 전원 관리자는 현재 사용 불가능하다. 기기가 suspend 상태로 들어가지 않고 따라서 wake lock 기능이 필요치 않다(wake locks는 sleep 상태로 들어가거나 sleep 상태에서 깨우는 기능).
  • 블루투스 API들은 사용 불가능하다.
  • USB API들은 사용 불가능하다.
  • 위험한 권한 요청에 대해서는 기기를 재부팅해야 승인이 이루어진다. 이런 현상은 새로운 앱 설치 및 앱 내에 
    <uses-permission>요소가 있는 경우를 모두 포함한다.
  • 부팅 시 dex를 미리 최적화하기 위해 Google Plat Service는 2~3분 정도의 시간을 필요로 한다. 이 프로세스가 완료되기 전에는 App을 설치할 수 없다.
  • IOT_LAUNCHER 카테고리를 위한 intent 필터에 여러 개의 activity가 포함되어있는 경우 시스템은 디스플레이 지원 장치 없이는 기기에서 접근할 수 없는 ‘앱 선택기’를 표시한다(즉, 이런 경우 디스플레이 장치가 반드시 필요하다는 의미). Android Things는 단일 launcher 앱만을 지원하며 따라서 이런 동작은 이후 릴리즈에서는 사용할 수 없게 될 것이다.


Peripheral I/O

  • 주변장치들은 close() 호출 후에 클리어 되거나 리셋되지 않는다. 출력은 그 장치들의 상태를 그대로 간직하고 있을 것이고 시리얼 포트에서는 이전에 버퍼링된 데이터들을 계속해서 보내려 할 것이다.
  • GPIO 핀들은 마지막으로 재부팅 된 이후에 엣지 트리거와 함께 이미 입력으로 활성화 된 경우 출력으로 사용할 수 없을 것이다.


User Driver

  • 사용자 센서들은 현재는 수동으로 해제할 수 없다. 이 센서들은 app 프로세스가 종료되면 자동으로 
    해제된다.
  • 사용자 센서로는 Continuous 및 On-change 센서만 지원 가능하다. One-shot과 special 리포팅 모드는 기대한대로 동작하지 않을 것이다.

Reporting modes란

센서들은 다양한 방법으로 이벤트를 생성하는데 이를 Reporting mode라고 부른다. 각 센서들은 하나의 유형에 오직 하나의 reporting mode와 관계를 맺으며 모두 4개의 reporting mode가 있다.

  • Continuous : 배치 함수로 전달된 sampling_period_ns 파라미터에서 정의한 일정한 주기로 이벤트를 생성하며 가속도 센서나 자이로 센서가 여기에 포함된다.
  • On-change : 측정된 값에 변화가 있을 때만 이벤트가 발생한다. step counter, 근접 센서, 심박 센서 등이 여기에 포함된다.
  • One-shot : 이벤트가 감지되는 시점에서 HAL을 통해 단일 이벤트를 전송하고 센서 자신은 비활성화 된다. 중요 동작(Significant motion) 센서가 여기에 포함된다.
  • special : 센서별로 특정한 방식으로 이벤트를 발생시키며 자센한 것은 센서 타입 설명을 참조. 기울기 센서, Step detector 등이 여기에 포함된다.


인텔 Edison과 NXP Pico 관련 내용은 생략합니다.


Raspberry Pi

  • Network : 인터넷 접속이 없더라도 이미 이더넷으로 네트워크에 연결되어있는 경우 Wi-Fi로 인터넷에 연결할 수 없다.
  • Graphic : 하드웨어 그래픽 가속은 현재는 사용 불가능하다.
  • Camera : 하나 이상의 대상 출력 화면에 대해서는 새로운 CameraCaptureSession을 생성할 수 없다.
  • Camera : 어던 CameraCaptureSession에 대해서라도 최초의 요청에 대해서는 2개의 이미지가 queues에 쌓이게 된다. 이런 동작은 같은 session 내에서 연이어 발생하는 CaptureRequest가 이전 capture에서 버퍼링된 프레임을 리턴하는 문제를 야기한다.
  • I/O : BCM13/PWM1, BCM18/PWM0 공유 핀들은 이미 PWM 핀으로 활성화 된 경우 재부팅하기 전까지는 GPIO로 사용할 수 없다.
  • I/O : GPIO핀인 BCM23, BCM24 핀은 두 개 모두 BCM23을 컨트롤하도록 매핑되어있다(물리적 핀 J8-16).
  • I/O : GPIO 핀인 BCM4, BCM5, 그리고 BCM6는 입력으로 사용할 경우 내부적으로 3.3V로 pulled-up(스위치가 Off 상태이면 전류가 흐르고 On 상태면 흐르지 않는 상태)된다.

Developer Preview 2

릴리즈 날짜 : 2017년 2월
빌드 번호 : NIG40
play service : 10.0.0


Preview API 참조 링크 : Javadoc reference.


개요는 Developer Preview 1과 동일하며 다만 지원 기종에 Intel Joule이 추가되었다.


◼︎New in Preview 2


Intel Joule 지원

Intel® Joule compute module 기기에 대한 지원 시작. 자세한 내용은 developer kits 문서 참조.


Native peripheral API

Android NDK를 위한 Native PIO library를 이용하여 C/C++ 코드를 이용하여 주변장치의 I/O에 접근 가능헤게 되었다.


USB audio 지원

기기의 온보드 아날로그 오디오 장치의 기능을 사용하지 않고도 USB 마이크나 스피커를 이용하여 녹음이나 오디오 재생이 가능하게 되었다. 프리뷰 2에서 이 기능을 지원하는 플랫폼은 다음과 같다

  • Intel® Edison
  • Intel® Joule
  • Raspberry Pi


TensorFlow sample

Android Things 기기에서 Tensorflow를 사용하는 방법을 보여주는 샘플을 작성하였다. 이 샘플은 카메라를 이용하여 사물을 인식하고 이미지를 분류한 후 text-to-speech(TTS)기능을 이용하여 결과를 음성으로 출력해준다.
자세한 사항은 samples page에서 확인하라.


Peripheral manager reporting

개발자들은 이제 dumpsys 명령어를 이용하여 개발이나 디버깅 중에도 개발보드 상에 연결된 주변 장치 포트의 활성 여부를 확인 가능하게 되었다. 명령어 사용 예제는 아래와 같다.

$ adb shell dumpsys com.google.android.things.pio.IPeripheralManager


◼︎Known Issues


처음 5개의 항목은 Preview 1과 동일하므로 생략한다.

  • 하드웨어 그래픽 가속 기능(OpenGL)은 현재 사용 불가능하다. 따라서 이러한 기능에 의존하는 API들(WebView같은) 역시 사용할 수 없다.


Peripheral I/O

Preview 1과 동일


User Drivers

Preview 1과 동일


인텔 Edison과 Joule 그리고 NXP Pico 관련 내용은 생략합니다.


Raspberry Pi

  • Network : 인터넷 접속이 없더라도 이미 이더넷으로 네트워크에 연결되어있는 경우 Wi-Fi로 인터넷에 연결할 수 없다.
  • Camera : 하나 이상의 대상 출력 화면에 대해서는 새로운 CameraCaptureSession을 생성할 수 없다.
  • Camera : 어던 CameraCaptureSession에 대해서라도 최초의 요청에 대해서는 2개의 이미지가 queues에 쌓이게 된다. 이런 동작은 같은 session 내에서 연이어 발생하는 CaptureRequest가 이전 capture에서 버퍼링된 프레임을 리턴하는 문제를 야기한다.
  • I/O : BCM13/PWM1, BCM18/PWM0 공유 핀들은 이미 PWM 핀으로 활성화 된 경우 재부팅하기 전까지는 GPIO로 사용할 수 없다.
  • I/O : GPIO 핀인 BCM4, BCM5, 그리고 BCM6는 입력으로 사용할 경우 내부적으로 3.3V로 pulled-up(스위치가 Off 상태이면 전류가 흐르고 On 상태면 흐르지 않는 상태)된다.
  • Audio : 온보드 아날로그 audio는 PWM과 동시에 사용할 수 없다.

Developer Preview 3

릴리즈 날짜 : 2017년 4월
빌드 번호 : NIG86E
play service : 10.0.0


개요 부분은 Preview 2와 동일하며 다만 지원 기종에 NXP Argon이 추가 되었다.


◼︎New in Preview 3


NXP Argon i.MX6UL support

Android Things는 이제 NXP® Argon i.MX6UL 개발 플랫폼을 지원한다. 기기와 성능에 대한 자세한 내용은 developer kits에서 확인하라.


Android Bluetooth APIs 지원

개발자들은 이제 Android Things가 지원하는 모든 기기들간에 Android Bluetooth APIs를 이용할 수 있다. 이 APIs는 
이전 세대의 Bluetooth기기와 BLE(Bluetooth Low Energy) 기기 간의 통신에도 사용 가능하다. Bluetooth audio와 Bluetooth GATT 서버 코드 샘플을 Samples 페이지에서 확인하라. 


USB host 지원

Android Things 기기들은 이제 USB host 모드로 작동할 수 있다. USB Enumerator 샘플을 통해 USB host에 연결된 USB 장치를 순회하면서 그 장치의 인터페이스와 연결점을 출력하는 데모를 확인할 수 있다.


Access to USB-serial devices

USB-serial 변환 장치는 연결 후 UartDevice로 확인된다. 개발자들은 getUartDeviceList() 함수를 이용하여 이 장치들의 이름을 발견할 수 있다.


Reference documentation

이제 온라인 상에서 reference documentation를 볼 수 있다.


◼︎Known Issues

  • 시스템 전원 관리자는 현재 사용 불가능하다. 기기가 suspend 상태로 들어가지 않고 따라서 wake lock 기능이 필요치 않다(wake locks는 sleep 상태로 들어가거나 sleep 상태에서 깨우는 기능).
  • 위험한 권한 요청에 대해서는 기기를 재부팅해야 승인이 이루어진다. 이런 현상은 새로운 앱 설치 및 앱 내에 
    <uses-permission>요소가 있는 경우를 모두 포함한다.
  • 부팅 시 dex를 미리 최적화하기 위해 Google Plat Service는 2~3분 정도의 시간을 필요로 한다. 이 프로세스가 완료되기 전에는 App을 설치할 수 없다.
  • 하드웨어 그래픽 가속 기능(OpenGL)은 현재 사용 불가능하다. 따라서 이러한 기능에 의존하는 API들(WebView같은) 역시 사용할 수 없다.
  • A2DP Bluetooth profile(Advanced Audio Distribution Profile : 블루투스를 이용하여 음악을 내보내는 기술)는 sink 모드로 설정된다. 이후 릴리즈에서는 Bluetooth profiles를 설정 가능한 방법을 제공할 예정이다. 이러한 이유로 현재 BluetoothAdapter.getProfileProxy()에서 BluetoothProfile.A2DP 인자가 사용될 경우 에러가 발생하게 된다.

Peripheral I/O

-Preview 2와 동일


User Driver

-Preview 2와 동일


인텔 Edison과 Joule 그리고 NXP Pico, Argon 관련 내용은 생략합니다.


Raspberry Pi

  • Audio : Wi-Fi와 Bluetooth가 모두 활성화 된 경우 Audio 품질 이슈가 발생한다.

나머지 내용은 Preview 2의 6개 항목과 동일


Developer Preview 3.1

릴리즈 날짜 : 2017년 5월
빌드 번호 : NIG86K
play service : 10.0.0


이번 릴리즈는 라즈베리파이 3 개발을 위한 릴리즈이다.
주의 : AIY Projects Voice Kit 지원을 위한 경우가 아니라면 이 버전으로 업데이트할 필요가 없다.


◼︎New in Preview 3.1


Voice Kit support

Android Things는 AIY Projects 프로젝트로부터 라즈베리파이 기반의 Voice Kit을 지원하게 되었다. Voice User Interface(VUI, 음성 사용자 인터페이스)를 빌드하기 위해 이 kit을 사용할 때 cloud 서비스를 이용할 수 있다(Google Assistant SDK 또는 Cloud Speech API 등의 서비스)


◼︎Known Issues


Preview 3과 동일

반응형




MagigMirror2 사용해보기


시간은 대체로 망설임을 잡아먹고 산다. 할까? 말까?, 이렇게 할까? 저렇게 할까?, 언제 할까? 물음표가 붙은 매 순간을
시간은 낼름낼름 받아먹으면서 점점 더 기운을 내서 빨리 가버린다.


아두이노를 이용하여 스마트 미러를 만들어보고자 시작한 것이 벌써 2달여가 지나가고 있다. 그 사이 많은 망설임과
다른 선택지에 밀려 별다른 진전도 없이 시간은 흘러갔다. 물론 그 사이 라즈베리파이로 방향 전환을 하여 Alexa도
한 번 테스트를 해보고 Android Things도 한 번 설치해보고 하긴 했으나 실질적인 도움이 되는 건 없었다.


이런 저런 상황을 보니 아무래도 직접 모든 것을 해결하기에는 역부족이다 싶어 그 유명하다는 스마트 미러용 오픈소스
Magic Mirror2를 설치해보기로 했다(2는 원래 윗첨자인데 안타깝게도 Ulyssess 편집기에는 첨자 기능이 없다…ㅠ.ㅠ)


설치와 실행이 너무도 간단하여 굳이 글로 정리하기도 민망하지만 간단한 특징들만 좀 정리해보도록 하겠다.


소스 위치


소스는 다음의 GitHub 경로에서 다운로드 받을 수 있으며 README에 필요한 내용들이 정리되어있다.

https://github.com/MichMich/MagicMirror#configuration


소스 구조


소스는 Node.js 기반의 javascript 코드로 작성이 되어있다. 많은 디렉토리와 파일들이 있으나 가장 중요한 것들은
다음과 같다.


js 디렉토리 : 가장 기본적인 앱 실행에 필요한 파일들이 들어있으며 defaults.js에는 기본 설정이 들어있다.



config 디렉토리 : 말 그대로 설정파일이 들어있다. 자동설치를 하면 달리 작업할 것이 없으나 수동 설치를 할 경우
config.js.sample 파일을 config.js 파일로 복사한 후 필요한 설정을 하면 된다. 앞서 말한대로 이 설정의 기본값들은
js/defaults.js에 코딩되어있다.


modules 디렉토리 : 각각의 기능을 담당하는 모듈들이 들어있다. 기본적으로 화면에 보여지는 모듈은 다음과 같다.
1. calendar : 날짜 및 기념일 과 휴일 표시. calendarlabs.com 서비스 이용
2. clock : 시간 표시
3. currentweather : 현재 날씨 표시. openweathermap.org 서비스 이용
4. weatherforecast : 1주일간의 일기 예보 표시. openweathermap.org 서비스 이용
5. newsfeed : 뉴욕 타임즈의 뉴스 피드 표시. nytimes.com 서비스 이용
6. compliments : ‘Hey there sexy!’ 등 뻘소리 뱉는 모듈…-.-


그밖에 다른 모듈로부터의 경고를 표시해주는 alert, 정적인 텍스트를 표시해주는 hello world, MagicMirror
자체의 버전 업데이트를 알려주는 updatenotification 등의 기본 모듈이 있다.


translations 디렉토리 : 다국어 지원을 위한 언어 파일들이 모여있는 곳. 하지만 사용되는 타이틀이 몇개 없어
다국어 지원의 의미가 별로 없다. 예를들어 한국어 파일인 kr.json의 내용은 전체 코드가 다음과 같다.


{
"LOADING": "로드 중 &hellip;",
"TODAY": "오늘",
"TOMORROW": "내일",
"DAYAFTERTOMORROW": "모레",
"RUNNING": "종료 일",
"EMPTY": "예정된 이벤트가 없습니다.",

"N": "북풍",
"NNE": "북북동풍",
"NE": "북동풍",
"ENE": "동북동풍",
"E": "동풍",
"ESE": "동남동풍",
"SE": "남동풍",
"SSE": "남남동풍",
"S": "남풍",
"SSW": "남남서풍",
"SW": "남서풍",
"WSW": "서남서풍",
"W": "서풍",
"WNW": "서북서풍",
"NW": "북서풍",
"NNW": "북북서풍",

"UPDATE_NOTIFICATION": "새로운 MagicMirror² 업데이트가 있습니다.",
"UPDATE_NOTIFICATION_MODULE": "MODULE_NAME 모듈에서 사용 가능한 업데이트 입니다.",
"UPDATE_INFO": "설치할 COMMIT_COUNT 는 BRANCH_NAME 분기에 해당됩니다."
}


그밖의 디렉토리들과 파일들은 자동을로 설치했다면 당장에 수정이 필요한 부분은 없다.


설치


나는 그냥 자동으로 설치 했다. 설치 방법은 GitHub 페이지의 README를 보는 것이 더 정확하고 빠를 것이다.
자동 설치는 콘솔 창에서 다음과 같이 입력한 후 엔터를 치면 된다.


bash -c "$(curl -sL https://raw.githubusercontent.com/MichMich/MagicMirror/master/installers/raspberry.sh)"


모든 설치가 자동으로 이루어지나 중간에 설치 확인하면서 ‘Y’를 한 번 입력해줘야 하고 마지막에 자동 실행 여부를
묻는 부분에서 ‘y/n’을 입력해 주어야 한다. 자동 실행 여부를 ‘y’로 입력해주면 설치 종료 후 바로 MagicMirror가
실행된다. MagicMirror가 실행되면 다음과 같은 화면이 표시된다.



실행과 종료


처음 자동실행을 했더니 바로 MagicMirror가 실행되면서 아무런 메뉴도 없고 키도 특별히 먹는 것이 없다보니
종료시키는 방법을 몰라 한참을 헤맸다.


일단 cmd+tab(나는 Mac을 쓰므로 이 조합이지만 다은 OS는 Ctrl-tab)을 누르면 설치를 진행했던 콘솔 창이
화면 앞으로 나타난다. 콘솔 화면은 아래 그림과 같은 내용이 떠있을 것이다.



MagicMirror는 PM2라는 툴로 시작과 종료를 한다. 각각 다음과 같은 명령어를 사용한다.


$> pm2 start MagicMirror
$> pm2 stop MagicMirror


이 명령어도 README에는 ‘pm2 start mm’이라고 적혀있어 좀 헤맸는데 나중에 위 이미지의 화면을 보니
name에 MagicMirror라고 되어있어서 이 이름으로 입력했더니 되었다…-.-


설정


설정 파일은 MagicMirror가 설치된 경로 아래에 config라는 디렉토리 바로 밑에 있다. 앞서 소스 구조에서 설명한
바와 같이 수동으로 설치할 경우 config.js.sample을 config.js로 복사하여 사용하면 되나 자동 설치할 경우에는
이 과정까지 모두 진행되어 바로 config.js 파일이 보일 것이다. 최초 코드는 다음과 같다.


/* Magic Mirror Config Sample
 *
 * By Michael Teeuw http://michaelteeuw.nl
 * MIT Licensed.
 */

var config = {
	port: 8080,
	ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], // Set [] to allow all IP addresses.

	language: "en",
	timeFormat: 24,
	units: "metric",

	modules: [
		{
			module: "alert",
		},
		{
			module: "updatenotification",
			position: "top_bar"
		},
		{
			module: "clock",
			position: "top_left"
		},
		{
			module: "calendar",
			header: "US Holidays",
			position: "top_left",
			config: {
				calendars: [
					{
						symbol: "calendar-check-o ",
						url: "webcal://www.calendarlabs.com/templates/ical/US-Holidays.ics"
					}
				]
			}
		},
		{
			module: "compliments",
			position: "lower_third"
		},
		{
			module: "currentweather",
			position: "top_right",
			config: {
				location: "New York",
				locationID: "",  //ID from http://www.openweathermap.org/help/city_list.txt
				appid: "YOUR_OPENWEATHER_API_KEY"
			}
		},
		{
			module: "weatherforecast",
			position: "top_right",
			header: "Weather Forecast",
			config: {
				location: "New York",
				locationID: "5128581",  //ID from http://www.openweathermap.org/help/city_list.txt
				appid: "YOUR_OPENWEATHER_API_KEY"
			}
		},
		{
			module: "newsfeed",
			position: "bottom_bar",
			config: {
				feeds: [
					{
						title: "New York Times",
						url: "http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml"
					}
				],
				showSourceTitle: true,
				showPublishDate: true
			}
		},
	]

};

/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {module.exports = config;}


이 기본 설정에 필요한 내용을 추가하거나 수정하여 사용하면 된다. 몇가지 팁을 정리하자면 다음과 같다.


  1. calendarlabs.com의 한국 휴일 데이터 URL은 다음과 같다. 하지만 모두 영어로 표시된다…ㅠ.ㅠ
    webcal://www.calendarlabs.com/templates/ical/SouthKorea-Holidays.ics
  2. openweathermap.org의 경우 서비스를 이용하기 위해서는 가입 후 appid를 발급받아야 한다.
    정상적인 appid를 입력해야 데이터가 화면에 표시된다.
  3. newsfeed의 경우 개발자 문서에 언급된 사용 가능 뉴스는 뉴욕타임즈와 bbc밖에 없다.
  4. 컨텐츠의 크기를 조절하는 zoom이라는 옵션이 있는데 왠지 적용되지 않는 것 같다.
  5. language 옵션을 kr로 하면 한글 표시가 나오나 앞서 말한 바와 같이 설정된 텍스트가 거의 없어 ‘로딩중…’이나 바람 방향을 표시하는 ‘남서풍’ 정도만 한글을 볼 수 있다.

요약


아직 설치하고 실행만 해본터라 이정도에서 정리를 마치고자 한다. 진짜 중요한 부분은 모듈을 만드는 부분일
것이다. 개발자 문서에도 모듈은 만드는 부분에 대해 별도의 페이지를 할애하여 안내하고 있다. 새로운 모듈이
없이는 스마트 미러라고 하기에는 많이 부족하다(그래서 이름이 그냥 MagicMirror인 것 같다). 적어도 음성 명령
정도는 포함시켜주어야…^^;;;


또 언제가 될지는 모르겠지만 일단 만들고 싶은 모듈은 소니에서 제공하는 안드로이드용 건강 앱인 ‘LifeLog’
API를 이용하여 LifeLog 정보를 MagicMirror에 표시해 보는 것이다. 그러려면 일단 소스 분석부터 제대로 좀
해봐야 할 것 같지만…-.-


그리고 현재 5인치 LCD를 사용하는데 아무래도 LCD 사이즈를 좀 더 키워야 할 것 같다.


그럼 기약 없는 다음 어느 날에 후속 이야기를 적어보도록 하겠다…^^;;;






  1. 2017.11.10 00:24

    비밀댓글입니다

    • 마즈다 2017.11.10 12:23 신고

      관심 가져주셔서 감사합니다.
      일단 라즈비안 자체가 리눅스 운영체제이기 때문에 콘솔 창 열고 cp 명령어로 처리해주시면 됩니다. 그렇게 한 후 말씀하신대로 config.js 열어서 필요한 부분 설정하시면 됩니다^^

  2. 하하하 2017.11.12 23:31

    그냥 라즈베리 터미널에 자동설치를 입력하고 y만 눌렀는데 안되네요 왜그러나요 ㅠㅠ 에러가 나요

    • 마즈다 2017.11.13 09:28 신고

      혹시 콘솔 상에 다른 메시지는 출력이 안되던가요? 어느 정도 정보가 있어야 답변을 드릴 수 있을 것 같습니다.

반응형


Android Things SDK 개요


이 글은 Android things의 공식 홈페이지에 있는 Overview를 번역한 것이다. 크게 보자면 Android Things가
embedded 기기에 대한 core Android의 확장이며 그에 따른 Android Things의 이점과 제약을 설명하고 있다.


개인적으로는 다른 어떤 이점보다도 단일 app 기반이라는 점과 UI의 부재라는 제약이 더 크게 보인다.
사실 라즈베리파이는 아두이노나 다른 embedded 기기에 비한다면 상당히 고사양의 기기이다. multi tasking이
가능한 CPU와 HDMI라는 고사양의 display 인터페이스를 가지고 있음에도 불구하고 그 것을 활용하지 못한다는
것은 하드웨어 리소스의 낭비로 보여진다. 


이런 측면으로 보자면 아두이노를 지원해야 할 것 같지만 그 또한 여의치 않은 것이 아두이노같은 저수준의 디바이스는
작은 크기의 소프트웨어와 빠른 속도, 저사양의 기기이므로 메모리 관리가 효율적으로 이루어져야 한다는 점에서 
VM 기반의 Android 플랫폼이 썩 어울려 보이지는 않는다.


이렇게 쓰고보니 Android Things가 마치 쓸모없는 것 처럼 표현이 되었는데…아직 개발자 프리뷰이니 조금 더
지켜보아야 할 것 같다.



Android Things는 동일한 Android 개발 툴, 동급 최고의 Android 프레임워크, 모바일에서 개발자들의 성공을 
보장하는 Google API를 제공함으로써 embedded 기기와 관련된 개발을 쉽게 할 수 있도록 한다.



embedded 기기를 위한 app들은 개발자로 하여금 폰이나 태블릿보다 하드웨어 주변 장치나 드라이버에 더 익숙하게
한다. 또한 일반적으로 embedded 기기는 사용자들에게 단일 app 환경을 제공한다. 이 문서는 core Android 
개발과 Android Things 사이에 추가된 것, 빠진 것 등 중요한 차이점을 설명한다.


Android Things는 Things Support Library를 통해 추가적인 API를 제공하는 Core Android 프레임워크의
확장이다. 이런 추가된 API들은 기존의 모바일 기기에서는 찾아볼 수 없었던 새로운 유형의 하드웨어를 통합시킬 수 
있는 app을 개발할 수 있도록 해준다.


또한 Android Things 플랫폼은 단일 애플리케이션 사용을 위해 간소화 되어있다. 시스템 app이 존재하지 않고
기기의 시작과 동시에 개발자가 개발한 app이 자동으로 실행됨으로써 사용자가 app 환경에 집중할 수 있도록 해준다.


Things Support Library


주변장치 I/O API

주변장치(Peripheral) I/O APIs는 개발한 app이 산업 표준의 프로토콜과 인터페이스를 통해 센서들이나 액츄에이터
들과 통신할 수 있도록 해준다. 다음의 인터페이스들을 지원한다 : GPIO, PWM, I2C, SPI, UART.

APIs의 사용 방법에 대한 추가적인 정보는 다음 링크에서 확인하라. Peripheral I/O API Guides


User Driver API

사용자 Drivers는 기존 Android 프레임워크 서비스를 확장하고 다른 app들이 표준 Android APIs를 통해 접근
가능한 프레임워크에 하드웨어 이벤트를 주입할 수 있도록 해준다.

APIs의 사용 방법에 대한 추가적인 정보는 다음 링크에서 확인하라. User Driver API Guides


동작의 변화


Core application packages


Android Things 는 시스템 apps와 content providers등 표준 API 모음은 포함하지 않는다. 앱을 개발할 때
common intents와 아래 목록의 content provider APIs는 사용을 하면 안된다.

  • CalendarContract
  • ContactsContract
  • DocumentsContract
  • DownloadManager
  • MediaStore
  • Settings
  • Telephony
  • UserDictionary
  • VoicemailContract


화면 출력은 선택 사항

Android Things는 전통적인 Android 애플리케이션과 마찬가지로 UI toolkit을 이용하여 그래픽 사용자 
인터페이스를 제공할 수 있다. 그래픽 모드에서는 애플리케이션의 Window가 전체 화면을 모두 차지하게 된다.
Android Things는 시각적인 사용자 환경을 완벽하게 제어할 수 있는 시스템 상태바라든지 네비게이션 버튼이
포함되어있지 않다. 


따라서 Android Things는 디스플레이가 필요하지 않다. 그래픽 디스플레이가 제공되지 않는 기기상에서도
여전히 Activity들은 Android Things app의 중요한 컴포넌트이다. 그렇기 때문에 프레임워크는 모든 입력 
이벤트를 포커스가 주어진 foreground Activity로 전달한다. 이렇게 개발된 앱은 service와 같은 다른 애플리케이션 
컴포넌트로부터 키 이벤트나 모션 이벤트 등을 수신받지 못한다.


Home activity 지원

Android Things는 부팅 시 자동으로 실행될 수 있도록 애플리케이션의 manifest에 주요 진입점으로 home
activity
를 노출하도록 하고 있다. 이 activity는 CATEGORY_DEFAULT와 IOT_LAUNCHER 두 개의 intent
filter를 반드시 포함시켜야 한다.


개발의 편의를 위해 이 activity는 CATEGORY_LAUNCHER 또한 포함시켜야 하므로 Android Studio를 통한
배포 또는 디버깅시에 이 activity를 기본 activity로 실행시킬 수 있다.

<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>


Google service들의 지원

Android Things는 Google APIs for Android의 일부를 지원한다. 아래의 목록은 Android Things에서의 
API 지원 여부를 보여준다.


Supported APIs


Unavailable APIs

주의 : 일반적으로 사용자의 입력이나 인증 절차가 필요한 APIs는 app에서 사용할 수가 없다.


Android Things의 각 배포버전은 Google Play Services의 최신 안정화 버전을 번들로 제공하며 적어도 
클라이언트 SDK 10.0.0 이상의 버전이 필요하다. Android Things는 기기의 Play 서비스를 자동으로 업데이트
해주는 Google Play Store를 포함하고 있지 않다. 기기의 Play 서비스 버전은 정적으로 고정되어있기 때문에
app은 대상 배포판에 번들로 제공되는 것 보다 큰 버전의 클라이언트 SDK를 대상으로 할 수 없다.


주의 : 개발자 프리뷰 상태인 동안 각 배포판의 번들 버전은 release notes에 그 목록이 표시되어 있다.


Permissions

embedded 기기에서는 app 실행 시에 대화상자에 접근할 수 있는 UI를 제공하지 않기 때문에 app 실행 시의 권한 
요청
은 지원되지 않는다. 필요하다면 app의 manifest 파일에 권한을 선언해야 한다. app의 manifest에 선언된 
정상적이고 워험한 권한들은 설치 시에 부여된다.


Notifications

Android Things에는 시스템 전반적으로 상태표시줄이나 창이 없으므로 notification은 지원되지 않는다.
따라서 NotificationManager APIs의 호출은 피해야 한다.






+ Recent posts