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개의 버튼이 눌렸을 때도 어떤 버튼이 눌렸는지 판단할 수 있다.

아두이노에서 RTOS 사용하기 (FreeRTOS in Arduino)

아두이노에 포팅된 FreeRTOS를 사용하면 아두이노에서도 RTOS를 사용해 멀티 쓰레드로 프로그램을 만들 수 있다. FreeRTOS를 설치하려면 먼저 브라우져에서 아래 주소로 이동한다.




Download ZIP을 클릭해 zip 파일을 다운받는다. 다운받은 파일의 압축을 풀면 디렉토리 내에 아래와 같은 파일들이 만들어진다.



이 중 libraries 디렉토리 내에 아래와 같은 3개의 서브디렉토리가 들어있다.



이 3개의 서브디렉토리를 자신의 아두이노 라이브러리 디렉토리에 복사 해 주면 된다. (아두이노에 외부 라이브러리 설치하는 법 참조)
아두이노 DUE를 사용하면 FreeRTOS_ARM을 사용하고 아두이노 UNO, MEGA등 AVR 프로세서를 사용한 아두이노의 경우 FreeRTOS_AVR을 사용하면 된다. 

라이브러리 설치가 끝나면 아두이노를 재시동하고 예제 -> FreeRTOS_AVR -> frBlink 파일을 선택해 보드에 업로드 해서 LED가 깜빡이면 FreeRTOS가 정상적으로 동작하고 있는 것이다. 

#include <FreeRTOS_AVR.h>

const uint8_t LED_PIN = 13;
SemaphoreHandle_t sem;

static void Thread1(void* arg) {
  while (1) {
    xSemaphoreTake(sem, portMAX_DELAY);
    digitalWrite(LED_PIN, LOW);
  }
}

static void Thread2(void* arg) {
  pinMode(LED_PIN, OUTPUT);
  while (1) {
    digitalWrite(LED_PIN, HIGH);
    vTaskDelay((200L * configTICK_RATE_HZ) / 1000L);
    xSemaphoreGive(sem);
    vTaskDelay((200L * configTICK_RATE_HZ) / 1000L);
  }
}

void setup() {
  portBASE_TYPE s1, s2;

  sem = xSemaphoreCreateCounting(1, 0);  // 세마포어를 생성
  s1 = xTaskCreate(Thread1, NULL, configMINIMAL_STACK_SIZE, NULL, 2, NULL); // Thread1 생성
  s2 = xTaskCreate(Thread2, NULL, configMINIMAL_STACK_SIZE, NULL, 1, NULL); // Thread2 생성
  vTaskStartScheduler();  // 태스크 스케쥴러 시작
  while(1) {}
}

void loop() {}

코드에서는 두개의 쓰레드 (Thread1, Thread2)가 동시에 실행되어 Thread2에서 LED를 켜고 200ms 후에 semaphore를 POST 해주어 Thread1이 semaphore를 받으면 LED를 끄게 되어 있다. Thread1은 LED를 끄고 나면 곧바로 다시 semaphore를 기다리지만 Thread2에서 200ms를 기다린 후 LED를 켜고 다시 200ms 후에 semaphore를 POST 하기 전에는 계속 semaphore를 기다리게 된다. 



그리고 일반 스케치와 다르게 loop() 에서는 아무 동작을 하지 않는다.

아두이노에서의 delay() 함수

아두이노를 처음 시작하면 누구나 한번은 보게 되는 가장 쉬운 프로그램(아두이노의 ‘hello world’)는 아마도 아래 코드일 것이다. 

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

void loop() {
  digitalWrite(13, HIGH);   // LED ON
  delay(1000);              // 1초 대기
  digitalWrite(13, LOW);    // LED OFF
  delay(1000);              // 1초 대기
}

예제 -> 01. Basics -> Blink 코드로 아두이노 보드에 붙어있는 LED를 깜빡이게 하는 코드이다.



위의 사진에 ‘L’자 옆의 LED가 digital I/O 13번 핀에 연결되어 있는 것으로 2초 간격으로 깜빡이게 된다.
코드는 매우 직관적이어서 곧바로 이해할 수 있고 아두이노에서 프로그램을 만들면 delay() 함수를 많이 사용하게 된다.
하지만 위와 같이 단순하게 한가지 작업만 하는 경우는 상관없지만 일반적인 프로그램의 경우 동시에 여러가지 작업을 수행해야만 한다. 그 경우 delay() 함수가 사용되면 delay()에 지정된 시간만큼 대기하는 동안 프로그램 실행이 중단되어 다른 작업을 할 수가 없게 된다. 

