레이블이 Raspberry Pi인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Raspberry Pi인 게시물을 표시합니다. 모든 게시물 표시

2019년 12월 12일 목요일

라즈베리 파이에서 REST API 사용해서 스마트폰으로 제어하기 (Use REST API in Raspberry Pi for controlling from smartphone)

라즈베리 파이에서 flask를 사용해 REST API를 구현하여 스마트폰이나 컴퓨터에서 원격으로 센서값을 읽거나 장치를 제어할 수 있도록 간단하게 구현 해 보았다.
HTTP GET과 POST method를 사용할 수 있도록 해 놓았고 POST method에서 파라미터는 JSON으로 넘겨주면 된다.
아래 코드가 구현해 놓은 API는 다음과 같다.

* /api/led :
    method: POST
    parameter: {'ledId': [id], 'val': [OnOff]}  - [id]는 조작하고자 하는 LED번호, [OnOff]에는 LED를 켜고 싶으면 1, 끄고 싶으면 0
    return: 명령이 실행되고난 후 LED 상태


* /api/led :
    method: POST
    parameter: {'ledId': [id], 'val': [OnOff]}  - [id]는 조작하고자 하는 LED번호, [OnOff]에는 LED를 켜고 싶으면 1, 끄고 싶으면 0
    return: 명령이 실행되고난 후 LED 상태

* /api/sw :
    method: POST
    parameter: {'id': [id]}  - [id]는 상태를 알고 싶은 스위치 번호
    return: 현재 스위치 상태

* /api/sensor/[sensorName] :현재 sensorName에 올 수 있는 센서는 temperature, humidity, pressure, light
    method:GET
    return:지정한 센서에서 읽은 값

* /api/sensors :
    method: GET
    return: 전체 센서 값 (JSON 포맷)

* /api/sensors :
    method: POST
    parameter: {'sensorList': [sensor list]}  -값을 읽고 싶은 센서 이름. 한개 이상이 올 수 있고 복수개일 경우 ','로 구분
    return: 지정한 센서에서 읽은 값 (JSON 포맷)

* /api/exec :
    method: POST
    parameter: {'cmd': [command line]}  - [commnad line]은 라즈베리 파이에서 실행할 명령
    return: 명령 실행 결과

코드를 실행하면 라즈베리 파이에 다음과 같은 화면이 나오게 된다.

PC의 브라우저에 [ip]:5000/api/sensors 를 입력해 주면 다음과 같은 결과를 얻을 수 있다. 이 경우 4개의 센서값을 모두 JSON 포맷으로 리턴해 준다.


[ip]:5000/api/sensor/temperature 를 읿력해 주면 다음과 같이 온도센서 값만 리턴 해 준다.


지금까지는 브라우저의 주소창에 입력했는데 이 경우 GET method를 사용하는 것이다. POST method를 확인하려면 curl, postman, rested 같은 툴을 사용해야 한다. 여기서는 파이어폭스에 설치한 rested를 사용했다.

앞으로 파란색 부분은 저 값으로 고정시켜 놓고 빨간색 부분만 바꿔주면서 테스트 하면 된다. 위의 사진은 api/led 를 호출해 원하는 LED를 on/off 하는 예제이다. 소스코드의 @app.route('/api/led', methods = ['POST']) 에서 빨간색 굵은 부분이 주소가 된다. 단 여기서는 flask 서버가 5000번 포트를 사용하고 있으므로 주소 뒤에 ':5000'을 추가해 줘야 한다.
Request Body 부분에 id와 val 이라는 두개의 키가 있어 id의 값 부분에 조작할 LED 번호를 넣어주고, val의 값 부분에 LED on/off 를 넣어주면 된다.
실행이 되면 바뀐 LED 상태값이 리턴된다. 위의 예에서는 4번 LED를 ON 시킨 것이다. 맨 마지막 부분에 결과로 'ON'이 리턴되었다.


라즈베리 파이 화면을 보면 이번엔 /api/led 주소로 POST emethod를 사용했음을 확인할 수 있다.

위의 화면은 LED를 끄는 것이다. 나머지 부분은 동일하고 val 의 값만 '0'으로 보내주면 된다.

다음은 /api/sw를 통해 스위치 상태를 확인해 본 것이다. URL 부분이 /api/led에서 /api/sw로 바뀌었다. 또한 이번엔 파라미터로 id만 보내주면 된다. 여기서는 2번 스위치의 상태를 확인해 본 것이다. 현재 스위치 상태 값이 리턴된 걸 확인할 수 있다.


다음은 /api/sensors를 호출하는데 위쪽에서는 GET method로 호출해서 모든 센서 값을 읽어왔는데 여기서는 POST method를 사용해 값을 읽기를 원하는 센서만 지정할 수 있다. sensorList 의 값 부분에 원하는 센서 이름을 넣어주면 된다. 두개 이상을 넣을 땐 센서 이름 사이에 ','로 구분해 주면 된다. 여기서는 temperature와 humidity 센서 값을 읽도록 해 보았다. 결과값으로 두 센서값이 JSON 포맷으로 리턴되었다.


마지막으로는 라즈베리 파이에서 명령을 실행할 수도 있다. /api/exe에 POST method로 실행하기 원하는 명령을 보내주면 된다. 실행된 결과가 리턴된다. 이 방법을 이용하면 라즈베리 파이에 연결해 놓은 장치가 API를 제공해 주지 않거나 python module이 없고 CLI로 제어하는 프로그램만 있어도 원격에서 제어가 가능해 진다.


