사운드 센서 모듈과 인터럽트 (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(): 화면 지우기

참고 자료)