초음파 센서 모듈은 초음파를 발생시켜서 물체에 반사되어 돌아오는 시간을 측정해서 물체까리의 거리를 구할 수 있는 장치이다. 다음 그림은 parallax.com 사의 초음파 센서 모듈이다. (저가형 중국산 모듈은 4핀이 달린 것도 있으나 성능이나 사용법은 동일하다.)


전원핀,

GND핀,

출력핀, 세 개로 동작한다.

2cm 부터 3m 범위를 측정할 수 있다.

[그림 1] parallax.com 사의 초음파센서모듈


이 모듈의 사용법은 굉장히 간단하다. 일단 5V, GND 핀을 연결한 후 신호핀은 아두이노의 디지털 핀에 연결한다. 그리고 디지털핀으로 시작 신호(펄스)를 보낸 후 같은 핀에서 되돌아오는 펄스의 폭(시간)을 잰다.



[그림 2] 초음파 모듈의 동작 순서

- 시작 펄스는 5micro-sec 동안 유지해야 한다.

- 응답 펄스는 초음파가 발생한 후 되돌아온 시간 동안 HIGH를 유지한다.


이 응답 펄스의 폭은 장애물과의 거리에 비례하며 초음파가 발생된 후 되돌아올 때 까지의 시간동안 HIGH로 유지된다. 오직 하나의 디지털 핀만을 사용하므로 시작 신호를 보낸 후 바로 핀의 모드를 입력으로 전환해야 한다.  시작 펄스는 5micro-sec 동안 유지해야 한다. 각 단계는 다음과 같다.


  1. 핀을 출력으로 설정한다.

  2. LOW신호를 2micros 동안 유지한다.

  3. HIGH 신호를 5micros동안 유지한다.

  4. LOW로 신호를 내보냄과 동시에 핀을 입력으로 설정한다.

  5. pulseIn() 함수를 이용하여 펄스가 HIGH인 구간 시간을 micros 단위로 받는다.



 초음파 모듈 시간은 초음파를 송신함과 동시에 핀을 HIGH로 올리고 물체에 반사된 초음파를 수신하면 LOW로 내린다. 따라서 핀이 HIGH로 유지되는 경과 시간은 초음파가 나가서 물체에 반사되어 되돌아온 시간인 것이다.


 이 시간을 이용한 계산으로 거리 값을 도출할 수 있는데 다음과 같은 식을 이용한다. 소리는 초당 343m를 이동하므로 1cm를 이동하는데 29.155micro-sec 이 걸린다. 따라서 측정된 시간(마이크로 초 단위)를 58.31 로 나누어야 한다. 2를 곱해주는 것은 측정된 시간이 왕복 시간이기 때문이다.


 이제 이러한 내용을 바탕으로 코딩을 해보자.


#define MS_PER_CM           58.31f
#define ULTRA_SONIC_SENSOR  2

void setup() {
 Serial.begin(9600);
}

void loop() {
 float fDist = measureDist();
 if (fDist < 0) {
   Serial.println("No obstable or no sensor.");
 } else {
   Serial.print("Distance: ");
   Serial.print(fDist);
   Serial.println(" cm");
 }
 delay(100);
}

float measureDist() {
 pinMode(ULTRA_SONIC_SENSOR, OUTPUT); //(1)단계
 digitalWrite(ULTRA_SONIC_SENSOR, LOW); //(2)단계
 delayMicroseconds(2);
 digitalWrite(ULTRA_SONIC_SENSOR, HIGH); //(3)단계
 delayMicroseconds(5);
 digitalWrite(ULTRA_SONIC_SENSOR, LOW); //(4)단계
 pinMode(ULTRA_SONIC_SENSOR, INPUT);

// (5) 단계
 unsigned long ulPulseTime = pulseIn(ULTRA_SONIC_SENSOR, HIGH);
 if (ulPulseTime == 0 )
   return -1.0f;
 else
   return ulPulseTime / MS_PER_CM;
}


위 코드에서 measureDist() 함수를 실행하면 위에서 설명한 각 단계를 거쳐서 거리까지 계산한 float 값을 반환한다. 만약 pulseIn()함수의 반환값이 0이라면 -1.0f 를 반환하여 장애물이 감지되지 않은 것임을 표시한다.


아두이노 강좌 전체 목록 (TOP) >>>

C++ 언어 전체 강좌 목록 >>>

c{ard},n{ad014}

Posted by 살레시오
,

