사운드 센서 모듈과 인터럽트 (2014-01-08)

소리로 전등을 켜고 끄거나 또는 주차 중인 차 안에 설치하여 주변에 차가 움직이는 소리가 나면 카메라가 동작하는(심지어는 소리가 나는 곳으로 카메라가 향하도록 서보를 동작시키는 등) 기능을 구현해 보기 위해 우선 간단한 사운드 센서 모듈을 DX에서 구입해 보았다.

하드웨어)

P20140108_222845234_55FD4FCB-5211-4B21-8561-5522A1A4DC47

  • 소리를 감지하는 소형 마이크, 감도를 조절하는 노브, 그리고 아두이노 등과 연결하는 3 개의 핀으로 이루어짐
  • 신호 핀은 위 사진에서 가운데 OUT에 해당하는 핀 하나이며, 소리를 감지한 경우 HIGH에서 LOW로 바뀐다!

P20140108_213555011_64DED0E0-949E-44B3-8E3B-353964B96BFF

  • OUT 핀을 아두이노 UNO 보드의 D2에 연결
  • 나머지 5V, GND도 마무리 연결

구현)

  • 가장 간단하게는 loop() 함수 내에서 적당한 delay를 주며 센서 OUT 핀 값을 digitalRead() 하고 그 값이 LOW 인지 체크하는 방법이 있다.
  • 그러나, 이 방식은 delay가 너무 긴 경우, 사운드 발생 이벤트를 놓칠 가능성이 있다. 그렇다고 delay를 너무 짧게 주면 digitalRead() 호출이 너무 많은 문제점을 갖고 있다. 특히 배터리로 운영하는 경우에는 배터리가 빨리 닳을 것이다.
  • 언제 어떤 빈도로 발생할 지 모르는 이벤트를 감지하는 가장 좋은 방법은 바로 인터럽트(Interrupt)다.
  • 인터럽트가 발생하면 loop() 함수를 잠시 멈추고 인터럽트 루틴으로 점프했다가 다시 돌아온다.

아두이노 인터럽트 설명)

  • 인터럽트는 디지털 핀을 사용한다.
  • 그러나, 모든 핀이 인터럽트를 지원하지는 않고 각 보드마다 특정 핀에서만 지원한다.
  • 아두이노 UNO를 포함한 대부분의 아두이노 보드는 2 개의 디지털 핀을 지원한다.
  • 인터럽트 번호라는 새로운 개념을 사용하며 이는 핀 번호와 무관하다. 번호는 0번부터 시작한다.
  • 아두이노 UNO에서 인터럽트 번호와 디지털 핀 번호의 관계는 다음과 같다:
    • 인터럽트 0번 –> 디지털 핀 2번
    • 인터럽트 1번 –> 디지털 핀 3번
  • 한편, 레오나르도는 5 개의 핀, 메가2560은 6 개의 핀에서 인터럽트를 지원한다.

소프트웨어)

volatile int state = 0;

void detectSound() {
  state = 1;
}

void setup() {
  Serial.begin(9600);
  attachInterrupt(0, detectSound, LOW); // on Uno, D2
}

void loop() {
  if (state == 1) {
    Serial.println("Sound!");
    state = 0;
  }
  delay(100);
}
  • 인터럽트 등록 함수 attachInterrupt(0, detectSound, LOW)의 의미는 다음과 같다:
    • 0: 인터럽트 0번 사용
    • detectSound: 인터럽트가 발생하면 detectSound() 함수 호출
    • LOW: 인터럽트와 연관된 디지털 핀의 상태가 LOW가 되면 인터럽트 발생
  • 인터럽트 서비스 루틴(ISR)인 detectSound()에서는 전역 변수 state 값만 변경하고 신속하게 함수를 끝낸다. 이것이 일반적인 관례다.
  • loop() 함수에서는 detectSound() 함수가 변경했을 지 모르는 state 변수를 보고 그에 알맞는 행위를 한다.

참고 자료)

I2C LCD 모듈 (2013-12-18)

표준적인 문자 표시 LCD를 Arduino나 RPi에 붙이는 것이 얼마나 번거로운 일인지는 해 본 사람만이 안다.

하드웨어)

IMG_20131019_170243

위 사진은 전형적인 히타치(Hitachi) HD44780 또는 그 호환되는 LCD를 Arduino에 연결한 모습이다.

다음은 I2C 지원하는 LCD 모듈을 연결한 모습이다.

P20131220_092111495_2FA643E7-B957-4C92-8656-79F7EE30EFEA

I2C 방식 연결이므로 2개의 전원 연결을 제외하고 SDA, SCL 2개의 연결 뿐이다.

다음은 I2C LCD 모듈의 뒷면 화면이다. 오리지널 LCD의 16개 핀에 LCM1602 IIC V1이라는 장치가 붙어 있다. 바로 이 장치가 모든 복잡한 배선 그리고 각 배선에 보내는 신호를 단순화해 주고 있다.

P20131220_092131768_E908BF33-7D6C-42B0-9AD1-216E420FF7B8

위에서 보는 것처럼 LCD의 뒷쪽 연결핀에 추가 모듈을 붙인 형태이므로 LCD “Backpack”이라 부른다. LCD가 “백팩” 즉 가방을 메고 있는 모습을 표현한 것이다.

  • 이 제품 Backlight ON/OFF를 소프트웨어적으로 제어하지 않고 점퍼를 가지고 제어한다.
  • CONTRAST는 파란색 가변저항을 드라이버로 돌려서 설정한다.
  • I2C 주소는 기본 설정값이 0x27
  • 나머지 납땜 가능한 부분들은 문서를 찾지 못해 알 수 없는 상태이다.

소프트웨어)

문제는 하드웨어가 아니고 소프트웨어다. I2C LCD는 Arduino IDE에 포함되어 있는 LiquidCrystal 라이브러리 및 예제를 사용할 수 없다.

이 I2C LCD 모듈을 사용하려면 F Malpartida (스페인 개발자?) 제작의 New LiquidCrystal 라이브러리를 사용해야 한다.

위 페이지에서 설명을 읽은 후, Downloads 링크에서 최신 라이브러리 zip 파일을 다운로드받아 Arduino library 디렉토리에 넣는다. (2013년 12월 현재 LiquidCrystal_V1.2.1.zip)

이 라이브러리는 기존 라이브러리를 확장한 것으로서, 기능 중복이 있으므로 Arduino IDE에 내장된 LiquidCrystal 라이브러리를 제거해야 한다.

다음 예제 코드는 New LiquidCrystal 사이트의 예제 코드에서  LiquidCrystal_I2c 객체의 초기화 값만 본인이 구입한 제품에 맞도록 변형한 것이다.

  • I2C 기본 설정 주소는 0x27 즉 16진수 27이다.
  • 그 다음 값들은 내부적인 LCD 핀 연결 값들이며, 표준 LCD의 핀 이름 E, RW, RS, D4, D5, D6, D7,  Bklt 등의 순서를 가진다.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
