아두이노에서 지원하는 통신 인터페이스를 살펴보고, 이에 대한 사용법을 설명한다.

시리얼 통신(UART)

임베디드 시스템의 포팅을 할 때, 가장 먼저 해야하는 인터페이스가 바로 UART 다. 그만큼 가장 중요하면서도 기본이 되는 인터페이스이기 때문이다.
흔히 포팅의 마지막 단계라하면, UART 가 뚫렸을 때를 말하는 것도 이런 이유다.

요즘은 노트북 뿐만아니라 PC 도 프린터 포트나 시리얼 포트가 없다. 그래서 요즘 사용하는 것이 USB-to-Serial 이다. 아두이노 역시 이를 지원한다. USB 를 PC 와 연결하는 순간, 전력 뿐만아니라 시리얼 포트가 열린다.

코드는 아래와 같다.

const int SENSOR = 0;
int val = 0;
 
void setup()
{
  Serial.begin(9600);        // 시리얼포트 초기화, 전송 속도 설정
}
 
void loop()
{
  val = analogRead(SENSOR);    // 센서로부터 값을 읽어들임
 
  Serial.println(val);         ---- 1
 
  delay(100);
}

프로그램 다운로드 후, IDE 의 가장 오른쪽 아이콘을 실행한다. 빠른 속도로 올라가는 숫자들이 보인다.
센서에 의해 밝기에 따라 변하는 저항값으로 인해 실시간으로 변하는 전압의 크기를 숫자로 읽어들인다.

  1. 시리얼 함수가 등장했다. 변수값을 시리얼 포트에 출력한다.

프로세싱(Processing)과 연동하기

PC 와의 인터렉티브한 통신을 원한다면, 이를 가능하게 해줄 언어가 있다. 바로 Processing 이라는 오픈소스 언어이다.
여러가지 API 와 라이브러리를 제공하고 이를 이용해서 도형 같은 그림을 그릴 수 있다. 그림을 그리는 것에 그치지 않고, 이를 아두이노 같은 하드웨어와 연동할 수 있다.
프로세싱의 문법은 C 와 동일하다.
아두이노 IDE 처럼 별도의 프로세싱 IDE 를 설치해야 한다. http://processing.org/download/ 에서 다운받을 수 있고 현재 최신버전은 2.0 Beta 7 이다.
바이너리 형태이기 때문에 압축을 풀고, 바로 실행하면 된다. 놀라울 정도로 아두이노 IDE 와 닮았다.

아래 그림처럼 회로를 구성한다.

여기서 만들어볼 것은 인터넷에 접속해 Make 블로그(blog.makezine.com)의 현재 기사 목록을 불러와서 peace, love, arduino 라는 단어가 얼마나 언급되었는지 횟수를 세고 이 값들로 색을 만들어 램프에 색을 밝히는 장치이다.
기사에 접근해서 횟수를 세는 것은 프로세싱으로 구현되고, 램프에 색을 밝히는 것은 아두이노로 구현될 것이다.

이를 통해 Serial 포트를 통해 프로세싱과 아두이노가 서로 정보를 원활히 주고 받는 지를 확인할 수 있다.
프로세싱은 블로그를 파싱한 정보를 시리얼 포트로 보내고, 아두이노에서 보낸 조광센서의 값을 받는다.
아두이노는 조광센서로 부터 받은 값을 시리얼 포트로 보내고, 프로세싱에서 파싱한 정보를 받는다.

먼저 프로세싱 코드부터 살펴보자.

// Example 08A: Arduino networked lamp
// parts of the code are inspired
// by a blog post by Tod E. Kurt (todbot.com)
//
// Copy and paste this example into an empty Processing sketch
 
import processing.serial.*;
import java.net.*;                     // 추가
import java.io.*;                       // 추가
import java.util.*;                     // 추가
 
String feed = "http://blog.makezine.com/index.xml";
 
int interval = 10;  // retrieve feed every 60 seconds;
int lastTime;       // the last time we fetched the content
 
int love    = 0;
int peace   = 0;
int arduino = 0;
 
int light = 0;  // light level measured by the lamp
 