전체 코드는 다음과 같다. 이 코드는 Raspberry Pi 4에서 python3를 사용해 동작시켜 보았는데 python3만 지원하는 환경이면 어디에서나 동작하는데 문제는 없을 것이다.

* 여기서는 Raspberry Pi에서 실행되는 서버 코드만 설명하고 있다. 스마트폰 앱에서 제어하려면 해당하는 GET, POST 메소드로 API를 사용하면 된다. 해당 부분 코드 작성은 직접 하지 않아도 postman같이 자동으로 생성해주는 툴을 사용하면 매우 간단하게 사용할 수 있다. 다음 포스트에서는 postman을 사용해 클라이언트의 http request 부분의 코드를 자동으로 생성하는걸 설명하겠다.


#!/usr/bin/python3

from flask import Flask, request, jsonify
import os, ntpath
import json
import subprocess

app = Flask(__name__)

# 실제로는 GPIO library를 사용해 읽은 스위치 값을 리턴해줘야 함
def readSwitch(id):
  print ("# readSwitch is called for switch %d" % id)
  return 1

# 실제로는 온도센서 값을 읽어 리턴해 줘야 함
def readTemp():
    return str(25.3)

# 실제로는 습도센서 값을 읽어 리턴해 줘야 함
def readHumid():
    return str(73)

# 실제로는 압력센서 값을 읽어 리턴해 줘야 함
def readPressure():
    return str(1025)

# 실제로는 조도센서 값을 읽어 리턴해 줘야 함
def readLight():
    return str(419)

def readSensor(s):
    return sensorFunc[s]()

sensorFunc = {'temperature': readTemp, 'humidity': readHumid, 'pressure': readPressure, 'light': readLight}

@app.route('/api/led', methods = ['POST'])
def ledSvc():
    data = request.json
    #ledId = data['id']
    #onOff = data['val']
    ledId = int(data['id'])
    onOff = int(data['val'])
    if onOff == 0:
      newLedState = 'OFF'
      # 여기에 해당 LED(LED번호는 변수 ledId에 들어 있음)를 끄는 코드를 집어 넣어 줌
    else:
      newLedState = 'ON'
      # 여기에 해당 LED(LED번호는 변수 ledId에 들어 있음)를 켜는 코드를 집어 넣어 줌
    print ("Turn LED %d to %s" % (ledId, newLedState))
    return newLedState

@app.route('/api/sw', methods = ['POST'])
def swStatus():
    data = request.json
    ledId = int(data['id'])
    swState = readSwitch(ledId)  # readSwitch() 함수에서 스위치(ledId) 값을 읽어 리턴해 줌
    print ("Switch %d state : %d" % (ledId,swState))
    return str(swState)

@app.route('/api/sensor/<string:sensorName>', methods = ['GET'])
def readSensor(sensorName):
    try:
        ret = sensorFunc[sensorName]()
        return ret
    except:
        return 'Wrong Sensor'

@app.route('/api/sensors', methods = ['GET', 'POST'])
def readSensors():
    if request.method == 'GET':
        result = {}
        for s in sensorFunc:
            result[s] = sensorFunc[s]()
    elif request.method == 'POST':
        result = {}
        sl = request.json['sensorList']
        print (sl)
        sl = sl.split(',')
        for s in sl:
            print (s)
            result[s] = readSensor(s)

    return json.dumps(result)

@app.route('/api/exec', methods = ['POST'])
def externalPgm():
    data = request.json
    cmds = data['cmd']
    print (cmds)
    cmdl = cmds.split(' ')
    result = subprocess.check_output(cmdl)
    return result

if __name__=='__main__':
    app.run(host='0.0.0.0', debug=True)











2017년 8월 2일 수요일

온라인으로 전자부품 구입하기 - 엘리먼트14

무언가를 만들기 위해 부품을 구매하는 방법은 크게 온라인, 오프라인 구매로 나뉜다.
실제로 눈으로 보면서 원하는 부품을 구하는게 좋기는 하지만 매장의 위치가 구로나 용산쪽에 몰려있고 영업시간이 보통 일반 업무시간과 동일하다 보니 무언가 다른 일을 하면서 취미로 작업을 하는 사람들이 매장을 방문하기는 매우 힘들다.

그런 이유로 인해 대부분 온라인으로 주문하게 되는데 국내에서는 주로 디바이스마트, 엘레파츠 같은 곳을 사용하게 된다. 하지만 흔하게 많이 사용되는 부품은 준비가 되어 있어도 조금만 특이한 종류의 부품이 되면 없는 경우가 많다.

그래서 최근에 많이 사용하는 곳 중에 하나가 엘리먼트14이다. 싱가폴에 있는 글로벌 전자부품 유통회사인데 국내에 사무소가 있어 한글 웹사이트에서 주문하고 원화로 결재할 수 있어 상당히 편리하다. 60만가지의 재고를 보유하고 있고 MOQ(Minimum Order Quantity, 즉 최소 주문 수량)가 없어 여러 종류의 부품을 소량구매 할 수 있는것이 장점이다. 또한 국내에 서비스센터가 있어 국제전화나 영어에 대한 부담 없이 통화가 가능하다.