// (i2c address, E, RW, RS, D4, D5, D6, D7, Backlight, Backlight Polarity)
// https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/schematics#!hardware-configurations-and-initialization
// Other Configurations - DFRobot

void setup()
{
  lcd.begin(16, 2); // 16x2 모듈 즉 한 줄에 16개의 문자, 2줄

  lcd.home(); // 홈 좌표 (0, 0)으로 이동
  lcd.print("Hello, Arduino");  
  lcd.setCursor(0, 1); // (행, 열). 1번행의 0번열로 커서 이동. (인덱스 시작번호가 0이므로 두번째 줄 첫번째 열)
  lcd.print("Let's go");      
}

void loop()
{
  lcd.clear();
  lcd.home();
  lcd.print(millis()/1000);
  delay(1000);
}

나머지 LCD를 초기화하고 커서를 이동하고 출력하고 화면을 지우는 함수는 기본 LiquidCrystal 라이브러리와 동일하다:

  • begin(열수, 행수): 가장 일반적이고 각종 키트에 들어있는 저가 LCD는 16×2
  • setCursor(열, 행): 커서 이동
  • home(): (0, 0)으로 이동
  • print(…): 현재 커서 위치에서 문자 출력
  • clear(): 화면 지우기

참고 자료)

Arduino Bluetooth와의 첫만남 (2013.11.16)

“우리는 이미 무선의 시대에 살고 있다.  무선화 기술이 발전해 나감에 따라 좀 더 재미있고 좀 더 많은 가능성이 열렸다. 이는 Arduino와 같은 마이크로컨트롤러 프로젝트에서도 예외는 아니다.  간단하게는 Arduino와 온습도 센서 등을 야외에 설치하고 주기적으로 온도와 습도 값을 중앙의 데이터베이스에 전송한다.  또는 PC나 스마트폰으로부터 명령을 받아 움직이는 로봇을 만들 수 있다.

Arduino 프로젝트를 무선화하는데는 다음과 같은 여러 가지 선택권이 존재한다:

  • WiFi
  • XBee
  • Bluetooth

이 중에서 특히 Bluetooth는 모든 노트북, 그리고 결정적으로 스마트폰에 탑재되어 있는 통신 방식이라는 강점을 가지고 있다.

  • WiFi는 가장 강력한 전송 속도를 자랑하지만 그 만큼 전력 소모가 많아 배터리로 동작하는 센서들에는 부적합하다.
  • 반면, XBee는 저전력 센서에 적합하지만 PC나 스마트폰에도 동일한 XBee 모듈이 있어야 하기 때문에 범용적이지 않다.

P20131116_013014845_8A274148-FBC4-43ED-ACED-10105C903FC4

(위 사진은 블루투스 시리얼 통신을 통해 릴레이(Relay)를 ON/OFF하는 예제다.)

Arduino 로봇 프로젝트에 사용하기 위해 DX에서 JY-MCU, HC-06 블루투스 2.0 모듈을 $7.90 에 구입했다.(*)

P20131116_003214893_30B2B6BB-642A-437B-8CC1-DAC9BFAE1B4F

P20131116_003129483_D9ABD2C2-11B7-48C0-B629-C8C3C9B1DD5D

  • 블루투스 제품은 워낙 많아서 일일이 그 특징을 거론하기 불가능하다. (또는 별 차이가 없다!)
  • 우선 중요한 2개의 핀은 RX, TX 뿐이다.
  • 시리얼 통신은 1대1 통신으로 매우 간단한 형태이며, 양쪽의 RX/TX를 서로 엇갈려 연결한다.
  • 즉, 블루투스 모듈의 TX는 Arduino의 RX에, 그리고 RX는 TX에 연결한다.
  • 나머지 다른 2 개는 전원과 관련된 VCC, GND 핀일 뿐!
  • 이 제품에서 사용 가능한 VCC 핀의 전원 범위는 3.6V~6V이므로 5V를 제공할 수 있는 Arduino에 적합
  • 이 제품의 나머지 두 핀에 대해서는 무시한다.(*)

연결)

  • Bluetooth 모듈에 전원이 공급되면 빨간색 LED가 빠르게 점멸한다.(*)
  • 이 점멸 상태는 아직 다른 장치와 ‘페어링(pairing)’되지 않은 상태라는 의미다.
  • PC나 스마트폰에서 페어링을 하면 점멸하지 않는다.

Arduino 측)

Arduino 측에서는 Bluetooth라고 해서 특별한 것이 없다. 기존의 시리얼 통신하듯 Serial 또는 SoftwareSerial 객체를 사용하여 송수신하는 코드를 작성한다.

다음은 간단한 송수신 예제다. 즉, 원격 PC로부터 받은 문자를 화면에 표시하거나 또는 Arduino 시리얼 모니터를 통해 입력받은 문자를 원격 PC로 보낸다.  실제 코드(센서로부터 값을 읽어 전송하거나 또는 스마트폰으로부터 방향 지시를 받아 서버 모터를 움직이거나 DC 모터의 속도를 제어하는 등)에서는 받은 문자열을 if나 switch 문에서 판단하여 적절한 행위를 하게 될 것이다.

hc-06-slave

/*

  Bluetooth Echo Example

  Man-Yong Lee, <manyong.lee@gmail.com>, 2013

*/

#include <SoftwareSerial.h>

const int ledPin = 13;
const int rxPort = 8; // connected to Bluetooth TX
const int txPort = 9; // connected to Bluetooth RX
SoftwareSerial bt = SoftwareSerial(rxPort, txPort);

void setup()
{
  pinMode(ledPin, OUTPUT);

  Serial.begin(9600);
  bt.begin(9600);
}

void loop()
{
  /* from Bluetooth to Serial Monitor */
  while (bt.available() > 0) {
    digitalWrite(ledPin, HIGH);

    char c = bt.read();
    Serial.print(c);

    digitalWrite(ledPin, LOW);
  }

  /* from Serial Monitor to Bluetooth */
  while (Serial.available() > 0) {
    char c = Serial.read();
    bt.write(c);
  }
}

PC 측)

Windows, MacOS, Linux OS에 따라 각각 블루투스 연결 관리하는 방법이 다르다.  다음은 MacOS에서 연결해 본 예제다.

Screen Shot 2013-11-16 at 12.28.03 AM

장치 스캔을 하면 위와 같이 HC-06 이라는 이름이 나타나야 한다. (이 이름은 변경할 수 있다. 이에 대해서는 나중에)

Screen Shot 2013-11-16 at 12.28.58 AM

HC-06 장치의 기본 패스코드는 0000 이 아니라 1234 다!!!

Screen Shot 2013-11-16 at 12.28.43 AM

페어링에 실패하면 위와 같은 에러 메시지가 나올 수 있다. 이 때는 Options를 선택하여 패스코드를 정확히 입력한다.

Screen Shot 2013-11-16 at 12.29.09 AM

페어링에 성공하면 상태가 Connected로 바뀔 것이다.