Serial port;
color c;
String cs;
 
String buffer = ""; // Accumulates characters coming from Arduino
 
PFont font;
 
void setup() {
  size(640,480);
  frameRate(10);    // we don't need fast updates
 
  //font = loadFont("HelveticaNeue-Bold-32.vlw");
  font = loadFont("Gulim-32.vlw");    
  fill(255);  
  textFont(font, 32);
  // IMPORTANT NOTE:
  // The first serial port retrieved by Serial.list()
  // should be your Arduino. If not, uncomment the next
  // line by deleting the // before it, and re-run the
  // sketch to see a list of serial ports. Then, change
  // the 0 in between [ and ] to the number of the port
  // that your Arduino is connected to.
  //println(Serial.list());
  String arduinoPort = Serial.list()[0];
  port = new Serial(this, arduinoPort, 9600); // connect to Arduino
 
  lastTime = 0;
  fetchData();
}
 
void draw() {
  background( c );
  int n = (interval - ((millis()-lastTime)/1000));
 
  // Build a colour based on the 3 values
  c = color(peace, love, arduino);
  cs = "#" + hex(c,6); // Prepare a string to be sent to Arduino
 
  text("Arduino Networked Lamp", 10,40);
  text("Reading feed:", 10, 100);
  text(feed, 10, 140);
 
  text("Next update in "+ n + " seconds",10,450);
  text("peace" ,10,200);
  text(" " + peace, 130, 200);
  rect(200,172, peace, 28);
 
  text("love ",10,240);
  text(" " + love, 130, 240);
  rect(200,212, love, 28);
 
  text("arduino ",10,280);
  text(" " + arduino, 130, 280);
  rect(200,252, arduino, 28);
 
  // write the colour string to the screen
  text("sending", 10, 340);
  text(cs, 200,340);
 
  text("light level", 10, 380);
  rect(200, 352,light/10.23,28); // this turns 1023 into 100
 
  if (n <= 0) {
    fetchData();
    lastTime = millis();
  }
 
  port.write(cs); // send data to Arduino
 
  if (port.available() > 0) { // check if there is data waiting
    int inByte = port.read(); // read one byte
    if (inByte != 10) { // if byte is not newline
      buffer = buffer + char(inByte); // just add it to the buffer
    }
    else {
 
      // newline reached, let's process the data
      if (buffer.length() > 1) { // make sure there is enough data
 
        // chop off the last character, it's a carriage return
        // (a carriage return is the character at the end of a
        // line of text)
        buffer = buffer.substring(0,buffer.length() -1);
 
        // turn the buffer from string into an integer number
        light = int(buffer);
 
        // clean the buffer for the next read cycle
        buffer = "";
 
        // We're likely falling behind in taking readings
        // from Arduino. So let's clear the backlog of
        // incoming sensor readings so the next reading is
        // up-to-date.
        port.clear(); 
      }
    } 
  }
 
}
 
void fetchData() {
  // we use these strings to parse the feed
  String data; 
  String chunk;
 
  // zero the counters
  love    = 0;
  peace   = 0;
  arduino = 0;
  try {
    URL url = new URL(feed);  // An object to represent the URL
    // prepare a connection   
    URLConnection conn = url.openConnection(); 
    conn.connect(); // now connect to the Website
 
    // this is a bit of virtual plumbing as we connect
    // the data coming from the connection to a buffered
    // reader that reads the data one line at a time.
    BufferedReader in = new
      BufferedReader(new InputStreamReader(conn.getInputStream()));
 
    // read each line from the feed
    while ((data = in.readLine()) != null) {
 
      StringTokenizer st =
        new StringTokenizer(data,"\"<>,.()[] ");// break it down
      while (st.hasMoreTokens()) {
        // each chunk of data is made lowercase
        chunk= st.nextToken().toLowerCase() ;
 
        if (chunk.indexOf("love") >= 0 ) // found "love"?
          love++;    // increment love by 1
        if (chunk.indexOf("peace") >= 0)   // found "peace"?
          peace++;   // increment peace by 1
        if (chunk.indexOf("arduino") >= 0) // found "arduino"?
          arduino++; // increment arduino by 1
      }
    }
 
    // Set 64 to be the maximum number of references we care about.
    if (peace > 64)   peace = 64;
    if (love > 64)    love = 64;
    if (arduino > 64) arduino = 64;
 
    peace = peace * 4;     // multiply by 4 so that the max is 255,
    love = love * 4;       // which comes in handy when building a
    arduino = arduino * 4; // colour that is made of 4 bytes (ARGB)
  } 
  catch (Exception ex) { // If there was an error, stop the sketch
    ex.printStackTrace();
    System.out.println("ERROR: "+ex.getMessage());
  }
 
}