단 한가지 약점이자 장점일 수 있는 부분이 배송비인데 6만원 이상 주문시 무료 배송을 해 준다는 것이다. 그래서 저렴한 부품 한두개 구매시는 배보다 배꼽이 훨씬 더 큰 경우(제품가격은 몇천원, 배송비가 2~3만원)도 생길 수 있는데, 대신 구매할 것들을 모아 한번에 주문하면 배송비 무료인데도 불구하고 UPS나 DHL등을 사용해 매우 빠르게 국제배송이 된다는 것이다. 보통 주문 완료하고 3~4일 내로 부품을 받아볼 수 있다. AliExpress를 사용해 본 사람이라면 알리의 무료배송을 생각해보면 요새 많이 빨라졌다고는 해도 아주 운 좋으면 일주일에서 재수 없음 한두달까지도 걸리는데 비해 얼마나 큰 장점인지 바로 알 수 있을 것이다. 그래서 주문할 것이 5만원 조금 넘는 경우 나중에 써 보고 싶은 부품 한두개 추가해서 오히려 더 싸게 주문하곤 한다.

원래 element14의 웹사이트 주소는 http://www.element14.com 이지만 국내에서 저 주소로 들어가면 자동으로 http://kr.element14.com 으로 리다이렉션 되어 한글 홈페이지가 나타난다.


검색어는 정확하게 다 치지 않아도 자동완성 기능으로 입력한 단어로 시작하는 부품들을 보여주기 때문에 쉽게 찾을 수 있다.


원하는 부품을 선택하면 아래와 같이 상세정보와 가격이 나오게 된다. 재고가 없는 경우는 백오더가 들어가기 때문에 배송에 얼마나 걸릴지는 상황에 따라 달라지지만 재고가 있는 경우는 보통 정상적으로 주문이진행되면 며칠 내로 받아볼 수 있다.


장바구니에 추가하면 추가되었다는 메시지와 함께 계속 쇼핑을 할 것인지 장바구니로 갈것인지 물어보는 창이 팝업으로 뜬다. 그리고 화면 우측상단 부분의 장바구니에 현재 몇개의 품목이 들어있고 총액이 얼마인지 표시하는 부분이 업데이트 된다.


장바구니로 가 보면 넣어놓은 품목들을 볼 수 있다. 다 넣었으면 오른쪽에 있는 오렌지색 '주문' 버튼을 누르면 된다. 이 단계에서는 아직 handling charge는 '알수 없음'으로 표시된다.

주문 버튼을 누르면 먼저 로그인을 위한 화면이 나온다. 기존에 아이디를 가지고 있으면 로그인 하면 되고없으면 위쪽의 '지금 회원가입하세요' 링크를 눌러 먼저 가입을 해 주면 된다.

 로그인을 하면 아래와 같이 주문정보 검토를 위한 화면이 나온다. 청구서 배송 주소와 배송 주소를 다르게 지정할수도 있지만 개인의 경우 보통 같이 하므로 '내 배송주소와 내 송장주소가 동일함'을 체크해 주면 된다.


 주문 검토 화면을 아래로 스크롤하면 배송방법과 지불방법을 지정할 수 있는 부분이 나온다. 개인정보 보호정책 부분을 체크해주고 배송방법을 선택한다. '전체 주문 묶음배송' 아니면 '제품별 개별 배송' 중에 하나를 고르게 될 것이다. 제품별 개별배송을 선택해도 배송비를 추가로 부담하지는 않으므로 여러개의 부품을 주문하는데 각각 따로라도 먼저 받고 싶으면 '제품별 개별 배송'을 선택하면 된다.
'지불방법 및 세부정보' 부분에서는 거래 계정이 없다를 선택하면 된다. 그리고 신용카드 정보를 입력해 준다. 단 비자, 마스터 카드만 가능하고 아멕스나 JCB등은 사용할 수 없다. 그리고 해외에서 결재하는걸로 되기 때문에 국내 주문같이 공인인증서니 그런 것들이 필요 없이 간단하게 주문이 처리된다.
모든 정보를 입력했으면 '주문 검토 진행'을 눌러 마지막으로 다시 확인할 수 있다.


'이용 약관 동의'에 체크하고 주소 및 주문정보를 꼼꼼하게 확인해야 한다. 아래쪽에 보면 제품가격 및 handling charge가 나오는데 여기서는 제품가격이 57568원이라 추가로 handling charge가 2만원이 붙었다. 이런 경우 4~5천원짜리 부품 하나 추가해서 6만원만 넘겨주면 handling charge가 0원이 되어 오히려 더 싸지게 되니 이 방법을 꼭 활용하기 바란다. 모든것을 확인했으면 '주문적용' 버튼을 눌러주면 주문이 끝나게 된다.



엘리먼트14으로 부품 구입하러 가기

* 이것은 '엘리먼트14 서포터즈'로서 엘리먼트14에서 제품(비글본 블랙)을 제공받아 작성된 글입니다.

조만간 구매한 비글본 블랙 리뷰를 올릴 예정입니다.

2017년 7월 13일 목요일

SIM5320E으로 3G 통신

SIM5320E는 SIMCOM이라는 회사에서 나온 3G 통신모듈이다.



