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년 10월 1일 목요일

안드로이드 BluetoothLeScanner 클래스 사용하기



안드로이드에서 Bluetooth LE (Low Energy)를 사용할 때 가장 기본적인 샘플 예제 코드로 https://github.com/googlesamples/android-BluetoothLeGatt 를 많이 사용한다.

하지만 API level 21부터는 API가 약간 바뀌어 LE 디바이스를 스캔할 때 사용하는 BluetoothAdapter 클래스의 startLeScan(), stopLeScan() 메소드가 deprecated 되어 빌드해 실행하는데 문제는 없어도 android studio에서 소스를 보면  mBluetoothAdapter.startLeScan(mLeScanCallback); 과 mBluetoothAdapter.stopLeScan(mLeScanCallback); 부분에 메소드 이름에 빨간색 줄이 쳐져 있는걸 보게 될 것이다.
API level 21부터는 BluetoothAdapter 클래스에서 스캔을 하지 않고 BluetoothLeScanner라는 클래스가 추가되어 이 클래스의 startScan(), stopScan() 메소드를 사용해 BLE 디바이스를 검색하도록 변하였다.
콜백 역시 Bluetooth.LeScanCallback 대신 ScanCallback 클래스를 사용해야 한다.

그러므로 android-BluetoothLeGatt 예제를 API level 21의 변화에 맞게 수정하려면 다음과 같다.

먼저 사용할 클래스  import를 추가한다. DeviceScanActivity.java 소스코드 위쪽의 import 부분에 아래 내용을 추가한다.

import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import java.util.List;

다음 DeviceScanActivity 클래스의 private 멤버 선언부에 밑줄 친 빨간색 내용을 추가한다.

public class DeviceScanActivity extends ListActivity {
    private LeDeviceListAdapter mLeDeviceListAdapter;
    private BluetoothAdapter mBluetoothAdapter;
    private boolean mScanning;
    private Handler mHandler; 
    private BluetoothLeScanner mBLEScanner;

다음 onCreate 메소드의 맨 뒤쪽 부분에 다음 줄을 추가해 BluetoothLeScanner 클래스의 인스턴스를 얻는다.

    if (mBluetoothAdapter == null) {
        Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
        finish();
        return;
    }
    mBLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
    // Checks if Bluetooth LE Scanner is available.    if (mBLEScanner == null) {
        Toast.makeText(this, "Can not find BLE Scanner", Toast.LENGTH_SHORT).show();
        finish();
        return;
    }
}

다음은 onListItemClick() 메소드와 scanLeDevice() 메소드 안에 있는 빨간색 줄이 쳐져 있는 startLeScan(), stopLeScan() 부분을 각각 다음 내용으로 바꿔준다.

mBLEScanner.startScan(mScanCallback); // mBluetoothAdapter.startLeScan() 부분
 
mBLEScanner.stopScan(mScanCallback);  // mBluetoothAdapter.stopLeScan() 부분

그리고 기존의 콜백 정의부분은 없애고 다음의 내용을 추가한다.

즉 아래 부분은 통채로 삭제

// Device scan callback.private BluetoothAdapter.LeScanCallback mLeScanCallback =        new BluetoothAdapter.LeScanCallback() {
    @Override    public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {        runOnUiThread(new Runnable() {            @Override            public void run() {                mLeDeviceListAdapter.addDevice(device);                mLeDeviceListAdapter.notifyDataSetChanged();            }        });    }};
 
위쪽 내용을 삭제한 부분에 아래 내용을 추가


    private ScanCallback mScanCallback = new ScanCallback() {
        @Override        public void onScanResult(int callbackType, ScanResult result) {
            processResult(result);
        }

        @Override        public void onBatchScanResults(List<ScanResult> results) {
            for (ScanResult result : results) {
                processResult(result);
            }
        }

        @Override        public void onScanFailed(int errorCode) {
        }

        private void processResult(final ScanResult result) {
            runOnUiThread(new Runnable() {
                @Override                public void run() {
                    mLeDeviceListAdapter.addDevice(result.getDevice());
                    mLeDeviceListAdapter.notifyDataSetChanged();
                }
            });
        }
    };
















