NERD WORLD

아이디어팩토리 - 아두이노 워크샵 본문

일상

아이디어팩토리 - 아두이노 워크샵

학부생7년차 2016. 3. 29. 15:17

지난번 3D 프린터 워크샵에 참석했다가 아이디어팩토리의 매력에 흠뻑 빠져버렸다. 현재 프로토타입을 만들어보고 싶은 주제가 없는지라 방문할 일이 없었는데, 페이스북 타임라인에서 아두이노 워크샵을 진행한다는 공지를 보고 신청기한이 다가오기를 벼르고 있었다. 서울대입구역에서 친구들이랑 5시에 만나기로 했는데, 신청시작 시각이 그날 5시였다. 의도한 것은 아니었지만 약속에 좀 늦게되면서(ㅋㅋ) 자연스레 지하철 내리기 직전에 신청을 할 수 있었다.

이번 아두이노 워크샵은 전기정보공학부의 서민국 조교님께서 수고해주셨다.

UNO 보드가 들어있는 키트를 제공받았다. 초음파 센서나 빵판, 그리고 점퍼케이블 같은 경우는 익숙한 애들이었으나 그 외에도 매우 다양한 센서와 모듈들이 함께 들어가있었다. 가격은 8만원 이라고 한다.


아두이노(Arduino)란?

아두이노 보드는 마이크로컨트롤러(Micro-Controller)의 일종이라고 보면 된다. 한글 위키백과의 정의를 그대로 가져와본다.

마이크로프로세서와 입출력 모듈이 하나의 칩으로 만들어져 정해진 기능을 수행하는 컴퓨터를 말한다. CPU 코어, 메모리 그리고 프로그램 가능한 입/출력을 가지고 있다.

마이크로프로세서는 쉽게 얘기해서 CPU(Central Processing Unit) 과 동의어라고 이해했다. 메모리는 기계어로 변환된 코드를 지닐 ROM 과, 연산 수행 과정에서 데이터를 저장할 작은 RAM 이 함께 들어있다고 한다. 

일반적으로 임베디드 시스템을 설계할 때, 시스템 구성에 마이크로컨트롤러들이 포함된다. Intel 이나 ARM 같은 유명 칩 제조회사들마다 저마다의 마이크로컨트롤러 제품군을 판매하고있다. 이런 상용 마이크로컨트롤러들은 아무래도 진입장벽이 높다. 마이크로컨트롤러가 임베디드 시스템을 구성하는 전체 회로 기판의 일부로 들어갈 것을 가정하기 때문에 다른 외부장치들과 연결하기도 쉽지 않다.

그런 맥락에서 아두이노 보드가 출시되었을 것이다. 똑같이 마이크로컨트롤러지만 사용하기에 훨씬 간편하다. 아무래도 성능은 떨어질 수 밖에 없지만, 비전문가가 취미용으로 접하기에 아래와 같은 장점을 갖추고 있다고 생각한다.

1) 연결이 간편하다 : 외부기기들과 연결하는 단자를 점퍼-케이블로 구성하여 손쉽게 연결할 수 있다. 납땜으로 연결하는 것은 비가역적이지만, 점퍼-케이블은 콘센트를 꽂았따 빼듯이 손쉽게 연결하고 분리할 수 있다. 그러므로 가지고 놀기에 아주 좋다.

2) 코딩이 간편하다 : C언어로 코딩하기에 언뜻 보면 하드하다고 생각할 수 있지만, 아주 직관적인 라이브러리를 제공하기 때문에 기초교육만 탄탄이 받으면 프로그래밍을 처음 접하는 사람이더라도 다채롭게 응용할 수 있다.

3) 다양한 외부 모듈이 존재한다 : 아두이노가 교육용으로나 취미용으로 대중에게 퍼지자, 이에 발맞춰 초음파, 레이저, 적외선, RFID, Wi-Fi 등 다양한 기능의 외부 모듈들이 아두이노에 연결하기 편리하게 출시되어 판매되고 있다.

4) 다양한 예제 코드들이 존재한다 : 비단 웹서핑을 할 필요도 없이, 공식 홈페이지 / 공식 IDE에 기본으로 내장된 예제 코드들만해도 매우 다양하다. 그러므로 3)에서 얘기한 다양한 외부 모듈들의 기본적인 사용법을 공인되고 꾸준히 관리되는 자료들로 학습할 수 있다.


아두이노 개발환경(IDE)