다만 이 모듈을 구해도 바로 사용하기 힘들기 때문에 Adafruit에서 이 모듈의 breakout 보드를 구했다.
정확한 명칭은 Adafruit FONA 3G module로 사이트에 가 보면 US용과 EU용 두가지 버젼이 있는데 국내에서 사용하려면 꼭 EU 버젼으로 구해야 한다. 3G 주파수가 EU는 국내와 동일하게 2100MHz인데 비해 미국용은 1900MHz라 국내에서 사용할 수가 없다. 그리고 보드를 구매해도 딱 보드만 들어있기 때문에 별도로 2.1GHz 안테나와 GPS안테나를 구입해야만 사용할 수 있다. (단 GPS안테나는 옵션)



사진에서 볼 수 있는것처럼 보드의 위쪽에 커넥터와 SIM5320 모듈이 붙어있고 보드의 아래쪽에는 USIM 카드를 장착할 수 있는 소켓이 장착되어 있다.
저 보드는 휴대폰에서 마이크/스피거, 액정화면, 버튼이 빠진거라고 생각하면 된다. 즉 빠진 부품만 붙이면 완벽한 휴대폰을 만들어 줄 수도 있는 것이다.

저 모듈은 시리얼 포트를 통해 모뎀과 마찬가지로 AT 명령으로 모듈을 제어할 수 있으므로 아두이노같은마이크로 프로세서를 연결해 동작을 제어할 수 있다.


동작을 시켜보려면 휴대폰과 동일하게 개통된 USIM이 있어야 한다. 저 모듈은 일반크기의 USIM을 꼽아야 하므로 USIM adapter를 사용했다.


  먼저 보드의 아래쪽에 있는 USIM 소켓을 열어준다.


방향을 맞춰 USIM을 꼽아준다.


꼽은 다음 소켓을 닫아주면 된다. 위의 사진은 USIM이 장착 완료된 것이다.


 이제 사진의 화살표 부분에 마이크로USB 케이블을 꼽아 컴퓨터와 연결해준다. 단 Mac OS X에서는 디바이스 드라이버가 없어 장치가 인식되지 않고 윈도우와 리눅스에서만 인식된다. 여러개의 시리얼 포트로 인식되는데 만일 인식되지 않으면 디바이스 드라이버를 설치해 줘야 한다. 


케이블이 연결되면 USB커넥터와 오른쪽의 검은색 커넥터 사이에 있는LED가 빠르게 깜빡인다. 이때는 아직 전원이 켜지기 전이라 컴퓨터에는 장치가 인식되지 않고 전원을 켜 줘야만 인식된다. 오른쪽 검은색 커넥터 옆에 있는 PWR key라는 버튼을 눌러주면 모듈의 전원을 켜 주는 것이다.

 

전원이 켜지면 빠르게 깜빡이던 LED가 켜져있게 되고 컴퓨터에서는 새로운 시리얼 포트가 보이게 된다.



컴퓨터에서 시리얼 터미널 프로그램을 실행해 시리얼 포트를 연결해서 명령을 주면 모듈을 제어할 수 있는 상태가 된 것이다.
이제 여기서는 SimTech HS-USB AT Port 9000 (COM7) 포트를 열어준다. 일반 모뎀과 동일하게 AT 명령어를 사용하면 되고 사용할 수 있는명령 목록은 문서를 참고하면 된다.

SIM5320E AT command set

매우 많은 명령이 있어 아주 세밀하게 조작이 가능하지만 전화기로 사용하려면 몇개의 명령만으로도 충분하다.

시리얼 포트를 연 다음 전화를 걸고 받는걸 직접 해 보도록 하겠다. 단 전화 통화를 해야 하므로 마이크가 달려있는 이어폰을 모듈에 먼저 꼽아줘야 한다. 아래가 명령과 그에 대한 응답을 캡춰한 것이다. 굵은 글씨체가 내가 직접 입력한 명령들이고 나머지가 모듈이 출력하는 메시지이다.


at+creset
OK

START

+CPIN: READY

SMS DONE

+VOICEMAIL: INIT_STATE, 0, 0

PB DONE
at+creg
OK
at+creg?
+CREG: 0,1

OK
at+cops?
+COPS: 0,0,"KT",2

OK
atd[걸고자하는 전화번호];
OK

+WAP_PUSH: "REC UNREAD","114",,"17/07/13,11:40:56+36"
2406226170706C69636174696F6E2F766E642E7761702E6D6D732D6D65737361676500AF848C8298323376483744423134304746353030008D928A828E017C88058103093A8083687474703A2F2F642D6D6D73632E6B746677696E672E636F6D3A393038332F32337648374442313430474635303000

+WAP_PUSH: "REC UNREAD","114",,"17/07/13,11:40:56+36"
89078005EA31313400960EEAECA09CEBAAA9EC9786EC9D8C00

VOICE CALL: BEGIN

VOICE CALL: END: 000005

NO CARRIER

RING

RING

RING

RING

RING

MISSED_CALL: 01:23PM 07077374920
atd[걸고자하는 전화번호];
OK

VOICE CALL: BEGIN
at+chupVOICE CALL: END: 000015

OK
RING
ata
VOICE CALL: BEGIN

OK

VOICE CALL: END: 000007

NO CARRIER


먼저 AT+CRESET 명령으로 모듈을 리셋 시킨다. 잠시 기다리면 초기화 과정이 끝나고 PB DONE이라는 메시지가 나올 것이다. 그럼 AT+CREG? 명령을 입력해 모듈이 통신사 3G망에 등록되었는가를 확인한다. 망에 등록이 되어야만 통신(전화 걸기/받기, 문자 송/수신, 데이터 전송등)이 가능하다. AT+CREG? 명령의 응답이 '+CREG: 0,1' 이런식으로 나오는데 여기서 맨 마지막 숫자가 '1'이 나와야 정상적으로 통신사의 망에 등록이 된 것이다. 만일 이 값이 1이 아니면 등록이 안된 것이므로 다시 확인해 봐야 한다. 모듈에 해당 USIM을 처음 꼽으면 망에 등록이 안되는 경우가 있는데 이 경우는 AT+CRESET 명령을 두세번 실행한 다음 다시 확인해보면 될 것이다. 