그러나, 여기서 끝이 아니다. 위와 같이 OS 설정 관리자에서 페어링을 해도 HC-06 장치는 계속 점멸 상태일 것이다.

마지막 단계는 시리얼 통신 프로그램을 실행하여 HC-06 장치와 ‘시리얼 연결’을 하는 작업이다.  MacOS X에서는 OS 설치할 때 기본 설치되어 있는 screen 이라는 프로그램을 사용하면 된다.

screen  /dev/rfcomm0  9600

사용법은 위와 같이 screen 명령 뒤에 /dev/rfcomm0 또는 /dev/rfcomm1과 같은 장치명 그리고 시리얼 전송 속도를 적는다.  (HC-06 장치의 기본 전송 속도는 9600 이며, 설정을 통해 변경할 수 있다.)

주의: 끊고 나올 때에는 Ctrl-a k 한 후, 질문에 y 해야 한다. 그렇지 않으면 다시 screen 으로 장치에 붙지 못하는 일이 발생한다.

기타)

  • 기기 간 무선 통신 방법으로 RF(Radio Frequency)를 사용하는 방법도 있다. 보통 315MHz, 434MHz, 915MHz 등 주파수 분류상 UHF(Ultra High Freqeuncy) 대역을 쓰는 장치들이 있으나 대체로 한방향의 제어 신호(예를 들어, 장난감 무선 자동차/비행기 제어)에 쓰이며 WiFi/XBee/Bluetooth 등 다목적의 양방향 통신으로는 부적합하다. 물리적으로도 송신 모듈과 수신 모듈이 따로 있는게 특징적이다. 뭐 VirtualWire 라이브러리 등을 사용하고 양쪽에 송/수신 모듈을 다 장착하면 양방향 통신을 할 수는 있다. 이렇게 하면 기존 WiFi 같은 방식보다 훨씬 더 먼 거리의 통신(1~2km)이 가능하지만 전송 속도는 신호를 보내는 수준 정도다.
  • 같은 HC-06 모델이라도 여기서 사용하고 있는 것처럼 헤더 핀이 달려 나와 빵판에 꼽기 좋은 제품이 있는 반면, 헤더가 없어 직접 납땜을 해야 하는 제품도 있다. 그러한 제품은 가격이 조금 더 싸다.
  • 여기서 사용하고 있는 HC-06 제품은 ‘슬레이브(slave)’ 역할만 가능한 제품이다.  마스터/슬레이브 모두 가능한 제품은 HC-05 모델이다.
  • Arduino의 TX를 Bluetooth 모듈의 RX에 연결할 때, Arduino의 신호가 5V이므로 엄밀하게는 전압 조정을 위한 저항을 연결해야 한다. 하지만 그냥 연결해도 별다른 문제는 경험하지 못했다.
  • HC-06의 나머지 2 핀 중 KEY 핀은 AT 명령 모드로 진입하여 전송 속도, 패스코드 등 설정을 변경할 수 있도록 해 주는 핀이다. 이 핀을 연결하고 HIGH 상태로 해 주면 명령 모드로 진입한다고는 하는데 본인은 실패했다. DX에서 구입하는 싸구려 모델이라 설정 모드를 지원하지 않는 것일지 모른다 =.=

참고)

Raspberry Pi – Arduino I2C 통신 (2013.10.26)

많은 사람들이 RPi(Raspberry Pi)와 Arduino를 결합하여 사용한다. Arduino를 통해 RPi에 부족한 아날로그 장치 입력/제어를 수행하고 그 결과를 RPi에 전달받아 웹이나 DB에 기록한다.

보통은 RPi와 Arduino를 USB로 연결하고 Serial 라이브러리를 사용하여 약속된 데이터를 주고 받지만, 지금 설명하고자 하는 I2C 방식으로 대체 가능하다. 특히 I2C는 마스터와 여러 개의 슬레이브가 데이터를 주고 받을 수 있도록 해 주므로 일대일 시리얼 연결과는 달리 확장성이 좋다.

[[ I2C 연결 ]]

RPi의 GPIO 핀 중 SDA, SCL을 각 Arduino 보드의 해당 핀에 연결한다.

다음은 RPi 하나와 Arduino 보드 2 개(UNO 보드 그리고 Micro 보드)를 연결한 그림이다.

i2c-pi-arduino

그림이 복잡해 보이는 것과 달리 규칙은 간단하다:

  • RPi와 Arduino 보드의 SDL 핀끼리, SDA 핀끼리 연결한다. (파랑, 노랑선)
  • RPi와 Arduino 보드의 GND를 공통으로 연결한다. (검정선)

위에서는 RPi와 Arduino 보드 각각 별도의 전원을 넣지 않고, RPi만 전원을 공급하고 RPi의 5V 전원은 끌어다 Arduino 보드의 Vin 핀에 연결해 주었다.  (빨강선)

[[RPi = 마스터, Arduino = 슬레이브 ]]

Arduino는 그 즉시 i2c를 사용할 준비가 되어 있지만, RPi는 그렇지 않다.

  • i2c에 관련된 linux 커널 모듈을 로드해야 한다: i2c-bcm2708, i2c-dev
  • i2c에 관련된 모듈, 지원 소프트웨어를 설치해야 한다: python-smbus, i2c-tools

자세한 사항은 맨 아래 참고 자료에서 Adafruit의 문서를 참고하기 바란다.

다음 사진은 Adafruit Pi Cobbler와 같은 빵판용 GPIO 케이블을 사용하므로 위 연결 그림과는 다르다🙂

P20131026_153729666_A5478F95-86D5-414A-999E-09F5C1627D49

[[ RPi 마스터 코드 ]]

  • 다음과 같은 Python 코드를 작성하여 (예를 들어) i2c-example.py로 저장한다.
"""

    i2c Python Example

    Man-Yong Lee <manyong.lee@gmail.com>, 2013

"""
import smbus
import time

class I2CComm(object):
    I2C_BUS_NUM = 1 # 0 for Model B Rev 1.

    def __init__(self):
        self.master = smbus.SMBus(self.I2C_BUS_NUM)
        self.slave_addr_list = [4, 5, 6] # 슬레이브 주소 목록

    def run(self):
        me = self.master
        on_off = True
        while 1:
            for addr in self.slave_addr_list:
                try:
                    me.write_byte(addr, int(on_off))
                except IOError:
                    pass
            on_off = not on_off
            time.sleep(1)

def main():
    i2c = I2CComm()
    i2c.run()

if __name__ == "__main__":
    main()

# END

[[ Arduino 슬레이브 코드 ]]

다음 코드는 마스터로부터 ON/OFF를 의미하는 1 또는 0 정수값을 수신하면 모든 Arduino 보드에 내장되어 있는 13번 LED를 ON/OFF하는 간단한 코드다.

  • my_i2c_addr 변수는 서로 충돌하지 않도록 정해야 한다.
  • 이 값은 위 Python 코드의 slave_addr_list 변수에 넣어야 한다.