그러므로 처음 코드를 이해할 때는 delay()함수가 편해도 실제 프로그램을 만들 때는 delay() 함수 사용을 최소화 해야만 한다. 예를 들어 2개의 스위치를 연결해 LED가 깜빡이는 속도를 조절하는 프로그램을 만든다고 생각해 보자.

단순하게 생각한다면 다음과 같은 식으로 코드를 만들 수 있을 것이다.

int gDelay = 1000;

void setup() {
  pinMode(13, OUTPUT);
  pinMode(7, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
}

void loop() {
  if (LOW == digitalRead(7)) {
    gDelay += 100;
  }
  if (LOW == digitalRead(8)) {
    gDelay -= 100;
  }
  gDelay = constrain(gDelay, 100, 2000);
    
  digitalWrite(13, HIGH);   // LED ON
  delay(gDelay);              
  digitalWrite(13, LOW);    // LED OFF
  delay(gDelay);              
}

위의 경우 스위치를 검사해 스위치가 눌리면 각각 delay 시간을 늘리거나 줄여주고, LED를 깜빡이는 코드가 다 포함되어 있다. 하지만 실행시켜 보면 스위치를 눌러도 깜빡이는 속도가 원하는대로 바뀌지 않을 것이다. 



위의 그림에서 볼 수 있는 것 처럼 (1)과 (2)에서 스위치가 놀렸는지 검사하는데 만일 gDelay 변수 값이 1000이었다고 하면 2초에 한번씩만 스위치가 눌렸는지 검사하게 된다. 스위치를 검사하고 다음번에 다시 검사하는 2초 사이에 스위치를 눌러다 떼면 프로그램은 스위치가 눌린걸 알수가 없게 된다. 즉 delay()로 프로그램 실행이 중단되어 있는 동안에는 다른 작업을 하지 못하게 된다.



이 문제에 대한 해결책으로 millis() 함수를 사용하면 된다. 아두이노에는 전원이 들어와 스케치가 시작되면 0에서 시작되어 1/1000초 단위로 1씩 증가하는 카운터가 있다. millis() 함수를 호출하면 호출된 시점의 카운트 값(unsigned long 타입)을 알려준다.



위의 그림에서 b에서 a 값을 빼면 그 값이 a에서 b까지의 시간(ms 단위)이 된다. 

그러므로 위의 코드를 delay()를 사용하지 않도록 다음과 같이 바꿀 수 있다.

int gDelay = 1000;

void setup() {
  pinMode(13, OUTPUT);
  pinMode(7, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
}

void loop() {
  static unsigned long last = 0;
  static boolean ledStat = LOW;

  if (LOW == digitalRead(7)) {
    gDelay += 100;
  }
  if (LOW == digitalRead(8)) {
    gDelay -= 100;
  }
  gDelay = constrain(gDelay, 100, 2000);

  if ((millis()-last) >= gDelay) {
    ledStat = !ledStat;
    digitalWrite(13, ledStat);
    last = millis();
  } 
}

delay() 함수를 사용한 스케치에서는 loop() 함수가 한번 실행되는데 2초(gDelay가 1000인 경우)가 걸리지만, delay()함수를 사용하지 않은 스케치는 loop() 함수가 한번 실행되는데는 매우 짧은 시간이 걸려 2초동안 loop()함수가 수만~수십만번 실행되므로 스위치 상태도 그 횟수만큼 검사가 되기 때문에 사람이 아무리 빠르게 스위치를 눌렀다 떼더라도 감지하지 못하는 경우는 없다. 하지만 LED의 상태를 토글하는 코드(위에서 빨간색 부분)는 그 중 두번만 실행되게 된다.


 
(물론 위의 코드는 스위치의 debouncing 처리가 안되어 있고 한번 눌린 동안 너무 여러번 스위치 상태를 감지하게 되기 때문에 스위치를 한번 눌렀을 때 gDelay 값이 크게 변하게 된다. 이런 문제들은 이 글의 주제와 다른 부분이라 다른 포스트에서 설명할 것이다.)

참고로 이런 문제들을 모두 해결한 코드는 아래와 같다.

#define SW1  8
#define SW2  9
#define LED  13

boolean gLedStat = HIGH;
int gDelay = 500;

void setup() {
  pinMode(SW1, INPUT_PULLUP);
  pinMode(SW2, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
  Serial.begin(115200);
}

void loop() {
  static unsigned long last = 0;
  static unsigned long lastSw = 0;
  unsigned long now = millis();
  static boolean prev1 = HIGH;
  static boolean prev2 = HIGH;
  boolean cur;

  if ((now - lastSw) >= 10) {   // Check switch for every 10ms
    cur = digitalRead(SW1);
    if ((HIGH == prev1) && (LOW == cur)) {
      gDelay -= 100;
      gDelay = constrain(gDelay, 100, 2000);
      Serial.print("Delay up to ");
      Serial.println(gDelay);
      prev1 = LOW;
    } else if ((LOW == prev1) && (HIGH == cur)) {
      prev1 = HIGH;
    }
    cur = digitalRead(SW2);
    if ((HIGH == prev2) && (LOW == cur)) {
      gDelay += 100;
      gDelay = constrain(gDelay, 100, 2000);
      Serial.print("Delay down to ");
      Serial.println(gDelay);
      prev2 = LOW;
    } else if ((LOW == prev2) && (HIGH == cur)) {
      prev2 = HIGH;
    }
    lastSw = now;
  }
  if ((now - last) >= gDelay) {
    gLedStat = !gLedStat;
    digitalWrite(LED, gLedStat);
    last = now;
  }

}

2015년 8월 6일 목요일

아두이노의 pinMode()에서 INPUT과 INPUT_PULLUP의 차이

아두이노의 digital I/O핀을 사용하려면 먼저 pinMode() 함수로 각각의 핀을 입력 또는 출력 모드로 사용하겠다고 먼저 선언해 줘야 한다.
그런데 입력으로 사용하는 경우 'INPUT'과 'INPUT_PULLUP' 두가지를 선택할 수 있다.
INPUT_PULLUP의 경우 프로세서 내부에 있는 pull-up 저항을 사용하겠다는걸 알려주는 것이다.

일반적으로 아두이노에 스위치를 연결하는 경우를 생각해 보자. 보통 아래 그림과 같이 외부에 pull-up 저항을 연결해 주는것이 보통이다. 하지만 ATmega168/328의 각 I/O 핀에는 노란색 부분의 회로가 들어있다. 하지만 디폴트는 노란색 부분을 사용하지 않게 되어 있다.

pinMode(7, INPUT); 

그래서 INPUT 대신 INPUT_PULLUP을 사용하면 녹색 부분을 사용하게 되어 외부에 별도의 pull-up 저항을 붙이지 않아도 된다. (그림을 보면 알겠지만 아두이노 바깥쪽에 있던 저항이 아두이노 안쪽으로 들어간 것 뿐이다.)

pinMode(7, INPUT_PULLUP);


이렇게 하면 외부에 연결해야 하는 부품이 줄어들고 배선도 간단해진다.

아래는 INPUT_PULLUP으로 두개의 스위치를 연결해 LED 깜빡이는 속도를 조절하는 예제이다.


사진에서 볼 수 있는것 처럼 외부 저항이 없어 케이블이 훨씬 간편해졌다.

#define SW1  8
#define SW2  9
#define LED  13

boolean gLedStat = HIGH;
int gSpd = 500;

void setup() {
  pinMode(SW1, INPUT_PULLUP);
  pinMode(SW2, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
  Serial.begin(115200);
}

void loop() {
  static unsigned long last = 0;
  static unsigned long lastSw = 0;
  unsigned long now = millis();
  static boolean prev1 = HIGH;
  static boolean prev2 = HIGH;
  boolean cur;

  if ((now - lastSw) >= 10) {   // Check switch for every 10ms
    cur = digitalRead(SW1);
    if ((HIGH == prev1) && (LOW == cur)) {
      gSpd -= 100;
      gSpd = constrain(gSpd, 100, 2000);
      Serial.print("Delay up to ");
      Serial.println(gSpd);
      prev1 = LOW;
    } else if ((LOW == prev1) && (HIGH == cur)) {
      prev1 = HIGH;
    }
    cur = digitalRead(SW2);
    if ((HIGH == prev2) && (LOW == cur)) {
      gSpd += 100;
      gSpd = constrain(gSpd, 100, 2000);
      Serial.print("Delay down to ");
      Serial.println(gSpd);
      prev2 = LOW;
    } else if ((LOW == prev2) && (HIGH == cur)) {
      prev2 = HIGH;
    }
    lastSw = now;
  }
  if ((now - last) >= gSpd) {
    gLedStat = !gLedStat;
    digitalWrite(LED, gLedStat);
    last = now;
  }
}