AT+COPS? 명령으로 어느 통신사를 사용하는지 확인할 수 있다. 현재 테스트에 사용한 USIM은 에넥스텔레콤 것으로 KT망을 빌려쓰는 MVNO이므로 KT망에 등록된 걸 확인할 수 있다.

전화를 걸려면 ATD 명령을 사용하면 된다. ATD 바로 뒤에 원하는 전화번호를 쓰고 ';'를 붙여주면 된다. 즉 010-2345-6789번으로 전화를 걸고 싶으면 'ATD01023456789;'라고 입력하면 된다. ATD 명령을 내리면 실제로 전화가 걸리고 전화를 받으면 음성통화가 가능하다. 만일 통화중에 모듈쪽에서 전화를 끊고 싶으면 'AT+CHUP' 명령을 내려주면 된다. 어느쪽이 끊건 통화가 끝나면 'VOICE CALL:END:000015' 같이 통화가 끝났다는 메시지와 함께 통화시간을 알려준다.

명령을 내리지 않고 가만히 놔둬도 전화가 오면 RING이라는 메시지가 출력된다. 의도하지는 않았지만 070에서 스팸전화가 와서 받지 않고 놔뒀더니 상대방이 벨이 몇번 울린 후에 끊어서 MISSED_CALL 이라는 메시지와 함께 시간 및 발신자 전화번호가 출력되었다. 전화가 왔을 때 받으려면 ATA 명령을 내리면 된다.

일단 모듈을 연결해 가장 기본적인 음성통화를 해 보았다. 다음에는 SMS 발신 및 인터넷 연결 방법에 대해 설명하도록 하겠다.

2017년 5월 11일 목요일

라즈베리 파이에서 MQTT를 이용해 ESP8266 제어하기

여기서는 라즈베리 파이, ESP8266(또는 와이파이 쉴드를 붙인 아두이노), PC를 MQTT로 연결해 보도록 하겠다.

이번에 하고자 하는 것은 라즈베리파이가 python을 사용해 PC에 있는 MQTT 브로커에 메시지를 보내면 그 메시지가 ESP8266으로 전달되어 거기에 있는 LED를 제어하는 것이다.

MQTT for PC


MQTT server(브로커)로 사용할 수 있는 옵션이 몇가지 있다. Amazon의 AWS IoT나 AdafruitIO 같은 서비스들은 클라우드 기반의 브로커이다. 각 서비스는 여러가지 장점이 있다. 프로젝트의 필요성에 따라 클라우드 기반 서비스를 사용할 필요가 있을수도 있다.

Mosquitto


자신의 호스트에 브로커를 실행하고 싶으면 오픈소스인 moqsuitto가 훌륭한 옵션이다. 이 브로커는 MQTT를 완전히 지원할 뿐 아니라 pub/sub를 위한 커맨드라인 유틸리티까지 제공한다.

윈도우를 사용하면 mosquitto.org에서 바이너리 인스톨러를 받을 수 있다. 리눅스의 경우 패키지 매니져에서 'mosquitto' 또는 'mosquitto-mqtt'를 찾으면 된다. 맥에서는 homebrew를 사용해 mosquitto를 설치할 수 있다.

* 맥에서 설치한 경우 '/usr/local/sbin'을 PATH에 넣어줘야 한다.

Running Mosquitto


Mosquitto는 디폴트로 1883번 포트를 사용하고 보안기능은 다 꺼져 있다. 처음 시작할 때는 이것으로 충분하지만 결국에는 보안기능을 추가해 줄 필요가 있다.

Mosquitto를 verbose 모드로 실행하길 권장한다. 그러면 출력을 보고 어떤 일이 일어나는지에 대한 아이디어를 얻을 수 있을 것이다.

MacMan:~ james$ mosquitto -v 
1456272937: mosquitto version 1.4.5 (build date 2015-11-09 14:23:46-0800) starting 1456272937: Using default config. 
1456272937: Opening ipv4 listen socket on port 1883. 
1456272937: Opening ipv6 listen socket on port 1883.

Testing Mosquitto


서버 코드를 verbose 모드에서 실행시키고, 추가로 두개의 명령어 프롬프트를 열어준다. 각각의 명령어 프롬프트에서 메시지 전송 테스트를 위해 mosquitto_pub와 mosquitto_sub를 사용할 것이다.

Subscribing to MQTT Topic with Mosquitto


먼저 클라이언트가"debug"라는 토픽을 subscribe하게 해 준다. 첫번째 명령어 프롬프트에서 아래 명령을 입력해 주면 된다.

$ mosquitto_sub -h 127.0.0.1 -i testSub -t debug

* -h : host flag는 mosquitto가 실행되고 있는 서버를 지정. 여기서는 localhost
* -i : identity flag 는 옵션이다. Client ID가 지정되지 않으면 mosquitto_sub가 임의로 만듬
* -t : topic flag 는 subscribe 하려고 하는 topic을 지정. 여기서는 "debug"