아두이노 보드를 획득하였고, 아두이노에 대해서 간단히 소개를 들었으니 이어지는 순서는 아두이노 보드에 올려볼 프로그램을 코딩해보는 것이다. 그리고 코딩을 하기 위해서는 코딩 환경을 구축해야 한다. 코드를 작성할 에디터, 작성한 코드를 머신코드르 변환해줄 컴파일러, 변환한 머신 코드를 보드에 올려줄 방법 등을 준비해야 할터인데 이 모든 것을 한큐에 해결해주는 것이 바로 통합 개발환경 IDE(Integrated Development Environment) 이다. 아두이노 프로그램 작성을 위한 통합 개발환경으로 공식 홈페이지에서 제공하는 IDE를 사용하였다 (다른 IDE가 무엇이 있을지는 모르겠다).

다운로드 받은 뒤 실행하고 프로그램을 보드에 올리기 위한 일련의 프로세스를 잠시 확인해보자.

1) 보드 설정 :

이번 워크샵에서 제공받은 키트는 아두이노 UNO 보드를 포함하고 있는 키트였다. 일반적인 기능을 모두 포함하고 있으면서, 불필요한 추가 기능을 모두 제거한 보드로 입문용으로 가장 많이 사용되는 모델이다. 실제로 아두이노 보드는 매우 다양한 종류의 모델이 존재한다. 각 보드마다 내부에 사용된 칩들이 좀 다를 수 있으므로, 다른 컴파일러를 사용할 수 있을 것이다. 그러므로 IDE에서 올바른 머신코드를 생성해낼 수 있도록, 어느 보드를 사용할 것인지 선택해주는 것이다.

2) 포트 설정 : 

컴파일한 머신코드를 보드에 올리기 위해서는 시리얼 케이블의 연결이 필요하다. 그리고 IDE에서 그 케이블이 꽂힌 USB 포트를 인식하게 해줘야 한다. 보통의 경우 케이블을 하나 연결하면 IDE가 그 포트를 인식하기 때문에 문제가 되지 않으나, 여러개의 포트를 인식하는 경우도 있을 수 있다. 그런 경우에 어느 포트가 아두이노 보드와 연결된 포트인지 확인할 필요가 있다. 제어판 > 장치 관리자 > 포트 (COM & LPT) 메뉴에서 이를 확인할 수 있다.

3) 코드 작성 : 

보드에 업로드할 코드를 작성한다.

4) 코드 확인 (컴파일) : 

작성한 코드를 컴파일한다. 코딩 오류가 있다면 이 과정에서 IDE가 알려준다. 직관적으로 메뉴가 구성되어 있다. 상단 메뉴 버튼중 가장 왼쪽에 위치한 체크 모양이 컴파일 버튼이다.

5) 코드 업로드 :

컴파일 버튼 오른쪽의 오른쪽 화살표 모양 버튼이 업로드 버튼이다. 보드와 컴퓨터가 시리얼 케이블로 연결되어 있는지 확인하자.

코드 업로드까지 완료된 후에는 보드가 올려진 프로그램대로 작동하는 것을 확인할 수 있다. IDE가 제공하는 기능은 위의 5가지 외에도 더 다양하겠지만, 입문하는 시점에서는 위 5가지 워크-플로우를 숙지하고 있는 것 만으로 충분하지 않을까?


워크샵에서는 총 6가지 실습을 진행했었다. 실습 내용을 기록하기 위해서 아두이노 코드와 전체 회로도를 포스트에 포함시켜야 했다. 다음의 툴을 이용하였다.

    • hilite.me - 소스 코드 HTML 변환
    • Fritzing - 아두이노 포함한 회로도 그리기

실습 1 - LED 점등

아두이노 IDE가 기본으로 제공하는 LED 점등 예제를 통해서 아두이노 보드를 통한 시스템 구성의 맛을 보도록 하자. 아래의 추가적인 준비물들이 필요하다. 준비되었다고 가정하겠다.

    • 브레드 보드(Bread Board), 속칭 빵판
    • 점퍼 케이블
    • LED
    • 저항(1k Ohm 이면 적당하다)
코드는 IDE의 메뉴에서 불러올 수 있다. 파일 > 예제 > 01.Basics > Blink 를 택하면 된다. 간단히 코드를 분석해보자.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin 13 as an output.
  pinMode(13, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);              // wait for a second
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);              // wait for a second
}

(코드 하이라이트는 hilite.me 서비스를 사용해서 manni 스타일의 HTML 코드로 변환한 뒤 사용하였다)

setup 함수 :

Line 4에서 pinMode 함수를 사용하여 13번 Digital I/O 핀을 OUTPUT 핀으로 사용하겠다고 선언하였다. 이 13번 PIN이 LED 양단에 전압을 인가해줄 것이다.

