2015년 3월 31일 화요일

Arduino IDE for ESP8266

최근 IoT쪽에서 가장 hot한 토픽중에 하나인 ESP8266용 Arduino IDE가 발표되었다.

물론 기존에 ESP8266용 gcc 개발환경이 이미 공개되어 있으나 프로그램을 작성하려면 API를 포함해 공부해야 할 것도 많고 개발환경을 설정하는것도 쉽지 않았다.

하지만 Arduino IDE를 ESP8266용으로 사용할 수 있게 되어 이제는 ESP8266용으로도 별도의 API를 공부할 필요 없이 아두이노 코드를 작성하는것과 동일하게 프로그래밍이 가능해졌다.



기본적으로 pinMode, digitalRead, digitalWrite는 아두이노와 동일하게 사용하면 된다. 단 핀 번호는 esp8266 GPIO 핀 번호를 사용하면 된다.

GPIO0~GPIO15는 핀 모드를 INPUT, OUTPUT, INPUT_PULLUP 및 OUTPUT_OPEN_DRAIN (이것만 추가됨)로 설정할 수 있고, GPIO16은 INPUT, OUTPUT으로만 설정이 가능하다. 

ESP8266의 경우 ADC는 1 채널이기 때문에 analogRead(0)만 사용할 수 있다. 이 경우 TOUT 핀의 입력전압 값을 변환하게 된다. (주의: ESP8266의 ADC의 변환 레인지는 0~3.3V가 아니고 0~1V이다)

GPIO 핀 인터럽트도 아두이노와 동일하게 사용할 수 있다. GPIO16을 제외한 모든 GPIO핀에 인터럽트를 사용할 수 있고, 인터럽트 타입은 CHANGE, RISING, FALLING을 사용하면 된다.

현재 Wifi, Ticker, I2C, 1-wire, mDNS, MQTT, DHT-11, LCD, Stepper 라이브러리등을 사용할 수 있고 나머지 라이브러리들도 계속 추가되고 있다.

ESP8266 지원이 포함된 arduino는 https://github.com/esp8266/arduino 에서 다운받으면 된다.

스케치를 업로드 할 때는 Tools->Programmer 에서 esptool을 선택해주면 된다. 물론 업로드 하기 전에 ESP8266을 bootloader 모드(GPIO0를 LOW로 하고 전원을 연결)로 만들어 줘야만 한다. 

기존에는 ESP8266을 아두이노에 연결해 저렴한 wifi 모듈로 많이 사용했지만, 이제는 별도의 아두이노 없이 ESP8266만으로 모든걸 쉽게(기존의 아두이노 코드와 유사하게) 처리할 수 있게 되었다.







라즈베리파이 시리얼포트 C 프로그래밍 (Raspberry Pi Serial port programming in C)

Raspberry Pi에서는 기본적으로 UART를 시리얼 콘솔로 사용하게 된다. 하지만 UART를 어플리케이션이 다른 용도로 사용하고자 하는 경우에는 콘솔로 동작하지 못하도록 해 줘야 한다.

먼저 이전 포스트(Raspberry Pi에서 UART를 console이 사용하지 않도록 설정하기)를 참조해 설정을 변경해준다.

C 프로그램에서 UART를 사용하기 위해서는 다음의 헤더 파일을 include 해 준다.

#include <stdio.h>
#include <unistd.h> //Used for UART
#include <fcntl.h> //Used for UART

#include <termios.h> //Used for UART

아래는 UART 설정 코드이다.

// -------------------------
// ----- SETUP USART 0 -----
// -------------------------
// At bootup, pins 8 and 10 are already set to UART0_TXD, UART0_RXD 
// (ie the alt0 function) respectively
int uart0_filestream = -1;

// OPEN THE UART
// The flags (defined in fcntl.h):
// Access modes (use 1 of these):
//   O_RDONLY - Open for reading only.
//   O_RDWR - Open for reading and writing.
//   O_WRONLY - Open for writing only.
//
// O_NDELAY / O_NONBLOCK (same function) - Enables nonblocking mode. 
//    When set read requests on the file can return immediately with a failure status
//    if there is no input immediately available (instead of blocking). 
//    Likewise, write requests can also return immediately with a failure status 
//    if the output can't be written immediately.
//
// O_NOCTTY - When set and path identifies a terminal device, 
//    open() shall not cause the terminal device to become 
//    the controlling terminal for the process.
uart0_filestream = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode
if (uart0_filestream == -1) {     // ERROR - CAN'T OPEN SERIAL PORT
  printf("Error - Unable to open UART. Ensure it is not in use by another application\n");
}

