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

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년 5월 12일 금요일

BusPirate를 파이선으로 제어하기

수년간 AVR-ISP mkII를 사용해 ATMEL AVR 마이크로 컨트롤러를 프로그래밍 해 오다가, 오늘 처음으로 Bus Pirate를 사용해 보았다. 이 작은 보드는 단순히 마이컴 프로그래머가 아니고 기본적으로 마이컴 주변장치와의 시리얼 인터페이스이다. 보드를 USB에 연결하면 커맨드라인 인터페이스를 가지고 있는 시리얼 포트로 보이는데 이를 통해 핀의 on/off, 전압 측정 뿐 아니고 I2C, SPI, UART 심지어 HD44780 시리즈 LCD 프로토콜까지도 양방향으로 사용할 수 있도록 네이티브하게 지원한다.
HyperTerminal을 사용해 BusPirate와 직접 인터페이스 할 수도 있지만 TeraTerm을 사용하는걸 권장한다. 작은 회로에 전원을 공급할 수 있게 3.3V와 5V 전원을 공급할 수 있고, 만일 전류를 과도하게 소비하면 자동으로 전원 공급을 끊어버린다.
BusPirate는 $30 이하로 구할 수 있는 매우 훌륭한 개발툴이다. 또한 AVRDUDE를 통해 네이티브하게 AVR 프로그래머로 동작할 수 있다. 어셈블리 코드를 작성할수도 있지만 편의성을 위해 거의 항상 C로 프로그램을 작성한다. 나의 레퍼런스 (또는 유사한 작업을 하길 원하는 다른 사람의 레퍼런스)를 위해 윈도우에서 BusPirate로 AVR 마이컴을 프로그래밍 하기 위해 사용하는 가장 쉬운 방법을 포스팅한다. 또한 BusPirate와 연결한 다름 간단한 명령들을 실행하는 파이선 스크립트(파워 서플라이를 켜고 프로그래밍이 끝나면 곧바로 VCC의 전압을 리포팅)를 작성했다.

물론 파이선으로 BusPirate와 인터액션 할 수 있게 해 주는 팬시한 패키지도 있지만 내 방법의 장점은 네이티브 파이선 라이브러리에서 실행된다는 것이다. 이렇게 사용하고 싶으면 WinAVR과 python 3.1을 설치하면 된다. 코드가 파이선 2에서도 동작할것이라고 생각하지만 테스트를 해 보지는 않았다.



BusPirate가 제대로 동작하는지 확인하기 위해 BusPirate의 빌트인 테스트 루틴을 실행하는걸로 시작했다. 상세한 내용은 가이드를 참고하면 된다. 이 테스트는 아래 그림처럼 두 쌍의 핀을 서로 연결해 준 다음, BusPirate를 시리얼 터미널로 연결하고 '~' 명령을 실행하면 된다. 그러면 모든 유용한 정보를 출력해 준다.



다음은 모든 핀들을 동작시키기 위해 마이컴에서 실행될 코드이다. (main.c로 저장) 여기서는 MCU로 ATTiny85를 사용했다. 표준 클럭 설정(내장 RC클럭, 8MHz)을 사용하지만 외부 클럭소스나 크리스털을 사용하기 위해 퓨즈 설정을 바꾸길 원하면 engbedded's handy dandy fuse calculator를 사용해 계산한다.

#define F_CPU (8000000UL)
#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
  DDRB = 255;
  while (1) {
    PORTB ^= 255;
    _delay_ms(500);
  }
}

코드 컴파일 및 MCU 프로그래밍을 위해 항상 같은 폴더에 bash 스크립트를 만들어 더블클릭하면 이전에 컴파일 된 파일들을 삭제하고, main.c를 컴파일 한 다음 BusPirate를 사용해 MCU에 프로그래밍 하게 해 놓았다. 스크립트에서 COM3는 자신의 컴퓨터에 BusPirate에 맞게 바꿔줘야 한다.

@echo off
del *.elf
del *.hex
avr-gcc -mmcu=attiny85 -Wall -Os -o main.elf main.c
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avrdude -c buspirate -p attiny85 -P com3 -e -U flash:w:main.hex
python up.py
 
비록 프로그래머가 +5V 핀을 통해 MCU에 전원을 공급해 주지만 프로그래밍이 끝나면 전원이 끊어진다.  수동으로 터미널 프로그램을 다시 열어 BusPirate에 재접속 하고 'm' 명령으로 모드를 리셋하고 '9' 명령으로 DIO 모드로 만든 다음 'W'명령으로 전원 출력을 재활성화 시키는 대신 이 모든것을 자동화 하길 원한다. 파이선을 사용하면 이 모든것을 쉽게 할 수 있다. 스크립트의 마지막 줄이 up.py를 호출한다. 이 팬시 스크립트는 전원을 켠 다음 심지어 Vcc라인의 전압까지도 출력 해 준다.  

""" python3 control of buspirate (SWHarden.com) """
import serial

BUSPIRATE_PORT = 'com3'

def send(ser.cmd):
  ser.write(str(cmd+'\n').encode('ascii'))
  for line in ser.readlines():
    print (line.decode('utf-8').strip())

ser = serial.Serial(BUSPIRATE_PORT, 115200, timeout=1)
assert ser.isOpen()
send(ser, '#')
send(ser, 'm')
send(ser, '9')
send(ser, 'W')
send(ser, 'v')
ser.close()
print ("disconnected!")

"burn.cmd"가 실행되면 코드가 컴파일되어 MCU에 프로그래밍 되고 전원공급을 켜고 Vcc의 전압을 알려준다. 출력은 다음과 같다.

C:\Users\scott\Documents\important\AVR\2016-07-13 ATTiny85 LEDblink>burn.cmd

