[아두이노] 자이로 센서(MPU-6050 or MPU-6500) 활용하기

기본 설명

가속도 센서란 지구의 중력가속도를 기준으로 사물이 얼마만큼의 힘을 받고 있는지를
측정하는 센서입니다.

자이로 센서의 구성

가만히 있을 때 센서에 작용하는 중력 가속도를 x, y, z 축으로 벡터 3개로 나누어 크기를 측정하고 가속도 센서의 값들은 정지된 상태에서도 특정한 값을 갖기 때문에 기얼어진 정도를 파악하거나 진동을 파악하는데 많이 사용됩니다.

※ 주의 사항 : 이번 코딩은 라이브러리를 I2Cdev_library, MPU6050_library를 전부 받아서 압축을 풀어줘야 합니다.

zip 파일로 된 라이브러리를 사용하려면 아래의 링크에서 다운로드 받으시고 [아두이노 zip 파일 라이브러리 추가하는 방법] 링크를 참고하세요.

아두이노 배선

아두이노I2C(IIC) 모듈
5VVCC (노랑색)
GNDGND (초록색)
A4SDA (보라색)
A5SCL (파랑색)
D2INT (주황색)
자이로 센서 배선도

아두이노 소스 코드

코드가 좀 깁니다만.
사실 #define, #if ~ #endif 만 잘 이해하는 기회로 생각해 보시기 바랍니다.

그리고…

Serial.println(mpu.testConnection() ? F(“MPU6050 connection successful”) : F(“MPU6050 connection failed”));

이게 무슨 뜻인지 한번 잘 찾아 보시기 바랍니다.

#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif

MPU6050 mpu;

#define OUTPUT_READABLE_YAWPITCHROLL
#define INTERRUPT_PIN 2
#define LED_PIN 13

bool blinkState = false;
bool dmpReady = false; 
uint8_t mpuIntStatus;  
uint8_t devStatus;     
uint16_t packetSize;   
uint16_t fifoCount;    
uint8_t fifoBuffer[64];

Quaternion q; 
VectorInt16 aa;
VectorInt16 aaReal; 
VectorInt16 aaWorld;
VectorFloat gravity;   

float euler[3]; 
float ypr[3];
uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };

volatile bool mpuInterrupt = false; 

void dmpDataReady() {
    mpuInterrupt = true;
}

void setup() {
    #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
        Wire.begin();
        Wire.setClock(400000); 
    #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
        Fastwire::setup(400, true);
    #endif

    Serial.begin(115200);
    while (!Serial);

    Serial.println(F("Initializing I2C devices..."));

    mpu.initialize();
    pinMode(INTERRUPT_PIN, INPUT);
    Serial.println(F("Testing device connections..."));
    Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
    Serial.println(F("\nSend any character to begin DMP programming and demo: "));

    while (Serial.available() && Serial.read()); // empty buffer
    while (!Serial.available());                 // wait for data
    while (Serial.available() && Serial.read()); // empty buffer again
    Serial.println(F("Initializing DMP..."));

    devStatus = mpu.dmpInitialize();
    mpu.setXGyroOffset(220);
    mpu.setYGyroOffset(76);
    mpu.setZGyroOffset(-85);
    mpu.setZAccelOffset(1788); 

    if (devStatus == 0) {
        // turn on the DMP, now that it's ready
        Serial.println(F("Enabling DMP..."));
        mpu.setDMPEnabled(true);
        Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
        attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
        mpuIntStatus = mpu.getIntStatus();
        Serial.println(F("DMP ready! Waiting for first interrupt..."));
        dmpReady = true;
        packetSize = mpu.dmpGetFIFOPacketSize();
    } else {
        Serial.print(F("DMP Initialization failed (code "));
        Serial.print(devStatus);
        Serial.println(F(")"));
    }
    pinMode(LED_PIN, OUTPUT);
}