loop 함수 :

digitalWrite 함수를 사용해서 OUTPUT 13번 핀에 신호를 인가해준다. LED 를 켰다가(HIGH), 1초 기다리고(delay 함수는 ms 단위로 인자를 받는다), 껐다가(LOW), 1초 기다리고 다시 켜지는 동작을 무한히 반복한다(Line 9-12). 이름과 동일하게 loop 함수안에 적힌 코드들은 보드에 공급되던 전원이 끊기거나, 새로운 프로그램이 업로드되지 않는 한 무한히 반복된다.

이 예제 코드가 그대로 동작할 수 있도록 LED와 저항, 아두이노를 연결해주는 회로도를 아래와 같이 구성한다. LED를 아두이노 보드의 13번 핀과 GND에 바로 연결할 수도 있겠으나, 안전하게 저항과 직렬로 연결한다. LED 의 내부저항이 작으므로 아두이노 보드에 너무 큰 전류가 흐를 수 있기 때문이다.

아래와 같이 올바르게 동작함을 확인하였다.

      

실습 2 - 스위치로 제어하는 LED

실습 1에서는 LED와 저항을 연결해서 1초 켜진 뒤 1초 꺼지는 동작을 반복하도록 했었다. 한 단계 더 나아가서 회로도에 추가한 스위치로 LED의 ON/OFF를 제어할 수 있도록 해보는 실습이 이어졌다. 코드와 회로를 모두 수정해줘야 한다. 파일 > 예제 > 02.Digital > Button 의 예제 코드를 사용한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// constants won't change. They're used here to
// set pin numbers:
const int buttonPin = 2;     // the number of the pushbutton pin
const int ledPin =  13;      // the number of the LED pin

// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);
}

void loop() {
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // check if the pushbutton is pressed.
  // if it is, the buttonState is HIGH:
  if (buttonState == HIGH) {
    // turn LED on:
    digitalWrite(ledPin, HIGH);
  } else {
    // turn LED off:
    digitalWrite(ledPin, LOW);
  }
}

declaration 파트 : 

setup 함수 이전에 필요한 핀 넘버들과 변수를 선언한다. buttonState 는 스위치 버튼의 ON/OFF 여부를 받아오기 위한 정수형 변수.

setup 함수 : 

실습 1에서와 마찬가지로 pinMode 함수로 사용할 Digital I/O 핀의 INPUT/OUTPUT 여부를 선언한다.

loop 함수 : 

buttonState 변수에 스위치 연결상태를 받아온다. 그 뒤 간단한 if-else 문으로 스위치 연결상태에 따라 LED 에 HIGH/LOW 신호를 인가한다.

회로도는 아래와 같이 구성한다. 스위치와 2번 핀이 연결된 회로와, LED와 13번 핀이 연결된 회로가 독립적으로 구성되었음을 주목하자.

아래와 같이 올바르게 동작함을 확인할 수 있다.

첨언하자면 스위치로 LED를 켜고 끄는 것은 아두이노 없이도 가능하다. LED와 저항으로 구성한 실습 1의 회로에 스위치를 직렬로 적절히 연결해주기만 하면 되는 것이다. 그럼에도 이 예제를 택한 이유는 실습 1에서 아두이노 핀으로 신호를 출력(output)하는 법을 익혔으니, 아두이노 핀으로 신호를 입력(input)해오는 법을 익히기 위함 아니었을까? 응용해서 스위치를 3초 이상 눌러야 LED가 켜진다던가 하는 식의 구현을 위해서는 아두이노가 필요할 것이다.


실습 3 - 스위치를 4번 누를때마다 켜지는 LED

실습 2에서는 스위치가 눌려있으면 LED가 켜지는 회로를 구성했었다. 실습 3에서는 한 단계 더 나아가 스위치를 누른 횟수가 4의 배수일때는 켜져있고, 아닐때에는 꺼져있는 회로를 구성한다. 파일 > 예제 > 02.Digital > StateChangeDetection 코드를 사용한다. 코드는 아래와 같다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// this constant won't change:
const int buttonPin = 2;    // the pin that the pushbutton is attached to
const int ledPin = 13;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);
  // initialize the LED as an output:
  pinMode(ledPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
}


void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      // if the current state is HIGH then the button
      // wend from off to on:
      buttonPushCounter++;
      Serial.println("on");
      Serial.print("number of button pushes:  ");
      Serial.println(buttonPushCounter);
    } else {
      // if the current state is LOW then the button
      // wend from on to off:
      Serial.println("off");
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state,
  //for next time through the loop
  lastButtonState = buttonState;


  // turns on the LED every four button pushes by
  // checking the modulo of the button push counter.
  // the modulo function gives you the remainder of
  // the division of two numbers:
  if (buttonPushCounter % 4 == 0) {
    digitalWrite(ledPin, HIGH);
  } else {
    digitalWrite(ledPin, LOW);
  }

}