Detecting BusPirate...
**
**  Bus Pirate v3a
**  Firmware v5.10 (r559)  Bootloader v4.4
**  DEVID:0x0447 REVID:0x3046 (24FJ64GA002 B8)
**  http://dangerousprototypes.com
**
BusPirate: using BINARY mode
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.12s

avrdude: Device signature = 0x1e930b
avrdude: erasing chip
avrdude: reading input file "main.hex"
avrdude: input file main.hex auto detected as Intel Hex
avrdude: writing flash (84 bytes):

Writing | ################################################## | 100% 3.12s

avrdude: 84 bytes of flash written
avrdude: verifying flash memory against main.hex:
avrdude: load data flash data from input file main.hex:
avrdude: input file main.hex auto detected as Intel Hex
avrdude: input file main.hex contains 84 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 2.72s

avrdude: verifying ...
avrdude: 84 bytes of flash verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

#
RESET

Bus Pirate v3a
Firmware v5.10 (r559)  Bootloader v4.4
DEVID:0x0447 REVID:0x3046 (24FJ64GA002 B8)
http://dangerousprototypes.com
HiZ>
m
1. HiZ
2. 1-WIRE
3. UART
4. I2C
5. SPI
6. 2WIRE
7. 3WIRE
8. LCD
9. DIO
x. exit(without change)

(1)>
9
Ready
DIO>
W
Power supplies ON
DIO>
v
Pinstates:
1.(BR)  2.(RD)  3.(OR)  4.(YW)  5.(GN)  6.(BL)  7.(PU)  8.(GR)  9.(WT)  0.(Blk)
GND     3.3V    5.0V    ADC     VPU     AUX     CLK     MOSI    CS      MISO
P       P       P       I       I       I       I       I       I       I
GND     3.17V   5.00V   0.00V   0.00V   L       L       L       H       L
DIO>
disconnected!

이는 최소한의 케이스 시나리오지만 더 복잡한 태스크를 수행하도록 확장할 수 있다. 예를 들어 모든 커맨드가 단일 파이선 프로그램에서 실행될 수 있다. BusPirate의 수 많은 다른 프로토콜과 통신할 수 있는 능력을 감안하면 별도의 특별한 추가 라이브러리를 설치하지 않고 파이선에서 네이티브하게 제어할 수 있다는건 매우 편리하다.

--
Credit goes to Scott W. Harden




2015년 12월 16일 수요일

Amazon Dash Button을 다른 용도로 사용하기

 아마존에서 판매하고 있는 Dash button을 구해 보았다. 이 버튼은 버튼에 특정 제품을 등록해 놓으면 버튼을 누르는 것 만으로 아마존으로 자동으로 주문이 되도록 되어 있어 화장지, 면도크림, 기저귀 등 반복적으로 주문해야 하는 제품을 매우 손쉽게 주문할 수 있도록 해 준다.

아마존 prime member면 개당 $4.99에 구입할 수 있다. (이 정도 가격이면 거의 하드웨어 가격 그대로거나 약간의 손해를 보고 판매하는 수준이라고 본다. 아마존의 킨들과 마찬가지로 비지니스 모델이 하드웨어는 손해를 보고 팔아도 그를 통해 아마존에서 반복적인 주문을 하도록 하는게 목적이기 때문)





이 버튼은 무선랜을 사용해서 통신을 하는데 인터넷을 검색해 보니 Ted Benson이라는 사람이 이 버튼을 해킹(?)해 다른 용도로 사용하는 방법을 소개해 놓았다. (How I hacked Amazon's $5 WiFi button to track baby data)

간단하게 설명하자면 버튼이 눌려도 주문하지 않도록 하고, 버튼의 ARP 패킷을 검출해 버튼이 눌린걸 알아내는 것이다. Ted는 2개의 dash 버튼을 사용해 하나는 아기가 똥을 쌌을 때 누르고, 다른 하나는 잠에서 깨었을 때 눌러 각각이 눌린 시간이 구글 스프레드쉬트에 자동으로 기록되도록 만들어 놓았다.

원 글에서는 python과 scapy를 사용해서 프로그램을 만들었는데 scapy는 설치하기가 까다롭고 이번에 필요한 것 이상의 기능을 가지고 있다보니 좀 무거운 느낌이 든다. 그래서 Bob Steinbeiser가 만든 python 코드를 수정해 보았다.

Dash 버튼을 사용하려면 먼저 설정을 해 줘야 한다.

스마트폰에서 Amazon 앱을 실행한다.


앱 좌측 상단의 메뉴 버튼을 클릭한다.


메뉴에서 Your Account를 선택한다.


화면을 아래로 내려 Dash Devices 에서 'Set up a new device'를 선택한다.



기본적으로 화면에 그림으로 잘 설명되어 있으므로 그대로 따라하면 된다.



자신이 현재 연결되어 있는 공유기의 SSID와 암호를 넣어준다. 


 이 단계에서 Dash button의  버튼 바로 위의 LED가 파란색으로 깜빡일 때 까지 흰색 버튼을 누르고 있는다.


 Dash button은 무선네트웍 정보(SSID, 암호)를 전달받기 위해 초음파를 사용하고 있다.위의 그림처럼 폰의 스피커와 Dash button의 마이크(흰색 버튼 반대쪽의 조그마한 구멍)을 가까이 가져다 대고 'Connect' 버튼을 누른다. 잠시 기다리면 깜빡이던 파란색 불빛이 켜져 있게 된다. 그러면 정상적으로 정보가 전달된 것이다.


마지막 단계로 Dash button에 특정 상품을 등록하는 단계인데 여기서는 이 단계에서 우측상단에 있는 'X' 버튼을 눌러 상품을 등록하지 않아야 한다. (여기서 상품을 등록해 놓으면 버튼을 누를 때 마다 해당 상품이 자동주문됨)

이제 버튼이 눌린걸 감지하기 위한 코드를 실행하면 된다. 여기서는 Raspberry Pi를 사용했다.

코드는 아래와 같다.

$ cat dash-socktest.py
import socket
import struct
import binascii





rawSocket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(0x0003))