2015년 9월 9일 수요일

라즈베리 파이 GPIO를 고속으로 제어하기



라즈베리 파이에서 GPIO 핀을 제어하는 가장 쉬운 방법은 sysfs를 사용하는 것이다.

쉘에서라면 다음과 같은 식으로 GPIO 핀을 제어할 수 있다.

$ echo "4" > /sys/class/gpio/export
$ echo "out" > /sys/class/gpio/gpio4/direction
 
# Set up GPIO 7 and set to input
$ echo "7" > /sys/class/gpio/export
$ echo "in" > /sys/class/gpio/gpio7/direction
 
# Write output
$ echo "1" > /sys/class/gpio/gpio4/value
 
# Read from input
$ cat /sys/class/gpio/gpio7/value 
 
# Clean up
$ echo "4" > /sys/class/gpio/unexport
$ echo "7" > /sys/class/gpio/unexport
 
프로그램 상에서라면 '/sys/class/gpio/' 디렉토리 내의 파일들을 open한 후 read/write로 GPIO를 제어할 수 있다.

이렇게 sysfs를 사용하면 매 비트를 조작할 때 마다 오버헤드가 커서 라즈베리 파이2에서 단순히 GPIO 핀을 H/L로 토글하는데도 6.8KHz로밖에 동작할 수 없다.

하지만 라즈베리 파이2에 들어있는 SoC인 BCM2836의 GPIO레지스터를 직접 조작하면 무려 50MHz의 속도로 GPIO핀을 토글할 수 있게 된다. (약 7350배의 속도 향상) 게다가 sysfs를 사용하는 경우는 한번에 한 비트씩만 조작이 가능하지만 GPIO 레지스터를 조작하면 한번에 여러 비트의 GPIO 조작이 가능하기 때문에 더욱 더 고속 제어가 가능해진다.

BCM2836의 경우 I/O제어 레지스터는 0x3f20 0000 ~ 0x3f20 00b0의 메모리 공간에 매핑되어 있다.

리눅스에서 프로세스는 하드웨어나 물리 주소에 직접 접근이 불가능하다. 그러므로 이 문제를 해결하기 위해 사용되는 것이 '/dev/mem' 디바이스이다. /dev/mem은 메모리 공간에 해당하는 가상 파일이다. 그러므로 이 파일을 오픈해서 파일에 read/write하면 파일이 매핑되어 있는 메모리 공간에 값을 읽고 쓸 수 있게 되는 것이다.

그런데 파일에 값을 read/write 하는건 귀찮기 때문에 등장하는 것이 mmap 함수이다. mmap은 파일의 일부를 메모리처럼 억세스 할 수 있게 해 준다. C에서 말하자면 파일의 특정 장소를 포인터로 지시해 그 포인터를 통해 직접 읽고 쓰기가 가능해진다.

아래 코드가 라즈베리 파이2에서 GPIO 레지스터에 대한 포인터를 얻어오는 함수이다.

unsigned int *get_base_addr()
{
  int fd=open("/dev/mem/", O_RDWR | O_SYNC);
  if (fd<0) {
    printf("can not open /dev/mem\n"); exit(-1);
  }
  #define PAGE_SIZE (4096)
  void *mmaped = mmap(NULL,
                      PAGE_SIZE,
                      PROT_READ | PROT_WRITE,
                      MAP_SHARED,
                      fd,
                      0x3f200000);
  if (mmaped<0) {
    printf("mmap failed\n"); exit(-1);
  }
  close(fd);
  return (unsigned int *)mmaped;
}

 
주의) 라즈베리 파이2는 BCM2836 SoC를 사용하는데 여기서는 GPIO 레지스터가 0x3f20 0000번지에 
매핑되어 있지만 라즈베리 파이1의 BCM2835는 0x2020 0000번지에 매핑되어 있다.
즉 라즈베리 파이1에서 사용하려면 위의 함수에서 0x3f200000을 0x20200000으로 변경 해 줘야만 한다.
 