시리얼 통신 두 번째 예제

 두 번째 예제로 이번에는 PC에서 문자 하나를 받아서 그것이 ‘0’이면 LED를 끄고 ‘1’이면 LED를 켜는 프로그램을 작성해 보자. 이 경우 아두이노는 데이터가 사용자로부터 들어올 때 까지 대기 상태로 있다가 데이터가 읽히면 거기에 맞는 동작을 수행해야 한다.


#define LED 13
void setup() {
 pinMode(LED, OUTPUT);
 Serial.begin(9600);
}
void loop() {
 if ( Serial.available() ) {
   char command = Serial.read();
   if (command == '0') {
     digitalWrite(LED, LOW);
     Serial.println("LED off.");
   }
   else if (command == '1') {
     digitalWrite(LED, HIGH);
     Serial.println("LED on.");
   }
     else {
     Serial.print("Wrong command :");
     Serial.println(command);
     }
 }
}


이 예제에서는 다음과 같은 함수들이 사용되었다.


   int Serial.available() 함수

  • 전송되어 내부버퍼(64바이트)에 저장된 데이터의 개수를 반환한다.

  • 입력인수는 없다.


   int Serial.read() 함수

  • 전송되어 내부 버퍼(64바이트)에 저장된 데이터 중 가장 첫 번째 데이터(ASCII코드)를 읽어서 반환한다.

  • 이 함수가 수행되면 내부 버퍼의 크기는 하나가 줄어든다.

  • 내부 버퍼가 비었다면 -1을 반환하다.


   int Serial.println() 함수

  • 출력 문자열의 끝에 줄바꿈 기호 ‘\r\n’ 가 자동으로 붙는다는 점 외에는 Serial.print()함수와 동일한 동작을 수행한다.


이 예제에서는 USRT 통신기의 내부 버퍼의 개념을 이해할 필요가 있다. 내부 버퍼란 전송된 데이터가 일시적으로 저장되는 내부 메모리를 말하며 데이터가 전송된 순서대로 저장된다. 아두이노의 내부 버퍼의 크기는 64바이트이다. 전송되어 저장된 데이터를 사용하려면 내부 버퍼에서 이 데이터를 읽어내야 하는데 이때 사용되는 함수가 Serial.read()함수이다. 가장 먼저 전송된 데이터 하나를 읽어낸 후 그 데이터는 버퍼에서 삭제되며, 만약 버퍼가 비었다면 -1을 반환한다. 따라서 버퍼에 읽어낼 데이터가 있는지 없는지를 먼저 검사하는 것이 일반적이고 이때 사용되는 함수가 Serial.available()이다. 이 함수는 버퍼에 저장된 데이터의 개수를 반환하며 따라서 버퍼가 비어있다면 0을 반환한다.


 이런 사항들을 이해하였다면 두 번째 예제를 이해하는데 어려움이 없을 것이다. 데이터가 전송되어 오면 그것을 읽어들여서 ‘1’이면 LED를 켜고, ‘0’이면 끈다. 그 이외의 데이터에 대해서는 잘못된 명령이라는 문자열을 출력한다.


시리얼 통신 세 번째 예제

 세 번째로 11번 핀에 부저가 연결되었다고 가정하고 명령어를 하나 받아서 다음과 같은 동작을 수행하는 예제를 작성해 보도록 하겠다.


       - ‘0’ : LED를 끈다.

       - ‘1’ : LED를 켠다.

       - ‘2’ : 부저를 짧게 한 번 울린다. (삑)

       - ‘3’ : 부저를 짧게 두 번 울린다.(삐삑)

       - 그 외의 명령어들은 잘못된 것이라는 메세지를 출력한다.


이 예제의 경우는 처리해야 될 경우의 수가 많기 때문에 if-else 명령보다는 switch-case 명령이 더 효율적이다.


#define LED 13
#define BUZ 11

void setup() {
 pinMode(LED, OUTPUT);
 pinMode(BUZ, OUTPUT);
 Serial.begin(9600);
}

void loop() {
 if ( Serial.available() ) {
   char command = Serial.read();
   switch(command) {
     case '0':
       digitalWrite(LED, LOW);
       Serial.println("LED off.");
       break;
   case '1' :
     digitalWrite(LED, HIGH);
     Serial.println("LED on.");
     break;
   case '2' :
     digitalWrite(BUZ, HIGH);
     delay(50);
     digitalWrite(BUZ, LOW);
     break;
   case '3' :
     digitalWrite(BUZ, HIGH);
     delay(50);
     digitalWrite(BUZ, LOW);
     delay(50);
     digitalWrite(BUZ, HIGH);
     delay(50);
     digitalWrite(BUZ, LOW);
     break;
   default:
     Serial.print("Wrong command :");
     Serial.println(command);
   }
 }
}