while True:
    packet = rawSocket.recvfrom(2048)

    ethernet_header = packet[0][0:14]
    ethernet_detailed = struct.unpack("!6s6s2s", ethernet_header)

    arp_header = packet[0][14:42]
    arp_detailed = struct.unpack("2s2s1s1s2s6s4s6s4s", arp_header)

    # skip non-ARP packets
    ethertype = ethernet_detailed[2]
    if ethertype != '\x08\x06':
        continue

    src_mac = binascii.hexlify(arp_detailed[5])
    dst_mac = binascii.hexlify(arp_detailed[7])
    src_ip = socket.inet_ntoa(arp_detailed[6])
    dst_ip = socket.inet_ntoa(arp_detailed[8])

    # print source_mac

    if src_mac == '001122334455' and dst_ip != '192.168.0.1':
        print "Tide button pressed!"
        print    "SrcIP = " + src_ip + ", SrcMAC = " + src_mac + \
                    ", DstIP = " + dst_ip + ", DstMAC = " + dst_mac


$

위에서 빨간색 001122334455 부분은 자신의 Dash button의 MAC address로 바꿔준다.

위의 코드를 실행할 때는 sudo로 실행시켜 줘야 한다.

$ sudo python dash-socktest.py
Tide button pressed!
SrcIP = 0.0.0.0, SrcMAC = 001122334455, DstIP = 192.168.0.20, DstMAC = 000000000000

위와 같이 버튼이 눌릴 때 마다 버튼의 정보를 출력해 준다.
코드를 백그라운드에서 실행하려면 다음과 같이 하면 된다.

$ (sudo python dash-socktest.py &)
$

위의 코드는 단순히 버튼이 눌리면 그 정보를 출력하도록 되어 있지만 이 코드를 약간 수정하면 원하는 여러가지 동작을 하도록 쉽게 만들어 줄 수 있다.





2015년 4월 3일 금요일

파이선에서 *args와 **kwargs

*args는 함수에 임의 갯수의 argument를 넘겨주기 위해 사용된다. '임의의' 라는 단어가 여기서 매우 중요하고 기본적으로 파이선에서 *args의 역할을 이해하는 키가 된다. 
새로운것을 배우는 가장 좋은 방법은 실제로 사용해 보는 것이다. 간단한 예제로 multiply 함수를 만들길 원한다고 가정하자. Multiply 함수는 여러개의 argument를 인자로 받아 모두 곱한 결과를 화면에 출력한다. 하지만 이 함수가 몇개의 argument를 인자로 받는지 모른다. 그러므로 파이선 식으로 이 문제를 어떻게 해결할 수 있을까? *args에 대해 이미 알고 있다면 별로 어렵지 않다. multiply함수를 아래와 같이 정의하면서 *args를 인자로 넘겨주면 된다.

def multiply(*args):
  pass

하지만 multiply 함수를 만들기 전에 파이선 인터프리터 쉘을 열어 다음 코드를 실행해본다. 

def echo_args(*args):
  for arg in args:
    print arg

'*'는 args를 iterable로 만들어주어 for 루프를 실행해 args list 안에 있는 모든 argument를 처리할 수 있다는걸 의미한다. 예를 들어 echo_args 함수에 1,2,3을 argument로 넘겨주면 화면에 다음과 같이 출력되는걸 볼 수 있다.

>>> echo_args(1,2,3)
1
2
3
>>> 

이제 *args에 대해 명확하게 이해했을것이라 생각한다. 마지막으로 multiply 함수를 완성해 본다. operator에서 mul과 functools에서 reduce를 import한다.

from operator import mul
from functools import reduce

def multiply(*args):
  return reduce(mul, args)

* mul과 reduce는 *args와는 아무 관계가 없다. 단지 함수를 만드는걸 도와주는 툴일 뿐이다.

정리하자면 *args는 사용자에 의해 함수에 몇개의 argument가 넘겨질 지 모를때 사용된다. 

def get_arguments(a, *args):
  print "The first argument is ", a
  for arg in args:
    print "This argument come from args: ", arg

다음은 위의 함수 get_argements의 사용 예이다.

>>> get_arguments(13,13,13)
The first argument is 13
This argument come from args: 13
This argument come from args: 13
>>>

이제 elements라는 변수에 몇개의 element를 가지는 tuple을 만들어준다.

>>> elements = (1, 2, 3)
>>>

그리고 elements를 get_arguments에 넣어 호출해 본다.

>>> get_arguments(elements)
The first argument is (1, 2, 3)
>>>

위의 출력 결과에서 볼 수 있는것처럼 elements를 함수에 넘겨주면 elements가 a에 할당된다.
그럼 다음과 같이 하면 어떻게 되는가 보자.

>>> get_arguments(*elements)
The first argument is 1
This argument come from args: 2
This argument come from args: 3

'*'는 튜플 elements를 unpack하기 때문에 get_arguments(1,2,3)을 호출한것과 같은 의미가 된다. 

----------------------

**kwargs는 *args와 마찬가지로 함수 선언에 사용되는 마법 변수지만 차이점은 함수에 keyworded argement를 넘겨줄 수 있는 것이다.

예제를 보도록 하자.

def display_stuff(**kwargs):
  if kwargs is not None:
    print kwargs

이제 다음과 같이 함수를 호출해 본다.

>>> display_stuff(name='Jeff', passion='development', language='python')
{'passion': 'development', 'name': 'Jeff', 'language': 'python'}
>>>