컴파일/업로드해 둔다.

#include <Wire.h>

const int my_i2c_addr = 4; // 이 부분은 각 장치마다 다르게 변경하여 컴파일/업로드
const int led_pin =  13;

void setup() {
  pinMode(led_pin, OUTPUT);

  Wire.begin(my_i2c_addr);
  Wire.onReceive(recvData);
  //Wire.onRequest(sendData); // 이 부분은 현재 사용하지 않음
}

void loop() {
  delay(100);
}

void recvData(int byte_count) {
  while( Wire.available()) {
    int on_off = Wire.read();
    if (on_off == 1) {
      digitalWrite(led_pin, HIGH);
    } else {
      digitalWrite(led_pin, LOW);
    }
  }
}

void sendData() {
  Wire.write(1);
}

[[ 실행 ]]

  • 우선 슬레이브로 동작할 Arduino 보드에 각각 서로 다른 슬레이브 주소를 가지도록 주의해서 스케치를 업로드해 둔다.
  • 빵판 등을 사용하여 RPi와 Arduino 보드의 I2C 핀과 전원 관련 핀을 상호 연결한다.
  • RPi에서 i2c-tools 패키지에 들어있는 i2c 점검 프로그램인 i2cdetect를 실행하여 i2c 버스에 슬레이브들이 잘 보이는지 확인한다.
$ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- 04 05 06 -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --

슬레이브들이 보이지 않는다면 배선을 잘못 연결했다든지 실수를 했을 것이다.

  • 위 예제 코드 i2c-example.py를 실행한다. 1초마다 Arduino 보드들의 13번 핀 LED가 점멸할 것이다.

[[ RPi I2C 준비 요약 ]]

자세한 사항은 Adafruit Learning System의 글을 참고하기 바란다. 다음은 linux에 익숙한 사람들을 위한 Quick Guide다.

  1. /etc/modprobe.d/raspi-blacklist.conf 파일 변경하여 spi-bcm2708, i2c-bcm2708을 블랙리스트에서 제거
  2. /etc/modules에 i2c-dev 모듈 추가
  3. python-smbus, i2c-tools 패키지 설치

조금 더 자세한 설명은 다음과 같다.

  • RPi에서는 기본적으로 “블랙리스트”라는 설정 파일을 두어 일반적으로 사용하지 않는 모듈을 부팅 시 로드하지 않도록 하고 있다.
  • 바로 /etc/modprobe.d/raspi-blacklist.conf 파일이 그것이다.
  • 이 파일에서 spi-bcm2708, i2c-bcm2708 이 blacklist로 들어가 있는데 이 두 줄을 삭제하거나 또는 줄 앞에 #를 넣어 주석처리한다.
  • 커널 모듈 설정 파일인 /etc/modules에 i2c-dev를 넣어야 한다.
  • apt-get을 사용하여 python-smbus, i2c-tools 설치

[[ 참고 자료 ]]

[[ 사족 ]]

  • 일부 사람들은 RPi의 등장으로 인해 Arduino가 위협받고 있다고 생각한다. 어느 정도 동의한다. Arduino도 최근 나오는 Yun, Galileo 같은 보드를 보면 더 이상 8비트 Atmel 마이크로컨트롤러 수준에 머무는 것이 아니라 ARM, MIPS 같은 칩에 Linux를 같이 제공하는 방향으로 나아가고 있다. RPi가 앞으로 어떤 방향으로 강화될 지 모르겠으나, 현재는 RPi나 Arduino 모두 하나만 가지고 모든 것을 만족스럽게 할 수 없고 상당 기간 결합해서 사용할 것이다. RPi가 편리하지만 온세상에 널려 있는 5V 장치들, Arduino 악세사리, 특히 Wearable 장치들은 Arduino가 아니고는 정말 쉽지 않다.
  • 뭐 물론 이런 단점을 보완하기 위해 A la mode, Embedded Pi, AruPi 등 각종 RPi용 확장 보드들이 나와 있기는 하다.
  • OMG! 3.3V로 동작하는 RPi 핀과 5V로 동작하는 Arduino 핀을 Logic Level Converter 없이 바로 연결한다고? 당연히 일반적인 상황에서는 불가능하다. 그러나, SDA, SCL 이 두 I2C 핀에 대해서는 예외적으로(?) 안전하다. 이에 대한 자세한 설명은 위 참고 자료 중 OscarLiang.net 사이트의 좋은 글을 참고하기 바란다.
  • 그 동안 RPi와 Arduino 간 연결을 불편한 Serail로 했는데 I2C로 할 수 있어 매우 만족스럽다.

Arduino I2C/TWI 통신 (2013.10.23)

2개 이상의 Arduino 보드를 서로 직접 연결하여 통신하려면? (또는 Raspberry Pi와 Arduino를 USB 시리얼이 아니라 직접 연결하여 통신하려면?)

I2C와 SPI, 이 두 가지 중 하나를 사용할 수 있으나 아주 간단한 저속 통신용으로는 I2C만으로 충분하다.

우선 I2C란 무엇인가?

  • 회로 상에서 장치들이 어떻게 통신할 것인지 필립스 사가 1980년대 고안한 BUS 방식
  • SDA(Serial Data Line), SLK(Serial Clock)라 부르는 2 개의 연결만 사용.
  • 단 2개의 연결을 사용하는 방식이어서 Two Wire Interface, 즉 TWI라고 부르기도 한다.
  • 하나의 마스터(Master) 장치와 여러 개의 슬레이브(Slave) 장치가 이 두 회선을 통해 연결.

어떻게 연결하는가? I2C로 통신하고자 하는 모든 장치는 SDA, SCL 핀끼리 연결한다. 만약 2 개의 장치라면 서로 직접 연결하고 그보다 많으면 빵판 등을 통해 공통의 선에다 연결한다. 참고로 연결에 참여하는 모든 장치는 공통의 GND로 연결해야 한다.

한편, I2C/TWI 핀은 각 아두이노 보드마다 그 위치가 다르다:

  • UNO, Duemilanove(2009), Pro, Pro Mini 등: A4 (SDA), A5 (SCL)
    • Atmega328 칩셋
    • UNO REV 3에서는 AREF 핀 옆에 SDA, SCL 핀이 있어 이 곳을 사용 가능
  • Leonardo, Micro: 2 (SDA), 3 (SCL)
    • Atmega32u4 칩셋
  • Mega2560: 20 (SDA), 21 (SCL)
  • Due: 20 (SDA), 21 (SCL)
    • 별도의 SDA1, SCL1 핀이 있어 총 4개의 핀

다음은 Arduino UNO 보드의 A4, A5를 연결한 그림이다.

i2c-bw-uno

Arduino UNO Rev 3 에서는 전용으로 추가된 핀을 사용해도 좋다. AREF라고 적힌 핀 옆에 있는 2 개의 핀이 SDA, SCL 이다. (AREF에 가까운 쪽이 SDA)

i2c-bw-uno-2