포인터를 얻었으면 사용하고자 하는 GPIO 핀을 입력 또는 출력으로 사용할 지 설정해 줘야 한다. 
각 I/O 포트의 설정은 3비트 값을 사용한다. 32비트 레지스터에 각각 10개 I/O핀을 설정한다.
 
예를 들어 GPIO0의 설정 레지스터는 0x3f20 0000 번지의 32비트 값 중 비트 2~0, GPIO11의 
설정 레지스터는 0x3f20 0004 번지의 32비트 값 중 비트 5~3이 된다.
공식화 하면 GPIO p의 설정 레지스터는 0x3f200000+(p/10) 번지의 비트 (p%10)*3+2~(p%10)*3가 된다.
 
아래 코드는 포트 설정을 위한 함수이다. 
 
void gpio_mode(unsigned int *addr, int port, int mode)
{
  if (0<port || port >31) {
    printf("port out of range: %d\n", port);
    exit(-1);
  }
  unsigned int *a = addr + (port/10);
  unsigned int mask = ~(0x7 << ((port%10) * 3));
  *a &= mask;
  *a |= (mode & 0x7) << ((port%10) * 3);
}  
 
포트 설정이 되었으면 이제 포트 값을 H/L로 변경할 수 있다. 
여기서는 GPIO핀을 H로 만들때 사용하는 레지스터와 L로 만들때 사용하는 레지스터가 따로 있다. 
즉 H로 만들 때 사용하는 레지스터에 '1'을 써 넣은 포트만 값이 H로 바뀌고 나머지 포트는 값이 
그대로 유지된다.
마찬가지로 L로 만들 때 사용하는 레지스터에 '1'을 써 넣은 포트만 값이 L가 되고 나머지 비트는 
값에 변화가 없게 된다.
예를 들어 GPIO0의 값을 H로 하고 싶으면 0x3f20 001c 번지에 0x0000 0001을 써 주면 GPIO0만 
H가 되고 나머지 GPIO는 값의 변화가 없다.
동일하게 GPIO1의 값을 L로 하고 싶으면 0x3f20 0028 번지에 0x0000 0002를 써 주면 된다. 
이렇게 레지스터를 이용하면 한번에 여러 비트를 H로 만들거나, 여러 비트를 L로 만들어 줄 수 있다.
 
void gpio_set(unsigned int *addr, int port)
{
  if (0<port || port>31) {
    printf("set: port out of range: %d\n", port);
    exit(-1);
  }
  *(addr+7) = 0x1 << port;
}

void gpio_clear(unsigned int *addr, int port)
{
  if (0<port || port>31) {
    printf("clear: port out of range: %d\n", port);
    exit(-1);
  }
  *(addr+10) = 0x1 << port;
} 
 

위의 함수들을 사용해 GPIO0를 제어하는 코드의 틀은 다음과 같다.
 

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mmap.h>
#include <unistd.h>

#define GPIO_IN     0
#define GPIO_OUT    1

unsigned int *get_base_addr();
void gpio_mode(unsigned int *, int, int);
void gpio_set(unsigned int *, int);
void gpio_clear(unsigned int *, int);

int main(int argc, char **argv)
{ 
  volatile unsigned int *addr = get_base_addr();
 
  ... 
  gpio_mode(addr, 0, GPIO_OUT);
  ...
  ...
  gpio_set(addr, 0);
  ... 
  gpio_clear(addr, 0);
  ...
}
 

참고로 라즈베리 파이2에서 모든 함수 호출이나 딜레이 없이 가장 빨리 GPIO 포트를 토글하는 경우 
최대 50MHz로 스위칭이 가능하다.
 
  ...
  for (;;) {
    *(0x3f200000+7) = 1<<0;   // GPIO 0 set
    *(0x3f200000+10) = 1<<0;  // GPIO 0 clear
  } 
  ...
 

2015년 9월 6일 일요일

라즈베리파이2에서 NEON을 사용하기 위해 gcc-5.1.0 컴파일하기