결과에서 볼 수 있는것처럼 **kwargs는 함수에 임의의 keyworded arguemts를 넘겨줘 함수 내에서 dictionary로 억세스 할 수 있게 해 준다. 예를들어 다음의 코드를 사용하면 키 'passion'에 대한 값을 쉽게 출력할 수 있다.

def display_stuff(**kwargs):
  if kwargs is not None:
    print kwargs['passion']

이제 다시 함수를 호출해 본다.

>>> display_stuff(name='Jeff', passion='development', language='python')
development
>>>

하지만 kwargs dictionary에 'passion' 키가 없는 경우는 어떻게 될까? KeyError가 발생하게 된다. 

>>> display_stuff(error='KeyError')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in display_stuff
KeyError: 'passion'
>>>

에러를 피하고 싶으면 kwargs를 확인할 때 억세스하려는 키가 dictionary에 존재하는지 체크할 수 있다.

def display_stuff(**kwargs):
  if kwargs is not None and 'passion' in kwargs:
    print kwargs['passion']

함수를 위와 같이 바꾸면 'passion' 키가 없어도 더 이상 KeyError가 발생하지 않는다. 

dictionary 내의 모든 키와 값을 출력하고 싶으면 다음과 같이 하면 된다.

def display_stuff(**kwargs):
  if kwargs is not None:
    for key, value in kwargs.items():
      print key, '==>', value

이제 실행해 본다.

>>> display_stuff(name='Jeff', passion='development')
passion ==> developer
name ==> Jeff
>>>

지금까지 본 것 처럼 *args와 **kwargs는 둘의 차이점을 이해하고 잘 사용하면 파이선 프로그래밍에 매우 유용하다.

2015년 4월 2일 목요일

파이선으로 만들어보는 간단한 MQTT 클라이언트

MQTT 브로커는 mosquitto나 rsmb같이 무료로 쉽게 구해 설치할 수 있다. 하지만 아직 설치하지 않았다면 테스트를 위해 공개되어 있는 브로커를 사용해도 된다.
공개 브로커 목록은 http://mqtt.org/wiki/doku.php/public_brokers 에서 확인할 수 있다.

MQTT 클라이언트는 publisher이건 subscriber이건 모두 다 브로커에 연결해야 한다. 브로커를 누구나 글을 붙이거나 읽을 수 있는 일종의 공개 게시판이라고 생각하면 된다.

여기서는 파이선 2.7을 사용해 간단한 MQTT 클라이언트를 만들어 MQTT 프로토콜을 직접 사용해 보도록 한다.



1. 인스톨

pip가 설치되어 있어야 한다. pip는 파이선 모듈을 설치하는데 귀찮은 문제들을 손쉽게 해결해주는 툴이다. 만일 설치되어 있지 않다면 아래 명령으로 pip를 설치한다.

$ sudo apt-get install python-pip

다음은 MQTT broker Mosquitto (지금은 Paho로 이름이 바뀌었음)

$ pip install paho-mqtt

2. Subscribe

아래는 로컬 머신에서 실행되고 있는 브로커에 토픽을 구독(subscribe)하기 위한 파이선 코드이다.

$ cat sub.py
import paho.mqtt.client as mqtt

# 클라이언트가 서버에게서 CONNACK 응답을 받을 때 호출되는 콜백
def on_connect(client, userdata, flags, rc):
  print ("Connected with result coe " + str(rc))
  client.subscribe("hello/world")

# 서버에게서 PUBLISH 메시지를 받을 때 호출되는 콜백
def on_message(client, userdata, msg):
  print "Topic: ", msg.topic + '\nMessage: ' + str(msg.payload)

client = mqtt.Client()        # MQTT Client 오브젝트 생성
client.on_connect = on_connect     # on_connect callback 설정
client.on_message = on_message   # on_message callback 설정

client.connect("test.mosquitto.org", 1883, 60)   # MQTT 서버에 연결

# 네트웍 트래픽을 처리, 콜백 디스패치, 재접속 등을 수행하는 블러킹 함수
# 멀티스레드 인터페이스나 수동 인터페이스를 위한 다른 loop*() 함수도 있음
client.loop_forever()
$


3. Publish

이제 /hello/world 토픽을 구독(subscribe)해서 해당 토픽의 메시지가 publish(발행)되기를 기다리는 노드를 만들었다. 아래 코드는 /hello/world 토픽으로 메시지를 발행하는 간단한 코드이다.

$ cat pub.py
import paho.mqtt.client as mqtt

mqttc = mqtt.Client("python_pub")      # MQTT Client 오브젝트 생성
mqttc.connect("test.mosquitto.org", 1883)    # MQTT 서버에 연결
mqttc.publish("hello/world", "Hello World!")  # 'hello/world' 토픽에 "Hello World!"라는 메시지 발행
mqttc.loop(2)        # timeout = 2초
$

4. 테스트

2번의 코드를 'sub.py'라는 이름으로 저장하고 3번의 코드를 'pub.py'라는 이름으로 저장했다면 터미널 창에서 'python sub.py'로 sub.py를 실행하고, 다른 터미널 창에서 'python pub.py'로 pub.py를 실행해 보면 sub.py를 실행한 창에 수신된 메시지가 표시되는걸 확인할 수 있을 것이다.

MQTT는 클라이언트/서버 모델처럼 publisher와 subscriber간에 1:1 연결이 아니기 때문에 여러 창에서 'sub.py'를 실행하고 난 후 'pub.py'를 실행하면 'sub.py'를 실행한 모든 창에 메시지가 수신되게 된다.

2015년 4월 1일 수요일

파이선 bytearray 오브젝트 (bytearray object in Python)