서버에서는 topic을 만들지 않았다는걸 주목해라. Topic은 subscriber나 publisher가 처음 지정할 때 만들어진다.

Publish to MQTT Topic with Mosquitto


이제 topic을 기다리는 클라이언트를 가지고 있으므로, 다른 클라이언트가 그 topic을 publish하게 해 본다.

$ mosquitto_pub -h 127.0.0.1 -i testPublish -t debug -m 'Hello World'


이제 첫번째 명령어 프롬프트 화면을 보면 publish된 메시지가 와 있는것을 확인할 수 있다. mosquitto_pub와 mosquitto_sub 명렁 모두 --help 플래그로 확인 해 봐라. 매우 여러가지 유용한 옵션들을 찾아볼 수 있을 것이다.

이제 mosquitto 브로커가 정상 동작하고 있는것이 확인되었으므로 디바이스와 통신을 하게 해 보자.

MQTT for Raspberry Pi (Python)


라즈베리 파이에서 MQTT 메지시를 보내고 받을 수 있는 옵션으로는 mosquitto를 설치하는 것이다. 그러면 라즈베리 파이에서도 동일하게 mosquitto_pub와 mosquitto_sub 명령어를 사용할 수 있다.

다른 방법으로는 자신이 선호하는 언어용 MQTT 라이브러리를 사용하는 것이다. 여기서는 파이선을 사용한다.

Install MQTT for Python


여기서 사용할 MQTT library는 Paho Python Client이다. 오픈소스로 가장 최신의 MQTT를 지원한다.

설치는 간단하다. 먼저 'pip'를 설치하고 나서 아래 명령을 실행한다.

$ pip install paho-mqtt

* 만일 python3를 사용한다면 'pip' 대신 'pip3'를 사용해야 하는걸 잊지 마라.

Example MQTT Python Code for Raspberry Pi


Paho는 MQTT 서버와 통신을 매우 쉽게 만들어 준다.

import paho.mqtt.publish as publish
import time
print ("Sending 0...")
publish.single("ledStatus", "0", hostname="macman")
time.sleep(1)
print ("Sending 1...")
publish.single("ledStatus", "1", hostname="macman")

진짜 간단하다. 스크립트에 서버가 실행되고 있는 호스트 이름(여기서는 'macman'), Topic (여기서는 'ledStatus'), 메시지(여기서는 '0' 또는 '1')만 알려주면 된다.

이 MQTT 튜토리얼 예제에서는 '0'을 보내고 1초 기다린 후에 '1'을 보낸다. 이 동작은 ESP8266에 있는 LED가 잠깐동안 꺼졌다 다시 켜지게 만든다.


이제 RPi가 MQTT 메시지를 보내게 만들었으니 그 메시지를 받도록 만들어 보자. 다음 섹션은 ESP8266이 topic "ledStatus"의 메시지를 받아 실제로 LED를 깜빡이게 만들 것이다.

MQTT for ESP8266


PubSubClient는 아두이노 기반의 MQTT 클라이언트이다. 단지 몇 줄의 코드만으로 매우 쉽게 topic을 subscribe하거나 새로운 topic을 publish할 수 있다.

여기서는 ESP8266을 프로그래밍 하기 위해 아두이노 IDE를 사용했다.

Installing MQTT for Arduino IDE


아두이노 IDE에 PubSubClient를 설치하는건 간단하다. Github에서 수동으로 설치하거나 또는 Arduino Package Installer를 사용할 수 있다.


Arduino ESP8266 and Uno Note


Ethernet 쉴드가 장착된 Uno와 ESP8266간에 스위칭에는 별도의 추가적인 노력이 필요 없다. 라이브러리는 양쪽 모두 잘 동작한다.

Code Example for Arduino MQTT


PubSubCilent에 포함되어 있는 예제는 매우 훌륭하다. 예제에는 특히 MQTT에 대한 ESP8266 코드도 들어있다. 처음 시작할 때 그것들도 꼭 확인해 보기 바란다.

아래는 WiFi에 연결하고 "ledStatus" topic을 subscribe하는 코드이다. ESP8266이 메시지를 받으면 그에 따라 ledPin을 on/off한다.

#include <EEPROM.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_INA219.h>

// Connect to the WiFi
const char* ssid = "Dear John";
const char* password = "password123";
const char* mqtt_server = "macman";

WiFiClient espClient;
PubSubClient client(espClient);

const byte ledPin = 0; // Pin with LED on Adafruit Huzzah

void callback(char* topic, byte* payload, unsigned int length) {
 Serial.print("Message arrived [");
 Serial.print(topic);
 Serial.print("] ");
 for (int i=0;i<length;i++) {
  char receivedChar = (char)payload[i];
  Serial.print(receivedChar);
  if (receivedChar == '0')
  // ESP8266 Huzzah outputs are "reversed"
  digitalWrite(ledPin, HIGH);
  if (receivedChar == '1')
   digitalWrite(ledPin, LOW);
  }
  Serial.println();
}


void reconnect() {
 // Loop until we're reconnected
 while (!client.connected()) {
 Serial.print("Attempting MQTT connection...");
 // Attempt to connect
 if (client.connect("ESP8266 Client")) {
  Serial.println("connected");
  // ... and subscribe to topic
  client.subscribe("ledStatus");
 } else {
  Serial.print("failed, rc=");
  Serial.print(client.state());
  Serial.println(" try again in 5 seconds");
  // Wait 5 seconds before retrying
  delay(5000);
  }
 }
}