라즈베리 파이에 사용된 프로세서에는 NEON이라는 SIMD (Single Instruction Multiple Data)처리 유닛이 들어있다. 이 NEON을 사용하면 최대 128비트까지 동시에 처리가 가능해 지기 때문에 프로그램 실행에 상당한 성능 향상을 가져올 수 있게 된다. x86계열로 치면 MMX, SSE, AVX 명령등에 이에 해당한다.


char 타입의 연산인 경우 약 5배, short 타입 연산은 약 3배, int 타입 연산은 약 1.7배, float 타입 연산은 약 2.3배 정도로 실행 속도가 향상된다. 즉 반복적인 연산이 많은 scientific computation이나 signal processing, 암호화, 압축, 인코딩 등에 사용하면 매우 유용하다.



부동 소수점만 보면 단정도(single precision)의 경우 라즈베리 파이1이 약 70 MFlops, 라즈베리 파이2에서 NEON을 사용하지 않으며 약 150 MFlops, NEON을 사용하면 약 310 MFlops의 속도가 나온다. 부동 소수점 배정도(double precision)의 경우 라즈베리 파이1이 약 35 MFlops, 라즈베리 파이2에서는 약 150 MFlops정도의 속도가 나오게 된다. (부동소수점 연산에서 배정도의 경우 NEON이 지원하지 않음)

단 컴파일 시 NEON 명령을 지원하도록 하려면 현재 distribution에 포함되어 있는 gcc로는 안되고 NEON을 지원하도록 gcc-5.1.0을 설치해 줘야 한다.

먼저 gcc-5.1.0 소스코드를 다운로드 한다.

$ curl -O http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-5.1.0/gcc-5.1.0.tar.bz2
$ tar xvf gcc-5.1.0.tar.bz2

gcc를 컴파일 하기 위해서는 디폴트 swap 파티션 용량이 부족하기 때문에 swap 파티션 용량을 늘려줘야 한다.

$ sudo dd if=/dev/zero of=/swapfile bs=1M count=2048
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile

현재 apt-get으로 설치되는 autoconf 버젼은 2.69인데 gcc-5.1.0은 2.64를 사용하도록 설정되어 있다. 그래서 이걸 2.69를 사용하도록 수정해 줘야 한다.

(1) gcc-5.1.0 디렉토리에 있는 configure.ac 파일 안의 AC_PREREQ 값을 '2.64'에서 '2.69'로 수정
(2) gcc-5.1.0/config 디렉토리에 있는 override.m4 파일 안의 _GCC_AUTOCONF_VERSION 값을  '2.64'에서 '2.69'로 수정

GCC-5.1.0에서는 병렬화를 위해 언어확장으로 cilk를 표준으로 사용하므로 cilk도 활성화 시켜 줌

(1) gcc-5.1.0/libcilkrts 디렉토리에 있는 configure.tgt 안의 UNSUPPORTED=1 을 앞에 '#'를 붙여 comment out 시킴 ('1'을 '0'으로 바꾸는건 안됨)
(2) gcc-5.1.0/libcilkrts/runtime/config/generic 디렉토리에 있는 cilk-abi-vla.c 안의 vla_internal_heap_free 함수를 호출하는 부분에서 첫번째 인자를 't'에서 'p'로 수정
(3) gcc-5.1.0/libcilkrts/runtime/config/generic 디렉토리에 있는 os_fence.h 안의 __cilkrts_fence의 정의 부분의 맨 앞에 '//'를 추가해 comment out 시킴
COMMON_SYSDEP void __cilkrts_fence(void); /// < MFENCE instruction
(4) gcc-5.1.0/libcilkrts/runtime/config/generic 디렉토리에 있는 os_fence.h 파일에 아래줄을 추가
#define __cilkrts_fence() __asm__ volatile ("DSB")

빌드를 위해 GMP, MPFR, LIBMPC가 필요하므로 설치해 줌

$ sudo apt-get install libgmp-dev libmpfr-dev libmpc-dev