Declaration 파트 : 

실습 2에서와 마찬가지로 스위치 상태를 받아올 buttonPin 과 LED 에 신호를 인가할 ledPin 을 선언한다. buttonState 와 lastButtonState 에는 현재 스위치 상태와 직전 스위치 상태를 저장하고, buttonPushCounter 에는 지금까지 스위치가 눌린 횟수를 저장할 것이다.

setup 함수

buttonPin 과 ledPin 핀 넘버에 INPUT / OUTPUT pinMode를 선언해준다. Serial.begin 함수는 아두이노 보드에서 컴퓨터의 시리얼 모니터로 무언가를 출력하고 싶을 때 사용된다. 인자로 받는 9600은 Baud Rate 값이다. 일단은 주파수와 같은 개념이라고 이해하자. 라디오를 방송국에서 송출하는 주파수에 맞춰야 해당 주파수의 방송을 들을 수 있듯이, 컴퓨터의 시리얼 모니터의 Baud Rate 값을 이 9600에 맞춰줘야 아두이노 보드가 출력하는 텍스트를 읽을 수 있다는 정도로만 이해하고 넘어가자.

loop 함수 : 

buttonState와 lastButtonState 의 값이 다른 경우는 2가지가 가능하다.

    • bS = HIGH, lBS = LOW : 스위치가 눌리는 순간
    • bS = LOW, lBS = HIGH : 스위치가 떼지는 순간

그러므로 둘중 한번만 카운트하는 것이 논리적으로 타당하다. 위의 코드에서는 첫번째만 카운트하도록 약속한 것이다.

Line 40에서 delay(50) 코드를 삽입한 것은, 스위치의 바운싱(bouncing)을 처리하기 위함이다. 스위치가 눌리고 떼지는 과정이 완벽히 이상적이지 않고, 변화의 순간에 짧은 간격으로 눌림-떼짐이 여러번 발생하는 현상을 바운싱이라고 한다. 아래의 그림이 잘 설명해주고 있다.

50ms 이상 유지되지 않은 HIGH는 ON으로 인식하지 않도록 처리하기 위해 50ms의 딜레이를 추가한 것이다. Line 51-55의 if-else 문으로 버튼 눌린 횟수가 4의 배수일때만 LED에 HIGH 신호를 인가하도록 처리되어 있는 것을 확인할 수 있다.


실습 4 - LED 밝기 조절 

LED의 마지막 실습으로 서서히 밝아졌다가, 어두워지도록 신호를 인가해본다. 단순히 켜고 끄는 것이 아니므로 실습 1-3에서 사용한 digitalWrite 함수 대신에 analogWrite 함수를 사용한다. LOW 와 HIGH 사이를 256 등분해서 0~255 의 값을 줄 수 있다.

한가지 중요한 포인트는 0~255 의 값을 인가하는 방법이다. LOW가 0V 이고, HIGH가 5V 이니 그 사이 값을 256 등분해서 준다고 생각할 수 있지만 그렇지 않다. OUTPUT 핀으로 나가는 신호는 LOW 아니면 HIGH 뿐이고 다만 전체 시간에서 HIGH 신호가 차지하는 시간의 비율을 조절해서 평균적인 analog 신호의 값을 조절하는 것이다. 이를 PWM(Pulse Width Modulation) 이라고 한다.

그러므로 LED 는 완전히 켜지거나, 완전히 꺼지는 것을 반복할 뿐이지만 그 변화 주기가 매우 짧으므로 우리 눈에 보기에는 중간 밝기로 보이게 된다. 이를 이용해서 LED 의 밝기를 조절한다.

이번에는 아두이노 IDE 가 제공하는 예제코드를 사용하지 않고, for 문 2개를 직접 작성해서 코드를 완성할 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
const int LED = 11;