파이선의 bytearray는 바이트 값을 저장하는 버퍼 오브젝트이다. 파이선으로 디바이스를 다루는 코드를 다루다 보면 bytearray를 자주 접하게 되기 때문에 이에 대해 간단하게 정리해 놓았다. bytearray는 원래 파이선3에서 처음 도입되었는데 파이선2.7에 백워드 포팅 되었다.

- 리스트에서 bytearray 만들기

>>> elements = [0, 200, 50, 25, 10, 255]
>>> values = bytearray(elements)
>>> values
bytearray(b'\x00\xc82\x19\n\xff')

- bytearray의 element를 변경

>>> values[0] = 5
>>> values[1] = 0

bytearray는 mutable 오브젝트이므로 element의 값을 변경할 수 있다.

- bytearray의 내용 출력

>>> for v in values:
...   print v
...
5
0
50
25
10
255


>>>

일반 리스트와 동일하게 iterate 할 수 있다.

- 문자열로 bytearray 만들기

>>> data = bytearray(b"abc")
>>> data
bytearray(b'abc')
>>> for d in data:
...   print d
...
97
98
99
>>>

문자열로 bytearray를 만들때는 문자열 앞에 ‘b’를 붙여줘야 한다. 

- bytearray slice

>>> values = [5,10,15,20]
>>> arr = bytearray(values)
>>> arr
bytearray(b'\x05\n\x0f\x14')
>>> arr[0:2]
bytearray(b'\x05\n')
>>> arr[0:2] = [100, 0, 0]
>>> arr
bytearray(b'd\x00\x00\x0f\x14')
>>> for v in arr:
...   print v
...
100
0
0
15
20
>>>

리스트와 동일하게 bytearray를 slice 할 수 있고, mutable이기 때문에 slice의 내용을 변경할 수도 있다. 

- Occurence Count

>>> arr = bytearray(b"aabbcccc")
>>> arr.count(b"c")
4
>>>

bytearray내의 각 element를 돌며 특정 패턴이 매치하는 횟수를 셀 수도 있다. count()의 argument는 byte 오브젝트여야만 한다. 즉 ‘b’로 시작하는 문자열 스트링이거나 0~255 사이의 정수값이어야 한다.

- Find pattern

>>> data = bytearray(b"python")
>>> data.find(b"on")
4
>>> data.find(b"java")
-1
>>>

bytearray 내에서 특정 문자열을 검색할 수도 있다. 이 경우 일치되는 문자열의 가장 왼쪽 인덱스를 리턴한다. 일치되는 문자열이 없으면 -1을 리턴한다.

- In operator

>>> data = bytearray([100,20,10,200,200])
>>> data
bytearray(b'd\x14\n\xc8\xc8')
>>> 200 in data
True
>>> 0 in data
False
>>>

in operator는 bytearray 내에 특정 element가 존재하는가를 확인한다.

- Merge

>>> left = bytearray(b"Hello ")
>>> right = bytearray(b"World")
>>> left+right
bytearray(b'Hello World')
>>>

list같이 “+”를 사용해 두개의 bytearray를 하나로 합칠 수 있다. 

- bytearray를 list로 변환

>>> init = [100,255,255,0]
>>> init
[100, 255, 255, 0]
>>> b = bytearray(init)
>>> b
bytearray(b'd\xff\xff\x00')
>>> result = list(b)
>>> result
[100, 255, 255, 0]
>>>

byte값들의 리스트 (0~255 사이의 숫자)는 bytearray로 변환할 수 있다. 반대로 bytearray를 list로 변환도 가능함

- Append, del, insert 

>>> vals = bytearray()
>>> vals.append(0)
>>> vals.append(1)
>>> vals.append(2)
>>> vals
bytearray(b'\x00\x01\x02')
>>> del vals[0:1]
>>> vals
bytearray(b'\x01\x02')
>>> vals.insert(1,3)
>>> vals
bytearray(b'\x01\x03\x02')
>>>

List와 동일하게 append, del, insert를 사용할 수 있다.


- ValueError 

>>> values = bytearray([1000,2000,3000])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: byte must be in range(0, 256)
>>>

bytearray에 삽입되는 값은 0~255 범위여야만 한다. 범위 외의 값을 넣으려고 하면 ValueError가 발생

- 치환

>>> val = b"aaabbb"
>>> val.replace(b"bbb",b"ccc")
'aaaccc'
>>>

Buffer protocol은 String과 같은 메소드를 지원한다. String에서처럼 replace()를 사용할 수 있다. 단 argument는 bytes 오브젝트, 즉 “b” 문자열을 사용해야만 함

- Compare 

>>> val1 = b"desktop"
>>> val1
'desktop'
>>> val2 = bytearray(b"desktop")
>>> val2
bytearray(b'desktop')
>>> val1 == val2
True
>>>

'b' 문자열은 bytes 오브젝트이다. bytes 오브젝트를 비교하려면 ‘==‘를 사용함

- Start, end 

>>> val = b"users"
>>> val.startswith(b"user")
True
>>> val.endswith(b"s")
True
>>>

bytes 오브젝트를 string과 같이 다룰 수 있음. startswith와 endswith 역시 사용 가능. 단 argument는 bytes 오브젝트여야만 함

- Split, join 

>>> data = b"cat,dog,fish,bird"
>>> data
'cat,dog,fish,bird'
>>> elem = data.split(b",")
>>> elem
['cat', 'dog', 'fish', 'bird']
>>> len(elem)
4
>>> res = b",".join(elem)
>>> res
'cat,dog,fish,bird'
>>>


2015년 3월 12일 목요일

Python으로 SensorTag 사용하기 (Control TI SensorTag using python)