다음은 다른 보드 간의 연결 예제다. UNO와 Micro를 연결해 보았다.

i2c-bw-uno-micro

다음은 UNO와 Micro 보드를 I2C 연결한 예제 사진이다:

  • 공통 GND 연결 (언제나 중요!)
  • 전원 입력
    • 각각 전원을 입력하거나
    • 또는 한 쪽에만 전원을 연결하고 한 쪽의 5V를 다른 한 쪽의 Vin으로 연결하여 전원 공급 가능
  • SDA, SCL 핀 연결:
    • 보드 타입이 같으면 둘 다 같은 핀을 연결
    • 보드 타입이 다르면 각각 SDA, SCL로 어떤 핀을 사용하는지 확인하고 연결.

P20131024_125709606_75EC1061-F82E-410A-B874-10EB87C37C7F

위 예제에서는 두 보드에 따로 전원을 연결하지 않고 한 쪽에만 전원을 공급한 뒤, 5V 핀을 다른 보드의 Vin을 통해 전원을 공급해 주고 있다.

[[ 통신 방식 ]]

I2C에서는 전형적으로 하나의 마스터(Master) 장치가 있고 1 개 이상의 슬레이브(Slave) 장치가 존재한다.  마스터 장치와 슬레이브 장치 간의 통신은 항상 마스터 장치가 어떤 요청을 특정 슬레이브 장치에게 요청하는 것으로 시작한다.  다음 두 가지 경우가 있다:

  • 마스터 전송 / 슬레이브 수신
  • 마스터 수신 / 슬레이브 송신

[[ 마스터 전송/슬레이브 수신 ]]

마스터가 전송하고 슬레이브가 수신하는 예제는 다음과 같다:

  • 마스터 쪽에서는 기본 예제 중 Wire -> master_writer 예제 선택하여 업로드
  • 슬레이브 쪽에서는 기본 예제 중 Wire -> slave_receiver 예제 선택하여 업로드

마스터 스케치는 다음과 같다:

#include <Wire.h>

void setup()
{
  Wire.begin(); // i2c 버스에 참여(마스터의 경우, 주소를 적지 않아도 된다)
}

byte x = 0;

void loop()
{
  Wire.beginTransmission(4); // 4번 슬레이브 장치로 전송 시작
  Wire.write("x is ");        // 5 바이트 전송
  Wire.write(x);              // 그 다음 1 바이트 전송
  Wire.endTransmission();    // 전송 종료

  x++;
  delay(500);
}
  • i2c 프로그래밍에는 Wire 라이브러리를 사용한다.
  • Wire.begin(addr)를 호출하면 i2c 버스에 참여하게 된다.
  • 이 때 마스터는 addr을 생략할 수 있다.
  • 슬레이브에게 데이터를 전송하고자 할 때에는 beginTransmission(slave_address)로 시작해서 endTransmission()으로 마친다.
  • 그 중간에 write()를 사용하여 실제 데이터를 자유롭게 전송한다.

이에 대응하는 슬레이브 스케치는 다음과 같다:

#include <Wire.h>

void setup()
{
  Wire.begin(4); // 자신의 주소를 4번으로 설정하고 i2c 버스에 참여
  Wire.onReceive(receiveEvent); // 수신 이벤트 함수 등록
  Serial.begin(9600);
}

void loop()
{
  delay(100);
}

// 다음 함수는 마스터가 데이터를 전송해 올 때마다 호출됨
void receiveEvent(int howMany)
{
  while(1 < Wire.available()) // 마지막 한 개의 데이터를 제외하고 읽어 들임
  {
    char c = Wire.read(); // byte를 읽어 char로 변환
    Serial.print(c);
  }
  int x = Wire.read(); // byte를 읽어 int로 변환
  Serial.println(x);
}
  • 슬레이브는 마스터와 달리 i2c 버스에 참여하기 위해 Wire.begin(my_address)를 호출할 때 자신의 주소를 적어야 한다.
  • 슬레이브의 프로그래밍 방식이 전혀 다르다.
  • loop()에서 데이터가 오기를 기다리는 것이 아니라 인터럽트 방식으로 작성한다.
  • 마스터로부터 데이터가 오면 onReceive()를 통해 등록한 함수가 호출된다.

[[ 주소 개념 ]]

일대일 통신이 아닌 경우에는 서로를 구별하기 위해 “주소(address)”라는 개념이 필요하다. 그런데 위에서 보는 것과 같이 i2c에서는 장치가 주소를 마음대로(?) 결정한다. 즉, 주소 결정의 책임이 우리에게 있으므로 장치들이 같은 주소를 사용하지 않도록 조심해야 한다. (I2C 방식을 사용하는 제품을 구입할 때에는 그 제품이 사용하는 기본 주소가 무엇인지 그리고 필요할 때 어떻게 변경할 수 있는지 점검해야 한다.)

[[ 마스터 수신/슬레이브 송신 ]]

이게 좀 독특하다. 일반적으로 송신하는 쪽은 자유롭게 송신하고 수신하는 쪽이 대기하기 마련이데, I2C에서는 마스터가 데이터를 수신하고자 할 때에도 마스터가 먼저 슬레이브에게 “너로부터 데이터를 받고자 한다”라고 신호를 보낸다.

마스터가 수신하고 슬레이브가 전송하는 예제는 다음과 같다:

  • 마스터 쪽에서는 기본 예제 중 Wire -> master_reader 예제 선택하여 업로드
  • 슬레이브 쪽에서는 기본 예제 중 Wire -> slave_sender 예제 선택하여 업로드

마스터의 스케치는 다음과 같다:

#include 

void setup()
{
  Wire.begin(); // i2c 버스에 참여
  Serial.begin(9600);
}

void loop()
{
  Wire.requestFrom(2, 6); // 슬레이브 장치 #2에게 6 바이트의 데이터를 요청

  while(Wire.available()) // 슬레이브가 요청한 바이트 수를 보내라는 보장은 없음. 데이터가 있는지 점검하면서 읽음
  { 
    char c = Wire.read(); // byte를 읽어 char로 변환
    Serial.print(c);
  }

  delay(500);
}
  • 마스터가 슬레이브로 데이터를 받고자 할 때에는 requestFrom(slave_address, how_many_bytes)를 호출한 뒤 기다린다.

슬레이브의 스케치는 다음과 같다:

#include <Wire.h>

void setup()
{
  Wire.begin(2);                // 자신의 주소를 2번으로 설정하고 i2c 버스에 참여
  Wire.onRequest(requestEvent); // 송신 요청을 받았을 때 호출할 함수 등록
}

void loop()
{
  delay(100);
}

// 마스터가 자신에게 데이터를 요청할 때 호출됨
void requestEvent()
{
  Wire.write("hello ");
}
  • 마스터가 데이터를 요청할 때 호출할 함수를 .onRequest()에서 등록한다.

[[ 참고 자료 ]]