void setup() {
  // put your setup code here, to run once:
  pinMode(LED, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  for (int i=0;i<256;i++){
    analogWrite(LED, i);
    delay(10);
  }
  for (int i=255; i>=0; i--){
    analogWrite(LED, i);
    delay(10);
  }
}

loop 함수 :

10ms 마다 밝기를 1씩 증가시키는 첫번째 for 문과(Line 10-13), 10ms 마다 밝기를 1씩 감소시키는 두번째 for 문(Line 14-16)으로 구성되어있음을 확인할 수 있다.

아날로그 출력이 가능한 핀은 따로 정해져있다. 보드에서 핀 번호 옆에 ~ 물결표시가 있는 핀 중 하나를 선택하면 된다. 그러므로 실습 1 의 회로도에서 13번 핀 대신에 11번 핀으로 수정만 해주면 된다.


실습 5 - RFID 태그 모듈 테스트

마지막 두개의 실습은 키트에 포함된 RFID 태그 모듈을 활용해서 진행되었다. 키트에 포함된 모듈은 RFID RC522 모듈이다. RFID 수신기와, 두 가지 서로 다른 ID의 태그가 제공되었다. 해당 RFID 태그 모듈의 사용법을 익힐 수 있는 자료는 구글링해보면 다양하게 나오지만, 본 실습에서는 아래의 라이브러리를 활용하였다. 튜토리얼 문서라이브러리, 예제 코드를 함께 제공해주기 때문에 아주 유용하다.

라이브러리를 다운로드 받아서, IDE에 추가하는 자세한 내용은 튜토리얼 문서를 참고하도록 하자.

모든 과정이 완료되었다면, 파일 > 예제 > 사용자지정 라이브러리의 예제 탭의 AddicoreRFID > Addicore_RFID_Example 파일을 그대로 컴파일해서 보드에 업로드한다. Line 32에 Baud Rate가 9600으로 선언되어있으므로, > 시리얼모니터 로 모니터를 킨 뒤에 Baud Rate 를 맞춰준다.

RFID 수신기에 태그를 가져다대면 1초 간격(Line 90)으로 해당 태그의 ID 값을 포함한 수신 정보를 출력함을 확인할 수 있다.


실습 6 - RFID 태그 로 LED 제어하기

마지막 실습으로, RFID 태그 모듈과 LED 를 결합해서 사용하는 실습이 진행되었다. 실습 5에서 사용한 예제 파일에 코드를 조금 추가해서 구현할 수 있었다. 예제 파일을 모두 이해할 필요 없이 대략의 구조만 파악하면 된다.

82
83
84
85
86
87
88
89
90
            // Should really check all pairs, but for now we'll just use the first
            if(str[0] == 156)                      //You can change this to the first byte of your tag by finding the card's ID through the Serial Monitor
            {
                Serial.print("Hello Craig!\n");
            } else if(str[0] == 244) {             //You can change this to the first byte of your tag by finding the card's ID through the Serial Monitor
                Serial.print("Hello Erin!\n");
            }
            Serial.println();
            delay(1000);

태그의 ID 값이 str[0] 에 저장되어, ID = 156 이라면 Craig 의 것으로 인식하고 ID = 244 라면 Erin 의 것으로 인식한다고 이해할 수 있다. 156 과 244 의 값은 자신이 가지고 있는 태그의 값과 다를 수 있다. 찍어보고 시리얼 모니터에 출력되는 값을 보고 확인할 수 있다.

그러므로 if-else 문의 적절한 위치에 LED 출력을 LOW/HIGH 로 넣어주는 코드를 삽입하여 태그를 갖다댈때마다 LED 신호값이 바뀌도록 구현할 수 있다.

1
2
const int LED = 8;
int LEDState = 0;

LED 출력을 내보닐 디지털 핀과, LED 상태를 담고있을 변수를 초기값과 함께 선언하고,

 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
            // Should really check all pairs, but for now we'll just use the first
            //if(str[0] == 156)                      //You can change this to the first byte of your tag by finding the card's ID through the Serial Monitor
            if(str[0] == 212)
            {
                Serial.print("Hello Craig!\n");
                if(LEDState == 0){
                  digitalWrite(LED, HIGH);
                  LEDState = 1;
                }
                else if(LEDState == 1){
                  digitalWrite(LED, LOW);
                  LEDState = 0;
                }
            } else if(str[0] == 244) {             //You can change this to the first byte of your tag by finding the card's ID through the Serial Monitor
                Serial.print("Hello Erin!\n");
            }
            Serial.println();
            delay(1000);

LED를 토글하는 if-else 문을 Line 90-98에 추가해준다.


생각보다 다양한 모듈들이 키트에 포함되어있다는 점이 매우 인상적이었고, RFID 태그 같은 언뜻 보기에 고급기술로 다가오는 기능들까지 100줄 이내의 간단한 코드로 제어할 수 있다는 점이 몹시 흥미로웠다. 창의력과 약간의 시간(+비용)만 투자한다면 얼마든지 자신이 원하는 재밌는 시스템을 구축할 수 있을거라는 확신이 생겼다.


Comments