void setup()
{
 Serial.begin(115200);

 client.setServer(mqtt_server, 1883);
 client.setCallback(callback);

 pinMode(ledPin, OUTPUT);
}

void loop()
{
 if (!client.connected()) {
  reconnect();
 }
 client.loop();
}


모든 마법은 "callback" 함수 안에서 일어난다. 실제 코드에서는 받은 메시지를 처리하는 함수를 작성하고 싶을 것이다. 여기서는 topic에 대해 한글자 메시지만을 보내기 때문에 이 간단한 코드로 충분하다.

Conclusion


이 MQTT 튜토리얼은 매우 기초적이다. 하지만 라즈베리 파이, ESP8266을 MQTT 브로커가 실행되고 있는 PC에 연결하는데 필요한 최소한의 것을 보여준다.

다음에는 MQTT를 통해 어떻게 더 복잡한 메시지를 보낼 수 있는가를 설명하겠다.




2016년 11월 28일 월요일

라즈베리 파이에 여러개의 시리얼 포트가 필요할 때 (Use multiple serial port in Raspberry Pi)

라즈베리 파이로 무언가를 만들려고 하다 보면 여러개의 시리얼 포트가 필요한 경우가 있다.  라즈베리 파이에는 기본적으로 두개의 시리얼 포트가 있지만 그 중 하나는 미니포트라 사용에 꽤 신경 써 줘야 하는 부분이 많고,  라즈베리 파이 3의 경우 시리얼포트를 블루투스가 사용하고 있어 블루투스를 동시에 사용하려고 하면 매우 골치 아퍼 진다.  (상세한 내용은 이전 포스트 (라즈베리 파이 3 시리얼/블루투스 문제 (Raspberry Pi 3 UART/Bluetooth problem))를 참고)

그럼 시리얼 포트를 추가할 수 있는 가장 쉬운 방법은 USB-to-Serial converter를 사용하는 것이다. 



시중에 다양한 종류의 제품이 나와 있고 가격도 그리 비싸지 않다.

다만 이 제품들을 사용해서 여러개의  시리얼 포트를 추가하려면 포트당 한개의 USB  포트가 필요하기 때문에 별도의 허브를 붙여줘야 하는 문제가 발생한다. 

그래서 FTDI에서는  USB-to-multiport  Serial  comverter  칩을 제공하고 있다.  FT2232H나 FT4232H  칩을 이용하면 한개의 USB포트로 2개 또는 4개의 시리얼 포트를 확장할 수 있게 된다. 






FT2232H breakout board

그리고  이 경우 가장 큰 장점은 raspbian에 디바이스 드라이버가 이미 포함되어 있어 따로 드라이버를 설치할 필요 없이 라즈베리 파이에 연결만 해 주면 장치를 인식해 곧바로 사용할 수 있다는 것이다. 


위의 화면 캡춰와 같이 breakout board를 연결하기 전에는 없었지만 breakout board를 USB에 연결하고 나면 /dev/ttyUSB0, /dev/ttyUSB1 이 자동으로 추가된다.  (FT2232H의 경우,  FT4232H의 경우는 저 두개 외에 추가로 /dev/ttyUSB2, /dev/ttyUSB3  도 만들어진다.)


FT2232H의 경우 단순히 asynchronous serial (우리가 일반적으로 사용하는 시리얼 통신방식) 뿐 아니고 synchronous serial, FIFO,  SPI,  I2C, Host emulation 등 매우 다양한 방식으로 동작할 수 있기 때문에 여러 핀이 나와 있지만 async serial로 사용하는 경우는 위의 4개 핀만 사용하면 된다. 

테스트를 위해 아래와 같이 uart0의 Tx핀을 uart1의 Rx에 연결해준다.


이제 라즈베리 파이에서 두개의 터미널 창을 열어준다.


minicom 또는 다른 시리얼 터미널 프로그램을 사용해 위쪽의 창은 /dev/ttyUSB0,  아래쪽 창은 /dev/ttyUSB1 포트를 열어준다.


미니컴을 사용하는 경우 디폴트로 하드웨어 플로우 컨트롤을 사용하는걸로 되어 있으므로 그걸 꺼 줘야 한다.  먼저 Ctrl-A 를 누른 다음 'O'를 누르면 위와 같은 메뉴 화면이 나온다.  거기에서 'Serial  port  setup'을  선택  해  준다.


위의 화면이 나오면 'F'  키를 한번 눌러 Hardware Flow Control 부분이 'Yes'로 되어 있는걸 'No'로 바꿔 준다.


위와 같이  되었으면 엔터키를 눌러주고 나서 'Exit'을 선택해주면 된다.  이 과정은 위쪽,  아래쪽 두 터미널 모두 해 줘야 한다.


이제 위쪽 터미널에서 글자를 입력해 보면 아래쪽 터미널에  입력한 글자가 나타나게 된다.  하지만 반대로 아래쪽 터미널에서는 아무리 글자를 입력해도 위쪽 터미널에는 나타나지 않는다.  테스트를 위해 Tx(uart0) -> Rx(uart1) 은 연결했지만 Tx(uart1) -> Rx(uart0)는 연결해 주지 않았기 때문에 그런 것이다. 