아두이노 강좌 전체 목록 (TOP) >>>

C++ 언어 전체 강좌 목록 >>>

c{ard},n{ad013}

Posted by 살레시오
,

 시리얼 통신(serial communication)은 기기들간 데이터를 주고 받는 방법 중 하나인데 병렬 통신 (parallel communication) 방식에 비해서 통신선의 갯수가 적다는 장점이 있다.  아두이노는 UART (universal asynchronous receiver and transmitter), SPI, I2C 방식의 시리얼 통신을 지원하는데 이중 UART는 주로 아두이노와 PC간의 통신을 하는데 사용된다. 아두이노는 하나 이상의 UART 통신기가 내장되어 있는데  라이브러리와 IDE에 내장된 터미널(terminal, 주고 받는 데이터를 확인할 수 있는 프로그램)을 이용하면 손쉽게 PC와의 통신을  수행할 수 있다.


 아두이노 우노의 경우 0번과 1번핀이 시리얼 통신에 사용된다. 이 핀들은 내부적으로 USB통신을 담당하는 칩과 연결되어서 USB신호로 변환된 후 PC에 전달된다. 반대로 PC에서 보내지는 USB신호는 이 칩에서 시리얼 통신 신호로 변환되어 아두이노의 AVR에 전달된다. 따라서 만약 아두이노가 PC와의 통신을 수행하고 있다면 이 핀들을 다른 용도로 사용하면 안된다. 그리고 통신을 수행할 때에는 TX, RX라고 마킹된 LED가 깜빡인다.  일단 사용자는 아두이노와 PC간에 USB케이블로 연결하면 통신 실습을 할 준비가 끝나게 된다.


  • 시리얼 통신에 사용되는 두 개의 핀(빨간색)과 usb변환 칩(하늘색)

  • 내부적으로 USART 신호는 USB신호로 변환되어 PC에 전송된다.


시리얼 통신 첫 번째 예제

 첫 번째 예제로 아두이노에서 PC로 간단한 데이터를 전송하는 예를 해보도록 하겠다. 단순히 아두이노에게 전원을 인가하면 “I’m ready.” 라는 메세지를 PC에 전송하는 예이다.


void setup() {
 Serial.begin(9600);
 Serial.printl"I'm ready.");
}
void loop() {
}


이 예제를 다운로드 한 후 터미널을 켜고 리셋버튼을 누르면 터미널에 “I’m ready.”라는 문자열이 찍히는 것을 확인할 수 있다. 터미널 실행 버튼을 누르면 아두이노에 자동으로 리셋신호가 걸려서 프로그램이 처음부터 수행된다. 그리고 프로그램 다운로드 중에서는 터미널을 실행시키지 못 한다는 것도 알아두자.



 UART와 관련되 아두이노의 라이브러리는 Serial클래스에 다 모여있다. (자세한 설명을 여기 참조) 일단 이 예제에서 보면 setup()함수내에서 두 개의 함수가 호출되었다.


void Serial.begin(long baud_rate) 함수

  • UART 통신을 초기화 시킨다.

  • 통신 속도(baud rate)를 입력으로 받는다.        

  • 9600, 19200, 57600, 115200 등 여러 baud rate를 지원한다.


long Serial.print(val) 함수

  • 입력값을 ASCII값으로 변환하여 PC쪽으로 출력한다.

  • 전송된 데이터의 바이트 수를 리턴한다. (잘 사용되지 않음)

  • 비동기 통신 방식이므로 데이터가 전송되기 전에 리턴된다.

  • 입력 변수 val은 어떤 데이터 타입도 가능하다. 예를 들면

    • Serial.print(78) gives "78"

    • Serial.print(1.23456) gives "1.23"

    • Serial.print('N') gives "N"

    • Serial.print("Hello world.") gives "Hello world."


  • 두 번째 인수로 출력 형식을 지정할 수 있다. 예를 들면

    • Serial.print(78, BIN) gives "1001110"

    • Serial.print(78, OCT) gives "116"

    • Serial.print(78, DEC) gives "78"

    • Serial.print(78, HEX) gives "4E"

    • Serial.println(1.23456, 0) gives "1"

    • Serial.println(1.23456, 2) gives "1.23"

    • Serial.println(1.23456, 4) gives "1.2346"


첫 번째 예제는 setup() 함수 내에서 UART를 초기화 하고 문자열 하나를 보내는 아주 간단한 예제이다.


아두이노 강좌 전체 목록 (TOP) >>>

C++ 언어 전체 강좌 목록 >>>

c{ard},n{ad012}

Posted by 살레시오
,