// CONFIGURE THE UART
// The flags (defined in /usr/include/termios.h 
//     - see http://pubs.opengroup.org/onlinepubs/007908799/xsh/termios.h.html):
// Baud rate:- B1200, B2400, B4800, B9600, B19200, B38400, B57600, 
//             B115200, B230400, B460800, B500000, B576000, B921600, 
//             B1000000, B1152000, B1500000, B2000000, B2500000, B3000000, 
//             B3500000, B4000000
// CSIZE:- CS5, CS6, CS7, CS8
// CLOCAL - Ignore modem status lines
// CREAD - Enable receiver
// IGNPAR = Ignore characters with parity errors
// ICRNL - Map CR to NL on input 
//         (Use for ASCII comms where you want to auto correct 
//         end of line characters - don't use for bianry comms!)
// PARENB - Parity enable
// PARODD - Odd parity (else even)
struct termios options;
tcgetattr(uart0_filestream, &options);
options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; //<Set baud rate
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
tcflush(uart0_filestream, TCIFLUSH);

tcsetattr(uart0_filestream, TCSANOW, &options);

코드가 좀 길기는 해도 코멘트를 참고하면 이해하는데 문제는 없을 것이다. 위의 코드는 시리얼 포트를 9600bps, 8 bit data, no parity, non-blocking 모드로 오픈해준다.

시리얼 포트가 오픈되었으면 아래와 같이 데이터를 송수신 할 수 있다.

데이터 전송

//----- TX BYTES -----
unsigned char tx_buffer[20];
unsigned char *p_tx_buffer;

p_tx_buffer = &tx_buffer[0];
*p_tx_buffer++ = 'H';
*p_tx_buffer++ = 'e';
*p_tx_buffer++ = 'l';
*p_tx_buffer++ = 'l';
*p_tx_buffer++ = 'o';

if (uart0_filestream != -1) {
  int count = write(uart0_filestream, &tx_buffer[0], (p_tx_buffer - &tx_buffer[0])); //Filestream, bytes to write, number of bytes to write
  if (count < 0) {
    printf("UART TX error\n");
  }


}

데이터 수신

//----- CHECK FOR ANY RX BYTES -----
if (uart0_filestream != -1) {
  // Read up to 255 characters from the port if they are there
  unsigned char rx_buffer[256];
  int rx_length = read(uart0_filestream, (void*)rx_buffer, 255); //Filestream, buffer to store in, number of bytes to read (max)
  if (rx_length < 0) {
    //An error occured (will occur if there are no bytes)
  } else if (rx_length == 0) {
    //No data waiting
  } else {
    //Bytes received
    rx_buffer[rx_length] = '\0';
    printf("%i bytes read : %s\n", rx_length, rx_buffer);
  }

}

일반적인 경우 수신된 데이터가 없을 때 read()를 호출하면 데이터가 수신될 때 까지 실행이 멈추게 되지만 여기서는 시리얼포트를  O_NDELAY 플래그를 사용해 non-blocking 모드로 오픈했기 때문에 read()를 호출했을 때 수신된 데이터가 없어도 바로 리턴하기 때문에 코드 내에서 데이터가 수신되기를 기다려야 한다면 while 루프등을 사용해야 한다.

시리얼 포트를 다 사용했다면 포트를 close하면 된다.

// —— CLOSE THE UART —— 
close(uart0_filestream);

시리얼 포트가 동작하는지 확인할 수 있는 가장 쉬운 방법은 Tx와 Rx핀을 서로 연결해 준 다음 프로그램에서 송신한 데이터가 그대로 수신되는지 확인해 보면 된다. (Loopback Test)



Raspberry Pi에서 UART를 console이 사용하지 않도록 설정하기



Raspberry Pi는 기본적으로 UART를 콘솔이 사용하도록 설정되어 있다. 하지만 arduino등의 보드를 UART를 통해 연결해 제어하는 경우 제어 프로그램이 UART를 사용하려면 콘솔이 UART를 잡고 있지 않도록 설정해 줄 필요가 있다.