현재  aliexpress에서 FT2232H breakout board는 약 $15  정도에 구할 수 있다.  다만 FT4232H breakout board는 aliexpress에서는 찾기가 쉽지 않고 Numato Lab, Mouser 등에서 약 3~4만원 정도(배송비 별도)에 구할 수 있다.

이 디바이스들은 맥,  윈도우용 디바이스 드라이버도 제공되고 있으므로 맥이나 윈도우 환경에서도 여러개의 시리얼 포트가 필요한 경우 유용하게 사용할 수 있다.


Mac Coolterm에서 사용하는 경우  1


Mac  Coolterm에서 사용하는 경우 2

2016년 10월 10일 월요일

Mac OS Sierra와 RNDIS (RNDIS in Mac OS Sierra)

Mac OS X를 사용하는 사람들에게 OS X 10.11 El Capitan은 몇가지 문제가 있었다. 그 중 가장 큰 문제가 RNDIS를 사용할 수 없다는 것이었다. 그 이전 버젼까지는 HORNDIS를 사용하면 되었는데 El Capitan으로 가면서 호환성에 문제가 생겨 horndis를 사용할 수 없게 되어 버렸다.
HORNDIS 개발자가 시간이 없어 변경된 부분을 수정해 주지 않아 다른 사람들이 몇가지 work around를 제시하긴 했지만 어느것도 안정적으로 사용하기에는 무리였다.
그런데 Mac OS 10.12인 Sierra로 오면서 OS내에 RNDIS 드라이버가 기본으로 들어있게 되어 이제는 horndis를 따로 설치하지 않아도 out-of-box로 바로 사용할 수 있게 되었다.

특히 Raspberry pi zero의 경우 별도의 이더넷 커넥터가 없어 따로 USB-to-serial 아답터를 사용해 콘솔을 연결해 주지 않으면, USB케이블로 맥과 연결해 rndis를 통해 USB/Ethernet gadget으로 사용해야 하는데 이제는 맥에 USB케이블로 연결만 하면 바로 USB/Ethernet gadget으로 잡히게 된다.


(처음 연결하면 IPv4 구성은 DHCP로 되어 있고 IP주소는 169.x.x.x로 나오는데 위와 같이 구성은 '수동', IP주소는 자신이 사용하고자 하는걸로 변경해 주면 된다.)

상세한 설정 방법은 이전 포스트(라즈베리 파이 제로를 USB 케이블 하나로 사용하기)를 참고하면 된다.


2016년 9월 22일 목요일

WS2812와 APA102의 차이 비교 (Comparison between WS2812 and APA102)

개별 칼라를 제어할 수 있는 LED strip으로 가장 많이 쓰이는 것이 WS2812B(Adafruit에서는 NeoPixel 이라는 제품명으로 판매)이다. 전원라인(+5V, GND)을 제외하면 단 한개의 신호선으로 수백개의 LED를 제어할 수 있어 매우 편리하다.




하지만 별도의 클럭신호 선 없이 한개의 신호선으로 데이터를 전송하다 보니 타이밍에 매우 민감하다. 아두이노용 WS2812 라이브러리(Adafruit_NeoPixel 등) 소스를 열어 본 사람들은 알겠지만 데이터를 전송하는 부분 코드는 타이밍을 정밀하게 맞춰주기 위해 전송중에는 인터럽트도 disable시켜 놓고 어셈블리 코드로 하드코딩 되어 있다.



최대 800KHz의 속도로 데이터를 전송하는데 만일 128개의 LED를 구동한다고 하면 한번 색을 변경하는데 약 4ms 정도의 시간이 걸리게 된다. 즉 이 시간동안은 프로세서가 다른 작업은 커녕 심지어 인터럽트도 안 걸리고 LED에 데이터를 보내야만 한다.
이는 특히 라즈베리 파이같이 OS를 사용하는 경우 매우 치명적이다. 물론 라즈베리 파이에서도 WS2812를 사용할 수 있게 DMA를 사용해 데이터를 전송하는 라이브러리가 나와 있긴 하지만 이런 라이브러리는 portability가 거의 없다. 즉 라이브러리를 가져다 다른데 포팅하는것이 매우 힘들다.

그래서 이런 문제를 해결한 비슷한 종류의 LED가 몇 종류 나와 있다. 그 중 하나가 APA102이다.




우선 WS2812와 가장 큰 차이점은 전원선(+5V, GND)외에 두개의 신호선(Data, Clk)이 있다는 것이다. (WS2812는 1개의 신호선(Data)만 있음)








그래서 SPI 버스의 MOSI, SCK 에 Data, Clk를 연결해서 SPI 방식으로 데이터를 보내주면 된다. 실제 데이터 전송은 SPI가 담당하기 때문에 프로세서가 데이터를 전송하는 동안 기다릴 필요도 없고 타이밍을 신경 쓸 필요도 없기 때문에 라이브러리 코드도 훨씬 간단해진다.

데이터 전송도 SPI 클럭 기준으로 이루어지기 때문에 16MHz의 속도로도 충분히 전송이 가능하다. (WS2812에 비해 20배 이상의 속도) 그래서 POV같이 LED 색이 빠른 속도로 바뀌어야 하는 경우 WS2812는 전환속도가 느려 적합하지 않지만 APA102는 충분히 빠르게 색을 바꿔줄 수 있다.

라즈베리 파이에서도 DMA를 사용하지 않아도 충분하기 때문에 순전히 파이선으로 만든 라이브러리도 있다.

https://github.com/tinue/APA102_Pi