[[ 사족 ]]

  • SMBus는 필립스 사의 I2C를 기반으로 인텔이 1990년대 만든 BUS 규격이며 System Management Bus의 약자
  • 각 보드마다 SDA, SCL 용도의 핀이 다르다는 것을 주의깊게 받아들이지 않아 UNO와 Micro를 연결하는데 한 시간을 허비했다!

Adafruit CC3000 Breakout (2013.10.21)

아두이노를 TCP/IP 네트워크에 연결하려고 하는 순간, 이더넷 또는 WiFi 쉴드의 가격이 만만치 않다는 사실을 알게 된다. Arduino UNO R3 (ATmega 328)의 가격이 $29.95 인데 반해, 쉴드의 가격은 상상을 초월한다.

  • 이더넷 쉴드 (PoE 없는 버전): 약 $41 ~ $46
  • 이더넷 쉴드 (PoE 버전): 약 $60 ~ $64
  • 아두이노 UNO 이더넷: $65 (이건 아예 UNO에 이더넷 기능을 통합한 보드)
  • WiFi 쉴드: 약 $85 ~ $98 !!!

아두이노로 하는 프로젝트들의 특성 상 유선을 사용하는 이더넷 쉴드보다는 무선을 선호하게 되는데 WiFi 쉴드의 가격이 거의 $100에 육박한다. 생각해 보라! 아두이노와 리눅스의 결합인 신제품 Arduino Yun의 가격이 $89.95 다. Yun은 이더넷과 WiFi 둘 다 가지고 있고 리눅스까지 포함한다.

이 상황에서 Adafruit으로부터 정말 착한 가격의 WiFi 제품이 나왔는데 바로 CC3000 쉴드와 보드다.

  • CC3000 쉴드: $39.95
  • CC3000 브레이크아웃: $34.95

쉴드 제품은 헤더 등을 고려하여 브레이크아웃 제품보다 $5 비싸다.

[[CC3000 Breakout]]

최근에 아두이노 우노, 레오나르도 같은 보드보다는 마이크로, 프로와 같은 소형 보드에 관심이 많아 브레이크아웃 제품을 주문했다. 빵판에 꼽기 위해서는 같이 들어있는 헤더 핀을 잘라서 납땜해야 한다.

헤더나 핀을 납땜할 때에는 빵판 그리고 땜질 중 흔들리지 않도록 고정하기 위해 3M 테이프를 사용한다.

CC3000 납땜 전

CC3000은 SPI(Serial Peripheral Interface) 방식을 사용하며, 핀 배열은 다음과 같다:

  • GND
  • 3v3
  • VIN
  • CLK
  • MISO (Master In Slave Out)
  • MOSI (Master Out Slave In)
  • CS
  • VBEN
  • IRQ

아두이노에서는 위 9개의 핀 중 3v3을 제외한 8개의 핀을 연결해서 사용한다.

CC3000 Breakout

CC3000 뒷면

연결)

헤더 핀과 아두이노 UNO 보드 간의 연결은 다음과 같다 (전원 관련 2개의 핀과 D3, D5, D10~D13 등 디지털 핀 6개를 합하여 총 8개)

  • GND –> GND
  • 3v3 –> 연결하지 않음
  • VIN –> +5V
  • CLK –> D13 (SPI SCK 핀)
  • MISO –> D12 (SPI MISO 핀)
  • MOSI –> D11 (SPI MOSI 핀)
  • CS –> D10 (SPI SS 핀)
  • VBEN(VBAT_EN) –> D5
  • IRQ –> D3

각종 보드와 결합해 본 예제 화면이다:

아두이노 UNO + 프로토타입 쉴드 그 위에 얹은 CC3000

Prototype Shield w/ CC3000

아두이노 Duemilanove(2009) 호환 중국 제품인 SeeedStudio의 Seeeduino V3.0 (ATmega 328P)와 빵판을 통해 연결한 예제.

IMG_20131016_121013

아두이노 Pro Mini 5V Atmega328P 버전과 빵판을 통해 연결한 예제.

IMG_20131021_142119

각종 보드 테스트 결과)

  • 아두이노 UNO, Duemilanove, Pro Mini 등 Atmega328을 사용하는 버전의 제품들은 CC3000용 예제 코드가 모두 잘 동작하였다.
  • 그러나, ATmega32u4를 사용하는 Leonardo 그리고 Micro 등에서는 아직 동작하지 않는다.
  • Adafruit 측은 기능과 지원 범위를 계속 넓혀 갈 생각이라고 한다.

소프트웨어)

  • 사용하는 칩셋이 완전히 다르므로 Arduino IDE에 들어있는 Ethernet 라이브러리나 WiFi 라이브러리는 사용할 수 없다.  Adafruit CC3000 라이브러리를 설치해야 한다.
  • 예제 코드만 봐도 어떻게 하는지 알 수 있을 정도로 자세히 잘 나와 있다. 역시 Adafruit!
  • 예제 스케치 중 buildtest 라는 스케치가 잘 동작하는지 보면 된다.
  • 빌드 크기는 무려 22,266 바이트!
  • 참고로 CC3000_TINY_DRIVER를 #define 하여 기능을 최소화한 상태로 빌드하면 15,748 바이트다.

예제 스케치)

buildtest는 1) 공유기에 접속, 2) DHCP IP 주소 받기, 3) http://www.adafruit.com DNS 룩업하기, 4) 앞에서 알아낸 http://www.adafruit.com의 IP 주소로 ping 하기를 테스트하고 그 결과를 시리얼 모니터로 보여 준다.

#include <Adafruit_CC3000.h>
#include <ccspi.h>
#include <SPI.h>
#include <string.h>
#include "utility/debug.h"

#define ADAFRUIT_CC3000_IRQ 3 // 인터럽트 핀을 사용해야 한다. UNO에서는 2와 3번이 가능하다.
#define ADAFRUIT_CC3000_VBAT 5
#define ADAFRUIT_CC3000_CS 10
// 하드웨어 SPI 핀 사용법:
// 아두이노 UNO의 경우: SCK = 13, MISO = 12, MOSI = 11
Adafruit_CC3000 cc3000 = Adafruit_CC3000(
    ADAFRUIT_CC3000_CS, 
    ADAFRUIT_CC3000_IRQ, 
    ADAFRUIT_CC3000_VBAT,
    SPI_CLOCK_DIV2
); // you can change this clock speed but DI

#define WLAN_SSID "myNetwork" // 최대 32 글자 제한
#define WLAN_PASS "myPassword"
// 무선 공유기(AP)의 연결 보안 방법: WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA, WLAN_SEC_WPA2 중 하나 선택
#define WLAN_SECURITY WLAN_SEC_WPA2