빌드를 시작

$ mkdir b; cd b
$ ../configure --enable-languages=c,c++ \
--prefix=/usr/local/gcc-5.1.0 \
--target=arm-linux-gnueabihf \
--with-arch=armv7-a \
--with-fpu=vfp \
--with-float=hard \
--build=arm-linux-gnueabihf \
--host=arm-linux-gnueabihf
$ make
$ sudo make install

설치가 완료된 후 gcc-5.1.0을 사용하도록 환경변수를 설정해 준다. '.profile' 또는 '.bashrc' 파일의 맨 마지막에 다음 두 줄을 추가한다.

export PATH=/usr/local/gcc-5.1.0/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/gcc-5.1.0/lib:$LD_LIBRARY_PATH

라즈베리파이1에서 컴파일 하는 경우 configure 옵션을 다르게 줘야 한다.

$ ../configure --enable-languages=c,c++ \
--prefix=/usr/local/gcc-5.1.0 \
--target=arm-linux-gnueabihf \ 
--with-fpu=vfp \
--with-float=hard \
--build=arm-linux-gnueabihf \
--host=arm-linux-gnueabihf

빌드에는 상당한 인내심이 필요하다. 'make' 명령으로 빌드하는데 라즈베리파이2에서 대략 30시간 정도 걸렸다.

바로 사용하고 싶은 사람들은 아래 링크에서 컴파일 된 gcc-5.1.0을 받아 /usr/local 디렉토리에 풀어주고 환경변수 설정만 하면 된다.

gcc-5.1.0.compiled_for_rpi2.tgz

2015년 8월 27일 목요일

Pillar bracket으로 선반이나 가벽 DIY

Pillar bracket은 집을 꾸미려고 하는데 벽에 구멍을 내거나 못을 함부로 박아 넣기 힘든 경우 매우 유용하게 사용할 수 있는 부품이다. 


벽에 기둥을 세우려면 천장 높이의 목재를 구해야 하는데 정확하게 자르기도 쉽지 않고 끼워 넣어도 약간 틈이 생겨 헐겁게 되기 쉽다. 이 때 pillar bracket을 사용하면 목재를 천장 높이보다 약 5cm 정도 짧게 자른 후 목재 위쪽에 pillar bracket을 끼워주고 나사를 돌려 높이를 조절하면 된다.



위의 케이스 사진에서 볼 수 있는것처럼 집에 가벽 또는 선반등을 깔끔하고 손 쉽게 만들 수 있다.

목재는 투 바이 포 규격을 사용하면 된다. 나는 이번에 알게 된 사실이지만 투 바이 포가 2 inch X 4 inch를 말하는게 아니고 목재에 사용되는 별도의 규격 단위이다. 밀리미터 단위로 표현하면 38mm X 89 mm가 된다.




이런식으로 되어 있어 목재를 끼워 넣은 다음 나사로 고정할 수 있게 되어 있다.


위쪽은 펠티어가 붙어 있어 천장에 딱 고정되어도 상처를 내지 않도록 신경을 썼다.








일본에서만 판매되고 있고 가격은 개당 1800엔(소비세 8% 별도)으로 좀 비싼 편인게 아쉽긴 하다. 다만 재미있는건 일본 내에서 주문하면 우송료가 별도인데 해외에서 주문하면 우송료가 무료이다. 일본에서 EMS로 오다 보니 주문하고 2~3일이면 제품을 받을 수 있다.

http://www.pinkflag.me/pillarbracket/index.html

아래는 pillar bracket으로 만든 선반이나 가벽 샘플들이다.




2015년 8월 20일 목요일

Blynk를 사용해 아두이노에서 IoT 맛보기