void loop() {
    if (!dmpReady) return;
    while (!mpuInterrupt && fifoCount < packetSize) {
    }

    mpuInterrupt = false;
    mpuIntStatus = mpu.getIntStatus();
    fifoCount = mpu.getFIFOCount();

    if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
        mpu.resetFIFO();
        Serial.println(F("FIFO overflow!"));
    } else if (mpuIntStatus & 0x02) {
        while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
        mpu.getFIFOBytes(fifoBuffer, packetSize);
        fifoCount -= packetSize;

        #ifdef OUTPUT_READABLE_QUATERNION
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            Serial.print("quat\t");
            Serial.print(q.w);
            Serial.print("\t");
            Serial.print(q.x);
            Serial.print("\t");
            Serial.print(q.y);
            Serial.print("\t");
            Serial.println(q.z);
        #endif

        #ifdef OUTPUT_READABLE_EULER
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetEuler(euler, &q);
            Serial.print("euler\t");
            Serial.print(euler[0] * 180/M_PI);
            Serial.print("\t");
            Serial.print(euler[1] * 180/M_PI);
            Serial.print("\t");
            Serial.println(euler[2] * 180/M_PI);
        #endif

        #ifdef OUTPUT_READABLE_YAWPITCHROLL
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
            Serial.print("ypr\t");
            Serial.print(ypr[0] * 180/M_PI);
            Serial.print("\t");
            Serial.print(ypr[1] * 180/M_PI);
            Serial.print("\t");
            Serial.println(ypr[2] * 180/M_PI);
        #endif

        #ifdef OUTPUT_READABLE_REALACCEL
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetAccel(&aa, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
            Serial.print("areal\t");
            Serial.print(aaReal.x);
            Serial.print("\t");
            Serial.print(aaReal.y);
            Serial.print("\t");
            Serial.println(aaReal.z);
        #endif

        #ifdef OUTPUT_READABLE_WORLDACCEL
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetAccel(&aa, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
            mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
            Serial.print("aworld\t");
            Serial.print(aaWorld.x);
            Serial.print("\t");
            Serial.print(aaWorld.y);
            Serial.print("\t");
            Serial.println(aaWorld.z);
        #endif

        #ifdef OUTPUT_TEAPOT
            teapotPacket[2] = fifoBuffer[0];
            teapotPacket[3] = fifoBuffer[1];
            teapotPacket[4] = fifoBuffer[4];
            teapotPacket[5] = fifoBuffer[5];
            teapotPacket[6] = fifoBuffer[8];
            teapotPacket[7] = fifoBuffer[9];
            teapotPacket[8] = fifoBuffer[12];
            teapotPacket[9] = fifoBuffer[13];
            Serial.write(teapotPacket, 14);
            teapotPacket[11]++; 
        #endif
        blinkState = !blinkState;
        digitalWrite(LED_PIN, blinkState);
    }
}

실행 화면

배선 모습

[아두이노] 온습도 센서 활용하기

기본 설명

습도센서는 두 전극 사이의 저항 변화를 측정함으로써 공기 중의 습도 변화를 알아 낼 수 있습니다. 습도 센서의 표면에는 전극이 부착된 얇은 판이 있는데 이 판이 공기 중의 수분을 흡수하게 되고 표면에 부착된 수분의 양에 의해 전극의 전도도에 변화가 일어나게 되면 이를 감지하게 됩니다. 온도센서는 고온에서 소결된 반도체 세라믹으로 온도에 따라서 물질의 저항값이 변하는 소재를 이용하여 저항값의 변화를 감지해서 온도를 측정하게 됩니다.

– 습도 측정 범위 : 20~90% (오차 : 5%)
– 온도 측정 범위 : 0~50℃ (오차 : 2℃)

아두이노 배선

아두이노DHT11(온습도 센서)
5VVCC (주황색)
GNDGND (갈색)
D4output (빨강색)
온습도 센서 배선도

아두이노 소스 코드

#include <DHT11.h>

int pin=4;
DHT11 dht11(pin);

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

void loop()
{
int err;
float temp, humi;

if((err=dht11.read(humi, temp))==0)
{
Serial.print("temperature:");
Serial.print(temp);
Serial.print(" humidity:");
Serial.print(humi);
Serial.println();
} else {
Serial.println();
Serial.print("Error No :");
Serial.print(err);
Serial.println();
}
delay(DHT11_RETRY_DELAY);
}

여기서 유의할 점.

소스코드에 ” #include <DHT11.h>”가 생소하지 않나요?

아두이노 코딩 전부를 모든 개발자가 처음부터 끝까지 한다면 세상에 코딩이 제일 어려운 일 중에 하나가 될 겁니다. 그래서 선지적인 선배 개발자들이 자신들이 만든 부품에 맞게 라이브러리(Library)를 제공합니다.
즉, 처음부터 기계적인 특성(Data sheet)을 모두 공부해서 개발하지 않아도 쉽게 사용할 수 있도록 개발에 활용할 수 있도록 방법을 제공하는 거죠.

이 온습도 센서에는 다른 개발자들이 이 센서를 잘, 쉽게 사용할 수 있도록 개발하여 제공하는 “DHT11″이라는 라이브러리가 존재합니다.

이 라이브러리를 활용하기 전에 먼저 다음의 링크에서 라이브러리 zip 파일을 다운로드 받으세요.

그리고 다음의 링크를 참고하셔서 라이브러리를 추가하시면 됩니다.

동작 화면

[아두이노] 라이브러리 zip 파일 추가

아두이노에서 라이브러리를 추가하기 위해서는 두가지 방법이 있습니다.

첫번째는 아두이노 IDE에서 라이브러리 관리 매니저를 통해 온라인으로 추가하는 방법이 있고요.
두번째는 인터넷에서 받은 라이브러리 zip파일을 직접 추가하는 방법입니다.

이번에는 zip파일로 라이브러리를 추가해 보겠습니다.

zip파일로 된 라이브러리 추가

첫째, 추가하려는 라이브러리 zip파일을 다운로드 받습니다. 물론, 어디에 다운로드 받았는지 정확히 기억해 두어야 겠죠? ^^

두번째, 아두이노 IDE에서 “스케치” -> “라이브러리 포함하기” -> “.ZIP 라이브러리 추가” 하면 파일 선택창이 뜹니다. 여기서 다운로드 받은 라이브러리 zip파일의 위치로 가서 해당 파일을 선택합니다.

세번째, 라이브러리가 추가 되었는지 확인하기 위해 “파일”->”예제” 아래쪽에 해당 예제가 있는지 확인합니다.

간단하죠?
이제 새로운 라이브러리를 활용할 수 있습니다.

[아두이노] 서보 모터 활용하기

기본 설명

서보모터는 기어를 이용해서 단거리를 힘 있게 움직여 주는 모터입니다.

0°~180° 내에서 원하는 각도로 모터를 구동할 수 있게 됩니다.

아두이노 배선

서보 모터는 배선이 간단합니다.

전원이 5V 아두이노 전원을 그대로 사용할 수 있기 때문에 간단한 동작에 활용하기 쉽습니다.

아두이노서보 모터
5VVCC (+, 빨간색)
GNDGND (-, 갈색)
D9S (signal, 주황색)

서보 모터는

아두이노 소스코드

#include<Servo.h>

Servo motor1;

void setup() {
motor1.attach(9, 600, 2400);
}

void loop() {
motor1.write(60);
delay(1000);
motor1.write(90);
delay(1000);
motor1.write(120);
delay(1000);
}

서버 모터의 움직이는 각도를 조정하기 위해서 숫자를 조정합니다.
움직이는 각도의 범위는 0~180입니다.

서보 모터의 원리를 좀 더 알아보려면 아래의 링크를 참고하세요.

서보 모터 배선 이미지

[아두이노] 릴레이 모듈 활용하기

기본 설명

안녕하세요. RealIdea 입니다.

아두이노가 자체적으로 쓰는 전원은 5V에서 7V 사이이기는 합니다만 이 전원을 모터나 외부 모듈을 연결해서 사용할 때는 전압이 안맞거나, 전류가 모자라 오동작하는 경우가 많습니다. 이 때 사용하는 것이 릴레이 입니다.

릴레이는 외부 전원의 On-Off를 아두이노에서 제어할 수 있도록 지원하는
모듈입니다.

5V 릴레이 모듈은 간단히 말해서 작은 전압으로 큰 전압을 사용할 수 있도록 해주는 부품입니다. 아두이노 보드의 경우 최대 전압이 5V입니다. 하지만 우리가 실생활에서 사용하는 전압의 경우 5V보다 큰 20V를 사용합니다. 그래서 이를 위해 릴레이 모듈을 사용해 20V전압을 가진 장치를 사용할 수 있습니다. 또한 릴레이 보드 코딩을 통해 전력을 일정 시간 공급하고 끊는 것이 가능합니다.

주의:
릴레이 모듈을 활용할 때는 반드시 5V 릴레이를 활용해야 합니다.
아두이노 보드가 출력할 수 있는 전압이 5V를 넘을 수 없기 때문입니다. 간혹, 7V 또는 12V 릴레이가 있는데 이를 사용하면 아두이노로 직접 제어하지 못하고 승압을 하여 사용해야 합니다.

주의 2:
릴레이를 아두이노로 제어할 때는 5V….
릴레이가 On/Off 시킬 수 있는 전압은 5~220V까지 가능합니다.
제어는 5V, On/Off는 220V까지…
혼동하지 마세요. ^^

아두이노 배선

아두이노브레드보드릴레이 모듈
5V5V (빨간색)
GNDLED 짧은 선GND (파란색)
D2LED 긴 선S (초록색)
[배선도]

릴레이 위쪽에 보면 NC, COM, NO가 있는데요.
NC(Normally Close)
COM (Common)
NO(Normally Open) 입니다.

즉, NC와 COM을 연결해 놓으면 평상시 닫혀 있으니 항상 켜져 있다가 아두이노 D2 핀이 HIGH(5V)이면 LED가 꺼집니다.
NO와 COM을 연결해 놓으면 평상시 열려 있으니 항상 꺼져 있다가 아두이노 D2 핀이 HIGH(5V)이면 LED가 켜집니다.

이를 제어하려면 릴레이모듈 뒤쪽에 S, +, -을 연결해야 합니다.
S는 signal을 의미합니다. 즉, 아두이노 D2와 연결된다는 거죠.
+는 5V, VCC를 의미합니다. 즉, 릴레이 모듈도 전자 부품이니 전기 공급이 필요하니 연결하는 겁니다.
-는 GND, ground를 의미합니다.

아래 그림은 동작 장면입니다.

아두이노 소스코드

int sensor = 2;

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

void loop() {
digitalWrite(sensor, HIGH);
delay(2000);
digitalWrite(sensor, LOW);
delay(2000);
}

실행 장면

[아두이노] LCD 모듈, IIC 모듈 활용하기

기본 설명

기본적으로 LCD는 두가지 방법으로 사용할 수 있습니다. IIC와 SPI의 회로 방식 두가지가 있는데 SPI식은 회로가 굉장히 복잡하기 때문에 여기서는 IIC 방식만 배우겠습니다. 아래 사진과 같은 ICC 모듈을 LCD 모듈에 연결하여 사용합니다.

I2C(IIC) 모듈? LCD모듈?

I2C(IIC) 모듈은 시리얼 통신(USART-유선 통신)과 유사한 통신 방식으로 여러 부품을 “통신”을 통해 연결할 때 사용됩니다.

여기서는 LCD 부품을 연결하는데 사용됩니다.

LCD 모듈
여기서 사용하는 모듈은 2line 모듈입니다.
텍스트 중심으로 화면에 출력하는 기능을 가지고 있습니다.

아두이노 배선

아래의 표와 그림처럼 연결하세요.
그리고 소스코드를 아두이노 코딩 프로그램(IDE)에 불러오기 하여, 아두이노에 업로드하면 LCD에 텍스트가 나타나는 모습을 볼 수 있습니다.

테스트해 보세요.

아두이노I2C(IIC) 모듈
5VVCC(빨강색)
GNDGND(검은색)
A4SDA(초록색)
A5SCL(파랑색)
I2C 모듈, LCD 모듈과 아두이노 연결

아두이노 소스 코드

#include<LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup()
{
lcd.init();
lcd.backlight();
}

void loop()
{
lcd.setCursor(0,0);
lcd.print(“Hello. World”);
delay(1000);
}

구동 화면

“Hello world”라는 문자열이 보이시죠?
이렇게 LCD 모듈을 활용할 수 있답니다.

파이팅 입니다. ^^

팅커러(Tinkerer)

어떤 물건을 분해하고 수정하고 새롭게 설계해서 완전히 새로운 제품으로 만드는 이들을 팅커러(tinkerer)라고 부른답니다.

옛날에는 장난감을 뜯어보면서 작동원리를 알아내기도 하고, 자신의 입맛에 맞게 바꾸거나 새로운 형태로 만들 수 있다는 사실을 깨닫기도 했었는데요.

이젠 기계적으로 원리를 알아보는 것도 좋지만.
아두이노 같은 전기, 전자, 디지털 기기를 활용하여 이런 활동을 할 수도 있겠네요. ^^

나도 팅커러가 되고 싶다. ^^

I Real Idea..

아두이노를 활용한 다양한 메이커 활동을 위해 우리가 알아야 할 것이 좀 많이 있습니다.

매번 찾아다니기 보다는 한 곳에 모아서 보면 더 빠르고,
믿을 수 있는 컨텐츠를 제공받을 수 있을 거라 생각했습니다.

만드는 것이 즐거운 생활.

내 아이디어가 현실이 되는 활동을 위해 열심히 활동해 보겠습니다.