/**************************************************************************/
/*!
@brief Sets up the HW and the CC3000 module (called automatically
on startup)
*/
/**************************************************************************/
void setup(void)
{
  Serial.begin(115200);
  Serial.println(F("Hello, CC3000!\n"));

  displayDriverMode();
  Serial.print("Free RAM: "); Serial.println(getFreeRam(), DEC);

  /* Initialise the module */
  Serial.println(F("\nInitialising the CC3000 ..."));
  if (!cc3000.begin())
  {
    Serial.println(F("Unable to initialise the CC3000! Check your wiring?"));
    while(1);
  }

  /* Optional: Update the Mac Address to a known value */
  /*
  uint8_t macAddress[6] = { 0x08, 0x00, 0x28, 0x01, 0x79, 0xB7 };
  if (!cc3000.setMacAddress(macAddress))
  {
    Serial.println(F("Failed trying to update the MAC address"));
    while(1);
  }
  */

  uint16_t firmware = checkFirmwareVersion();
  if ((firmware != 0x113) && (firmware != 0x118)) {
    Serial.println(F("Wrong firmware version!"));
    for(;;);
  }

  displayMACAddress();

  /* Optional: Get the SSID list (not available in 'tiny' mode) */
#ifndef CC3000_TINY_DRIVER
  listSSIDResults();
#endif

  /* Delete any old connection data on the module */
  Serial.println(F("\nDeleting old connection profiles"));
  if (!cc3000.deleteProfiles()) {
    Serial.println(F("Failed!"));
    while(1);
  }

  /* Attempt to connect to an access point */
  char *ssid = WLAN_SSID; /* Max 32 chars */
  Serial.print(F("\nAttempting to connect to ")); Serial.println(ssid);

  /* NOTE: Secure connections are not available in 'Tiny' mode! */
  if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY)) {
    Serial.println(F("Failed!"));
    while(1);
  }

  Serial.println(F("Connected!"));

  /* Wait for DHCP to complete */
  Serial.println(F("Request DHCP"));
  while (!cc3000.checkDHCP())
  {
    delay(100); // ToDo: Insert a DHCP timeout!
  }

  /* Display the IP address DNS, Gateway, etc. */
  while (! displayConnectionDetails()) {
    delay(1000);
  }

#ifndef CC3000_TINY_DRIVER
  /* Try looking up www.adafruit.com */
  uint32_t ip = 0;
  Serial.print(F("www.adafruit.com -> "));
  while (ip == 0) {
    if (! cc3000.getHostByName("www.adafruit.com", &ip)) {
      Serial.println(F("Couldn't resolve!"));
    }
  delay(500);
  }
  cc3000.printIPdotsRev(ip);

  /* Do a quick ping test on adafruit.com */
  Serial.print(F("\n\rPinging ")); cc3000.printIPdotsRev(ip); Serial.print("...");
  uint8_t replies = cc3000.ping(ip, 5);
  Serial.print(replies); Serial.println(F(" replies"));
  if (replies)
    Serial.println(F("Ping successful!"));
#endif

  /* You need to make sure to clean up after yourself or the CC3000 can freak out */
  /* the next time you try to connect ... */
  Serial.println(F("\n\nClosing the connection"));
  cc3000.disconnect();
}

void loop(void)
{
  delay(1000);
}

/**************************************************************************/
/*!
@brief Displays the driver mode (tiny of normal), and the buffer
size if tiny mode is not being used

@note The buffer size and driver mode are defined in cc3000_common.h
*/
/**************************************************************************/
void displayDriverMode(void)
{
#ifdef CC3000_TINY_DRIVER
  Serial.println(F("CC3000 is configure in 'Tiny' mode"));
#else
  Serial.print(F("RX Buffer : "));
  Serial.print(CC3000_RX_BUFFER_SIZE);
  Serial.println(F(" bytes"));
  Serial.print(F("TX Buffer : "));
  Serial.print(CC3000_TX_BUFFER_SIZE);
  Serial.println(F(" bytes"));
#endif
}

/**************************************************************************/
/*!
@brief Tries to read the CC3000's internal firmware patch ID
*/
/**************************************************************************/
uint16_t checkFirmwareVersion(void)
{
  uint8_t major, minor;
  uint16_t version;

#ifndef CC3000_TINY_DRIVER
  if(!cc3000.getFirmwareVersion(&major, &minor))
  {
    Serial.println(F("Unable to retrieve the firmware version!\r\n"));
    version = 0;
  }
  else
  {
    Serial.print(F("Firmware V. : "));
    Serial.print(major); Serial.print(F(".")); Serial.println(minor);
    version = major; version <<= 8; version |= minor;
  }
#endif
  return version;
}

/**************************************************************************/
/*!
@brief Tries to read the 6-byte MAC address of the CC3000 module
*/
/**************************************************************************/
void displayMACAddress(void)
{
  uint8_t macAddress[6];

  if(!cc3000.getMacAddress(macAddress))
  {
    Serial.println(F("Unable to retrieve MAC Address!\r\n"));
  }
  else
  {
    Serial.print(F("MAC Address : "));
    cc3000.printHex((byte*)&macAddress, 6);
  }
}
/**************************************************************************/
/*!
@brief Tries to read the IP address and other connection details
*/
/**************************************************************************/
bool displayConnectionDetails(void)
{
  uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;

  if(!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv))
  {
    Serial.println(F("Unable to retrieve the IP Address!\r\n"));
    return false;
  }
  else
  {
    Serial.print(F("\nIP Addr: ")); cc3000.printIPdotsRev(ipAddress);
    Serial.print(F("\nNetmask: ")); cc3000.printIPdotsRev(netmask);
    Serial.print(F("\nGateway: ")); cc3000.printIPdotsRev(gateway);
    Serial.print(F("\nDHCPsrv: ")); cc3000.printIPdotsRev(dhcpserv);
    Serial.print(F("\nDNSserv: ")); cc3000.printIPdotsRev(dnsserv);
    Serial.println();
    return true;
  }
}

/**************************************************************************/
/*!
@brief Begins an SSID scan and prints out all the visible networks
*/
/**************************************************************************/

void listSSIDResults(void)
{
  uint8_t valid, rssi, sec, index;
  char ssidname[33];

  index = cc3000.startSSIDscan();

  Serial.print(F("Networks found: ")); Serial.println(index);
  Serial.println(F("================================================"));

  while (index) {
    index--;

    valid = cc3000.getNextSSID(&rssi, &sec, ssidname);

    Serial.print(F("SSID Name : ")); Serial.print(ssidname);
    Serial.println();
    Serial.print(F("RSSI : "));
    Serial.println(rssi);
    Serial.print(F("Security Mode: "));
    Serial.println(sec);
    Serial.println();
  }
  Serial.println(F("================================================"));

  cc3000.stopSSIDscan();
}

참고)