책(손에 잡히는 아두이노)에 나와있는 소스코드를 그대로 컴파일하면 'Cannot find a class or type named URL' 에러가 발생한다. 인터넷을 뒤져 위의 코드를 찾아냈다. 그대로 사용하면 에러없이 컴파일된다.
실행하기 전에 선행되어야 할 것이 있다.

  1. 인터넷에 연결되어 있을 것
  2. 아두이노를 USB 에 연결하고 있을 것

특히 2번째가 중요한데, 코드에 시리얼 포트를 여는 부분이 있기 때문에 연결되지 않을 경우 에러가 발생한다.

다음은 아두이노 코드다.

// Example 08B: Arduino Networked Lamp
const int SENSOR = 0;
const int R_LED = 9;
const int G_LED = 10;
const int B_LED = 11;
const int BUTTON = 12;
 
int val = 0; // variable to store the value coming from the sensor
 
int btn = LOW;
int old_btn = LOW;
int state = 0;
char buffer[7] ;
int pointer = 0;
byte inByte = 0;
 
byte r = 0;
byte g = 0;
byte b = 0;
 
void setup() {
  Serial.begin(9600); // open the serial port
  pinMode(BUTTON, INPUT);
}
 
void loop() {
  val = analogRead(SENSOR); // read the value from the sensor
  Serial.println(val);      // print the value to
  // the serial port
 
  if (Serial.available() >0) {
 
    // read the incoming byte:
    inByte = Serial.read();
 
    // If the marker's found, next 6 characters are the colour
    if (inByte == '#') {
 
      while (pointer < 6) { // accumulate 6 chars
        buffer[pointer] = Serial.read(); // store in the buffer
        pointer++; // move the pointer forward by 1
 
      }
 
      // now we have the 3 numbers stored as hex numbers
      // we need to decode them into 3 bytes r, g and b
      r = hex2dec(buffer[1]) + hex2dec(buffer[0]) * 16;
      g = hex2dec(buffer[3]) + hex2dec(buffer[2]) * 16;
      b = hex2dec(buffer[5]) + hex2dec(buffer[4]) * 16;
 
      pointer = 0; // reset the pointer so we can reuse the buffer
    }
  }
 
  btn = digitalRead(BUTTON); // read input value and store it
 
  // Check if there was a transition
  if ((btn == HIGH) && (old_btn == LOW)){
    state = 1 - state;
  }
 
  old_btn = btn; // val is now old, let's store it
 
  if (state == 1) { // if the lamp is on
 
    analogWrite(R_LED, r); // turn the leds on
    analogWrite(G_LED, g); // at the colour
    analogWrite(B_LED, b); // sent by the computer
  } 
  else {
 
    analogWrite(R_LED, 0); // otherwise turn off
    analogWrite(G_LED, 0);
    analogWrite(B_LED, 0);
  }
 
  delay(100); // wait 100ms between each send
}
 
int hex2dec(byte c) { // converts one HEX character into a number
  if (c >= '0' && c <= '9') {
    return c - '0';
  } 
  else if (c >= 'A' && c <= 'F') {
    return c - 'A' + 10;
  }
}

실행 순서는 '아두이노 빌드 및 컴파일 및 실행 → 프로세싱 컴파일 및 실행' 순으로 한다.
제대로 동작한다면, 윈도우에 light level 항목이 빛 센서의 값에 따라 변화됨을 볼 수 있다. 또한 파싱된 3 단어의 수에 따라 3 개의 LED 가 각각 다른 밝기로 켜질 것이다.