SD카드를 PC에 연결하고 오픈해보면 cmdline.txt 파일이 있을 것이다. 기본적으로 수정을 하지 않았다면 파일의 내용은 다음과 같을 것이다.


dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

여기서 "ttyAMA0"에 관련된 모든 파라미터를 삭제해주면 된다.

즉 위의 경우 'console=ttyAMA0,115200' 부분을 삭제한 후 파일을 저장해 주면 된다.

또한 /etc/inittab 파일도 수정해 줘야 한다.

에디터로 파일을 열고 'ttyAMA0' 를 검색하면 시리얼 포트를 지정하는 라인을 찾을 수 있다.

해당 라인의 맨 앞에 '#'을 추가해 주고 저장해 주면 된다.

이제 리부팅 하고 나면 UART핀은 다른 프로세스가 사용하거나, 또는 그 핀을 일반 GPIO 용도로 사용할 수 있게 된다.

2015년 3월 26일 목요일

에디슨에서 128x64 OLED 모듈 사용하기 (Using SSD1306 based 128x64 I2C OLED module in Intel Edison)

인텔 에디슨에 SSD1306 기반의 128x64 OLED 모듈을 연결해 구동해 보았다.


사진에서 볼 수 있는것처럼 I2C 방식을 사용해 단 4핀(Vcc, GND, SCL, SDA)만으로 구동이 가능하다. Aliexpress등에서 개당 약 $3.5 정도에 구입이 가능하다. 크기는 0.96", 해상도는 128x64, 흑백(단색) OLED 모듈이다.
인터넷을 찾아보면 아두이노에 이 모듈을 사용한 예제는 여러가지 종류의 라이브러리를 사용한걸 쉽게 찾아볼 수 있는데 에디슨에서 사용한 예제는 찾아보기가 쉽지 않다.

그래서 라이브러리들을 참조해 에디슨용 파이선 클래스를 만들어 보았다. 일단 두가지 폰트(6*8, 8*16)로 화면에 텍스트를 출력하는게 가능하다.

에디슨에서는 I2C를 사용하기 위해 mraa 라이브러리를 사용했다.


에디슨용 아두이노 breakout 보드에서 위와 같이 연결해 줬다. 아두이노 쉴드에 나와있는 I2C를 사용하려면 mraa에서 6번 버스를 사용하면 된다. 그리고 OLED 모듈의 경우 I2C 주소가 0x78로 표시되어 있지만 실제로 0x3C를 사용해야 한다.

즉 파이선에서 I2C를 사용하기 위한 초기화는 다음과 같은 식으로 하면 된다.

import mraa

display = mraa.I2c(6)
display.address(0x3c)

일단 I2C가 설정되고 나면 이후 초기화나 ssd1306의 레지스터 설정값들은 아두이노용 라이브러리를 참조해 작성해주면 된다. 작성한 코드는 아래 링크의 zip 파일을 다운받으면 된다. 압축을 해제하면 Oled.py 파일이 생길 것이다.

사용하는 방법은 간단하다.

python
>>> import Oled
>>> display = Oled.Oled()
>>> display.cls()
>>> display.printStr(0,0,"Hello",1)
>>> display.printStr(0,1,"World", 1)
>>> display.printStr(0,1,"Edison rules!",2)
>>>

위와 같이 모듈을 import 한 후 Oled() 클래스 인스턴스를 만들어 주면 모듈이 초기화 된다. cls()는 화면을 지우는 메소드이고 printStr(x, y, string, size)은 화면에 문자열을 출력한다. 처음 x, y는 문자열을 출력할 좌표이고, string은 출력할 내용, size는 폰트 크기로 여기서는 1(6*8) 또는 2(8*16)만 사용할 수 있다.

* 단 mraa 라이브러리는 레지스터에 값을 기록할 때 한꺼번에 여러 바이트를 보내는 함수가 없어 cls()같은 경우 실행속도가 좀 느리다. mraa 라이브러리를 조금 수정하면 좀 더 빠르게 최적화가 가능하다.

명령어 프롬프트에서 다음 명령으로 직접 프로그램을 실행했을 때 실행 결과 동영상이다.

# python Oled.py