사족)

  • DX 같은 사이트에서 동일한 Wiznet W5100 이더넷 칩을 사용하는 이더넷 쉴드를 $20 근처에서 구입할 수 있다. 다른 이더넷 칩을 사용하는 이더넷 쉴드도 있지만 그 경우에는 Arduino 기본 라이브러리로는 사용할 수 없고 별도의 라이브러리를 추가해야 하는 불편함이 있다. 무엇보다 헤더가 달린 아두이노 쉴드류를 DX에서 구입하는 경우에는 99% 배송 과정에서 처참하게 휘어져 버리므로 제품을 받은 후 일일이 펴 줘야 하는 더 큰 불편함이 있다!
  • PoE는 Power Over Ethernet의 약자로 이더넷 라인을 통해 전원까지 같이 공급하여 선을 하나 줄여 주는 방식이다.
  • SeeedStudio에서 판매하는 W5200 칩 사용 이더넷 쉴드의 가격은 $29 ! 역시 중국 제조의 힘.  중국제라 해도 SeeeduStudio는 믿을 수 있다. 이제 한국이 중국에게 따라잡혀 곤역을 치룰 날이 정말 코 앞에 와 있는데 새마을 운동 v2(?)와 (말만~ 그리고 말하는 년놈들도 자기 하는 말이 무슨 말인지도 모르는) 창조 경제 운운하고 있다 허허. Creator가 되려면 Maker가 먼저다!
  • 전원 2 개, SPI 관련하여 6개, 총 합하여 8개의 핀이 필요하므로 UNO 등의 보드를 갖고 있을 때에는 그냥 쉴드 형태를 구입하는 것도 나쁘지 않다🙂

아이폰5s 유심 교체 (2013.10.20)

그 동안 구글 I/O에서 모든 개발자들에게 나누어 준 넥서스 폰을 사용해 오다 미루고 미뤄 온 아이폰으로 잠시 전환해 보기로 했다. 나의 첫번째 아이폰이 잡스가 훌쩍 떠나버린 뒤에나 나온 아이폰5s라니… 남들에겐 모두 아이폰을 사주면서 정작 내가 사용하지 않은 이유는 허접하지만 자유롭게 앱을 프로그래밍해서 넣을 수 있는 안드로이드 폰이 내게 적합하다고 생각했기 때문이다.  아직 한국에 출시되지 않은데다 LTE 강제 가입이 싫어 지난 여행 때 아이폰5s 공기계를 사왔는데 벌써 한 달 가까이 박스에서 꺼내지도 않은 상태 =.=

그러다 아이폰5s 10월 내 한국 출시라는 기사를 보고 더 이상 미루지 말아야겠다 생각하고 유심 교체를 시도해 보았다. 우려했던 것과 달리 한 방에 성공.

준비물)

  • 아이폰5s 또는 5c (당근!)
  • 유심 절단기 세트 (보통 절단기와 유심 어댑터, 아이폰 유심 추출 핀 등이 함께 들어있음)

IMG_20131021_124418

IMG_20131021_124443

기존 갤럭시 넥서스 폰 유심은 유심 초기의 크기이며, 아이폰 5부터는 나노(Nano) 유심이라 부르는 작은 크기의 유심을 사용한다.  참고로 아이폰 4는 마이크로(Micro) 유심이라는 조금 더 큰 규격의 유심을 사용한다.  크기 순으로 나노 유심 –> 마이크로 유심 –> 그냥 유심

다음은 원래의 유심이다.

IMG_20131020_115914

우선 결론적으로 유심 절단기를 사용하여 퍽! 자른 결과다.  이렇게 많이 잘려 나가도 되나? 하는 의구심이 들었지만 어찌 되었든 잘 동작했다 =.=

IMG_20131020_121651

아이폰 유심 추출핀으로 구멍을 찔러서 유심 홀더를 빼낸 후, 이제 나노 유심으로 변해 버린 유심을 끼워 넣는다.

IMG_20131021_125527

그런데 생각보다는 딱 들어맞지 않는다. 절단면이 매끄럽지 않기 때문에 가위나 커터의 날을 세워서 절단면을 긁어 매끄럽게 해 주어야 한다.

IMG_20131020_121938

유심 절단기를 사용하는 법은 매우 간단.  방향에 맞게 유심을 끼워 넣고 퍽! 자르면 된다.

IMG_20131020_115842

IMG_20131021_124508

절단해서 만든 나노 유심으로 동작하는 아이폰5s

  • 처음에는 유심을 인식하지 못해 절단면을 다시 매끄럽게 다듬어 꼭 맞도록 조정했다.
  • 그 다음에는 No Service 등의 메시지를 뿌리며 통신사 네트워크에 붙지 않았는데, 다시 뺐다 끼우니 통신사 네트워크에 붙었다.
  • 이건 뭐 다른 폰에 유심을 끼울 때도 마찬가지로 발생하는 일시적인 증상이다.
  • 잘 안되면 폰을 리부팅한다든지 하면 된다.

IMG_20131021_124850

아이폰5s 소감)

  • 아, 폰이 길어졌을 뿐 기존 안드로이드 거대(?)폰들보다 확실히 작아서 메일 보기가 정말 불편하다 =.=  아무래도 화면 크기 때문에 다시 안드로이드 폰으로 돌아갈 가능성이 높다.
  • 하지만 안드로이드 폰들과 달리 반응 속도가 정말 빠르다. (뭐 이건 너무 당연하다. 자바와 Objective-C의 성능을 어떻게 비교하랴? 경량화된 버전이긴 해도 안드로이드 폰은 태생적으로 무거운 JVM 상에서 돌고 있다. JellyBean에서 조금 빨라졌을 뿐. 갈 길이 아직도 남았다.)
  • 역시 같은 연장 선상에서 카메라 반응 속도 만족스럽다.
  • 무엇보다 디스플레이 색상은 최고. 도대체 삼숭 갤럭시 시리즈의 색상, 이건 비정상이어도 너무 비정상 아닌가?
  • 역시 작으니까 가볍고 주머니에 쏙 넣고 다니기 좋다.
  • 그러나 자판이 작아서 정말 불편하다 =.=  안드로이드 폰은 크기도 크지만 좀 더 편리한 자판 앱을 사서 사용해 왔는데 이게 가장 큰 걸림돌이 되지 않을까 한다.
  • 아, 마지막으로 아이폰5s의 지문 인식, 정말 편하다! 매번 슬라이드하거나 비밀번호를 치지 않아도 순식간에 잠금이 풀린다.
  • 또한 앱 스토어에서 앱 받을 때마다 앱 스토어 패스워드 답해야 하는 불편함으로부터 해방되었다.
  • 지문은 여러 개 등록할 수 있으므로 양쪽 엄지 손가락 그리고 검지를 등록해 두면 좋다.

구글 ON 아이폰)

아이폰을 켜자 마자 한 일, 구글 앱을 설치하는 일이었다.

  • 크롬(Chrome): 도대체 이 브라우져 말고 다른 브라우져를 쓸 수 있단 말인가???
  • 구글 맵
  • 업무를 위해 구글 드라이브
  • 유투브

내겐 아직 LTE 필요 없다. 트윗, G메일, 이 정도이기 때문에 이전에 가입한 3G 무제한으로도 충분하다. 공기계 가격이 만만치 않지만 결국 한 번에 내느냐 나눠서 내느냐일 뿐.  며칠, 몇 달을 쓸 지는 모르겠으나 당분간은 아이폰5s를 즐길 예정이다.