앞서 설명했듯이, 아두이노와 프로세싱은 하나의 인터페이스를 사용해 통신한다. 아두이노를 USB 에 연결하면 리눅스에서는 '/dev/ttyACM0' 로 인식한다.
아두이노를 실행하고, 시리얼 창을 띄우고 난 후, 프로세싱을 실행하면 에러가 발생한다.

/dev/ttyACM0 라는 디바이스를 먼저 선점하고 있기 때문이다. 이럴 때는 실행순서(아두이노 → 프로세싱)에 따라 동작시키고, 아두이노의 시리얼 창은 띄우지 않는다. 만일 부득이하게 시리얼 창을 봐야할 일이 있다면, 아래처럼 꼼수를 쓴다.

#cd /dev
#ln -s /dev/ttyACM0 /dev/ttyUSB0

IDE 에서는 두개의 디바이스로 보이기 때문에, 아두이노, 프로세싱 IDE 각각 지정해서 사용할 수 있다.

SPI 통신

이더넷 쉴드에는 mini SD 카드 슬롯이 장착되어 있다. 여기에 mini SD 메모리 카드를 삽입해서 Read/Write 를 할 수 있다 .
유의할 점은 이더넷 쉴드에서 Ethernet 모듈과 mini SD card 모듈을 동시에 사용할 수 없다는 것이다.

mini SD card 를 컨트롤 할 때 사용하는 인터페이스는 SPI 에는 서로 분리되어 있는 입력(MOSI) 및 출력(MISO)회선과 함께 하나의 클럭회선이 있다.
이 세 회선은 하나 이상의 슬레이브에 있는 해당 회선에 연결된다. 슬레이브는 Slave Select(SS) 회선을 통해 전송되는 신호를 사용해서 식별된다.

SPI 핀에 사용되는 핀 번호는 아래와 같다.

SPI 신호 표준 아두이노 보드
SCLK(클럭) 13
MISO(데이터 출력) 12
MOSI(데이터 입력) 11
SS(슬레이브 선택) 4

아두이노의 예제 코드를 수정없이 바로 사용해서 mini SD 카드에 파일을 R/W 할 수 있다. '파일 → 예제 → SD → ReadWrite' 를 선택한다.

/*
  SD card read/write
 
 This example shows how to read and write data to and from an SD card file 	
 The circuit:
 * SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4
 
 created   Nov 2010
 by David A. Mellis
 modified 9 Apr 2012
 by Tom Igoe
 
 This example code is in the public domain.
 
 */
 
#include <SD.h>       
 
File myFile;       // File 구조체 선언(SD.h 파일 참조)
 
void setup()
{
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
 
 
  Serial.print("Initializing SD card...");
  // On the Ethernet Shield, CS is pin 4. It's set as an output by default.
  // Note that even if it's not used as the CS pin, the hardware SS pin 
  // (10 on most Arduino boards, 53 on the Mega) must be left as an output 
  // or the SD library functions will not work. 
   pinMode(10, OUTPUT);
 
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
 
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  myFile = SD.open("test.txt", FILE_WRITE);                        // test.txt 파일이 없으면 생성
 
  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to test.txt...");
    myFile.println("testing 1, 2, 3.");                      // text.txt 파일에 write
	// close the file:
    myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
 
  // re-open the file for reading:
  myFile = SD.open("test.txt");
  if (myFile) {
    Serial.println("test.txt:");
 
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
    	Serial.write(myFile.read());
    }
    // close the file:
    myFile.close();
  } else {
  	// if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
}
 
void loop()
{
	// nothing happens after setup
}

TEST.TXT 라는 파일의 생성여부와 내용을 확인하기 위해 mini SD card 를 아두이노로부터 분리하여 마운트하여 확인해보자.
아두이노의 가장 큰 장점이라고 한다면, API 가 직관적이고 단순하다는 것이다.
위의 예제 만으로도 파일 I/O 의 모든 것을 할 수 있다.

  • computer/embedded/아두이노_그대로_따라하기_-_4.인터페이스_통신하기.txt
  • Last modified: 4 years ago
  • by likewind