Download : Oled.py.zip

2015년 3월 23일 월요일

MQTT(Message Queue Telemetry Transport) 소개

Introduction

MQTT(Message Queue Telemetry Transport) 프로토콜은 기계간 통신(M2M)/IoT를 위한 프로토콜이다. HTTP의 request/response 방식 대신 브로커를 사용한 publish/subscribe 방식으로 메시지를 주고 받는다. 물론 MQTT 역시 HTTP와 마찬가지로 TCP/IP 프로토콜 위에서 동작한다. 이 프로토콜의 첫번째 버젼은 1999년 Andy Stanford-Clark와 Arlen Nipper에 의해 발표되었다. 2013년 IBM이 OASIS 표준화 기구에 MQTT v3.1을 제출하여 승인받았다. 또한 비 TCP/IP 네트웍 상에서 동작하는 MQTT의 변종도 존재한다. MQTT-SN은 ZigBee 기반 네트웍 상에서 사용된다.
MQTT는 최소한의 프로토콜 오버헤드만으로 동작할 수 있기 때문에 임베디드 시스템같이 한정된 자원을 가진 시스템이나 저대역폭(low bandwidth) 또는 불안정한 네트웍 환경에서도 사용할 수 있다. 또한 다양한 언어를 지원하는 라이브러리들이 공개되어 있다. mbed에서라면 mbed MQTT library, 아두이노에서는 arduino client for MQTT를 사용할 수 있다.
이 기술이 특히 흥미로운 것은 모든 데이터를 단일 transport 메커니즘으로 처리한다는 것이다. 즉 특정 서비스를 위해 각각 따로 방화벽을 설정할 필요 없이, 단일 TCP 포트만으로 모든 종류의 메시지를 전달하는데 충분하다.