파이선에서 SensorTag를 사용하기 위해 Ian Harvey의 bluepy(https://github.com/IanHarvey/bluepy) 모듈을 사용한다.
링크에서 다운받으면 다음과 같은 파일이 들어있다. bluepy 디렉토리로 가서 make 명령을 실행한다. 이 모듈은 실행하기 위해서 bluepy-helper라는 프로그램이 필요해 make를 실행하면 이 프로그램을 컴파일 해 준다.

# ls -scF
     4 bluepy/         4 README.md       4 bluez-5.4/
     4 docs/           0 __init__.py
# cd bluepy
# make
gcc -L. -O0 -g -DHAVE_CONFIG_H -I../bluez-5.4/attrib -I../bluez-5.4 -I../bluez-5.4/lib -I../bluez-5.4/src -I../bluez-5.4/gdbus -I../bluez-5.4/btio `pkg-config glib-2.0 dbus-1 --cflags` -o bluepy-helper bluepy-helper.c ../bluez-5.4/lib/bluetooth.c ../bluez-5.4/lib/hci.c ../bluez-5.4/lib/sdp.c ../bluez-5.4/lib/uuid.c ../bluez-5.4/attrib/att.c ../bluez-5.4/attrib/gatt.c ../bluez-5.4/attrib/gattrib.c ../bluez-5.4/attrib/utils.c ../bluez-5.4/btio/btio.c ../bluez-5.4/src/log.c `pkg-config glib-2.0 --libs`
# ls -scF
Makefile         bluepy-helper    btle.py          sensortag.py
__init__.py      bluepy-helper.c  btle.pyc
# 

두개의 파이선 파일이 들어있는데 btle.py는 BLE 관련 클래스가 정의되어 있고 sensortag.py는 btle 모듈을 사용해 SensorTag에 연결해 센서값을 읽어오는 기능을 하는 코드가 들어있다. 먼저 btle.py를 사용하면 BLE 디바이스의 service, characteristic 관련 정보를 확인할 수 있다.

# python btle.py BC:6A:29:C3:54:33
Connecting to: BC:6A:29:C3:54:33, address type: public
Service <uuid=Generic Access handleStart=1 handleEnd=11> :
Characteristic <Device Name>, supports READ
-> 'TI BLE Sensor Tag'
Characteristic <Appearance>, supports READ
-> '\x00\x00'
Characteristic <Peripheral Privacy Flag>, supports READ WRITE
-> '\x00'
Characteristic <Reconnection Address>, supports READ WRITE
-> '\x00\x00\x00\x00\x00\x00'
Characteristic <Peripheral Preferred Connection Parameters>, supports READ
-> 'P\x00\xa0\x00\x00\x00\xe8\x03'
Service <uuid=f000aa10-0451-4000-b000-000000000000 handleStart=43 handleEnd=53> :
Characteristic <f000aa11-0451-4000-b000-000000000000>, supports NOTIFY READ
-> '\x00\x00\x00'
Characteristic <f000aa12-0451-4000-b000-000000000000>, supports READ WRITE
-> '\x00'
Characteristic <f000aa13-0451-4000-b000-000000000000>, supports READ WRITE
-> 'd'
Service <uuid=Generic Attribute handleStart=12 handleEnd=15> :
Characteristic <Service Changed>, supports INDICATE
Service <uuid=ffe0 handleStart=93 handleEnd=97> :
Characteristic <ffe1>, supports NOTIFY
Service <uuid=f000aa30-0451-4000-b000-000000000000 handleStart=62 handleEnd=72> :
Characteristic <f000aa31-0451-4000-b000-000000000000>, supports NOTIFY READ
-> '\x00\x00\x00\x00\x00\x00'
Characteristic <f000aa32-0451-4000-b000-000000000000>, supports READ WRITE
-> '\x00'
Characteristic <f000aa33-0451-4000-b000-000000000000>, supports READ WRITE
-> '\xc8'
Service <uuid=f000aa00-0451-4000-b000-000000000000 handleStart=35 handleEnd=42> :
Characteristic <f000aa01-0451-4000-b000-000000000000>, supports NOTIFY READ
-> '\x00\x00\x00\x00'
Characteristic <f000aa02-0451-4000-b000-000000000000>, supports READ WRITE
-> '\x00'
Service <uuid=f000aa60-0451-4000-b000-000000000000 handleStart=98 handleEnd=104> :
Characteristic <f000aa61-0451-4000-b000-000000000000>, supports READ
-> '3\x00'
Characteristic <f000aa62-0451-4000-b000-000000000000>, supports READ WRITE
-> '\x00'
Service <uuid=f000aa50-0451-4000-b000-000000000000 handleStart=85 handleEnd=92> :
Characteristic <f000aa51-0451-4000-b000-000000000000>, supports NOTIFY READ
-> '\x00\x00\x00\x00\x00\x00'
Characteristic <f000aa52-0451-4000-b000-000000000000>, supports READ WRITE
-> '\x00'
Service <uuid=f000aa20-0451-4000-b000-000000000000 handleStart=54 handleEnd=61> :
Characteristic <f000aa21-0451-4000-b000-000000000000>, supports NOTIFY READ
-> '\x00\x00\x00\x00'
Characteristic <f000aa22-0451-4000-b000-000000000000>, supports READ WRITE
-> '\x00'
Service <uuid=Device Information handleStart=16 handleEnd=34> :
Characteristic <System ID>, supports READ
-> '3T\xc3\x00\x00)j\xbc'
Characteristic <Model Number String>, supports READ
-> 'N.A.\x00'
Characteristic <Serial Number String>, supports READ
-> 'N.A.\x00'
Characteristic <Firmware Revision String>, supports READ
-> '1.0 (Dec 10 2012)\x00'
Characteristic <Hardware Revision String>, supports READ
-> 'N.A.\x00'
Characteristic <Software Revision String>, supports READ
-> 'N.A.\x00'
Characteristic <Manufacturer Name String>, supports READ
-> 'Texas Instruments\x00'
Characteristic <IEEE 11073-20601 Regulatory Cert. Data List>, supports READ
-> '\xfe\x00experimental'
Characteristic <PnP ID>, supports READ
-> '\x01\r\x00\x00\x00\x10\x01'
Service <uuid=f000ffc0-0451-4000-b000-000000000000 handleStart=105 handleEnd=65535> :
Characteristic <f000ffc1-0451-4000-b000-000000000000>, supports READ WRITE NO RESPONSE WRITE
-> Error from Bluetooth stack (comerr)
Characteristic <f000ffc2-0451-4000-b000-000000000000>, supports READ WRITE NO RESPONSE WRITE
-> Error from Bluetooth stack (comerr)
Service <uuid=f000aa40-0451-4000-b000-000000000000 handleStart=73 handleEnd=84> :
Characteristic <f000aa41-0451-4000-b000-000000000000>, supports NOTIFY READ
-> '\x00\x00\x00\x00'
Characteristic <f000aa42-0451-4000-b000-000000000000>, supports READ WRITE
-> '\x00'
Characteristic <f000aa43-0451-4000-b000-000000000000>, supports READ
-> '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
#

SensorTag가 제공하는 각 서비스마다 characteristic에 관한 정보를 출력해준다.

sensortag.py는 실행하면 SensorTag에 연결해 원하는 센서값을 지속적으로 읽어온다.

# python sensortag.py --all BC:6A:29:C3:54:33
Connecting to BC:6A:29:C3:54:33
('Temp: ', (26.03125, 26.074251481475812))
('Humidity: ', (26.394373168945314, 41.00469970703125))
('Barometer: ', (26.645779013633728, 1016.758906206612))
('Accelerometer: ', (0.15625, 0.59375, 0.828125))
('Magnetometer: ', (-20.20263671875, 22.4609375, 95.458984375))
('Gyroscope: ', (7.95745849609375, 10.711669921875, -69.57244873046875))
** Right button DOWN
('Temp: ', (26.0625, 25.675194203681826))
('Humidity: ', (26.394373168945314, 41.00469970703125))
('Barometer: ', (26.760699868202209, 1016.8384757575767))
** Right button UP
('Accelerometer: ', (0.171875, 0.75, 0.515625))
('Magnetometer: ', (-22.857666015625, 25.787353515625, 92.315673828125))
** Right button DOWN
('Gyroscope: ', (4.44793701171875, -15.93017578125, -25.39825439453125))
** Right button UP
('Temp: ', (26.09375, 28.389209302147549))
('Humidity: ', (26.394373168945314, 41.00469970703125))
('Barometer: ', (26.8482586145401, 1017.0108461132135))
('Accelerometer: ', (0.15625, 0.765625, 0.625))
('Magnetometer: ', (-22.857666015625, 25.787353515625, 92.315673828125))
('Gyroscope: ', (6.591796875, 3.23486328125, 3.387451171875))
** Left button DOWN
('Temp: ', (26.15625, 29.185403134000069))
** Right button DOWN
('Humidity: ', (26.394373168945314, 41.00469970703125))
** Both buttons UP
('Barometer: ', (26.922136306762695, 1017.0096061331639))
('Accelerometer: ', (0.21875, 0.703125, 0.671875))
('Magnetometer: ', (-22.15576171875, 24.627685546875, 93.20068359375))
^C#

파이선 소스코드를 분석하면 SensorTag에 어떻게 연결하고 필요한 센서를 활성화 한 후 값을 읽어오는 방법을 확인할 수 있다.

여기서는 예제로 에디슨 보드에서 파이선 쉘을 실행해 직접 온습도 센서값을 읽어와 보도록 하겠다.

# rfkill unblock bluetooth
# hciconfig hci0 up
# hcitool lescan
LE Scan ...
D0:39:72:D3:4A:AF (unknown)
D0:39:72:D3:4A:AF Bean
EC:C3:09:E2:18:A4 (unknown)
BC:6A:29:C3:54:33 (unknown)
BC:6A:29:C3:54:33 SensorTag
# python
>>> import struct
>>> from btle import UUID, Peripheral, DefaultDelegate
>>> sensorOn = struct.pack("B",0x01)
>>> sensorTag = Peripheral("BC:6A:29:C3:54:33")
>>> svcUUID = "f000aa20-0451-4000-b000-000000000000"
>>> dataUUID = "f000aa21-0451-4000-b000-000000000000"
>>> ctrlUUID = "f000aa22-0451-4000-b000-000000000000"
>>> svc = sensorTag.getServiceByUUID(svcUUID)
>>> data = svc.getCharacteristics(dataUUID)[0]
>>> ctrl = svc.getCharacteristics(ctrlUUID)[0]
>>> ctrl.write(sensorOn, withResponse=True)
>>> data.read()
'\xb8l\xdfD'
>>> data.read()
'\xb8l\xb2D'
>>> (rT,rH) = struct.unpack('<HH',data.read())
>>> temp = -46.85+175.72*(rT/65536.0)
>>> RH = -6.0+125.0*((rH&0xfffc)/65536.0)
>>> temp
27.8610302734375
>>> RH
27.57696533203125
>>> def tempHum(d):
...  (rT, rH) = struct.unpack('<HH', d.read())
...  temp = -46.85+175.72*(rT/65536.0)
...  RH = -6.0+125.0*((rH&0xfffc)/65536.0)
...  return (temp, RH)
...
>>> tempHum(data)
(27.689428710937499, 25.34918212890625)
>>> tempHum(data)
(27.689428710937499, 25.31103515625)
>>> tempHum(data)
(27.692109985351557, 25.34918212890625)
>>> sensorTag.disconnect()
>>> 

예제에서 svcUUID, dataUUID, ctrlUUID는 위에서 btle.py를 실행한 결과로 출력된 UUID 값이다. 

...
Service <uuid=f000aa20-0451-4000-b000-000000000000 handleStart=54 handleEnd=61> :
Characteristic <f000aa21-0451-4000-b000-000000000000>, supports NOTIFY READ
-> '\x00\x00\x00\x00'
Characteristic <f000aa22-0451-4000-b000-000000000000>, supports READ WRITE
-> '\x00'
...

다른 센서들도 이와 유사한 방법으로 사용하면 된다.

2015년 2월 17일 화요일

ESP8266 펌웨어 업데이트 하기

ESP8266의 펌웨어를 업데이트 하려면 GPIO0가 GND에 연결된 상태로 전원이 켜져 UART UPGRADE 모드가 되어야 한다. 이 상태에서 컴퓨터에서 업데이트 프로그램을 실행하면 된다. 현재 GUI로 동작하는 툴은 윈도우용만 나와있다. 
하지만 커맨드라인에서 사용할 수 있는 esptool을 사용하면 맥이나 리눅스에서도 문제없이 펌웨어 업데이트가 가능하다. 

esptool은 GitHub에서 다운받을 수 있다. esptool을 사용하려면 pyserial이 미리 설치되어 있어야만 한다.

설치가 되었고 ESP8266이 UART UPGRADE 모드인 경우 다음과 같이 명령을 주면 된다.

./esptool.py --port [Serial port] write_flash [address] [filename]

[Serial port]에는 ESP8266이 연결되어 있는 시리얼 포트 디바이스 이름을 써주면 된다. 디폴트는 /dev/ttyUSB0로 되어 있다. 
[filename]이 지정한 바이너리 파일의 내용을 ESP8266의 [address]가 가르키는 주소에 기록을 하게 된다.

아래는 맥에서 0.95 펌웨어로 업데이트 하는 예제이다.

$ ./esptool.py /dev/tty.usbserial-A4013ESQ write_flash 0x00000 AI-v0.9.5.0\ AT\ Firmware\ \(1\).bin
Connecting...
Erasing flash...
Writing at 0x0007ec00... (100 %)

Leaving...
$ 

위와 같이 되면 업데이트가 끝난 것이다. 이제 다시 부팅해서 시리얼 터미널을 열고 아래 명령을 입력해보면 새로운 펌웨어로 동작하는걸 확인할 수 있다.

AT+GMR
00200.9.5(b1)
compiled @ Dec 25 2014 21:40:28
AI-THINKER Dec 25 2014

OK

바이너리 파일이 여러개로 되어 있는 경우는 write_flash 뒤의 [address] [filename] 부분을 반복적으로 써 주면 된다. 즉 file1.bin은 0x00000 번지에 쓰고, file2.bin은 0x40000 번지에 쓰고 싶으면 esptool.py --port /dev/ttyusbserial-A4013ESQ write_flash 0x0000 file1.bin 0x40000 file2.bin 으로 하면 된다.



2015년 2월 2일 월요일

에디슨에서 pushetta를 사용해 스마트폰으로 푸쉬 메시지 보내기

Pushetta documentation에 보면 실제로 메시지를 보내는 간단한 예제들을 찾아볼 수 있다.
여기서는 에디슨에서 바로 사용할 수 있는 python과 node.js를 사용해 메시지를 보내보겠다.

python 의 경우 documentation에 있는 코드를 그대로 사용하면 된다.


import
urllib2 import json data = { "body" : "Hello World", "message_type" : "text/plain", "expire" : "2015-01-01" } req = urllib2.Request('http://api.pushetta.com/api/pushes/my%20%edision%20alert/') req.add_header('Content-Type', 'application/json') req.add_header('Authorization', 'Token c4d......89') response = urllib2.urlopen(req, json.dumps(data))

위의 코드에서 빨간색 볼드체 부분만 자신의 경우에 맞게 수정해주면 된다. 'Hello World' 부분이 자신이 실제로 보내고자 하는 메시지가 되고, my%20edision%20alert에는 자신이 만든 채널 이름을, c4d......07 부분에 자신의 API key (Pushetta 홈페이지의 Dashboard에서 확인할 수 있음)을 넣어주면 된다.

만일 위의 코드를 pushettatest.py라는 이름으로 저장했다면 다음과 같이 실행하면 된다.

# python pushettatest.py
# 

실행한 후 스마트폰을 보면 아래와 같이 메시지가 온걸 확인할 수 있을 것이다.


Node.js에서도 유사한 방법으로 메시지를 보낼 수 있다.

# cat pushettatest.js
var http=require('http');

var opt = {
  host: 'api.pushetta.com',
  path: '/api/pushes/my%20edison%20alert/',
  headers: {'Content-type': 'application/json',
            'Authorization': 'Token c4d....89'},
  method: 'POST'
};

cb = function(resp) {
  var str = '';
  resp.on('data', function(chunk) {
    str += chunk;
  });

  resp.on('end', function() {
    console.log(str);
  });
}

var msg = {'body':'Hello from nodejs'};
msg.message_type='text/plain';
msg.expire='2015-12-31';

var req = http.request(opt,cb);

req.write(JSON.stringify(msg));
req.end();
# node pushetta.js
{"success": true, "error_code": ""}
#



여기서는 에디슨에서 실행을 시켰지만 리눅스를 실행하는 다른 보드(Raspberry pi, Beaglebone Black 등)에서도 동일하게 적용할 수 있다. 또한 documentation을 참고하면 아두이노에 ethernet shield를 붙여서도 메시지를 보낼 수 있다.