Blynk (http://www.blynk.cc)를 사용하면 아두이노를 스마트폰이나 PC와 연결하여 매우 쉽게 제어가 가능해진다. 물론 아두이노 뿐 아니고 ESP8266이나 Raspberry Pi등에서도 사용이 가능하다. 현재 사용할 수 있는 하드웨어 목록은 http://community.blynk.cc/t/hardware-supported-by-blynk/16 에서 확인할 수 있다.


여기서는 아두이노 우노에 CC3000 Wifi shield를 사용해 보도록 한다.



먼저 CC3000 Wifi shield를 위해 Adafruit_CC3000 Library를 설치해준다. https://github.com/adafruit/Adafruit_CC3000_Library 에서 다운받을 수 있다. (라이브러리 설치 방법은 이전 포스트 (http://arsviator.blogspot.kr/2015/07/install-library-in-arduino.html)

다음은 Blynk 라이브러리를 설치해 줘야 한다. https://github.com/blynkkk/blynk-library 에서 다운받으면 된다.

정상적으로 설치를 완료했으면 아두이노를 재시동하고 예제 -> Blynk -> BoardsAndShields 에서 CC3000을 선택한다.


코드 구조는 매우 간단하다. 단 코드중에 아래 표시한 부분은 각자 자신에 맞게 변경해 줘야만 한다.


일단 여기까지 진행하였으면 아두이노는 일단 잠시 놔두고 스마트폰을 설정해 준다.



Blynk 홈페이지에 보면 iOS와 안드로이드용 앱을 다운받을 수 있는 링크가 있다. 자신의 폰에 맞는걸 선택해 자신의 폰에 Blynk 앱을 설치해 준다.


앱을 실행해 서비스에 가입해주면 된다. 가입 후 로그인 되면 다음과 같은 초기화면을 볼 수 있을 것이다.


Create New Project를 눌러 새 프로젝트를 생성한다.


프로젝트 이름(여기서는 FirstTest)을 써주고, 사용할 보드(여기서는 아두이노 우노)를 선택한다. 그 아래쪽의 Auth Token이 아두이노 스케치 코드에 넣어줘야 하는 값이다. E-mail 버튼을 누르면 가입시 사용한 메일주소로 이 토큰값이 메일로 보내지기 때문에 메일에서 복사해 붙이면 된다. 이제 Create Project 버튼을 눌러주면 프로젝트가 만들어진다.


이제 화면을 디자인해야 하므로 화면 검은색 부분중에 원하는 부분을 터치한다.


그럼 위와 같이 설치할 수 있는 위젯 목록이 나타난다. 여기서는 단순히 아두이노에 있는 LED on/off를 제어하기 위해 Button을 선택한다.


원하는 위치에 버튼이 추가되었다. 이제 버튼의 설정을 변경해 주기 위해 버튼을 터치한다.


버튼 설정 화면이다. 여기서 버튼의 이름(여기서는 LED)을 지정하고, 이 버튼이 아두이노의 어떤 핀과 연동될 것인지 알려준다. (아두이노 보드에 붙어있는 LED는 D13에 연결되어 있으므로 Digital에 D13을 선택)


설정이 끝나면 스위치의 이름과 연동된 핀 번호가 스위치에 표시된다. 디자인이 다 끝나고 실제 동작을 시키려면 맨 오른쪽 위의 삼각형 버튼을 선택해주면 된다.


아두이노 코드에 Auth Token 값을 넣어주고, setup() 함수에 pinMode(13, OUTPUT); 을 추가한 다음 아두이노에 업로드 하면 된다.

업로드 후 시리얼 모니터를 열어봤을 때 다음과 같이 출력되면 된다.


이제 스마트폰의 버튼을 눌러보면 그에 따라 아두이노의 LED가 on/off 되는걸 볼 수 있을 것이다.

Blynk에는 단순히 스위치 뿐 아니고 슬라이드, 조이스틱, 그래프등의 다양한 위젯이 있어 스마트폰에서 아두이노에 연결된 액츄에이터를 제어하거나, 반대로 아두이노에 연결되어 있는 센서들의 상태를 다양한 방법으로 확인이 가능하다. 또한 Push notification이나 Twitter 위젯도 있어 아두이노가 특정 상태가 되면 폰으로 push 메시지를 보내거나 트위터에 메시지를 트윗하는것도 가능하다. 다음 포스트에서 blynk를 좀 더 다양하게 사용하는걸 다뤄보도록 하겠다.

























2015년 8월 19일 수요일

아두이노에서 u8glib로 0.96" OLED 사용하기

Aliexpress에서 $4~5에 구입할 수 있는 0.96" 128*64 monochrome OLED이다. I2C 방식을 사용하기 때문에 배선도 매우 간단하다. (컨트롤러로는 SSD1306)


아두이노에서 이 모듈을 사용하기 위해 여기서는 u8glib를 사용한다. 먼저 라이브러리를 다운받아 설치해 줘야 한다. 다운로드를 위한 주소는 https://bintray.com/olikraus/u8glib/Arduino 이다.  

아두이노에 외부 라이브러리를 설치하는 방법은 이전 포스트(http://arsviator.blogspot.kr/2015/07/install-library-in-arduino.html)를 참조하면 된다. 

배선은 매우 단순하다. Vcc는 +3.3V에, 나머지는 아두이노의 동일한 이름의 핀에 연결해 주면 된다. 


아두이노 우노의 경우 A4/A5가 각각 SDA/SCL이므로 위와 같이 연결해 주면 된다. 또는 좌측 상단에도 동일한 핀이 있으므로 SDA/SCL을 A4/A5대신 위쪽에 연결해 줘도 상관없다. 

이제 아두이노를 실행해 예제->U8glib를 보면 여러가지 예제 코드가 들어있다. 그 중 가장 기초인 HelloWorld를 선택해보자. 코드를 보면 위쪽에 엄청나게 많은 constructor가 comment out (//) 되어 있다. 



 U8glib는 여러가지 타입의 컨트롤러/해상도/통신방식을 지원하기 때문에 각각 자신이 사용하는 디스플레이에 맞는 constructor를 선택해줘야만 한다. 여기서는 "U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_FAST); // Dev 0, Fast I2C / TWI" 를 사용하면 된다. 즉 해당 라인 앞의 '//'를 없애고 나머지 라인은 다 지워주면 된다. 


디스플레이는 128*64 픽셀의 해살도를 가지고 X축 좌표는 왼쪽부터 오른쪽으로 0 ~ 127, Y축 좌표는 위쪽부터 아래쪽으로 0~63이 된다. 특정 픽셀의 좌표를 지정할 때는 (x, y)로 표현한다.


이제 아두이노에 다음 프로그램을 넣고 업로드 해 준다.

#include "U8glib.h"

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 |
                          U8G_I2C_OPT_NO_ACK |
                          U8G_I2C_OPT_FAST); 

void setup() 
{
  u8g.setFont(u8g_font_unifont);   
  u8g.setColorIndex(1);  

void loop() 
{    
  u8g.firstPage();   
  do {      
    draw();   
  } while( u8g.nextPage() );   
  delay(1000);   
}   

void draw()
{   
  u8g.drawStr( 0, 20, "Hello World");     
}

정상적으로 컴파일이 끝나고 업로드가 되었다면 화면에 다음과 같이 출력될 것이다. 



코드를 보면 setup()에서 가장 먼저 어떤 폰트를 사용할지 지정해 주었다. 라이브러리는 여러가지 폰트를 지원한다. 그 후 u8g.setColorIndex(1); 로 무언가를 그린다는건 픽셀을 켜는 것이라는걸 알려준다. 

u8glib는 picture loop라는 구조를 사용한다. Picture loop는 u8g.firstPage()로 시작해서 u8g.nextPage()로 끝난다. Picture loop 내에서 여러가지 그래픽 명령을 사용할 수 있다. 위의 예제에서는 draw()를 호출했다. draw() 함수에서 화면에 'Hello World'를 표시하라고 지시한다. 

* drawStr()을 호출할 때는 문자열을 표시할 위치를 지정해 줘야 한다. 위의 예제에서는 (0, 20)을 사용하였다. 이 좌표는 문자열의 좌측 하단의 위치가 된다. 




위의 코드에서 drawStr의 좌표를 0, 20에서 -20, 20으로 바꿔서 실행시켜 보면 다음과 같은 화면을 볼 수 있게 된다. 



void draw()
{   
  u8g.drawStr( -20, 20, "Hello World");     
}

대부분의 u8glib 함수들은 화면 바깥의 좌표도 허용한다. (이 모듈의 경우 X축 좌표가 0보다 작거나  127보다 큰 경우, Y축 좌표가 0보다 작거나 63보다 큰 경우)

* X 좌표가 0보다 작으면 텍스트나 도형이 화면 왼쪽으로 쉬프트되어 짤려나감
* X 좌표가 127보다 크면 텍스트나 도형이 화면 오른쪽으로 쉬프트되어 짤려나감
* Y 좌표가 0보다 작으면 텍스트나 도형이 화면 위쪽으로 쉬프트되어 짤려나감
* Y 좌표가 63보다 크면 텍스트나 도형이 화면 아래쪽으로 쉬프트되어 짤려나감

텍스트 스크롤

화면 바깥 좌료를 허용해주는 것을 이용하면 텍스트나 도형 스크롤등에 매우 유용하다. 

#include "U8glib.h"

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 |
                          U8G_I2C_OPT_NO_ACK |
                          U8G_I2C_OPT_FAST); 
                          
int yPos = 0; 

void setup() 
{
  u8g.setFont(u8g_font_unifont);   
  u8g.setColorIndex(1); 

void loop() 
{    
  u8g.firstPage();   
  do {      
    draw();   
  } while( u8g.nextPage() );     
  
  if (yPos < 83) {          
    yPos++;  
  } else {          
    yPos = 0;   
  }  
}  

void draw()
{   
  u8g.drawStr( 0, yPos, "Hello World");    
}

위의 코드를 실행시켜 보면 Hello World 라는 글자가 위에서 아래쪽으로 스크롤 되는걸 확인할 수 있다.




* Henry's bench의 http://henrysbench.capnfatz.com/henrys-bench/u8glib-arduino-oled-tutorial-1-hello-world-on-steroids/ 를 기반으로 작성한 포스팅임. Thanks for great tutorial, Henry.



2015년 8월 10일 월요일

아두이노에서 여러개의 스위치를 1개의 analog input핀으로 검사하기

아두이노를 사용하다 보면 Digital I/O핀이 모자라게 되는 경우가 있다. 이럴 때 1개의 analog input 핀으로 여러개의 스위치 입력을 받을 수 있다.
이 방식을 이해하려면 가장 먼저 이해해야 하는 것이 voltage divider 회로이다. 




Vin(여기서는 +5V)과 GND 사이에 두개의 저항이 있으면 두 저항 사이의 전압은 두 저항값의 비율로 결정되게 된다.



R1=1K, R2=1K인 경우




R1=1K, R2=4K인 경우


R1=4K, R2=1K인 경우


그러므로 이 성질을 이용해 여러개의 스위치를 연결하는데 각각의 스위치에 저항값이 다른 저항을 붙여주면 각 스위치를 누를 때 마다 출력되는 전압값이 달라지게 되므로 그 전압값을 읽어보면 어떤 스위치가 눌렸는지 판단이 가능해진다.



만일 S1이 눌리면 R1=82K, R2=100K가 되고, S5가 눌리면 R1=10K, R2=100K가 된다. 즉 각각의 스위치가 눌린 경우 출력전압과 그에 해당하는 ADC 값을 계산하면 다음과 같다. (아래 표는 Vin이 3.3V일 때의 계산결과임. 하지만 Vin이 5V이건 3.3V이건 ADC value는 변하지 않음)




그러므로 analogRead()로 읽은 값이 대략 910~950 범위 내의 값이면 S5가 눌렸다고 판단하고, 820~860 범위 내의 값이면 S4가 눌렸다고 판단하면 된다. 

또한 각각의 저항값을 잘 결정해주면 동시에 2개의 버튼이 눌렸을 때도 어떤 버튼이 눌렸는지 판단할 수 있다.