MQTT에서 publish/subscribe를 위한 메시지 큐를 '네트웍의 트위터'라고 생각할 수도 있다. 클라이언트는 특정 토픽에 대한 메시지를 수신하기 위해 토픽을 구독(subscribe)하고, 다른 클라이언트는 그 토픽에 대한 메시지를 발행(publish)한다. 메시지들은 토픽으로 분류된다. 예를들어 nagios/mta, nagios/disk, test/kr/private 같은 식의 토픽을 가질 수 있다. 클라이언트는 임의 갯수의 토픽을 구독할 수 있고, 구독할 토픽에 와일드카드를 포함할 수도 있다. (예: nagios/#) MQTT에서 메시지는 최대 256MB 크기의 UTF-8 BLOB이다.

Publish/Subscribe 모델


MQTT는 일반적으로 웹에서 사용되는 client/server 모델 대신 publish/subscribe(발행/구독) 모델을 사용한다. Client/server 모델에서는 클라이언트가 그 상대방이 되는 서버와 직접 통신을 하게 된다. 하지만 publish/subscribe 모델은 특정 메시지를 보내는 클라이언트(즉 publisher)와 그 메시지를 받는 다른 클라이언트들(즉 subscriber)를 분리시켜 서로는 상대방의 존재 여부를 모른다. 그러므로 통신을 하기 위해서는 publisher와 subscriber 양측이 모두 다 알고 있는 broker라 불리는 3번째 컴포넌트가 필요하게 된다. (publisher건 subscriber건) 모든 클라이언트는 브로커와 통신을 하고, 브로커는 자신이 받은 메시지들을 필터링 해 적절하게 분배하게 된다.



위의 그림에서 온도센서가 publisher, 랩탑과 스마트폰이 subscriber, HiveMQ가 MQTT 브로커가 된다. Subscriber들이 구독하길 원하는 토픽(여기서는 'temperature')이 있으면 그 토픽에 대해 브로커에게 구독신청(subscribe)을 한다. 그 후 온도센서가 측정한 온도값을 'temperature'라는 토픽으로 브로커에게 발행(publish)하게 된다. MQTT 브로커는 'temperature' 토픽에 대한 메시지를 받으면 해당 토픽을 구독하고 있는 subscriber들(여기서는 랩탑과 스마트폰)에게 이 메시지를 전달해준다. 토픽을 구독하는 클라이언트는 여러대가 될 수도 있다. 이 경우 publisher는 자신이 발행한 토픽을 구독한 subscriber가 몇개인지 신경 쓸 필요가 없다. (해당 토픽을 구독한 subscriber가 하나도 없을수도 있다.) 메시지는 브로커를 통해 전달되기 때문에 publisher와 subscriber는 서로의 IP주소와 포트를 알고 있을 필요도 없고, 서로 동시에 실행되고 있을 필요도 없다.

QoS (Quality of Service)


모든 메시지는 QoS값을 가지고 publish 된다. MQTT에는 3가지 QoS 타입이 있다.

  • QoS_0 - "At most once" TCP/IP 네트웍 상에서 최대의 노력을 기울여 메시지가 전달되도록 함. 하지만 메시지가 중복되거나 분실될 수도 있음. 센서값이 한번 분실되어도 금새 다음번 값을 publish하기 때문에 분실되는 것이 큰 문제가 되지 않는 경우에 사용
  • QoS_1 - "At least once" 메시지가 전달되는걸 보장. 하지만 이로 인해 중복이 발생할 수도 있음
  • QoS_2 - "Exactly once" 메시지가 한번만 전달되는걸 보장. 과금 정보처럼 메시지 분실이나 중복이 문제를 발생시킬 수 있는 경우에 사용


Scalability


그리고 publish/subscribe 모델은 기존의 client/server 모델에 비해 훨씬 더 큰 확장성을 가지고 있다. 브로커의 동작이 event-driven 방식이기 때문에 쉽게 병렬화가 가능하기 때문이다. 또한 메시지 캐슁과 지능적 라우팅이 확장성을 증가시키는데 결정적이다. 하지만 publish/subscribe를 동시에 수백~수천만개를 처리하도록 확장시키는건 역시 큰 문제이다. 하지만 이 경우도 클러스터 브로커 노드를 상요해 개별 서버의 로드를 로드밸런싱 하는 것으로 해결할 수 있다.

2015년 3월 17일 화요일

NodeJS에서 시리얼 포트 사용하기

하드웨어 디바이스와 통신에 가장 쉽고 많이 사용되는것이 시리얼포트이다. NodeJS에서 시리얼 포트를 사용하려면 Chris Williams가 만든 serialport 모듈을 사용하면 된다.

먼저 serialport 모듈을 설치한다.

$ npm install --save-dev serialport

> serialport@1.6.2 install /home/test/node_modules/serialport
> node-pre-gyp install --fallback-to-build

[serialport] Success: "/home/test/node_modules/serialport/build/serialport/v1.6.2/Release/node-v11-darwin-x64/serialport.node" is installed via remote
serialport@1.6.2 ../node_modules/serialport
├── bindings@1.2.1
├── sf@0.1.7
├── nan@1.7.0
├── optimist@0.6.1 (wordwrap@0.0.2, minimist@0.0.10)
└── debug@2.1.3 (ms@0.7.0)


설치가 완료되면 시리얼 포트를 열어준다.

$ node
> var serialport = require("serialport");
undefined
> SerialPort = serialport.SerialPort;
{ [Function: SerialPort]
  super_:
   { [Function: Stream]
     super_: { [Function: EventEmitter] listenerCount: [Function] },
     Readable:
      { [Function: Readable]
        ReadableState: [Function: ReadableState],
        super_: [Circular],
        _fromList: [Function: fromList] },
     Writable:
      { [Function: Writable]
        WritableState: [Function: WritableState],
        super_: [Circular] },
     Duplex: { [Function: Duplex] super_: [Object] },
     Transform: { [Function: Transform] super_: [Object] },
     PassThrough: { [Function: PassThrough] super_: [Object] },
     Stream: [Circular] } }
> var serialPort = new SerialPort("/dev/cu.usbmodem14131", {
      baudrate: 9600,
      parser: serialport.parsers.readline("\n")
});
> serialPort = new SerialPort("/dev/cu.PL2303-00003014", {
... baudrate: 9600,
... parser: serialport.parsers.readline("\n")
... });
{ domain: null,
  _events: {},
  _maxListeners: 10,
  fd: null,
  paused: true,
  bufferSize: 256,
  readable: true,
  reading: false,
  options:
   { baudrate: 9600,
     parser: [Function],
     baudRate: 9600,
     dataBits: 8,
     stopBits: 1,
     parity: 'none',
     rtscts: false,
     xon: false,
     xoff: false,
     xany: false,
     bufferSize: 256,
     platformOptions: { vmin: 1, vtime: 0 },
     dataCallback: [Function],
     disconnectedCallback: [Function] },
  path: '/dev/cu.PL2303-00003014' }


위에서 '/dev/cu.PL2303-00003014' 부분은 시스템에 맞게 바꿔주면 된다. 리눅스라면 보통 '/dev/ttyUSB0' 같은 이름이고 윈도우라면 'COM1' 같은 이름이 된다.

테스트하기 위해 아두이노에 0.1초마다 숫자를 하나씩 증가시켜 시리얼포트로 출력하도록 하는 스케치를 업로드 해 준다.

setup() { 
  Serial.begin(9600); 


int i=0; 
void loop() { 
  Serial.println(i++); 
  delay(100); // poll every 100ms 
}

Reading from the port

시리얼포트에서 값을 읽어 화면에 표시하기 위해 다음의 코드를 작성해 'dump.js'라는 이름으로 저장해준다. 

var serialport = require("serialport"); 
var SerialPort = serialport.SerialPort; 

var serialPort = new SerialPort("cu.PL2303-00003014", { 
  baudrate: 9600, 
  parser: serialport.parsers.readline("\n") 
}); 
serialPort.on("open", function () { 
  console.log('open'); 
  serialPort.on('data', function(data) { 
    console.log(data); 
  }); 
});

이제 아두이노를 USB-to-Serial 케이블에 연결하고 dump.js를 실행한다.

$ node jump.js
...
5
6
7
....
^C


좀 더 상세한 정보와 예제 코드는 https://www.npmjs.com/package/serialport 를 참조하면 된다.

2015년 3월 16일 월요일

무료 DDNS 서비스 DuckDNS 설정하기

http://duckdns.org에서 제공하는 DDNS 서비스를 설정해 보겠다.


DuckDNS 사이트에서 먼저 로그인을 한다. Twitter, Facebook, reddit, google+ 계정중에 하나로 로그인을 하면 된다.


여기서는 트위터로 로그인을 해 보았다. 위와 같은 화면이 나타나면 'Authorize app'을 선택해 주면 된다.


로그인 되면 위와 같은 화면이 나타난다. 무료인 경우 4개까지 서브도메인 이름을 추가할 수 있다. 추가를 원하는 이름을 'sub domain' 부분에 넣고 'add domain' 버튼을 누르면 도메인이 추가된다. 웹 인터페이스 대신 커맨드 라인 명령을 이용하거나 GUI 어플을 사용하는 경우는 위의 token 값을 알고 있어야만 한다.

아래는 맥용 GUI 를 사용하는 경우이다.


리눅스용GUI 앱도 제공된다. 앱을 사용하려면 먼저 zenity가 설치되어 있어야 한다.

$ sudo apt-get install zenity

그 다음 파일을 다운받아 압축을 해제하면 duck-setup-gui.sh 가 들어있을 것이다. 이 파일을 원하는 디렉토리로 옮겨 준 다음 실행권한을 주고 실행하면 된다.

$ chmod +x duck-setup-gui.sh
$ ./duck-setup-gui.sh



맥/리눅스에서 사용할 수 있는 커맨드라인 버젼 스크립트도 있다. 이 스크립트를 사용하면 시스템에 cron job을 만들어, 5분마다 자동으로 DuckDNS를 업데이트 하도록 만들어진다.

$ chmod +x duck-setup.sh
$ ./duck-setup.sh




원문 링크
https://www.thefanclub.co.za/how-to/free-ip-ddns-service-duck-dns-setup-script-linux-and-osx

2015년 3월 13일 금요일

8pino 처음 사용해보기

이전 포스트에서 소개한 초소형 아두이노 호환보드 8pino(http://arsviator.blogspot.kr/2015/02/8pino.html)를 Seeedstudio에서 8pino 판매 시작하였다. 가격은 $8 


위와 같이 패키징 되어 있고, 케이스에 각 핀에 대한 설명이 표시되어 있음


MicroSD 카드와의 크기 비교. 사진에서 8pino의 아래쪽 Break-away universal pads 부분은 분리해도 상관없음.


별도의 USB 커넥터 없이 보드 PCB 자체를 위의 사진과 같이 micro USB 커넥터에 바로 꼽아 넣으면 됨.

Adafruit Trinket 펌웨어가 들어있어 Trinket과 동일하게 사용하면 됨
현재 Arduino v1.6.1이 발표되었는데 보드를 추가하는 방법이 달라지면서 Adafruit의 튜토리얼에 있는대로 해도 설정파일이 맞지 않아 당분간은 adafruit의 튜토리얼에 링크되어 있는 arduino v1.05를 다운받아 사용하는게 가장 편함. 

상세한 내용은 https://learn.adafruit.com/introducing-trinket/setting-up-with-arduino-ide 를 참고.

위의 사진에 2개의 LED가 켜져 있는데 왼쪽 LED는 전원LED이고 오른쪽은 D1에 연결되어 있음

void setup()
{
  pinMode(1, OUTPUT);
}

void loop()
{
  digitalWrite(1, HIGH);
  delay(1000);
  digitalWrite(1, LOW);
  delay(1000);
}

위의 코드로 보드를 테스트하면 됨. 프로그래밍을 하려면 보드는 'Adafruit Trinket 8MHz'를 선택하고 'USBtinyISP'를 선택해 줘야 함.



한번 프로그램을 upload 하고 나서 다시 upload 하려고 하면 에러가 발생. 다시 upload 하려면 먼저 8pino를 케이블에서 분리했다가 다시 꼽은다음 upload 해야 함.


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년 3월 11일 수요일

Ti SensorTag UUIDs

TMP006 IR Temperature Sensor

Type
UUID
Handle
Read/Write
Format
<Data>
AA01 *
0x25
Read/Notify
ObjLSB ObjMSB AmbLSB AmbMSB (4 bytes) 
<DataNotification>

0x26
R/W
2 bytes
<Configuration>
AA02 *
0x29
R/W
1 byte
<Period>
AA03 *

R/W
1 byte

Kionix KXTJ9 Accelerometer

Type
UUID
Handle
Read/Write
Format
<Data>
F000 AA11 *
0x2D
Read/Notify
X : Y : Z (3 bytes)
<DataNotification>

0x2E
R/W
2 bytes
<Configuration>
F000 AA12 *
0x31
R/W
1 byte
<Period>
F000 AA13 *
0x34
R/W
1 byte


Sensirion SHT 21 Humidity Sensor

Type
UUID
Handle
Read/Write
Format
<Data>
AA21 *
0x38
Read/Notify
TempLSB TempMSB HumLSB HumMSB (4 bytes) 
<DataNotification>

0x39
R/W
2 bytes
<Configuration>
AA22 *
0x3B
R/W
1 byte
<Period>
AA23 *

R/W
1 byte


Freescale MAG3100 Magnetometer

Type
UUID
Handle
Read/Write
Format
<Data>
AA31 *
0x40
Read/Notify
XLSB XMSB YLSB YMSB ZLSB ZMSB (6 bytes)
<DataNotification>

0x41
R/W
2 bytes
<Configuration>
AA32 *
0x44
R/W
1 byte
<Period>
AA33 *
0x47
R/W
1 byte


Epcos T5400-C953 Barometric Pressure Sensor

Type
UUID
Handle
Read/Write
Format
<Data>
AA41 *
0x4B
Read/Notify
TempLSB TempMSB PressLSB PressMSB (4 bytes)
<DataNotification>

0x4C
R/W
2 bytes
<Configuration>
AA42 *
0x4F
R/W
1 byte
<Calibration>
AA43 *
0x52
Read only
C1LSB C1MSB .. C8LSB C8MSB (16 bytes)
<Period>
AA44 *

R/W
1 byte


Invensense IMU-3000 Gyroscope

Type
UUID
Handle
Read/Write
Format
<Data>
AA51 *
0x57
Read/Notify
XLSB XMSB YLSB YMSB ZLSB ZMSB (6 bytes)
<DataNotification>

0x58
R/W
2 bytes
<Configuration>
AA52 *
0x5B
R/W
1 byte
<Period>
AA53 *

R/W
1 byte

Simple Key Service

Type
UUID
Handle
Read/Write
Format
<Data>
FFE1
0x5F
Notification only
Bit2: side key, Bit1: right key, Bit0: left key