[아두이노] 3x3x3 CUBE LED 제어 III

IOT/아두이노|2019. 3. 21. 09:00

[아두이노] 3x3x3 CUBE LED 제어 III



오늘은 좀 더 프로그램 코딩에 관해서 이야기 하도록 하겠습니다. 그리고 제목을 3x3x3 CUBE LED를 다룬다고 해놓고 지난시간에 2x2x2 CUBE LED를 실험해서 좀 찜찜한 기분이 들어서 노가다를 감수하고 코딩이 간소화 되니깐 3x3x3 CUBE LED를 해도 되겠다 싶어서 회로도 디자인을 했네요. 우선 배치도를 구상하고 최대한 보기 편하게 만들려고 노력했네요. 전개는 우선 지난시간에 코딩한 2x2x2 CUBE LED 코딩을 기반으로 프로그래머 스타일의 문법을 가미하면서 간소화 시키는 것을 설명드리고 그다음 간소화한 코딩을 3x3x3 CUBE LED 회로도에 맞게 변경하여 실험하는 결과를 보여드리도록 하겠습니다.

1. 2x2x2 CUBE LED 코드 간소화


아래 코드만 봐도 머리가 아프죠. 이걸 프로그램 문법을 이용해서 간소화 하도록 하겠습니다.

int layer1 = 11;
int layer2 = 12;

int room1 = 2;
int room2 = 3;
int room3 = 4;
int room4 = 5;

void setup()
{
  pinMode(layer1, OUTPUT);
  pinMode(layer2, OUTPUT);

  pinMode(room1, OUTPUT);
  pinMode(room2, OUTPUT);
  pinMode(room3, OUTPUT);
  pinMode(room4, OUTPUT);

  //초기화 각 층을 막아놓습니다.
  digitalWrite(layer1, HIGH);
  digitalWrite(layer2, HIGH);
}

void loop()
{
  //1층 점등
  digitalWrite(layer1, LOW);  
  digitalWrite(room1, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room2, LOW);  
  digitalWrite(room3, HIGH);  
  delay(1000);

  digitalWrite(room3, LOW);  
  digitalWrite(room4, HIGH);  
  delay(1000);
  
  digitalWrite(room4, LOW);  
  digitalWrite(layer1, HIGH);  
  delay(1000);
  
  //2층 점등
  
  digitalWrite(layer2, LOW);  
  digitalWrite(room1, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room2, LOW);  
  digitalWrite(room3, HIGH);  
  delay(1000);

  digitalWrite(room3, LOW);  
  digitalWrite(room4, HIGH);  
  delay(1000);
  
  digitalWrite(room4, LOW);  
  digitalWrite(layer2, HIGH);  
  delay(1000);

}

1) 변수를 배열변수로 바꾼다.

기존수정
int layer1 = 11; int layer2 = 12;int layer[2] = {11, 12};
int room1 = 2; int room2 = 3; int room3 = 4; int room4 = 5;int room[4] = { 2, 3, 4, 5}




2) for문을 사용하여 중복 코드를 제거한다.

기존 코드

  pinMode(layer1, OUTPUT);
  pinMode(layer2, OUTPUT);
  //초기화 각 층을 막아놓습니다.
  digitalWrite(layer1, HIGH);
  digitalWrite(layer2, HIGH);

수정 코드

    for(int i=0;i<2;i++){
        pinMode(layer[i], OUTPUT);
        digitalWrite(layer[i], HIGH);
  }

기존 코드

  pinMode(room1, OUTPUT);
  pinMode(room2, OUTPUT);
  pinMode(room3, OUTPUT);
  pinMode(room4, OUTPUT);

수정 코드

    for(int i=0;i<4;i++){
        pinMode(room[i], OUTPUT);
    }

3) void loop() 안의 코드를 간소화

수정 코드

  for(int i=0;i<2;i++){
    digitalWrite(layer[i], LOW);              
    for(int j=0;j<4;j++){
           digitalWrite(room[j], HIGH);  
           delay(1000);
           digitalWrite(room[j], LOW);  
    }    
    digitalWrite(layer[i], HIGH);      
  }

1,2번의 수정은 코드만 보면 대충 뭘 줄였는지 알 수 있지만 3번 코드 간소화 경우는 로직이라서 간단히 설명을 드리겠습니다. for문은 순차적으로 반복하는 문법입니다.

예)

for(int i=0;i<3;i++) { 
    Serial.println("Hellow" );
}

for문은 i가 0시작해서 i<3보다 작을때까지 반복합니다. 뒤에 i++은 증감연산자로 'i=i+1'로 생각하시면 됩니다. for문이 한번씩 돌때마다 i++로 i가 1씩 증가하는데 그렇게 되면 결과는 다음과 같습니다.

결과 :

Hellow
Hellow
Hellow

이렇게 i가 0부터 0,1,2까지 참임으로 for문 안의 명령어 Serial.println()함수를 수행하지만 i가 3이되면 거짓으로 for문을 빠져 나옵니다.

이제 for문을 이해했으면 왜 수정코드를 이렇게 코딩했을까요.

2x2x2 cube led은 1층 4개, 2층 4개의 LED로 구성되어 있잖아요. 여기서 1층부터 순차적으로 1,2,3,4호실에 불이 들어와야 하니깐 4번을 순차적으로 같은 동작을 수행해야 합니다. 그래서 for문으로 4번을 순차적으로 반복할려면 아래와 같은 구조가 됩니다.

for(j=0;j<4;j++){
  명령문;
}

1호실 부터 1초 단위로 불이 들어올려면 1호실만 HIGH 상태가 1초동안 대기 되었다가 다시 LOW로 바뀌고 2호실, 3호실, 4호실 순으로 반복되어야 겠죠.

digitalWrite(layer[i], LOW);  // 해당 i층 Gnd 상태 (해당층 개방)
for(int j=0;j<4;j++){
  digitalWrite(room[j], HIGH);  //해당 j호실 전원 공급
  delay(1000);
  digitalWrite(room[j], LOW);  //해당 j호실 전원 차단  
}
digitalWrite(layer[i], HIGH);  //해당 i층 HIGH 상태 (해당층 닫힘)

이렇게 되는 것이죠. 그리고 Vcc와 Gnd은 한쌍이라고 했죠. 1층을 Gnd(-)로 하려면 1층을 LOW상태로 만들어 합니다. 그래서 for문 밖에다가 1층을 LOW상태로 만들었네요. 왜 안에다가 안넣고 밖에다가 표현했을까요. 그 이유는 아두이노가 1층을 Gnd 상태로 만들었는데 또다시 Gnd 상태 만들어라고 반복 명령을 내리면 비효율적인 코딩이 되잖아요. 구지 반복 명령을 할 필요없이 한번만 해도 되는 문장은 for문에 넣을 필요는 없습니다.

여기서 for문이 2중 for문으로 구성되었는데 그 이유는 1층 1,2,3,4호실을 깜박였으면 2층 1,2,3,4호실 깜박여야 하잖아요. 각 호실을 깜박이는 동작을 for문으로 만들었는데 그 for문을 2층이니깐 2번 반복해야 하니깐 2중 for문을 만들어야 합니다.

for(int i=0;i<2;i++){ // i=0,1 일때까지만 2번 반복 (층 반복)
 해당층 개방;
 for(int j=0;j<4;j++){ // j=0,1,2,3 일때까지만 4번 반복 (호실 반복)
    명령문;
 }
 해당층 닫힘;
}

그래서 아래와 같은 로직이 나오게 된 것이죠.

    for(int i=0;i<2;i++){
        digitalWrite(layer[i], LOW);  // 해당 i층 Gnd 상태 (해당층 개방)
        for(int j=0;j<4;j++){
            digitalWrite(room[j], HIGH);  //해당 j호실 전원 공급
            delay(1000);
            digitalWrite(room[j], LOW);  //해당 j호실 전원 차단  
        }
     digitalWrite(layer[i], HIGH);  //해당 i층 HIGH 상태 (해당층 닫힘)
    }

전체적으로 종합해보면

int layer[2] ={11,12};
int room[4] = {2,3,4,5};


void setup()
{
  for(int i=0;i<2;i++){
    pinMode(layer[i], OUTPUT);
    digitalWrite(layer[i], HIGH);
  }
  
  for(int i=0;i<4;i++){
    pinMode(room[i], OUTPUT);
  }
}

void loop()
{
    for(int i=0;i<2;i++){
        digitalWrite(layer[i], LOW);  // 해당 i층 Gnd 상태 (해당층 개방)
        for(int j=0;j<4;j++){
            digitalWrite(room[j], HIGH);  //해당 j호실 전원 공급
            delay(1000);
            digitalWrite(room[j], LOW);  //해당 j호실 전원 차단  
        }
     digitalWrite(layer[i], HIGH);  //해당 i층 HIGH 상태 (해당층 닫힘)
    }
}

2. 3x3x3 CUBE LED 회로도 구성


  • 준비물 : Red, Blue, Green LED 각각 9개, 저항 220옴 3개, 아두이노우노, 뻥판
  • 내용 : 3x3x3 CUBE LED 형태를 표현하자.


입체적 이미지를 평면적으로 뻥판에 나타내면 아래와 같은 회로도가 그려집니다.


실제로 제작을 한다면


위 그림처럼 철사를 사각형으로 만들고 빨간 꼭지점에 LED의 '-' 쪽을 각 꼭지점에 납땜하시면 됩니다. 그러면 총 9개가 하나의 극으로 연결이 되는데 이걸 1층이라고 생각하시면 됩니다. 이걸 3개를 만들어서 LED의 '+'쪽을 세로로 연결하시면 3층이 완성되어 전체 이미지는 3x3x3 CUBE LED가 됩니다. 실제로 만들면 무지 간단한데 이걸 가상시뮬레이터에서 표현하고 뻥판에 배치하니깐 무지 복잡해 보이네요.

최대한 보기 쉽게 표현 한건데 이것 역시 완성하고 나니깐 지난시간의 회로도보다 더 복잡하네요.

3. 코딩


  • 사용함수 : pinMode(), digitalWrite(), delay()
  • 내용 : 간단히 2x2x2 CUBE LED를 순차적으로 깜박이기
  • 참고 : [아두이노] LED 제어

복습

  • pinMode(사용핀, 사용모드) : 사용핀을 INPUT/OUTPUT/INPUT_PULLUP 모드중 뭘로 사용할지를 정함.
  • digitalWrite(사용핀, 출력형태) : 사용핀을 HIGH(5V) or LOW(0V) 중 출력 형태를 정함.
  • delay(1000) : 1초(1000)를 대기한다.

2x2x2 CUBE LED 간소화 코딩을 3x3x3 CUBE LED 형태로 수정

int layer[3] = {A0,A1,A2};
int room[9] = {10,9,8,7,6,5,4,3,2};
int m_layer = 3;  // 층 수
int m_room = 9; // 호실 수

void setup()
{
  for(int i=0;i<m_layer;i++){
    pinMode(layer[i], OUTPUT);
    digitalWrite(layer[i], HIGH);    
  }
  
  for(int i=0;i<m_room;i++){
    pinMode(room[i], OUTPUT);
  }
}

void loop()
{
  
  for(int i=0;i<m_layer;i++){
    digitalWrite(layer[i], LOW);  
    for(int j=0;j<m_room;j++){
      digitalWrite(room[j], HIGH);  
      delay(100);
      digitalWrite(room[j], LOW);  
    }
    digitalWrite(layer[i], HIGH);  
  }
}

층수와 호실수를 변수로 따로 빼서 setup(), loop() 함수 로직은 이제 수정하지 않는 방향으로 변경했네요. 이렇게 하면 위에 변수 선언부분만 수벙하시면 2x2x2 CUBE LED로 바꿀 수 있고 3x3x3 CUBE LED로도 쉽게 변경이 가능해집니다.

4. 결과


지난 시간에 가상시뮬레이터 결과가 지연 렉 때문에 정상적으로 결과가 안나와서 실제 아두이노로 표현했는데 브라우저 문제였네요. 오늘 크롬에서 가상시뮬레이터를 돌리니깐 어제 1초단위로 깜박이는 동작이 깔끔하게 보여줬네요. 오늘도 가상시뮬레이터로 돌린 결과와 현실 아두이노에서 돌린 결과를 실제 촬영해서 기록했네요.

그런데 실제 아두이노에서는 전선이 부족하여 정상적으로 동작은 하지만 일부 LED은 연결할 수 없어서 일부 LED에 불이 안들어온 점은 감안하고 보세요. 최대한 선들을 구햇지만 선 부족으로 1층은 9개 연결을 다 했지만 2,3층은 6개까지 연결하고 나니깐 더이상 선이 없어서 2,3층 3개의 LED은 돌릴 수 없었네요. 그래도 전달하고자하는 실험 내용의 결과는 정상적으로 얻었으며 안켜진 LED은 오류가 아니니깐 감안하시고 보세요.

추가 내용

마지막 동영상 장면에서 delay()시간차 연결을 몇초 담았는데요. 혹시 궁금하실분이 잇을 것같아서 만약 1층1호실 2층 2호실을 같은 시간대 불이 들어오게 할려면 어떻게 해야할지 의문을 품는 분들이 아마 있을꺼에요. 큐브 자체가 회로도를 저리 구성하기 때문에 물리적으로는 사실 어렵습니다. 하지만 인간의 시각을 이용한 방법으로 착시 효과로 표현은 가능합니다. 이 말은 인간의 눈은 두 LED가 있으면 delay()시간을 아주 짧게 주면 동시에 불이 들어오는 것처럼 착시 현상이 발생합니다. 아두이노에서는 그 착시효과를 이용해서 delay()시간으로 각 층에 LED에 불을 넣으면 됩니다.

  digitalWrite(layer[0], LOW);  
  digitalWrite(room[0], HIGH);   
  delay(20);
  digitalWrite(room[0], LOW);
  digitalWrite(layer[0], HIGH);

  digitalWrite(layer[1], LOW);  
  digitalWrite(room[2], HIGH);   
  delay(20);
  digitalWrite(room[2], LOW);   
  digitalWrite(layer[1], HIGH); 

즉, 1층 1호실에 불이 0.02(20)초 동안 전원 공급했다가 차단하고 바로 2층 2호실에 0.02(20)초 동안 정원 공급하면 어떻게 될까요. 분명 코딩상으로 차이가 나지만 시각적으로 보면 delay(20)초 간격으로 1층 1호실과 2층 2호실이 동시에 불이 들어오는 것처럼 착시효과가 발생합니다. 이러한 방법으로 서로 다른층의 LED를 동시에 불을 들어오게 합니다.

위 그림을 보듯이 짧게 딜레이를 줘서 각 층의 원하는 위치에 거의 동시에 불이들어오게 착시효과를 나타내고 잇네요.

마무리


원래는 한개의 포스팅으로 3편의 내용을 함축하고 싶었는데 그냥 회로도 그리고 코딩하고 결과만 딸랑 보여주면 별로 의미가 없을 것 같아서 최대한 많은 것을 설명할려고 노력했는데 빠진 부분이 아직도 너무 많네요. RGB LED를 이용한 CUBE LED도 표현하면 좋은데 실제 보유한 RGB LED가 한개 뿐이고 가상시뮬레이터로 표현하자니 2핀짜리 LED 선연결도 이리 복잡한데 4핀짜리 RGB LED를 큐브 모양으로 만들 엄두가 안나네요. 원리를 이번 포스팅에 설명한 원리랑 같기 때문에 도전하고 싶은 분들은 도전해보세요. 지금 표현한 것에 3배 노가다를 하시면 충분히 표현이 가능하실꺼에요.
전 도저히 못하겠네요. 도전하실분들은 RGB LED를 큐브로 만들어 보세요.


댓글()

[아두이노]3x3x3 CUBE LED 제어 II

IOT/아두이노|2019. 3. 20. 09:00

[아두이노]3x3x3 CUBE LED 제어 II



지난 시간에 간단히 3x3x1 LED를 제어 해봄으로써 3x3x3 CUBE LED의 원리를 이해 했습니다. 오늘 실험에서는 3x3x3 CUBE LED를 실험할 예정이였지만 실제 구현까지 할려고 하니 총 27개 LED를 다뤄야 하고 뻥판에 배치하자니 지져분해지고 오히려 의미 전달이 잘 안될 것 같아요. 2x2x2 CUBE LED 간단히 소개할까 합니다.

1. 3x3x3 CUBE LED


모습은 아래 그림처럼 구성되어 있습니다. 가로로 보시면 Gnd(-) 부위에 녹색선으로 사각형 모양을 만들어 하나로 선을 만듭니다. 그게 세로로 보는 것 처럼 3층으로 이루어진 것을 3x3x3 CUB LED라고 부릅니다.

[ 세로 ][ 가로 ]


여기서, 무조건 Gnd(-)로 9개 LED를 하나로 묶어을 필요는 없습고 반대로 Vcc(+)로 9개 LED를 하나로 묶을 수 있습니다. 그냥 선택사항일 뿐이죠. 저는 9개 LED를 Gnd로 묶어서 하나의 선으로 만든 것일 뿐이죠.

이걸 실험할려면 총 27개의 LED를 제어를 해야하고 실제로 회로도를 만들면 좀 복잡해 질 수 있어서 의미 전달보다 시각적으로 복잡하게 느낄 수 있어서 차라리 2x2x2 CUBE LED를 실험하는게 의미 전달하는데 더 나을 것 같아서 수정하였습니다.

2. 2x2x2 CUBE LED


2x2x2 CUBE LED 모습인데 가상시뮬레이터에서 그릴려고 하니깐 모양이 참 애매하네요. 대충 이런식으로 배치한다고 생각하시면 됩니다.


실제로 CUBE 모양을 만들려면 납땡을 하고 철사가 필요한데 납땜 도구를 찾아서 제작을 할까 고민을 하다가 CUBE를 만드는데 LED를 다쓰면 나중에 다른 실험을 할때 LED가 부족할 것 같았서 뻥판에 배치하여 그 의미만 전달하는 걸로 표현했습니다.


[ 실행 결과 ]


녹색은 1층을 나타내고 노랑은 2층으로 생각하시면 됩니다. 위 그림에서 각 호실은 오렌지 1호실, 블루 2호실, 엘로우 3호실, 화이트 4호실를 가리킵니다. 여기서, 오렌지 1호실은 2층 1호실과 연결되고요. 블루, 엘로우, 화이트 들은 각층에 해당 호실과 선이 연결됩니다. 각층 Gnd(-)은 아두이노의 Gnd(-)에 연결 되었고, 각 오렌지, 블루, 엘로우, 화이트 선은 아두이노의 Vcc(5V)에 연결되었습니다. 결론은 각 층은 Gnd(-) 연결하고 각호실에 Vcc(5V)에 모두 연결되었기 때문에 전부 불이 들오게 됩니다.

만약, 오렌지 1호실만 Vcc(5V)에 연결하고 각층 Gnd(-)은 전부 연결했다며 아래 그림처럼 1층 1호실, 2층 1호실에 불이 들어 오겠죠. 다시 정리하자면, Vcc의 전류가 엘로우 1호선만 공급되는데 1층, 2층 중 Gnd(-)로 모두 연결되었기 때문에 둘다 전류가 흘러가기 때문에 각 층 1호실에 불이 들어오게 됩니다. 만약 1층이 Gnd(-)와 연결이 끊어졌고 2층만 Gnd(-)에 연결되었다면 2층 1호실만 불이 들어 오겠죠.


이렇게 Vcc(5V)가 연결된 선을 따라서 최종 목적인 Gnd(-)로 연결된 곳만 전류가 정상적으로 흘러가게 됩니다. 이것만 이해하시면 됩니다.


[ 참고 ] 전류의 흐름

[예로 1층 1호실에 불이 들어왔다면]


전류의 흐름을 빨간선으로 표시한 부분이 정상적으로 연결되어 전류가 흘러가는 표시 이고요. 위 그림처럼 화살표 방향으로 흘러간다는 것을 참고해 주세요. 대충 어떤 느낌이신지 아시겠지요.


아두이노에서는 디지털 핀에 연결하여 제어를 하게 되는데 그 과정을 배워보도록 하겠습니다.

3. 회로도 구성


  • 준비물 : Green LED 4개, Yellow LED 4개, 저항 220옴 2개, 아두이노우노, 뻥판
  • 내용 : 2x2x2 CUBE LED 형태를 표현하자.


총 6개의 핀을 사용하게 됩니다. 2,3,4,5번 핀은 각 호실을 나타내는 핀이고 11,12번 핀은 층을 나타내는 핀입니다. 지금까지 LED 제어를 할때 Vcc(+) 방향에 저항을 붙여왔습니다. 하지만 Gnd(-)방향에 저항을 붙여 놓아도 상관 없습니다. 저항으로 흐르는 전류가 제어가 되기 때문에 어느 방향으로 저항을 붙이든 상관이 없다는 점을 참고해 주세요.

4. 코딩


  • 사용함수 : pinMode(), digitalWrite(), delay()
  • 내용 : 간단히 2x2x2 CUBE LED를 순차적으로 깜박이기
  • 참고 : [아두이노] LED 제어

복습

  • pinMode(사용핀, 사용모드) : 사용핀을 INPUT/OUTPUT/INPUT_PULLUP 모드중 뭘로 사용할지를 정함.
  • digitalWrite(사용핀, 출력형태) : 사용핀을 HIGH(5V) or LOW(0V) 중 출력 형태를 정함.
  • delay(1000) : 1초(1000)를 대기한다.

설계

이전 시간에 직접 전류를 선으로 Vcc(+), Gnd(-)를 연결해서 해당 위치의 불이 들어오게 만들었습니다. 그렇다면 아두이노에서는 어떻게 명령을 내려야 할까요.

1층 1호실에 불이 들어올려면 오레지(2)핀에 Vcc(5V)가 공급되고 1층(11)핀이 Gnd(-)가 되면 되겠죠. 직접 선을 연결한다는 생각을 하시면 됩니다.

그걸 코딩화 하면은 digitalWrite()함수를 사용해야 합니다.

  • 오렌지(2)핀이 Vcc(5V)가 공급 => digitalWrite(2, HIGH);
  • 1층(11)핀이 Gnd(0V)가 되어야 하니깐 => digitalWrite(11, LOW);

이렇게 표현하시면 됩니다.

1층을 순차적으로 깜박이게 하려면 어떻게 해야 할까요.

층별로는 1층(11)핀이 Gnd 상태이고 2층은 Gnd 상태가 아니면 되죠.

  • digitalWrite(11, LOW);
  • digitalWrite(12, HIGH);

이렇게 표현합니다. 왜 2층에 HIGH를 공급했을까요. 디지털 출력은 HIGH or LOW 두가지 상태만 존재합니다. 그리고 LED가 불이 들어올려면 Vcc와 Gnd가 연결해야 전류가 흘러서 LED에 불이 들어온다고 했죠. 가령 해당 LED가 Vcc쪽으로 5V를 공급하고 Gnd쪽으로 5V 상태면 서로 충돌하게 되잖아요. 그러면 전류가 흐를 수 없게 됩니다. 같은 극이 되면은 전류를 흐르지 않습니다. 이런 원리를 이용해서 LED를 제어하게 됩니다.

1층만 순차적으로 LED가 1,2,3,4 호실에 불이 들어올려면

digitalWrite(12, HIGH);

2층이 HIGH 상태에서 1층1호실을 불이 들어올려면

digitalWrite(11, LOW); 
digitalWrite(2, HIGH);
delay(1000);

2번 Vcc, 11번 Gnd로 1층 1호실에 불이 들어오고 1초간 대기한다는 의미입니다.

그러면 1층 2호실이면 같은 문장으로.

// 2층 잠금
digitalWrite(12, HIGH);

// 1층 개방
digitalWrite(11, LOW); 

// 1층 1호실 켜기
digitalWrite(2, HIGH);
delay(1000);

// 1층 1호실 끄고 2호실 켜기
digitalWrite(2, LOW);
digitalWrite(3, HIGH);
delay(1000);

.....

주석에서 설명하는 것처럼 켠 LED은 끄고 다음 LED은 켜고 이 문장을 반복해서 표현하면 됩니다.

2층으로 넘어갈때는

// 1층 잠금
digitalWrite(11, HIGH); 

// 2층 개방
digitalWrite(12, LOW); 

// 2층 1호실 켜기
digitalWrite(2, HIGH);
delay(1000);

// 2층 1호실 끄고 2층 2호실 켜기
digitalWrite(2, LOW);
digitalWrite(3, HIGH);
delay(1000);

.....

이렇게 반대로 생각하시면 됩니다.

전체적으로 코딩을 하면

int layer1 = 11;
int layer2 = 12;

int room1 = 2;
int room2 = 3;
int room3 = 4;
int room4 = 5;


void setup()
{
  pinMode(layer1, OUTPUT);
  pinMode(layer2, OUTPUT);

  pinMode(room1, OUTPUT);
  pinMode(room2, OUTPUT);
  pinMode(room3, OUTPUT);
  pinMode(room4, OUTPUT);

  //초기화 각 층을 막아놓습니다.
  digitalWrite(layer1, HIGH);
  digitalWrite(layer2, HIGH);
}

void loop()
{
  //1층 점등
  digitalWrite(layer1, LOW);  
  digitalWrite(room1, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room2, LOW);  
  digitalWrite(room3, HIGH);  
  delay(1000);

  digitalWrite(room3, LOW);  
  digitalWrite(room4, HIGH);  
  delay(1000);
  
  digitalWrite(room4, LOW);  
  digitalWrite(layer1, HIGH);  
  delay(1000);
  
  //2층 점등
  
  digitalWrite(layer2, LOW);  
  digitalWrite(room1, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room2, LOW);  
  digitalWrite(room3, HIGH);  
  delay(1000);

  digitalWrite(room3, LOW);  
  digitalWrite(room4, HIGH);  
  delay(1000);
  
  digitalWrite(room4, LOW);  
  digitalWrite(layer2, HIGH);  
  delay(1000);

}

코딩만 길게 표현이 된 것일뿐 LED을 켜고 끄고 이렇게 순차적으로 반복되는 문장일뿐 길다고 복잡하게 생각하실 필요는 없어요.

아래 두 표현으로만 코딩한 내용을 순차적으로 깜박이는 패턴을 만들려고 하니깐 전체적 코딩에서 길어지게 보였을분 아래 두 표현만 이해하시면 됩니다. 순차적으로 깜박이는게 중요한게 아니라 어떻게 전류를 흐르게 하고 차단을 하는지를 이해하시면 됩니다.

  digitalWrite(layer1, HIGH); //1층 잠금   
  digitalWrite(layer1, LOW); //1층 열림
  

  digitalWrite(room1, HIGH); //켜기 
  digitalWrite(room1, LOW);  //끄기

오늘 코딩한 표현은 흐름의 동작을 직설적으로 다 일일히 코딩으로 보여준거고요. 프로그램 문법을 사용하면 간단히 줄일 수 있습니다. 프로그램 문법보다는 일일히 동작하는지를 살펴보는게 주 목적입니다. for문을 사용하면 아주 간단하게 축소 시킬 수 있지만 오늘은 그 부분을 다루지 않을께요.

5. 결과


가상시뮬레이터에 부품이 많이 배치되면은 온라인상에서 실험하는거랑 렉이 좀 발생하네요. 실험 소스대로 하면 컴사양과 인터넷 사양에 따라 달라지겠지만 제 컴에서는 1초가 꽤 길게 느껴지더군요. 아주 짧은 시간으로 돌렸는데 첫음에는 빠르게 동작하다가 나중에는 거의 1초 단위로 순차적으로 깜작이더군요. 마지막에 실제 아두이노로 실험한 결과를 올려놨으니깐 젤 마지막 부분인 14분 20초부터 영상을 보시고 동작을 살펴보세요.

마무리


원래는 3x3x3 CUBE LED를 할려고 계획했는데 실제로 뻥판에 구현하는 장면까지 넣을려고 하니깐 뻥판이 너무 지져분해지고 의미 전달이 안될 것 같아서 2x2x2 CUBE LED로 변경했네요. 이 경우는 총 8개 LED만 제어하면 되기 때문에 코딩량도 적고 회로도 배치하는 것도 복잡하지 않고 실제 제작에서도 간단하게 실험할 수 있어서 좋은 것 같더군요.
개인적으로 3x3x3 CUBE LED를 실제로 납땜하면서 구현해보는게 가장 이상적이라고 생각되네요. 문제는 딱 제가 보유한 LED 개수가 딱 색상별로 10개씩 있어서 만약에 3x3x3 CUBE LED를 실제로 만들면 색상별로 1개씩 밖에 남지않아서 다음 실험에 LED를 활용할 수 없어서 실제로 제작한 모습은 보여드릴 수 없어서 아쉽네요. 이런건 실제로 만들어지는 걸 봐야 시각적 효과가 큰데 말이죠.

참고로 RGB LED로 구현도 가능합니다. 이경우는 총 4핀이여서 제작 과정이 좀 복잡하지만 원리는 동일합니다. 한번 연구해 보세요.


댓글()

[아두이노] 3x3x3 CUBE LED 제어 I

IOT/아두이노|2019. 3. 19. 09:00

[아두이노] 3x3x3 CUBE LED 제어 I



오늘의 주제는 흥미를 유발할 수 있는 3x3x3 CUBE LED 원리를 배우도록 하겠습니다. 지난 [아두이노] 스위치버턴을 이용한 Keypad 제어에서 스위치 버턴을 건물의 층과 호실로 나눠서 살펴봤는데 스위치 버턴은 아두이노로 입력을 받는 방식이라면 역으로 3x3x3 CUBE LED은 아두이노에서 LED로 출력하는 방식이라고 생각 하시면 됩니다. 스위치 버턴은 시각적으로 이해하기는 어렵지만 이번 포스팅 내용은 최대한 시각적인 표현을 통해 LED 제어하는 원리를 쉽게 이해할 수 있도록 표현해 보겠습니다.

1. 3x3x3 CUBE LED 원리


무료이미지 없고 실제로 제작한 것이 없어서 남의 이미지를 가져오는게 좀 문제가 있어서 가상시뮬레이터 최대한 표현은 해 봤네요. 검색어로 3x3x3 CUBE LED를 치시면 사진으로 화려한 이미지들을 볼 수 있을꺼에요. 이런걸 만드는 거구나 하고 우선 구경만 해보세요.


3x3x3 CUBE LED은 x,y 축으로 3x3 LED가 있고 z축으로 3x3 LED가 3개가 있는 것을 3x3x3 CUBE LED라고 부릅니다. 평면이 아닌 3차원 LED라고 생각하면 편하실꺼에요.

아래 그림은 x,y축으로 1개의 LED 단면을 나타낸 실험 그림입니다. 그림을 보면 5V 파워서플라이를 이용해서 전류를 보낼때 회로도 처럼 +, - 를 보내면 아래 한줄이 전부 불이 들어오는 결과를 보실 수 있을거에요. 왜 이렇게 불이 들어왔는지 그 원리에 대해서 공부해보도록 하겠습니다.

회로도결과

2. LED 원리



LED은 다리가 긴쪽이 '+' 이고 다리가 짧은쪽이 '-' 입니다. 가상시뮬레이터에서 사용하는 LED은 다리가 긴쪽이 꺽여있는 걸 보실꺼에요. 다리가 길다는 것을 표현한 이미지 입니다.


위 그림처럼 LED은 5V의 전류가 공급되면 바로 터져버립니다. 그래서 저항을 붙여줘서 전류의 양을 필요한 만큼만 공급되게 해야 아래와 같이 정상적으로 켜집니다. 220옴을 사용했네요. [아두이노] LED 제어 편에서 LED 색상별 필요한 전류가 표로 나와 있으니깐 참조하세요.



3. LED 동작



전류의 공급이 이루어진 LED만 불이 들어오는 것을 보실 수 있죠. 해당 LED에 Vcc(+) 전류가 공급되면 Gnd(-)핀과 쌍으로 연결된 LED에만 정상적으로 전류가 흐르니깐 LED에 불이 들어오게 됩니다. 이 동작 원리를 잘 이해해 주세요. 3x3x3 CUBE LED의 기본 동작 원리이기 때문입니다.


4. 3x3x3 CUBE LED 동작



위 LED 동작에서 보신 3개의 LED를 하나의 층으로 생각하고 총 3층으로 구성되었다고 생각하세요. 3개의 LED가 3개씩 총 9개가 배치된 그림입니다.

전선의 색을 잘 살펴봐주세요. 세로로 같은색을 이루고 있죠. 그 색은 서로 연결되어 있는거라고 생각해 주세요. 오렌지, 블루, 핑크색 선들이 세로로 연결되어 있으니깐 혼동하지 마세요

이제 3층 3호실로 이루어진 건물이 있다고 생각하세요. 각층의 1호실은 핑크선, 각층의 2호실은 블루선, 각층의 3호실은 오렌지선으로 연결되어 있다고 생각하세요.

1층 1호실방에 불이 들어올려면 LED 동작에서 이해 했다면 Vcc(+)와 Gnd(-)를 연결을 어떻게 해야 할까요. 핑크선에 Vcc(+)을 연결하고 1층의 녹색선에 Gnd(-)를 연결하면 됩니다.


여기서 핑크선에 Vcc(+)를 연결하면 1층1호실, 2층1호실, 3층1호실에 Vcc(+) 전류가 공급됩니다. 여기서 1층 녹색선만 Gnd(-)선이 연결되어 있다면 1층1호실만 불이 들어옵니다. 하지만 2,3층 녹색선도 Gnd(-)에 연결되어 있다면 어떻게 될까요.



위 그림처럼 각층에 1호실에 Vcc(+)가 공급되니깐 각층마다 Gnd(-)가 연결된다면 각층 1호실 전부다 불이 들어오게 되겠죠. 이 의미를 잘 이해해 주세요.

그러면 다시 2층 2호실에 불이 들어올려면 어떻게 해야 할까요. 블루선에 Vcc(+)가 연결되고 2층 녹색선만 Gnd(-)선이 연결된다면 아래와 같이 불이 들어 오겠죠.


어렵지 않죠. 여러분들도 원하는 위치에 불이 들어오게 한다면 Vcc(+)와 Gnd(-)를 어떻게 연결할지 그려보시거나 가상시뮬레이터에서 한번 실험 해보세요.

이게 3x3x3 CUBE LED의 원리입니다. 오늘 실험은 3x3x1 CUBE LED 라고 생각하시면 되겠죠. 3x3x3 CUBE LED라면 9 호실로 이루어진 3층 건물로 생각하시면 됩니다. 3호실이 아니라 9호실로 늘어났다고 생각하시면 됩니다. 단지 LED를 큐브 모양으로 배치했을뿐 위에서 다룬 LED 동작을 3층 9호실로 호실만 늘어난 구조라고 생각하시면 오늘 내용은 이게 전부입니다.

Vcc(+)와 Gnd(-)을 어떻게 연결하느냐에 따라 LED에 불이 들어오는지만 이해하시면 됩니다. 핑크, 블루, 오렌지 선에 공급되는 Vcc(+)와 각층에 연결된 Gnd(-)선이 서로 연결되었을때 원하는 위치에 LED에 불이 들오는 것만 이해하시면 됩니다.

설명보다는 실제로 가상시뮬레이터에서 선을 연결해보세요. 그래야 이해가 되실 듯 싶네요

마무리


위에서 Vcc(+), Gnd(-) 선을 연결하여 해당 위치의 LED에 불이 들어오게 하는 방법을 배웠습니다. 하지만 중요한것은 아두이노에서는 위에서 실험한것처럼 그때 그때마다 선을 바꿀 수 없잖아요. 그걸 아두이노가 디지털핀으로 Vcc(+0)와 Gnd(-)을 출력함으로써 그 역활을 대신합니다.


파워서플라이에서 선이 전부다 Vcc(+), Gnd(-)가 연결되어 있죠 1층 1,2,3호실에 불이 들어오고 나머지는 불이 안들어오게 하기 위해서 표현한 예인데 아두이노였다면 디지털핀 6개를 Vcc(+)와 Gnd(-)를 출력으로 보내면 보시는 것처럼 LED불이 들어오게 되겠죠.

그리고 선을 보시면 녹색선에 Gnd(-) 대신에 Vcc(+)를 연결하신 걸 보실꺼에요. 양극이 같은 극이 되면은 전류가 흐를 않는 원리를 이용한 것입니다. 왜 이렇게 구지 연결했냐면 다음편에 소개할 아두이노에서는 해당핀을 Vcc(+) or Gnd(-) 에서 핀은 둘중 하나의 상태를 유지하기 때문에 시각적으로 보여주기 위해서 파워서플라이 연결을 이런식으로 표현 한 것이죠.

이부분은 다음편에서 자세히 이야기 하도록 하겠습니다. 오늘은 LED 선을 어떻게 연결하면 어느 위치에 LED가 불이 들어오는지 전류의 흐름을 잘 관찰하시고 그 원리를 이해만 해주시면 어렵지 않을 것에요. 그리고 이 부분이 핵심이니깐 전류의 흐름의 느낌을 잘 이해해 주세요.

댓글()

[아두이노] Debounce에 대해 살펴보자

IOT/아두이노|2019. 3. 18. 09:00

[아두이노] Debounce에 대해 살펴보자



오늘은 Debounce에 대해서 이야기를 하도록 하겠습니다. 채터링 or 바운딩이라는 단어가 있는데 이 의미는 실제 스위치 버턴과 같은 인간이 터치하는 센서가 있을때 이 센서를 누르고 때는 순간에 센서의 접전 부분에서 On/Off 가 여러번 반복되는 현상을 말합니다. 스플링 있는 버턴을 누를고 때는 순간에 그 버턴이 미세하지만 흔들리는 모습을 보시거나 손끝에서 느끼신적이 있을거에요. 키보드 자판을 한번 눌렀다 때 보세요. 느끼실지 모르겠지만 손끝에서 스플링이 누를때랑 땔려는 순간 약하게 미세하게 흔들림을 느끼실꺼에요. 그때가 아주 짧은 시간이지만 스위치 버턴 접전 부위에서 On/Off 가 여러번 반복되는 상황이라고 생각하시면 됩니다. 이런걸 하드웨어인 방법과 소프트웨어적인 방법으로 해결하는데 하드웨어적인 경우는 회로도에서 스위치 버턴이 흐르는 전류를 안정적 제어하기 위한 부품을 달아야 하는데 저항과 캐피시터인가 달아서 해결한다고 하는데 저도 정확히는 하드웨어적인 제어는 해보지 못해서 설명드리기 애매하네요.

아두이노 공식홈페이지에서 소개된 소프트웨어적인 방법을 소개할 하도록 하겠습니다.

1. 회로도 구성


  • 준비물 : 스위치 버턴 1개, Red LED 1개, 저항 220옴 1개, 아두이노우노
  • 내용 : 스위치 버턴을 눌렸을때 Red LED가 켜지게 한다.


3. 코딩



[ 스위치 버턴 기본 소스 ]

void setup()
{
  pinMode(13, OUTPUT);
  pinMode(7, INPUT_PULLUP);
}

void loop()
{
  digitalWrite(13, digitalRead(7));  //버턴을 누르면 불이 꺼지고 안누르면 불이 들어온다.
}

내부풀업모드를 사용할 시에 해당 핀에 초기 상태는 1이고 버턴을 누르면 0이 된다. 그래서 스위치 버턴 기본소스에서는 위 회로도에서는 누르면 불이 들어오고 때면 불이 꺼진다.



[ Dedboune 기본 소스 ] 출처는 아두이노공식홈페지의 튜토리얼 소스 내용으로 링크 걸어 놨네요. 가셔서 한번 읽어보세요.

int debounceDelay = 50; //바운드에 대한 기준 시간값
unsigned long lastDebounceTime = 0; // 현재시간값을 가져와서 저장할 변수 자료형은 같은형으로 맞춤
int buttonstate=1; //내부풀업모드 버턴이 초기값이 1이여서 초기값을 1로 잡음.
int lastButtonState=1; //마지막 버턴상태를 기록변수

void loop(){

    int reading = digitalRead(BUTTONPIN); //스위치 버턴상태값을 읽는다.

    if (reading != lastButtonState) { //현재 읽은 버턴의 상태값과 이전상태가 다른지 체크
        lastDebounceTime = millis(); // 다르면 변화(On/Off)가 일어난거니 그 시간을 변수에 저장시킨다.
    }

    if ((millis() - lastDebounceTime) > debounceDelay) {  //버턴변화가 50시간값 안에 발생했으면 바운스(채터링)으로 무시
                
                if(reading!=buttonState){ // 버턴이 On/Off 됐는지 확인(버턴의 변화가 일어났는가)       
                    buttonSate=reading; //버턴의 변화가 일어났으면 그 변화를 기록했다가 다음 버턴 변화의 비교대상이 됨
                    
                    스위치 On/Off로 인정한 동작 수행;                  
                }
        }
    
    lastButtonState = reading; //현재 읽은 버턴의 상태를 다음 버턴의 상태와 비교하기 위해 변수에 저장한다.
}

쉽게 말해서, 현재의 버턴이 누른다면 이전상태변수(lastButtonState)은 누르지 않는 상태이겠죠. 누르니깐 현재상태변수(reading)와는 다른 상태 변화가 일어나니깐 그 변화가 일어난 시간을 기록해서 그 변화가 일어나는 간격이 50이라는 시간값과 비교해서 50이하의 시간에 바운스(On/Off) 상태가 일어나면 그냥 무시한다는 의미이지요.

번역하자면

  1. 버턴의 상태값 읽기
  2. 버턴의 상태가 이전버턴의 상태와 다른가 체크하여 변화가 일어나면 그 시간을 저장한다.
  3. 현재의 시간과 이전시간과 차이가 50이하면 바운스(채터링)으로 간주하고 50이상일때는 버턴이 눌러진걸로 확정한다.

결론은 50이라는 시간값 안에 일어난 변화는 바운스(채터링)이다. 이런뜻이죠.

두 코딩을 합치면

int debounceDelay = 50; //바운드에 대한 기준 시간값
unsigned long lastDebounceTime = 0; // 현재시간값을 가져올꺼니간 자료형은 같은형으로 맞춘다.
int buttonstate=1; //왜! 1이냐면 내부풀업모드 버턴이 초기값이 1이여서 초기값을 1로 잡음.
int lastButtonState=1; //마지막 버턴상태를 기록한다. 왜냐면 다음 버턴상태와 비교하기 위해서이다.
boolean ledState = false; // LED 반전 상태값을 만들려고 변수를 선언했군요.


void setup()
{
  pinMode(13, OUTPUT); //13(RedLED)핀을 출력모드로 쓸꺼야!
  pinMode(7, INPUT_PULLUP); //스위치버턴을 내부풀업모드로 쓸꺼야.
}

void loop()
{
    int reading = digitalRead(7); //버턴값 읽는다.
    
    if (reading != lastButtonState) { //버턴의 변화가 일어났니!
        lastDebounceTime = millis(); // 그러면 현재 시간을 저장한다.
    }
    if ((millis() - lastDebounceTime) > debounceDelay) {  //버턴변화가 50시간값 안에 발생했으면 바운스(채터링)으로 무시
                                
        //진입! 성공! 50시간값이상 벌어졌으니 버턴으로 눌러진 걸로 인정 받는 거야!
            if(reading!=buttonState){ // 버턴이 On/Off 됐는지 확인(버턴의 변화가 일어났는가)       
                buttonSate=reading; //버턴의 변화가 일어났으면 그 변화를 기록했다가 다음 버턴 변화의 비교대상이 됨
                ledState=!ledState; 
        }

    }
    digitalWrite(13, ledState);  //버턴을 On/Off를 인정받은 ledState값으로 Red LED을 깜박인다.
  lastButtonState = reading; //현재 읽은 버턴의 상태를 다음 버턴의 상태와 비교하기 위해 변수에 저장한다.
}

주석 처리한 내용만 읽어보시면 대충 이해가 되실꺼에요.

이해가 안된다면 다시 한글로 로직을 설명


void loop(){
reading 변수에 현재 스위치버턴값을 읽는다.

만약, 현재스위치버턴(reading)의 상태가 이전스위치버턴상태1(lastButtonState) 다르면 변화시간(lastDebounceTime)을 저장 한다.

만약, 현재시간(millis())-변화시간(lastDebounceTime) 차 값이 50보다 작으면 채터링으로 무시하고 넘어가고 50보다 크면 스위치 버턴의 변화를 인정한다.

만약, 현재스위치버턴(reading)이 이전버턴상태2(buttonState)가 다르면 스위치 On/Off로 간주하고 다음 동작을 수행한다. 여기서 이전버턴상태2(buttonState)은 변화로 인정받은 스위치버턴상태값이다.

인정받았으니 현재스위치버턴(reading)값은 이전버턴상태(buttonState)에 저장한다. 다음에 비교 대상이 되기 때문이다.

마지막으로 이전스위치버턴상태1(lastButtonState)은 현재스위치버턴(reading)값을 저장한다. 왜냐면 다음에 변화가 일어나는 시간을 기록하기 위해서 다음에 비교 대상이 되기 때문이다.


대충 이렇게 글로써 표현할 수 있습니다. 여기서 왜 이전스위치상태변수를 lastButtonState, buttonState로 비슷한 변수로 둘을 사용했냐면 lastButtonState은 우선 버턴의 변화가 일어나는 시간을 찾기 위해서 reading가 비교하기 위해서 입니다. 버턴이 On/Off 인정을 받던 안받던 무조건 버턴의 변화가 일어나면 그 시간을 기록해서 그 시간값이 50 이하면 채터링으로 간주하기 위해서 체크용 변수입니다. buttonState은 실제 버턴이 On/Off 된것로 인정하고 그 변화를 상태를 비교하기 위해 체크용으로 사용하는 변수입니다. 둘 차이점을 정확히 이해하시고 보면 어렵지 않을거에요.

4. 결과


스위치를 누르니깐 정상적으로 불이 들어 왔네요.


마무리


오늘은 프로그래언어쪽으로 좀 더 깊은 내용이였네요. 어려우시다면 그냥 잊으셔도 됩니다. 원리는 정리하자면 50이라는 시간값을 주고 버턴이 On/Off가 발생하면 그 버턴의 On/Off를 무시한다는 내용입니다. 50이상일때 버턴의 On/Off를 인정받고 스위치가 눌러진 다음 동작인 Red LED에 불이 들어오게 한다는게 오늘 주제의 핵심입니다.

가상시뮬레이터에서 실험하시면 컴퓨터상이니간 바운스(채터링)은 발생하지 않습니다. 실제 스플링이 붙어 있는 버턴을 누르면 원치 않는 결과를 얻을 수 있습니다. 어디까지 버턴의 터치를 인정받아야 하는지를 개발자거 설정해야 합니다. 오늘 주제는 소프트웨어적인 방식으로 제어 했지만 이 방법이 정석이라고 할 수 없습니다. 하드웨어적으로 저항+캐피시터를 부착해서 전류를 안정적으로 제어해서 채터링이 사전에 발생하지 않게 하드웨어적으로 아예 만들어 버리면 쉽게 해결 되겠죠.

하지만 우리가 실험을 하면 하드웨어적 부분은 약할 수 밖에 없습니다. 어떤 부품을 부착해야하고 어느 위치에 그 부품을 위치해야할지 막막합니다. 최대한 기초적 지식으로 아두이노를 다루면서 나머지 부분은 소프트웨어적으로 접근해서 문제를 해결하는게 더 빠릅니다. 프로그램적으로는 구글검색만 조금만 하면 왠만한 표현이 오픈소스로 거의 대부분 제공되고 있으니깐 어렵지 않게 아두이노를 공부하실 수 있을거에요.

댓글()

[아두이노] 아두이노 끼리 통신하기

IOT/아두이노|2019. 3. 17. 09:00

[아두이노] 아두이노 끼리 통신하기



시리얼통신에서 블루투스 실험을 하면 좋은데 가상 시뮬레이터에서 블루투스를 제공하지 않아서 그 대안으로 2대의 아두이노를 연결해서 시리얼통신하는 실험을 갖도록 하겠습니다. 지난시간에 배웠던 시리얼통신을 이용하여 한쪽에서는 명령을 내리고 한쪽에서 시리얼통신으로 통해서 받은 명령을 실행하는 동작을 간단하게 어떤식으로 수행되는지 진행해 보겠습니다.

1. SoftwareSerial 라이브러리


아두이노는 내장된 0,1번 핀이 시리얼 통신핀입니다. SoftwareSerial는 소프트웨어를 사용하여 아두이노의 다른 디지털 핀에서 직렬 통신을 허용하게 하기 위해서 개발된 라이브러리 입니다.

#include <SoftwareSerial.h>

  • SoftwareSerial mySerial (rx, tx) : 소프트시리얼 객체선언(rx(수신), tx(전송))
  • mySerial.begin(2400) : 시리얼 통신 시작(예로 2400 통식속도를 사용해 봤네요.)
  • mySerial.write(값) : 데이터 전송
  • mySerial.available() : 데이터 들어왔는 확인
  • mySerial.read() : 전송된 데이터 1byte 읽기

시리얼통신 함수와 별다를 게 없죠. 단지 객체변수로 선언할대 rx, tx 핀번호를 지정해 줘야 한다는 것 외에는 다 동일합니다. 시리얼통신에서 배웠던 함수들임으로 별로 어렵지 않을꺼에요.

2. 회로도 구성


  • 준비물 : Red LED 1개, 저항 220옴 1개, 아두이노우노 2개
  • 내용 : Red LED 깜박이는 기본 예제 회로도


2대의 아두이노를 보면 10, 11번의 두개의 핀을 시리얼 통신용으로 사용 됩니다. 아두이노의 전용 시리얼통신핀은 0, 1번핀입니다. 이 핀을 사용할 수는 있지만 사용을 안하는 걸 추천 드려요. 나중에 블루투스 통신에서 0,1핀을 사용하기는 하지만 여기에서는 아두이노 IDE 시리얼모니터를 사용하기 때문에 이쪽 0,1 핀을 제외한 다른 핀을 시리얼통신으로 사용할려고 10, 11번 핀을 임의로 지정했습니다.

3. 코딩


[ 시리얼통신 기본예제 소스 ]

void setup() {
  Serial.begin(9600);    //시리얼 통신 9600 통신속도로 시작
}
void loop() {
  
  if (Serial.available() > 0) { //데이터가 수신되는지 확인
    char ch = Serial.read(); //1byte 읽음
    Serial.println(ch); //1byte 읽은거 출력
  }
}

[ LED 기본예제 소스(1초 단위로 LED 깜박이기) ]

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

void loop() {
    digitalWrite(13, HIGH);
    delay(1000);
    digitalWrite(13, LOW);
    delay(1000); 
}

위 기본예제를 2번 아두이노에 합쳐서 동작하게 되는데 1번 아두이노의 명령에 의해서 깜박이게 코딩하도록 하죠.

시리얼통신에 사용되는 라이브러리는 SoftwareSerial 라이브러리입니다. SoftwareSerial 클래스을 이용하여 2대의 아두이노의 통신을 하게 됩니다.

전제척으로 코딩을 하면

[ 1번 아두이노 ]

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10,11); //시리얼통신 핀

void setup()
{
    mySerial.begin(2400); //기존 9600이여서 다른 통신속도를 사용해야함
}

void loop()
{
  mySerial.write('1'); // 2번 아두이노에 '1'값 전송
  delay(1000);
  
  mySerial.write('0');  // 2번 아두이노에 '1'값 전송
  delay(1000);
}

간단하죠. SoftwareSerial를 인쿠루드 시키고 SoftwareSerial 객체변수를 선언할대 아두이노에서 사용될 디지털 핀을 10, 11번으로 지정했습니다. 그리고 나서 따로 할 것은 없고. write()함수 명령어로 값을 2번 아두이노에 보내는 명령 코드가 되겠습니다.


[ 2번 아두이노 ]

#include <SoftwareSerial.h>
SoftwareSerial mySerial(10,11);

char state;
int rPin=13;

void setup()
{
  Serial.begin(9600);
  mySerial.begin(2400);
  pinMode(rPin, OUTPUT);
}

void loop()
{ 
  if(mySerial.available() > 0) { //수신되었는지 상태 확인
    state = mySerial.read(); //수신된 데이터를 1byte 읽어와서 state 변수에 저장함
    
    if(state=='1') digitalWrite(rPin, HIGH); //읽어온 값이 '1'이면 참으로 Red에 HIGH로 불이 들어옴
    else digitalWrite(rPin, LOW); //'0'이면 1이 아니기 때문에 거짓으로 Red에 LOW로 불이 꺼짐
    
    Serial.println(state);  //정상적으로 읽은 값이 들어왔는지 아두이노 IDE 시리얼모니터로 출력
  }
  
}

2번 아두이노는 좀 코딩이 긴 편입니다. 정상적으로 값을 가져왔는지 아두이노 IDE 시리얼모니터로 그 값을 찍어봐야 하기 때문에

 Serial.begin(9600);
 erial.println(state);

이 두문장으로 아두이노 IDE로 state 값을 전송하게 됩니다. 여기서 state 값은 1번 아두이노에서 보내진 데이터를 수신한 값이 되겠습니다.

1번 아두이노에서 데이터를 수신하여 state 변수에 저장되는데 그 로직은 시리얼통신에서 기본적으로 사용하는 시리얼통신으로 데이터가 들어왔을때 그 값을 읽는 로직은 아래와 같습니다. 이건 왠만하면 계속 사용하기 때문에 숙지해 주세요.

  if(mySerial.available() > 0) {
        state = mySerial.read();
    }

if 조건문으로 '1'이면 불이 들어오고 '0'이면 불이 꺼진다는 조건을 만든다.

if(state=='1') 불켜!
else 불꺼;

간단하지요.

4. 결과


오른쪽 아두이노가 SoftwareSerial 을 사용하여 왼쪽 아두이노에게 '1'과 '0'의 값을 전달하고 왼쪽 아두이노도 전송된 값을 읽어서 Red LED가 깜박이게 동작을 수행하게 하는 사진입니다.


마무리


시리얼통신과 차이가 없습니다. 단지 SoftwareSerial 라이브러리를 이용하여 소프트웨어적으로 제어 했다는 것만 다를 뿐이죠.
실험에서는 코드를 따로 어떻게 가상시뮬레이터에서 하냐고 생각 하실 수 있습니다. 그냥 코딩창을 띄워놓은 상태에서 해당 아두이노를 클릭하면 해당 아두이노의 코딩창으로 바뀌게 됩니다. 그리고 해당 아두이노 코딩에 맞게 개별적으로 시뮬레이션이 되니간 쉽게 생각하세요.


그리고 이 그림처럼 직접 마우스로 해당 아두이노를 선택하시면 됩니다. 참고로 이 소스를 기반으로 블루투스를 실제 가지고 있는 분이라서 블루투스 통신 코드로 활용하셔도 됩니다.

마지막으로, 이걸로 뭘 할 수 있을지 한번 상상의 나래를 펼쳐보세요.

댓글()

[아두이노] delay()함수 안쓰고 delay 제어하기

IOT/아두이노|2019. 3. 16. 09:00

[아두이노] delay()함수 안쓰고 delay 제어하기



지금까지 delay()함수를 통해 시간 제어를 해왔습니다. delay()함수는 아두이노를 그 시간동안 암것도 하지 못하게 강제휴식 상태가 되도록 합니다. 지금까지는 대부분 하나의 부품을 제어 할때 delay()함수를 써왔고 별 문제가 발생하지 않았습니다. 하지만 부품 둘 이상 결합해서 표현 해보셨다면 정상적으로 동작하지 않는 현상이 생겼을거라 생각됩니다. 그 이유는 2개 부품이 있는데 한개의 부품의 delay() 시간을 줄때 다른 부품도 같이 대기 상태에 빠지게 됩니다. 가령 A라는 부품을 1초 대기할때 강제적으로 B도 같이 1초 대기하기 하게 됩니다. B 부품은 뭔가 작업을 해야하는데 A 부품의 delay()함수에 의해서 강제로 대기상태에 빠지게 됨으로 제대로 동작하지 않는 현상이 발생하게 됩니다. 이런 문제를 해결하기 위해서 delay() 함수를 쓰지 않고 delay 효과를 얻는 방법을 살펴보도록 하겠습니다.

오늘은 간단한 코딩을 배우는 시간입니다.

1. delay() 함수 없이 delay 시키는 코딩


  • 사용함수 : millis()(타이머 작동하면서 시간이 생성되며 그 시간값을 추출할때 사용)
  • 내용 : 현재시간값 - 이전시간 = delay 시간인지 조건문으로 비교해서 delay 시키자.

  • 현재시간값 : 현재 시간값을 저장할 변수로 unsigned long(부호가 없는 long형 정수) 자료형으로 currentMillis 변수를 선언했습니다. 시간값을 int형으로 하기 보다는 최대한 큰 변수로 선언하는게 좋습니다. 시간에는 음수가 없기 때문에 숫자를 양수만 표기하는게 좋겠죠. 그래서 기존 long형에서 표현하는 숫자의 두배정도 양수로 표기할 수 있는 부호가 없는 long형으로 변수를 선언했습니다.
    그냥 변수에 대해 이해가 안가시면 큰수를 저장할 수 있는 자료형이구나 정도로 이해하세요. 작은 수면은 int형 큰수면 long형 이정도만 우선 머리속에 담아두시면 됩니다.
unsigned long currentMillis = millis();

현재 시간값을 가져왔으면 다한 거나 마찬가지입니다. loop() 함수가 어떻게 동작한다고 했죠. 아두이노가 처음 전원이 공급되면 setup()함수가 처음 한번 수행한 뒤에서 loop()함수가 무한 반복 수행 한다고 했죠.

만약에 시간값 구하는 위 코드가 loop() 함수안에 있다면

void loop(){
    unsigned long currentMillis = millis();
}

loop()함수가 매 반복할 때마다 현재 시간을 추출하여 currentMillis 변수에 저장하게 됩니다. 처음 loop()함수가 호출이 되면 currenMillis 변수에 현재시간이 저장된 후에 두번째 loo()함수가 호출하게 되면 두번째 현재시간이 저장되겠죠. 그러면 첫번째 호출되어 저장된 현재시간은 이전시간이 되고 두번째 호출된 시간이 현재시간이 되겠죠. 여기서 currentMillis는 두번째 현재시간을 저장하기 전에 첫번째 저장된 값을 어딘가에 옮겨서 보관해야겠죠.

previousMillis = currentMillis;

간단하죠. 변수를 하나 더 만들어서 현재변수에 저장된 값을 이전변수에 저장하면 쉽게 해결됩니다.

딜레이 시간은 어떻게 구할까요.

대기시간 == 현재시간 - 이전시간

delayTime == currentMillis - previousMillis;

참 쉽죠. 현재시간에서 이전시간을 뺀 시간이 delayTime과 같아야 겠죠.

이제는 조건문으로 1초 단위로 딜레이 시켜볼까요.

unsigned long currentMillis = millis();

if(currentMillis - previousMillis >= delayTime){
  previousMillis = currentMillis;   
}

currenMillis의 시간값이 previousMillis값을 뺀 값이 delayTime시간보다 클경우 if문은 참이 됩니다. 왜 같다가 아니라 delayTime시간 이상일 경우에 참이라고 했을까요. 아두이노는 각 코딩라인을 수행할때마다 인간이 감지할 수 없지만 그래도 시간이 소요 됩니다. 결론은 현재시간을 측정할때 정확히 1초(1000)를 일치시키기 어렵습니다. 근사 초로 비교해야 합니다. 가령 1001이 현재시간이면 이것을 1초가 아닌게 아니잖아요. 1000,1001,1002 이렇게 측정될 경우 우리는 근사초로 1초로 보정해줘야 합니다. 그래서 delayTime 초보다 클경우를 참으로 잡는 이유가 되겠습니다.

delayTime(대기시간) 만큼이 만족하면 if문 안의 문장이 수행되고 이때 delay시간만큼 이동한 현재 시간이 previousMillis(이전시간) 변수에 currenMillis(현재시간)값을 저장해야겠죠. 왜냐면 다음 시간값과 비교해야하니깐요. 이렇게 표현함으로서 delayTime(대기시간)값에 만족하는 참이 될때마다 if문 안의 문장을 수행하는 로직이 됩니다.

void loop() {
  unsigned long currentMillis = millis();
  
  if(currentMillis - previousMillis >= delayTime){
    previousMillis = currentMillis;
    원하는 동작;
  }  
}

여기서 previousMillis, delayTime 변수는 외부로 빼줘야 합니다. 그 이유는 함수내에 선언된 변수는 함수가 끝나면 소멸합니다. 그래서 안에다 변수를 일반적으로 선언하면 이전 시간값은 사라지게 됩니다. 간단히 외부에 선언하시면 되겠습니다. 이전시간변수는 현재시간과 동일하니깐 부호가 없는 long형으로 해줘야하고 대기시간은 1(1000)초만 할꺼니깐 작은수이기 때문에 int형으로 선언하시면 되겠습니다.

unsigned long previousMillis = 0; 
const long interval = 1000;

2. 회로도 구성


  • 준비물 : Red LED 1개, 저항 220옴 1개, 아두이노우노
  • 내용 : Red LED 깜박이는 기본 에제 회로로


참 쉽죠. 회로도 별거 없지요. delay 원리를 쉽게 이해하기 위해 기본 LED 제어를 활용 했네요.

3. 코딩



LED 기본예제 소스(1초 단위로 LED 깜박이기)

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

void loop() {
    digitalWrite(13, HIGH);
    delay(1000);
    digitalWrite(13, LOW);
    delay(1000); 
}

딜레이효과 소스

unsigned long previousMillis = 0; 
const long interval = 1000;

void loop() {
  unsigned long currentMillis = millis();
  
  if(currentMillis - previousMillis >= delayTime){
    previousMillis = currentMillis;
    원하는 동작;
  }  
}

두 코딩을 합치면

int led = 13; //Red LED 핀번호
unsigned long previousMillis = 0; //이전시간
const long delayTime = 1000; //1초(1000) 대기시간
boolean ledState = false; //LED 현재상태

void setup() {
  pinMode(led, OUTPUT); //13번 핀을 출력모드로 사용.
}

void loop() {
  unsigned long currentMillis = millis(); //현재시간값 가져옴
  
  if(currentMillis - previousMillis >= delayTime){ //1000초 시간이 흘렀는지 체크
    previousMillis = currentMillis; //1초가 지나 참임으로 1초 지난 현재시간을 이전시간에 저장
    ledState=!ledState;   //1초 if문이 참이니깐 1초 단위로 ledState 값을 반전시키면 1초 단위로 참/거짓됨.
    digitalWrite(led, ledState);   //참(5V) or 거짓(0V) 
  }  
}

코드 주석을 달아 놓았으니깐 따로 설명은 필요 없겠죠.

두개 소스를 합쳐놓은 것에서

ledState=!ledState;   
digitalWrite(led, ledState);

이 표현만 추가된거죠. 인터럽트 예제에서 활용했던 방식인데 부울변수로 현재의 상태를 반전시킬때 많이 사용합니다. 디지털 출력은 0이 아닌 모든 값은 참이되고 0은 거짓이 됩니다.
그래서 부울변수로 True(1), False(0)의 값을 이용하여 1(5V), 0(0V)의 전류상태가 되는 원리를 이용한 것이죠. 반전원리를 사용할때 가장 많이 사용되는 기법입니다. 위 두 코딩을 합치는 부분에서 이 부분만 약간 추가되었을 뿐 나머진 그대로니깐 어렵지 않을거에요.

4. 결과


delay() 함수를 안써도 1초 단위로 그림에서 보는 것처럼 정상적으로 동작하네요.


마무리


delay() 함수를 안쓰게 되면 그 시간에 다른 작업을 수행 할 수 있게 됩니다. delay()함수를 써서 아두이노가 강제로 넌 쉬어! 올 스톱이 되는일은 이제 없게 된거죠. 그러면 2개이상의 부품에서 delay()함수를 사용할 경우 대기시간 문제에서 벗어났습니다.

코딩은 어렵지 않은데 정확히 어디에 써야할지 잘 연상이 안되실 수 있지만 우선 이런식으로 delay()함수 없이 1초 단위로 RED Led를 깜박이게 할 수 있구나 정도만 이해하시면 되겠습니다.

어제 포스팅한 실험을 그제 밤에 증흥적으로 3개의 주제를 합쳐서 만들어 실험 포스팅을 해서 사전 준비 없이 좀 시간을 잡아 먹었네요 그래오 오늘 포스팅은 쉬운 주제로 휴식하는 기분으로 간단한 주제로 내용을 담았네요.

댓글()

[아두이노] 서보모터를 리모콘(IRremote)으로 제어

IOT/아두이노|2019. 3. 15. 09:00

[아두이노] 서보모터를 리모콘(IRremote)으로 제어 



이번 내용을 지금까지 복습한 내용들을 종합해서 사용해보는 시간을 갖도록 하겠습니다. 총 3개의 주제를 하나로 묶어서 회로도를 구성할 예정입니다. 다 합쳐진 모습은 좀 복잡해 보일 수 있지만 개별 포스트 내용을 참고하시면 어렵지 않을거라 생각됩니다. 전체 코딩한 내용을 보면 이런걸 어떻게 코딩을 초보분들이 할 수 있냐고 생각 될 수 있지만 따로 각 주제별로 접근하면 단순한 코딩입니다. 그걸 합쳐놓아서 복잡해 보일 뿐입니다. 위에 참고자료 3개의 포스팅한 내용을 한번 다시 읽고 이번 포스팅을 보시면 "그냥 합쳐놓은거네!" 이라고 말할 꺼에요.

1. IRremote + Servo모터 + LCD16x2 소스


IRremot 소스

#include <IRremote.h>
 
int IRpin = 11;  
IRrecv irrecv(IRpin);
decode_results results;

void setup()
{
  irrecv.enableIRIn(); // 리모콘 시작
}
void loop()
{
  if (irrecv.decode(&results)) //리모콘 누른값이 없다면 패스
    {
     
      "results.value의 키값을 사용할 예정";
            
     irrecv.resume(); // 다음값
    }
}

Servo모터 소스

#include <Servo.h> 
 
Servo servo; 
int SERVO_PIN = 10;

void setup() 
{ 
    servo.attach(SERVO_PIN); 
}

void loop() 
{
    "servo.write(180) 함수로 서모모터회전시킬 예정";
}

LCD16x2 소스

#include <LiquidCrystal.h>

//LiquidCrystal(rs, enable, d4, d5, d6, d7) 
LiquidCrystal lcd(3, 4, 9, 10, 11, 12);

void setup()
{
  lcd.begin(16,2);
  lcd.setCursor(0, 0);
  lcd.print("Servo Angle :");
  delay(1000);  
}

void loop() {
  lcd.setCursor(0, 1);
  "lcd.print("각도")함수로 각도값을 출력할 예정";
}

2. 회로도 구성


  • 준비물 : IR Sensor, 리모콘, 서보모터 1개, LCD16x2 1개, 저항 220옴 1개, 가변저항 10k옴 1개, 아두이노우노, 뻥판
  • 내용 : 리모콘을 눌러서 서보모터를 회전 시키고 LCD16x2에 회전각을 출력시켜보자.


참 쉽죠. 회로도 별거 없지요. 그러면 욕먹겠지요. 지금까지 배운 서보모터 제어, LCD16x2 제어, 리모콘 제어로 총 3편의 포스팅 내용을 토대로 간단히 회로도를 구성해 봤습니다. 개별적으로 포스팅한 내용 보시면 이 회로도 구성은 별거 없다는 것을 아마 아실꺼에요. 다 합쳐놓으니깐 좀 복잡해 보일뿐이죠. 실상은 간단합니다.

만약 LCD16x2 I2C 로 출력한다면 더 간단해 집니다. 해보고 싶은분은 LCD16x2 I2C 제어편을 보시고 실제로 실험을 도전하세요.

3. 코딩


  • 사용함수 : 리모콘 함수, 서보모터 함수, LCD16x2 함수
  • 내용 : 리모콘를 화살표키를 눌러 10도씩 회전 시키고 그 결과를 LCD 16x2로 출력해보자.

[ 리모콘 함수 ]

  • irrecv.enableIRIn() : 리모콘 시작
  • irrecv.decode(&results) : 키가 눌러진지 확인
  • irrecv.resume() : 다음값

[ 서보모터 함수 ]

  • servo.attach(사용핀) : 사용핀을 서보모터 제어로 사용
  • servo. write(각도) : 각도로 회전시킨다.

[ LCD16x2 함수 ]

  • lcd.begin(가로,세로) : 화면나누기
  • lcd.setCursor(0, 0) : LCD16x2 모니터의 커서의 위치
  • lcd.print(출력값) : LCD16x2에 값을 출력

설계

1. 리모콘의 화살표 위아래 버턴만 사용한다.
2. 리모콘의 키값을 읽는다.

if (irrecv.decode(&results)) {                     
  키가 눌러졌다면 => results.value 값에 키값이 있겠죠
}

3. 각도변수에 화살표 키값을 위아래 누를때 증가/감소 시켜서 각도값을 만들어 낸다.

    if(리모콘키값 == 증가키값){  
      if(m_Angle<180){ //0~180도로 제한으로180도 이상 증가하면 안되니깐 각도 증가는 180보다 작아야함
        m_Angle=m_Angle+10; or m_Angle+=m_Angle;  =>  같은 표현
      }          
    }
    else if(리모콘키값 == 감소키값){ //0~180도 제한으로 0도 이하로 감소하면 안되니간 각도 감소는 0보다 커야함 
      if(m_Angle>0){
        m_Angle=m_Angle-10; or m_Angle-=m_Angle; => 같은 표현
      }      
    }  
<

4. LCD16x2에 각도값을 출력한다.

lcd.setCursor(0, 1); //두번째 줄에 첫칸에 커서 위치
lcd.print("                ");  //두전째 줄에 이전 기록된 값을 지우기 공백 16칸
lcd.setCursor(0, 1); //두번째 줄에 첫칸에 커서 위치한 이유 데이터를 기록할 위치를 다시 잡아줘야함
lcd.print(m_Angle); // 두번째 첫칸에 각도값이 출력됨

5. 서보모터에 각도변수에 저장된 값을 출력(회전) 한다.

servo.write(m_Angle);

간단하지요.

전체적으로 코딩을 하면

#include <IRremote.h>
#include <LiquidCrystal.h>
#include <Servo.h> 

//1
Servo servo; 
int SERVO_PIN =10;
int m_Angle = 0;

//2
//LiquidCrystal(rs, enable, d4, d5, d6, d7) 
LiquidCrystal lcd(2, 3, 4, 5, 6, 7);

//3
int RECV_PIN = 11;
IRrecv irrecv(RECV_PIN);
decode_results results;

void setup()
{  
  servo.attach(SERVO_PIN);
  
  irrecv.enableIRIn(); 
  
  lcd.begin(16,2);
  lcd.setCursor(0, 0);
  lcd.print("Servo Angle : ");
  
  
}

void loop() {
  
  if (irrecv.decode(&results)) {                     
   
     // Serial.println(results.value);
    
    if(results.value == 0xFD50AF){  //16601263 or 0xFD50AF
      if(m_Angle<180){
        m_Angle=m_Angle+10; // m_Angle+=m_Angle;
      }          
    }
    else if(results.value == 0xFD10EF){ //16584943 or 0xFD10EF
      if(m_Angle>0){
        m_Angle=m_Angle-10; // m_Angle-=m_Angle;
      }      
    }  
   
    lcd.setCursor(0, 1);
    lcd.print("                ");   
    lcd.setCursor(0, 1);
    lcd.print(m_Angle);
  
    
    irrecv.resume(); 
  }
  servo.write(m_Angle);
  delay(100);
}

4. 결과


리모콘 화살 키를 눌렀을때 결과입니다. 각도 0도에서 각도 10도가 증가하고 LCD16x2에 각도 값이 출력되고 서보모터는 10도 회전하는 장면 입니다.


동영상으로 제가 실험한 장면을 녹화 했네요. 어떤식으로 진행 했는지 보시면 그리 어렵지 않을꺼에요. 3개의 주제를 다 합치고 또 코딩하는 부분 까지 전부 다 녹화하다보니 꽤 길어졌네요. 동영상이 너무 길기 때문에 결과 이미지만 보시고 대충 이렇게 결과가 나오는 구나 정도로만 이해하시도 됩니다.


마무리


이번 포스팅은 응용편으로 각 하나의 부품을 제어하다 보면은 너무 쉽고 간단해서 이걸로 뭘 할 수 있지 생각하는 분들이 아마 있을거에요. 각 부품 포스팅 마다 예제소스도 너무 단순하게 표현해서 뭘 만들어 볼려는 분들은 부품간 연결이 이미지화 하기가 좀 힘드실꺼에요. 그래서 이번에는 좀 고려하고 코딩해야하는 부분이 있는데 그걸 다 무시하고 읽기 편하게 코딩을 배치했네요. 원래를 코딩을 하실때 이렇게 배치하시면 안되고요. 변수와 객체를 체계적으로 선언하고 코딩도 각 경계의 구분을 명확하게 하고 너무 한곳에 집중 코딩하기 보다는 외부함수로 빼서 분산시켜서 loop()함수 내 코딩은 최소화로 간결하게 가독성을 높이는 코딩을 해야 합니다. 그걸 다 무시하고 우선 초보분들은 가독성 위주로 코딩 했네요.

오늘은 서보모터랑 LCD16x2 모니터를 사용하여 리모콘을 제어했네요. 여러분들오 한번 지금까지 포스팅한 부품 중에 맘에 드는 것을 골라서 한번 만들어 보세요. LED가 맘에 든다면 LED 부품을 사용하여 숫자키 값에 따라서 전원 스위치로 활용하여 LED를 켜고 끄기를 할 수 있습니다. 또는 RGB LED로 리모콘 키값을 이용해서 컬러값을 만들어내서 RGB LED의 색을 다양하게 표현하는 실험을 할 수 있을거에요.

그외도 많지만 한번 상상의 나래를 펼쳐 보세요.


댓글()

[아두이노] 리모콘(IRremote) 제어

IOT/아두이노|2019. 3. 14. 09:00

[아두이노] 리모콘(IRremote) 제어



오늘은 리모콘을 사용하는 법을 배워보도록 하겠습니다. 제가 참고했던 라이브러리 파일이 있는 github의 자료입니다. 링크된 곳에 들어가시면 기본 데모 예제가 있습니다. 그걸 이용해서 실험하시면 됩니다.

1. IRremote 라이브러리 설치


가상시뮬레이터에서 기본적으로 제공됩니다. 라이브러리 소스를 가져올 필요 없이 라이브러리 파일을 인크루드만 하면 됩니다.

#include <IRremote.h>

실제로 한다면 라이브럴리 파일을 추가해야겠죠.


라이브러리 매니저에서 IRremote 치시면 라이브러리가 여러게 보이는데 젤 위에거로 그냥 설치하시면 됩니다.

설치하시면 예제에 들어가시면 IRremote라는 예제가 추가되었고 거기에 IRrecvDemo 예제를 실행 시키면 됩니다.


아래 소스를 보면 대충 예제가 11번 핀을 IR Sensor핀으로 사용했네요. 그리고 두개의 IRrecv, decode_results 클래스가 있는데 소스상으로 대충 보면은 IR Sensor값을 IRrecv 클래스에서 받아서 뭔가 처리를 할꺼고 decode_results 클래스는 변수명을 보면 먼지는 모르지만 센서의 결과값이 여기에 있겠구나 하고 유추가 되실 꺼에요. println()에 results.value 값을 찍은거 봐서는 리모콘를 누른 값이겠군 하고 생각하시면 됩니다.


대충 이런느김으로 라이브러리를 살펴보실때 클래스 객체명이나 또는 함수 등을 통해서 데모 예제들을 통해 유추해보면서 한번 코딩을 해독 해보세요. 데모 예제들은 심풀하게 나와 있어서 어렵지 않게 해독이 될꺼에요.

2. 회로도 구성


  • 준비물 : IR Sensor, 리모콘, 아두이노우노
  • 내용 : 키를 누르면 그 값을 시리얼모니터로 출력


참 쉽죠. 측정하는 센서를 사용할때는 전원(Vcc,Gnd)핀과 Dout핀으로 구성된다고 했죠. 아두이노에 해당 핀에 연결만 하면 도비니다. 가상 리모콘은 따로 할 건 없고요. 아주 간단하게 구성 됩니다.

3. 코딩


  • 사용함수 : irrecv.enableIRIn(), irrecv.decode()
  • 내용 : 리모콘를 누른 값을 아두이노 IDE의 시리얼 모니터로 간단히 출력하자.
  • 참고출처 : http://playground.arduino.cc/Code/Keypad

  • irrecv.enableIRIn() : 리모콘 시작
  • irrecv.decode(results) : 키가 눌러진지 확인
  • irrecv.resume() : 다음값

데모소스를 기반으로 해서 수정해 보자. 우선 위에 링크된 라이브러리 파일이 있는 github로 가보세요. 이 라이브러리를 만드신 분의 라이브러리 IRremot.h 파일을 열어 볼 수 있습니다. 거기에 decode_results 클래스를 열어 보시면 여기에 리모콘의 정보가 어떤것을 저장하는지 잘 나와 있습니다.

보시면, decode_results 클래스에서는 decode_type, address. value, bits, *rawbuf, rawlen, overflow 변수들을 담고 있습니다. 여기서 decod_type, bits, value 값만 찍어보도록 하죠.

#include <IRremote.h>
 
int IRpin = 11;  
IRrecv irrecv(IRpin);
decode_results results;

void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // 리모콘 시작
}

void loop()
{
  if (irrecv.decode(&results)) //리모콘 누른값이 없다면 패스
    {
     Serial.print("results.decode_type : "); //리모콘 타입
     Serial.println(results.decode_type);
     
     Serial.print("results.bits : "); //키값길이
     Serial.println(results.bits);
     
     Serial.print("results.value : "); //키값
     Serial.println(results.value);
     
     Serial.print("results.value, HEX : "); //키값을 16진수로변환
     Serial.println(results.value, HEX);
   
     irrecv.resume(); // 다음값
    }
}

대충 11번 핀을 IR Sensor 값을 읽는 핀입니다. IRrecv 객체변수를 선언할대 인자값으로 IR핀을 넘겨줍니다. 그리고 우리가 리모콘의 키값을 얻어야 하기때문에 리모콘 키값이 저장되어 있는 decode_results 클래스 객체 변수를 선언합니ㅏㄷ.

시리얼모니터로 출력하기 때문에 지난시간에 배웠듯이 begin(9600)으로 시리얼통신 시작을 선언하고 시리얼출력을 하기 위해서 print(), println()함수를 사용했네요.

리모콘을 사용하기 위해서 enableIRin()함수를 리모콘을 사용을 선언해야겠죠.

loop()함수에서 irrecv.decode(&reults)함수로 리모콘 키가 눌러졌는지 확인 작업을 합니다. 눌러졌다면 results 객체 변수에는 키값이 저장되어 있고 리모콘 decode_type, bits, value 정보를 print(), println()함수로 출력하는 로직입니다.

여기서 중요한 것은 value값입니다. 이 값을 토대로 리모콘을 눌렀을때 누른키값의 동작을 아두이노에서 명령을 내리면 됩니다.

핵심은results.value 이거 입니다.
다른 건 몰라도 대충

  irrecv.enableIRIn(); // 리모콘 시작
    
    if(irrecv.decode(&results)){ //리모콘 눌렀는지 확인
     if(results.value==0xFD48B7) {
       Serial.println(" key : 3"")
     }
     irrecv.resume(); //다음값
 } 

리모콘을 3번키를 누르면 3번키값이 'FD48B7' 16진수 값입니다. 3번키가 눌러졌다면 프린트문장을 수행되겠죠. 만약에 특정 동작을 프린트문장 대신에 모터를 돌린다거나 불을 끈다든가 다른 동작을 수행하도록 할 수 도 있겠죠.

다 빼고 부분만 빼내오니간 별거 없죠. 키값을 미리 알수가 없으니깐 실제로 실험하실때 리모톤의 기본 정보를 출력하시고 각 키값을 누러서 값을들 미리 적어놓고 그 값을 기준으로 제어 하시면 될꺼에요.

어려운 것은 없습니다.

4. 결과


리모콘 3번 키를 눌렀을때 결과입니다.


동영상은 실험과정을 담아 놨습니다. 보시고 따라해보세요. 소스를 복사한 곳은 위에 링크된 라이브러리 Github에 등록된 자료입니다. 위 링크된 곳을 미리 창을 열어 놓고 따라서 해보세요.

마무리


IRremote 라이브러리를 누군가 이미 만들어 놓았습니다. 우리는 단지 실험을 할때 그걸 이용할 뿐이죠. 리모콘을 제어하기 위해서 라이브러리를 개발 해야하는 소모적 시간을 줄일 수 있습니다. 아두이노를 다루는 가장 큰 장점은 이미 많은 라이브러리들이 오픈 되어 있어서 우리들은 창의적인 개발만 신경쓰면 됩니다. 뭘 만들지 그것만 여러분들이 상상의 나래만 펼치면 되고 기본 베이스 소스나 코딩은 오프라인으로 다 공개되어 있기 때문데 가져다가 잘 응용하여 표현만 하시면 됩니다.

처음부터 하나하나 프로그래머로써 코딩을 한다면 리모콘 하나를 다루기 위해서 엄청난 많은 시간을 소비해야 합니다. 전문적 지식도 필요하고요. 짧게는 몇일 길게는 한달이 걸리지 모릅니다.
하지만 IRremote 라이브러리를 이미 오프라인으로 공개되어 있어서 누구나 쉽게 이걸 활용할 수 있습니다. 물론 상업적으로는 저작권의 문제가 있겠지만 단지 실험을 목적으로 우리가 다룬다면 사실 부담없이 실험 할 수 있습니다.

이 라이브러리를 통해서 리모콘 키값을 얻을 수 있게 되었고 그 키값을 통해서 해당 동작만 설계만 우리들이 표현하면 되니깐 개발 시간을 엄청 단축시킬 수 있습니다. 참 좋은 세상에 살고 있는 거지요.
암튼 리모콘의 키값을 읽을 수 있게 되었으니깐 이 키로 뭘 제어할지 상상의 나래를 펼쳐 보세요


댓글()

[아두이노] Serial 통신 제어

IOT/아두이노|2019. 3. 13. 09:00

[아두이노] Serial 통신 제어



원래 시리얼 통신은 초반부에 자세히 소개 했어야 했는데 프로세싱+아두이노 연결을 하면서 그냥 넘어가면 안될 것 같아서 이제서야 소개하게 되었네요. 센서 부품를 다룰때 그 결과를 출력하는데 많이 사용하기 때문에 매우 중요합니다. 나중에 블루투스 통신에서도 사용하니깐 위에 참고출처를 링크한 아두이노 홈페이지의 레퍼런스를 잘 살펴보시기 바래요. 오늘은 시리얼통신 기본만 소개하겠습니다.

1. Serial 통신



그림에서 보듯이 아두이노 IDE 시리얼모니터에서 아두이노로 "123"이란 값을 보내게 됩니다. 그리고 나서 아두이노는 이 값을 읽어서 다시 아두이노 IDE로 데이터를 보내고 시리얼모니터로 그 값을 출력합니다.

[ 기본 소스 ]

void setup() {
  Serial.begin(9600);    //시리얼 통신 9600 통신속도로 시작
}
void loop() {
  
  if (Serial.available() > 0) { //데이터가 수신되는지 확인
    char ch = Serial.read(); //1byte 읽음
    Serial.println(ch); //1byte 읽은거 출력
  }
}

기본 소스의 주석을 다 달았습니다. 주석만 읽으시면 대충 어떻게 통신이 이루어지는 아시겠지요.

위 이미지에서 123을 보내면 아두이노에서 1, 2, 3 이렇게 하나씩 아두이노 IDE에 출력되는 이유가 과연 뭘까요. 이것은 123을 전송했는데 byte 단위로 읽고 char(문자형) 변수 ch에 1byte 문자가 저장되는데 "123" 값에서 순차적으로 '1','2','3'을 저장하고 순자척으로 Serial.println('1'), Serial.println('2'), Serial.println('3') 이렇게 출력하게 됩니다.
이걸 정수형으로 받으면

2. 시리얼 통신 함수


기본

  • Serial.begin(통신속도) : 시리얼통신 시작. 기본통신속도는 9600을 많이 사용합니다.
    많은 아두이노 시리얼통신에서 9600을 사용합니다.
    통신속도 : 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200
  • Serial.end() : 시리얼통신 중단
  • Serial.available() : 데이터 도착했는지 확인
  • Serial.println(데이터) : 데이터를 상대쪽으로 전송(출력)
  • while(!Serial) : 통신 연결 확인(연결 안되면 무한 루프를 돌면서 연결을 기다림)

위 함수중에서 abailable()함수만 좀 더 살펴보도록 하죠.

예제)


데이터가 들어왔는지 상태값을 확인하는게 available()함수라고 했죠. 그러면 정확히 어떤값이 들어있는지 시리얼 모니터에 출력을 해봤습니다. 'a'라는 문자를 보내면 아두이노는 available()함수에 수신이 되면 수신되었다고 1의 상태값을 갖게 됩니다. 그러면 if문이 참이고 첫라인에서 출력이 '1'이 나오게 됩니다. 그다음 read()함수로 데이터를 읽고 정상적으로 시리얼모니터로 'a'가 두번째라인에 출력 됩니다. 그러면 데이터를 read()함수로 읽었다면 다음 available()함수의 상태값은 데이터를 읽었기 때문에 지워지고 상태값은 '0'이 됩니다 즉, read()으로 읽고 나면은 abailable()은 초기화 상태가 되는 것이죠. 그래서 세번째 라인이 0이 출력 됩니다.
쉽게 말해서, 데이터가 전송되면 available() 함수는 '1'의 상태가 되고 데이터를 읽고나면 '0'의 상태가 됩니다. 아래 그림을 보시면 좀 더 쉽게 이해가 되시겠지요.


begin(), available(), read(), print(), println() 정도만 우선 알아두시고 아래에서 소개하는 함수들 읽기와 출력에 대해 몇가지 소개만 하고 나머지는 레퍼런스를 참고해 주세요. 나중에 센서값을 어떻게 읽고 어떻게 출력할지 그 센서에 따라서 표현이 좀 달라지니깐요 레퍼런스를 꼭 봐주셔야 해요. 가상시뮬레이터에서는 위 5가지 함수만 기본 베이스로 이해하시고 사용하시면 쉽게 실험하실 수 있을꺼에요.


읽기

  • Serial.read() : 1byte 읽음
  • Serial.readBytes(buffer, length) : buffer에 length 길이만큼 읽음
  • Serial.readBytesUntil(character, buffer, length) : buffer에 length 길이만큼 읽어오는데 character가 끝문자 표시로 이문자가 있는 곳까지 읽어오게 됨.
    예) Serial.readBytesUntil('s', buffer, 10)일때 전송데이터 "100s1000"이면 buffer "100"이 첨에 저장되고 다음에 "1000"이 저장됨.
  • Serial.parseInt() : 정수형으로 읽기
  • Serial.parseFloat() : 실수형으로 읽기

출력

  • print(값) : 데이터를 출력하고 현재 라인에 커서가 그대로 유지됨.
  • println(값) : 데이터를 출력하고 새로운 라인으로 커서가 이동함.(Enter로 생각하시면됨)

주의 :

  • print(값) : 데이터를 아스키코드값 바꿔서 전송.
  • wirte(값) : 데이터를 그대로 전송.


위 그림에서 보는 것 처럼 print()함수는 '7'이란 값을 전송할때 '55'라는 아스키코드값으로 전송하고 그 값이 출력되는 걸 보실꺼에요. 하지만 write()함수는 '7'을 그래도 전송하고 받는쪽에서는 '7'을 아스키코드값으로 여기고 출력된다는 차이점을 보입니다. 둘 차이를 잘 비교하세요.


마무리


그외도 여러 함수들이 있는데 오늘 소개한 함수들도 몇개를 제외하고는 잘 사용하지 않습니다. 가상시뮬레이터에서는 그냥 단순하게 read(), print(), println()정도로 데이터를 읽거나 쓰는 정도만 합니다.

- if(Serial.available()) {  }
- while(!Serial){  }
- Serial.read()
- Serial.print()
- Serial.println()

이정도 함수만 외워두시면 기본적인 통신을 할 수 있습니다. 나중에 센서를 이용한 특정값이나 부품을 제어하는 통신을 할 경우에는 레퍼런스를 살펴 보셔야 겠지만 우선은 시리얼통신이 어떻게 이루어지는지만 살펴보세요.

댓글()

[아두이노] Processing를 이용한 아두이노 제어 III

IOT/아두이노|2019. 3. 12. 09:00

[아두이노] Processing를 이용한 아두이노 제어 III 



세번째 시간으로 이제는 프로세싱과 아두이노 두 곳에서 개별적으로 코딩하고 시리얼통신을 이용해서 데이터를 주고 받아서 제어하는 방법을 살펴보겠습니다. 기본적으로 아두이노는 원래의 코딩 방식으로 코딩하면 됩니다. 변경되는 부분은 없고 단지 시리얼통신을 할 수 있도록 세팅만 해주면 됩니다. 프로세서는 시리얼통신 부분만 코딩해주고 프로세스는 그래픽 코딩만 중점적으로 하면 됩니다. 쉽게말해서 각자의 역할만 하고 단지 시리얼통신이라는 매개체로 연결된다고 생각하시면 됩니다.

1. Processing + Arduino



[ LED 깜박이는 예제 ]

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

void loop() {
  digitalWrite(13, HIGH);
  delay(1000);             
  digitalWrite(13, LOW);
  delay(1000);   
}

LED 깜박이는 기본 예제를 통해서 프로세싱과 아두이노의 역활을 나눠 보겠습니다. 프로세싱에서는 스위치 역활로 LED가 1초단위로 깜박이도록 명령을 내리고 아두이노는 그 명령을 받아서 LED를 깜박이게 하도록 역활을 나눠 보겠습니다.

아래 그림처럼 프로세싱는 시리얼통신을 통해 아두이노에게 LED 상태값을 보내고 아두이노는 이 값을 읽어서 LED를 깜박이게 하는 합니다. 1초 단위로 깜박이는 제어권은 프로세싱에게 있고 아두이노는 프로세싱의 명령에 따라서 LED의 상태변화만 시키게 됩니다.



[ Prcessing 코딩 ]

import processing.serial.*; 

Serial mPort;

void setup() {  
    println(Serial.list());
    mPort = new Serial(this, Serial.list()[0], 9600);   
} 
void draw() {  
  mPort.write(1);
  delay(1000);
  mPort.write(0);
  delay(1000);
}

시리얼통신 라이브러리를 가져옵니다. 그리고 Serial 클래스를 하나의 객체 변수(mPort)로 선언하고 인스턴트 해야합니다. mPort = new Serial(this, Serial.list()[0],9600) 이렇게 해서 시리얼통신 객체가 만들어 집니다.

이걸 통해서 mPort.write(1)은 byte전송 명령함수인데 print(), println()으로 그동안 써왔지만 이번에는 write()함수를 사용해 봅니다.

이곳에 가셔서 시리얼통신 함수들을 보시고 사용하시면 됩니다.

프로세싱에서 1초 단위로 깜박이는 명령 시리얼통신을 통해서 1(HIGH)과 0(LOW)값을 아두이노로 보냅니다.

[ Arduino 코딩 ]

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

void loop(){
  byte val;
  
  if(Serial.available()) {  
    val = Serial.read();
    
    digitalWrite(13,val);
  }
}

아두이노는 if 조건문으로 Serial.available()함수로 통신 데이터가 들어왔는지 확인하는 문장입니다. 데이터 들어 왔다면 0이 아니고 데이터가 들어오지 않았다면 이 Serial.available()함수의 반환되는 값은 0으로 if문 이하를 수행하지 않습니다. 쉽게 말해서 통신을 통해 데이터가 도착했다면 참이고 도착하지 않았다면 거짓이다 이걸 체크한다고 생각하세요.

데이터가 도착했으면 Serial.read()함수로 데이터를 읽습니다. 아두이노에서 read라는 단어가 보이면 뭘 읽는 함수구나 하고 생각하시면 됩니다. 프로세싱에서 넘어 온 1과 0의 값이 val 변수에 저장됩니다. 프로세싱에서 byte 단위로 숫자를 전송했고 아두이노에서는 byte변수로 그 값을 받아서 저장하게 됩니다.

실제아두이는 통신을 통해서 1과 0을 읽어 오고 그 값을 기준으로 LED를 On/Off 하게 됩니다.

  digitalWrite(13,val);

이 한문장이 아두이노가 하는 역활입니다. 그러면 반대의 경우도는 온도센서가 있는데 그 온도 값을 프로세싱에 보내서 시각화 하고 싶다면 방금한 시리얼통신 코딩부분을 아두이노에서 보내고 프로세싱에서 읽도록 바꿔서 코딩하면 되겠죠.


3. 간단한 버턴클릭 예제로 아두이노 LED 제어


두번째 시간에는 프로세싱에서 아두이노 동작제어하는 코딩까지 전부 했었습니다. 위에서 프로세싱과 아두이노가 개별적으로 코딩하고 시리얼통신으로 주고 받은 LED 깜박이는 예제처럼 코드를 변경해 보죠.


[ Prcessing 코딩 ]

import processing.serial.*;

Serial mPort;
boolean cnt = false;

void setup() {  // this is run once.   

    // set the background color
    background(255);
    
    // canvas size (Integers only, please.)
    size(600, 600); 
    
    println(Serial.list());
    mPort = new Serial(this, Serial.list()[0], 9600);  
    
} 

void draw() {  // this is run repeatedly.  
    rect(10,10,580,300,10);    
    rect(200,350,200,100,10);    
}

void mousePressed(){
 if(mouseX>200 && mouseX<400 && mouseY>350 && mouseY <450){
     cnt=!cnt;
 
 }
 
 if(cnt==true){
   fill(255,0,0);
   mPort.write(1);
 }
 else{
   fill(0,0,0);
   mPort.write(0);
 }
}

(기본 예제)

boolean cnt = false;

void setup() {  // this is run once.   
    
    // set the background color
    background(255);
    
    // canvas size (Integers only, please.)
    size(600, 600);       
    
} 

void draw() {  // this is run repeatedly.  
    rect(10,10,580,300,10);    
    rect(200,350,200,100,10);    
}

void mousePressed(){
 if(mouseX>200 && mouseX<400 && mouseY>350 && mouseY <450){
     cnt=!cnt;
 }
 if(cnt==true)fill(255,0,0);
 else fill(0,0,0);
}

이 소스에서 프로세싱에서 아두이노 문법을 위에서 설명했으니 기본 예제 소스에다가 아래 코딩 부분을 삽입하시면 됩니다.

import processing.serial.*;

Serial mPort;

void setup() {  // this is run once.   

    println(Serial.list());
    mPort = new Serial(this, Serial.list()[0], 9600);  
    
} 

void draw() {  // this is run repeatedly.  

}

void mousePressed(){
     mPort.write(1); 
 }
}

여기서, mPort.write(값)은 버턴을 눌렀을때 색상을 바꾸는 곳에다 LED 제어한 부분에 아두이노로 데이터를 전송하는 명령을 삽입하면 되겠죠.

if(cnt==true) {
   //arduino.digitalWrite(ledPin,Arduino.HIGH); 
     mPort.write(1);
   fill(255,0,0);
    }
 else{
   //arduino.digitalWrite(ledPin,Arduino.LOW);
     mPort.write(0);
   fill(0,0,0);
     
 }

[ Arduino 코딩 ]

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

void loop(){
  byte val;
  
  if(Serial.available()) {  
    val = Serial.read();
    
    digitalWrite(13,val);
  }
}

아두이노는 코드가 동일합니다.

결과는 아래 사진처럼 정상적으로 동작 했습니다.


6. 결과


라즈베리파이에 프로세싱과 아두이노 IDE를 설치해어 그곳에서 실험한 과정을 녹화 했네요. 간단히 살펴보도록 하세요.

마무리


프로세싱 툴이 시각화 하기에 재밌는 프로그램 입니다. 그리고 그 프로세싱을 두가지 형태로 아두이노를 접근 할 수 있습니다. 사용하기에 따라 장단점이 있습니다. 첫번째 프로세싱이 아두이노 코딩까지 전부하게 되면 코딩량이 늘어나고 복잡해 질 수 있지만 프로세싱에서 직접 제어하니깐 빠르게 부품 제어와 시각화 코딩을 진행 할 수 있습니다. 하지만 프로세싱과 아두이노가 개별적으로 코딩이 들어가면 매 실험때마다 아두이노는 프로세싱에 맞춰서 코딩하고 아두이노 코딩과 프로세싱 코딩을 맞추는 작업을 계속 해야 하는 단점이 발생 합니다. 프로세싱과 아두이노가 개별적으로 코딩해야한다는 장점이자 단점이 될 수 있습니다.

그러면 프로세싱에서 무조건 코딩을 전부하는게 좋냐고 물으신다면 꼭 그렇지는 않습니다. 아두이노 코딩은 기존의 방식으로 그대로 진행하면 됩니다. 이미 코딩된 형태에서 변경할 필요 없이 시리얼통신 부분만 코딩해주면 서로 통신데이터를 주고 받으면서 제어를 하면 되기 때문에 심풀하게 아두이노코딩만 신경쓰면 됩니다. 프로세싱도 아두이노로 보낼 데이터나 받을 데이터만을 기준으로 시각화 하면 되니깐 구지 아두이노 코딩까지 신경 쓸 필요가 없어집니다. 괜히 한곳에서 합쳐지면 가독성도 떨어지고 복잡해 보이고 불편한 코딩이 될 수 있습니다.

둘다 장단점이 있고 자신이 코딩하기에 편한 걸 선택하셔서 실험 하시면 됩니다.


댓글()

[아두이노] Processing를 이용한 아두이노 제어 II

IOT/아두이노|2019. 3. 11. 09:00

[아두이노] Processing를 이용한 아두이노 제어 II 



두번째 시간으로 프로세싱을 통해 아두이노를 직접 제어하는 방법을 살펴보도록 하겠습니다.

1. Processing 세팅


[1단계] 도구->추가도구 설정 눌러 줍니다.


[2단계] 라이브러리에서 arduino라고 치시면 아래 그림처럼 검색되는데 하당 라이브러리를 선택하시고 Install 하시면 됩니다.


[3단계] 프로세싱 편집기를 종료하시도 다시 실행 시킵니다. 그리고 파일->예제를 누르시면 따로 창이 뜨고 Arduino(Firmata) 가 추가된 것을 보실 수 있을거에요.


아무 예제나 클릭하시면 되는데 좀 코딩량이 많습니다. 그래서 간단히
[4단계] https://playground.arduino.cc/Interfacing/Processing 이곳 주소로 가시면 프로세싱 예제가 있습니다.

프로세싱 편집기에 그래도 코딩하시면 됩니다.


2. 아두이노 세팅


[1단계] 파일->예제->Firmata -> StandardFirmata 를 눌러줍니다.


[2단계] 예제가 뜨고 이걸 아두이노에 이식 시키면 됩니다.


3. Processing + Arduino


아두이노에서 StandardFirmata를 아두이노에 이식 시키고 Processing은 Arduino(firmata) 라이브러리를 설치해서 아두이노 라이브러리를 통해서 실제 아두이노를 직접 제어하게 됩니다.


쉽게말해서 아두이노는 프로세싱을 받을 준비를 해놓고 프로세싱에서는 직접 아두이노 함수를 통해 제어를 하게 된다고 생각하시면 됩니다.

4. Processing에서 아두이노 문법


import processing.serial.*;
import cc.arduino.*;

Arduino arduino;
int ledPin=13;

void setup() {  
  println(Arduino.list());
  arduino = new Arduino(this, Arduino.list()[0], 57600);
  arduino.pinMode(ledPin, Arduino.OUTPUT);
}

void draw() {
  arduino.digitalWrite(ledPin,Arduino.HIGH);
  delay(1000);
  arduino.digitalWrite(ledPin, Arduino.LOW);
  delay(1000);  
}

소스로 설명드리겠습니다. 아두이노 LED 깜박이는 기본베이스 소스입니다.

기본적으로

import processing.serial.*;
import cc.arduino.*;

통신과 아두이노 라이브러리를 가져오게 와야겠죠. 그리고 아두이노 클래스를 사용할때 클래스 객체를 선언해야합니다.

Arduino arduino;
arduino = new Arduino(this, Arduino.list()[0], 57600);

클래스 객체를 선언하고 객체를 인스턴트 합니다. 쉽게 말해서 객체를 만들고 초기화 한다고 생각하세요.

그다음 아두이노에서 쓰던함수들은 객체명.함수 함수로 객체가 앞에 연결자로 모두 표현해야 합니다. 아두이노 클래스내에 아두이노 함수들이 있으니깐 객체.함수() 해야지 객체속의 함수를 호출할 수 있게 됩니다.

pinMode(13,OUTPUT) => arduino.pinMode(13,Arduino.OUTPUT);

됩니다. 무슨 의미인지 아시겠지요. delay(1000) 이것은 왜 안붙이냐 오타이냐 할 수 있는데. 프로세싱에서도 있는 함수임으로 붙이지 않습니다.

임포턴트하는 것과 객체 선언한것만 이해하시면 간단 합니다.

5. 간단한 버턴클릭 예제로 아두이노 LED 제어


지난시간에 만들어 봤던 작은사각형 클릭했을때 사각형들의 색상을 바꾸는 예제를 아두이노 LED를 On/Off 시키는 스위치 버턴으로 사용하겠습니다.

예제)

boolean cnt = false;

void setup() {  // this is run once.   
    
    // set the background color
    background(255);
    
    // canvas size (Integers only, please.)
    size(600, 600);       
    
} 

void draw() {  // this is run repeatedly.  
    rect(10,10,580,300,10);    
    rect(200,350,200,100,10);    
}

void mousePressed(){
 if(mouseX>200 && mouseX<400 && mouseY>350 && mouseY <450){
     cnt=!cnt;
 }
 if(cnt==true)fill(255,0,0);
 else fill(0,0,0);
}

이 소스에서 프로세싱에서 아두이노 문법을 위에서 설명했으니 해당 위치에 넣으시면 됩니다.

mport processing.serial.*;
import cc.arduino.*;

Arduino arduino;
int ledPin=13;

boolean cnt = false;

void setup() {  // this is run once.   
    
    // set the background color
    background(255);
    
    // canvas size (Integers only, please.)
    size(600, 600); 
        
    //println(Arduino.list());
  arduino = new Arduino(this, Arduino.list()[0], 57600);
  arduino.pinMode(ledPin, Arduino.OUTPUT);
    
} 

void draw() {  // this is run repeatedly.  
    rect(10,10,580,300,10);    
    rect(200,350,200,100,10);    
}

void mousePressed(){
 if(mouseX>200 && mouseX<400 && mouseY>350 && mouseY <450){
     cnt=!cnt;
 }
 if(cnt==true) {
   arduino.digitalWrite(ledPin,Arduino.HIGH);
   fill(255,0,0);
    }
 else{
   arduino.digitalWrite(ledPin,Arduino.LOW);
   fill(0,0,0);
 }
}

버턴을 눌렀을때 색상을 바꾸는 곳에다 LED 제어해야겠죠.

if(cnt==true) {
   arduino.digitalWrite(ledPin,Arduino.HIGH);
   fill(255,0,0);
    }
 else{
   arduino.digitalWrite(ledPin,Arduino.LOW);
   fill(0,0,0);
 }

딱히 수정할 것은 없습니다. 아두이노 소스를 해당 위치에다만 삽입하면 됩니다.

6. 결과


프로세싱과 아두이노에 코드를 복사하고 실제 동작하는 전과정을 담았습니다. 동영상을 보는게 더 쉽게 와 닿을지 모르겠네요.

마무리


종합해 보면, 아두이노에서 StandardFirmata를 아두이노에 이식 시키고 Processing은 Arduino(firmata) 라이브러리를 설치해서 아두이노 라이브러리를 통해서 StandardFirmata를 이식된 아두이노를 직접 제어를 하게 됩니다.

이 방법은 아두이노에서 코딩을 할 필요가 없습니다. 프로세싱에서 그래픽 시각화와 아두이노 제어를 둘 다 할 수 있습니다.

또 다른 방법은 시리얼통신으로 서로 데이터를 주고 받는 방법이 있습니다. 프로세싱과 아두이노가 데이터를 주고 받으면서 제어할 데이터 또는 시각화 데이터를 서로 시리얼통신으로 통해 주고 받는 방식이 있습니다.
이 경우는 아두이노는 아두이노 코딩만 하면 되고 프로세싱은 프로셋이 코딩만 하면 됩니다. 그 중간 연결을 통신이 대신 하는 것이죠.

우선은 오늘 배운 프로세싱에서 아두이노를 제어하는 방법만 이해해 주시고 다음에 따로 통신을 통해 데이터를 주고 받는 방식을 살펴보도록 하겠습니다.


댓글()

[아두이노] Processing을 이용한 아두이노 제어 I

IOT/아두이노|2019. 3. 10. 09:00

[아두이노] Processing을 이용한 아두이노 제어 I 



오늘은 즐거운 토요일입니다. 매주 토요일이면 재밌는 프로그램을 소개하는 날입니다. 이번주 소개 할 프로그램은 아두이노와 연결해서 재밌게 시각화 실험을 할 수 있는 프로그램입니다. 스크래치나 프로세싱으로 아두이노 제어하거나 결과를 시각화 하는데 많이 사용합니다. 잘 알려진 프로그램 툴이고 컴퓨터 공부를 좀 하신분들은 한번쯤은 다뤄봤을 툴이라고 생각되네요. 아두이노 처음 입문자분들은 아직 프로세싱에 대해서 잘 모르고 프로그램 언어 영역이라서 다루기 불편하실 수 있지만 그래픽 툴로서는 쉬운 편에 속하고 함수만 잘 활용한다면 재밌는 시각화 프로그램을 만들 수 있으니깐 한번 참고하시면 좋을 듯 싶네요.

좀 포스팅 내용이 길어질 수 있어서 나눠서 소개할까 합니다. 오늘은 간단히 프로세싱 소개를 하는 시간으로 채울 듯 싶네요.

1. Processing 다운로드


[ PC에 실치 ]


위에 공식 홈페이지에 다운로드 주소까지 링크 걸려있습니다. 들어가시면 자신의 운영체제에 맞게 다운로드 하시면 됩니다. 압축(zip) 파일을 다운로드 하시면 폴더를 만드시든 그냥 압축 파일명으로 압축된 파일을 푸시면 됩니다. 설치는 따로 사실 필요없이 폴더 안에 들어가서 실행파일을 누르시면 자동으로 실행 됩니다.

[ 라즈베리파이에 설치 ]

$ curl https://processing.org/download/install-arm.sh | sudo sh 

[출처] 라즈베리파이에 프로세싱 설치하기(작성자 코스모스)

라즈베리파이에 설치할때 잘 몰라서 이건 배우는 단계라서 두가 설치법이 있는데 구글링 해서 보니깐 코스모스님이 블로그에 간단하게 설치하는 법을 소개했네요. 이 한줄로 라즈베리파이에 프로세싱을 설치하여 현재는 재밌게 사용하고 있네요.

2. Precessing 웹 편집기


위에 링크된 sketchpad 사이트 가시면 온라인 상에서 프로세싱을 즐길 수 있습니다. 다운 받을 필요도 없고 회원가입을 할 필요도 없이 그냥 코딩 하고 싶을때 가셔서 코딩 하시면 됩니다.


3. 편집창


[ PC 편집기 ]


다운로드 받은후 압출파일을 풀고 실행파일을 실행 시키면 실행됩니다. 간단한 코딩을 넣고 실행한 창입니다.
나중에 아두이노 연결에 이 편집기를 이용할 예정입니다.

[ 온라인 편집기 ]



뭔가 코딩 되어 있으며 실행 버턴을 눌러보세요. 간단한 그래픽 쇼가 보여집니다. 간단한 예제로 시뮬레이터가 돌아간 모습을 보시고 코딩이 어떻게 했는지 한번 잘 살펴보세요.

4. Precessing의 문법 구조


void setup()
{
     초기화 명령이나 한번만 수행하는 명령들을 코딩;
}
void draw(){
    반복적으로 계속 그리는 동작명령들을 코딩;
}

아두이노랑 구조가 같습니다. 아두이노에서는 loop()함수이지만 프로세싱에서는 draw()함수입니다. 프로그램이 종료되기 전까지는 반복 호출되는 함수인 것죠.

5. Precessing의 간단한 버턴 예제


  • 사용함수 : background(), size(), rect(), fill(), mousePressed()
  • 내용 : 버턴 클릭 이벤트로 사각형의 색을 변경한다.

사용함수

  • background(255) : 배경색인데 0~255로 검색에서 흰색사이의 색을 지정해줍니다.
  • size(600,600) : 윈도우 사이즈
  • rect(100,200,100,200,10) : 사각형을 그리기인데 rect(x1,y1,x2,y2,모서리)를 나타냅니다. 시작꼭지점(100,200) 얼마만큼 이동한 끝점(100,200)을 나타냅니다. 그리고 마지막 인자 10은 모서리를 매끄렇게 하는 값인데 안써도 상관없습니다. 그냥 rect(100,200,100,200)이렇게 해도 됨.
  • fill(255,0,0) : 채우기 함수입니다. fill(R,G,B)값으로 생각하시면 됨
  • mousePressed()은 마우스 버턴을 눌렀을 호출하는 함수임(다른 표현도 있지만 이정도만 이해하세요.)

간단한 버턴 클릭 예제

boolean cnt = false;

void setup() {  // this is run once.   
   
   // set the background color
   background(255);
   
   // canvas size (Integers only, please.)
   size(600, 600);       
   
} 

void draw() {  // this is run repeatedly.  
   rect(10,10,580,300,10);    
   rect(200,350,200,100,10);    
}

void mousePressed(){
if(mouseX>200 && mouseX<400 && mouseY>350 && mouseY <450){
    cnt=!cnt;
}

if(cnt==true)fill(255,0,0);
else fill(0,0,0);
}

코딩을 보시면 총 세개로 나눠 졌습니다. 온라인 편집기에 있는 소스를 최대한 안건들고 간단한 버턴 클릭하는 예제를 코딩해 봤습니다.

setup() 함수에서 윈도우 설정을 했는데 배경을 background(255)로 흰색을 지정하고 크기를 size(600,600)으로 600x600 크기로 만들었습니다.

draw()함수에서는 만든 윈도우에서 rect()함수로 사각형을 두개 그렸습니다.


대충 위 그림처럼 윈도우 600x600 크기의 창이 뜹니다. 그리고 그 안에 rect()로 만든 사각형이 2개 보이시죠. 여기서 아래 작은 사각형을 버턴으로 이용할 예정입니다.

작은사각형 이미지를 버턴이라고 생각하고 해당 버턴을 누를려면 마우스 버턴을 클릭했을때 이벤트가 발생해야 겠죠. mousePressed()함수를 사용합니다. 이 함수는 마우스가 클릭했을때 호출되는 함수입니다. 그안에 버턴을 클릭했을때의 이벤트 로직을 작성해야 합니다.

void mousePressed(){
  마우스 클릭했을 때 호출;
}

위 마우스가 클릭했을때 호출되는 함수인데 간단하죠.
작은사각형 이미지가 버턴으로 사용되기 때문에 작은사각형 이미지 안을 마우스로 클릭했을때 이벤트를 실행시키면 됩니다.

쉽게 설계를 하면

  1. 사각이미지를 영역을 마우스로 클릭한다.
  2. 마우스 클릭 이벤트 발생한다.

그러면 사각형을 마우스클 클릭했을때 이벤트가 발생해야 합니다. 여기서 마우스가 아무곳이나 클릭한다고 해서 클릭 이벤트가 발생하면 안됩니다. 마우스를 클릭했을때 mousePressed()함수가 호출된다고 해서 무조건 동작해서는 안되고 사각형 안에서만 클릭하도록 코딩을 짜야 됩니다. 클릭했을때 호출되니깐 그 마우스 클릭한 좌표(mouseX,mouseY)가 작은 사각형버턴 영역에서 클릭했냐로 체크하는 로직을 짜면 쉽게 사각형을 버턴으로 만들 수 있겠죠.

버턴을 마우스 클릭했을때 원리

   rect(200,350,200,100,10);    

이 사각형을 클릭하기 위해서 사각형의 영역을 클릭하는 영역으로 잡아 줘야 합니다.


X축을 기준으로 하면 초록색 범위가 마우스 클릭 할 X축의 영역이 됩니다. 즉, 꼭지점 x1에서 x2까지이죠.
Y축을 기준으로 하면 노란색 범위가 마우스를 클릭 할 Y축의 영역이 됩니다. 즉, 꼭지점 y1에서 y2까지이죠.

그러면 사각형이 그러진 영역은 X축과 Y축이 겹쳐지는 저 영역이 바로 버턴의 영역이 되는 것이죠.

이걸 코딩으로 하면

if(mouseX>x1 && mouseX<x2 && mouseY>y1 && mouseY <y2){
        사각버턴 영역을 클릭 했을때 처리문;
}

코드를 분석으을 해보죠.

먼저 윈도우 상에서 마우스 좌표(mouseX,mousY) 추출 할 수 있습니다. 즉, 이 변수명으로 현재 마우스가 위치를 알 수 있게 됩니다.

if(mouseX>x1 && mouseX < x2)

이게 뭘 말할까요. X축 기준으로 클릭 범위가 초록색 범위여야 하잖아요. 그러면 마우스가 클릭한 mouseX의 값은 x1보다 크고 x2보다는 작을때 위 그림에서의 초록색 영역이 됩니다. 그래서 두 조건식으로 클릭한 마우스 mouseX좌표가 x1보다 크고 x2보다 작은가를 조건문으로 해서 작은사각형 이미지의 X축 영역을 체크하는 것이죠. 마우스 Y좌표도 이와 같습니다.

참고로 '&&' 기호는 "그리고" 이고 '||' 기호는 "또는"라는 뜻입니다.

if(mouseX>x1 && mouseX<x2 && mouseY>y1 && mouseY <y2)

총 4개의 조건식으로 모두 만족한 영영이 바로 X축 영역과 Y축 영역이 겹쳐지는 교집합 영역으로 바로 윈도우창의 작은 사각형의 영역이 됩니다. 이 조건을 만족하면 마우스 좌표는 작은 사각형 영역에 위치해 있다는 의미가 됩니다.

 rect(200,350,200,100,10);    

윈도우에서 작은사각형은 P1(200,400), P2(350,450) 꼭지점의 영역을 갖습니다.
이부분을 햇갈리시는 분들이 있습니다. 시작점 P1은 (200,350) 입니다. P2은 (x1+x2,y1+y2) 입니다. 사각형의 시작점을 기준으로 하는게 아니라 윈도우상에서 작은사각형이 위치좌표는 윈도우좌표에서 접근해야 하기 때문에 윈도우 상에서 작은사각형의 좌표를 지정해야 합니다.

rect()함수는 시작점(x1,y1)을 기준으로 끝점(x2,y2)만큼 크기의 사각형이냐의 표현입니다. 즉, 끝점은 윈도우 좌표에서 시작점 (x1,y1)이 이동했고 거기서 다시 사각형이 끝점으로 이동하기 때문에 시작점까지 이동한 거리를 더해줘야 합니다. 그래서 P2(200,100)이 아니라 (200+200,350+100)해서 (400,450)이 끝점이 되는 것이죠.

그러면 윈도우 상에서 마우스(mouseX,mouseY)가 작은사각형의 좌표 영역에 들어갔는지 if문으로 아래와 같이 표현이 됩니다.

 if(mouseX>200 && mouseX<400 && mouseY>350 && mouseY <450)

마우스를 클릭했을때 mousePressed()함수가 호출되고 위 if문을 통해서 작은 사각형 영역에 마우스 좌표가 현재 위치해 있는지 확인하는 문장이 됩니다. 이렇게 해서 작은사각형 이미지는 버턴의 능력을 갖게 됩니다.

작은사각형을 버턴으로 표현을 했으면 실제로 간단하게 색상을 변경하는 명령을 내려봅시다. 2가지 색을 교대로 바꾸게 할려고 별도로 cnt라는 변수를 하나 선언 했네요. 참/거짓으로 부울변수를 만들어서 참일때 빨강색, 거짓일때 검은색으로 바꿔 볼려고 합니다.

boolean cnt =fasle;

체크변수로 초기값으로 false를 주고 클릭했을때

cnt=!cnt;

이 문장으로 변수값을 반전 시켰습니다. 거짓이면 참으로 참이면 거짓으로 반전 문장입니다. 이코딩은 작은 사각형을 클릭했을때 발동해야 하기 때문에 마우스클 클릭했을때 호출되는 함수에서 다시 if문 사각형 영역에 있는 지 확인 되었을때 그 안에다가 표현 해야겠죠. 간단히 그안에다 이 한줄을 삽입하면 됩니다. 참 쉽죠.

그다음 채우기 fill()함수를 이용해 사각형을 채우는 로직을 if문으로 간단히 표현했습니다.

 if(cnt==true)fill(255,0,0);
 else fill(0,0,0);

if문으로 cnt가 참이면 색채우기를 빨강색으로 fill(255,0,0)으로 cnt가 거짓이면 검정색으로 fill(0,0,0)으로 바꿔줍니다. 색을 작은사각형 버턴을 클릭할때마다 빨강색과 흰색이 계속 바뀌게 됩니다.

이렇게 해서 간단한 로직에 대해서 설명했습니다. 그런데 왜 fill()함수만으로 색이 바꿔지는이 햇갈리는 분들이 아마 있을거에요. draw() 함수에서 매번 호출된다고 했는데 그안에서는 사각형 2개를 그립니다. 그리기 전에 사각형의 채우기 색을 지정해주면 그릴때마다 사각형의 색을 바꿀 수 있게 된다는 원리를 이용한거니 어렵게 생각하지 마세요.

쉽게 말해서,

  • cnt가 true 인 경우
fill(255,0,0)
rect(10,10,580,300,10);    
rect(200,350,200,100,10);  
  • cnt가 false 인 경우
fill(0,0,0)
rect(10,10,580,300,10);    
rect(200,350,200,100,10);  

이렇게 마우스를 클릭할때마다 색이 변경되어 그려진다는 것이죠. 어떤 느낌이신지 아시겠지요.

그래서 마우스클릭 이벤트 함수에서 작은사각형의 이미지를 클릭했다면 그안에다가 fill()함수로 draw()함수로 그리기 전에 채우기 색을 지정하면 쉽게 색상을 바꾸게 되는 것이죠.

결과



이걸로 아두이노랑 연결해서 버턴이미지를 클릭 할 때 빨강색으로 바꾸고 Red LED에 불이 들어오게 하고 다시 클릭 하면 검정색으로 바뀌게 해서 Red LED를 깜박이게 제어할 예정입니다.


마무리


많은 예제 중에서 왜 이런 예제를 설명하냐면요 다음편에 아두이노와 연결해서 이 예제로 간단히 LED를 On/Off 버턴으로 활용하기 위해서 입니다.
작은사각형 이미지를 클릭이벤트를 활용하여 큰 모니터 사각형과 버턴 사각형이 색이 빨강색으로 바뀌면 Red LED에 불이 들어오고 검은색으로 바뀌면 Red LED가 꺼지는 스위치버턴으로 활용하기 위해서이죠.
바로 프로세싱으로 간단히 아두이노를 제어하고 그 제어하는 방법을 이해하면 프로세싱을 통해서 아두이노를 시각화 할 수 있다는 것을 이 예제를 통해 소개하기 위해서 입니다.

오늘은 온라인 상에서 프로세싱을 간단히 다뤄보세요. 구글링 치면 강좌도 많고 간단히 함수들을 불러다가 배치해서 시각화 해보세요.

댓글()

[아두이노] 온도센서(TMP36) 제어

IOT/아두이노|2019. 3. 9. 09:00

[아두이노] 온도센서(TMP36) 제어



오늘은 온도센서(TMP36)를 제어 해보는 시간을 가지도록 합시다. 원래 예전에 공부할때 온도계산에 대한 부분은 다른분의 블로그를 보고 했는데 기억이 안나네요. 암튼 온도 계산식을 제가 만든것도 아니고 기존에 공식이 있는 거라서 제가 참조한 출처는 못찾아서 해당 온도계산식이 나와 있는 블로그를 하나 링크를 걸어 놓았네요.

1. 온도센서(TMP36)



대부분의 측정센서는 Vcc, Gnd로 전원을 공급하고 센서에서 측정한 값은 Vout 출력됩니다. Vout은 온도에 따른 전류를 측정하는 것이죠. 그래서 전류를 계산하는 공식이 필요합니다.

 float V = analogRead(A0)*5.0/1023;
 float C = (V-0.5)*100;  //섭씨 C = (F-32)*1.8;
 float F = C*9.0/5.0+32; //화씨 F = C*1.8+32;

이게 기본베이스 입니다. 제가 만든건 아니고 기본적으로 이 공식으로 섭씨와 화씨를 측정하더군요.
제 블로그에서는 약간 수정하여 map()함수를 응용해서 함수를 따로 만들었습니다. 정수를 실수형으로 변수를 바꾼것 밖에 없지만요,

float fmap(long x, long in_min, long in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) /(float) (in_max - in_min) + out_min;
}
<

V 계산을 map()함수 로직에다 삽입해서 바로 V값이 나오도록 했죠. 그냥 이렇다는 것이고 위의 공식으로 그냥 하셔도 됩니다.

2. 회로도 구성


  • 준비물 : TMP36 센서, 아두이노우노
  • 내용 : 온도센서에서 읽어들인 값을 아두이노 IDE 시리얼모니터로 출력시키기 위한 회로도 만들자.


선 연결 참 쉽죠. 원래는 LCD16x2로 온도값을 출력할려고 했는데 그러면 TMP36 제어를 어려워 할 것 같아서 최소한 코딩으로 원리만 전달하고자 간단히 표현했네요.

원리를 이해했으면 LCD16x2로 출력해 보세요. 아두이노는 하나의 부품을 배우면 지난 시간에 배운 부품들과 계속 결합해서 응용력을 키워야 실력이 늘어 납니다.

3. 코딩


  • 사용함수 : analogRead(), map(), Serial.begin(9600), Serial.print(), Serial.println()
  • 내용 : 온도계산공식을 이용하여 간단히 아두이노 IDE 시리얼모니터로 현재 온도를 출력하자.

복습

  • analogRead(A0) : A0핀으로 아날로그 값을 읽음
  • map(입력값,입력최소값,입력최대값,출력최소값,출력최대값) : 입력값이 입력범위에 기준에 맞게 출력범위의 해당값을 출력.
  • Serial.begin(9600) : 시리얼 통신 시작
  • Serial.print(출력값) : 출력값을 시리얼 모니터로 출력하는데 새로운 라인으로 안넘어가고 현재라인에 커서가 위치함.
  • Serial.println(출력값) : 출력값을 시리얼 모니터로 출력한 다음 새로운 라인으로 커서가 이동하는데 Enter(\n) 의미로 이해

설계

  1. 온도센서값을 읽어옴
  2. 섭씨와 화씨를 구함
  3. 시리얼모니터로 출력

온도센서는 analogRead(A0)로 읽어온다. 0~1023값을 읽어오는데 V를 구해야한다. 0~5V의 아두이노는 흐른다. 즉, 0~1023의 수치를 0~5V 값으로 출력을 맞춰야 합니다.

가령, map()함수라는 것은 0~100까지의 입력 범위가 있으면 출력을 0~10사이로 맞출때 사용합니다.

입력이 10이면 출력은 1
입력이 20이면 출력은 2

...

입력이 100이면 출력은 10

이렇게 되게죠. map()함수에 대해서 이해 하셨을거라 생각 합니다. 그런데 map()함수는 정수값으로 나오기 때문에 실수값으로 0~5V사이 값이 나오기 때문에 기존의 map()을 그대로 쓰면 안됩니다. 변수를 실수형태로 변경해 줘야 합니다. 실수형으로 변수를 선언해주면 간단히 해결 됩니다.

float V =fmap(analogRead(A0),0,1023,0,5); //map함수 원리를 이용한 다이렉트 Voltage계산

V값이 구해지면 섭씨(C)와 화씨(F)를 공식에 대입하면

float C = (V-0.5)*100;  //섭씨 C = (F-32)*1.8;
float F = C*9.0/5.0+32; //화씨 F = C*1.8+32;

이렇게 섭씨(C)와 화씨(F)가 구해지겠죠. Serial.print(), Serial.println()함수로 좀 이쁘게 그 값을 출력하면 됩니다.

코딩을 전체적으로 하면

void setup() {
  Serial.begin(9600); //시리얼통신 시작
}

void loop() {
 
  float V =fmap(analogRead(A0),0,1023,0,5); //map함수 원리를 이용한 다이렉트 Voltage계산
 
  //공식
  //float V = analogRead(A0)*5.0/1023;
  float C = (V-0.5)*100;  //섭씨 C = (F-32)*1.8;
  float F = C*9.0/5.0+32; //화씨 F = C*1.8+32;
  
  Serial.print("V : ");
  Serial.println(V);
  Serial.print("C : ");
  Serial.println(C);
  Serial.print("F : ");
  Serial.println(F);
  
  delay(1000);  
}

float fmap(long x, long in_min, long in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) /(float) (in_max - in_min) + out_min;
}

4. 결과


가상시뮬레이터에서지만 약간은 오차가 발생 하네요. 실제로 하더라도 보정이 좀 필요할 듯 합니다.

마무리


오늘은 온도센서(TMP36)을 이용해서 아두이노에서 간단히 계산하니깐 온도계가 되었네요. 온도를 측정할 수 있다는 것은 많은 것들을 할 수 있게 됩니다.

가령 방마다 온도를 측정한다 그리고 무선통신을 이용해서 그 값을 따로 컴퓨터에 저장시켜서 4계절 집안 온도의 데이터를 수집할 수 있게 되고 그걸 통해서 집안 난방을 관리할 수 있게 되겠죠. 또 다른 예로 식물재배에서도 온도를 측정하면 온도에 맞게 자동으로 제어를 할 수 있겠죠.

온도를 측정한 다는 것은 그 데이터를 수집해서 다른 용도로 사용할 수 도 있고 아니면 현재의 온도에 맞게 특정한 동작을 수행하도록 하는 목적으로 사용할 수 도 있겠죠.

암튼 온도를 측정할 수 있으면 여러분들은 뭘 하고 싶을지 상상의 나래를 펼쳐보세요


댓글()

[아두이노] 스위치버턴을 이용한 Keypad 제어

IOT/아두이노|2019. 3. 8. 09:00

[아두이노] 스위치버턴을 이용한 Keypad 제어



오늘은 KeyPad의 내부 동작을 살펴보도록 하죠. 가장 잘 알려진게 스위치버턴으로 표현한 예제입니다. 아래 스위치 버턴의 연결한 회로도를 잘 살펴보세요. 이 원리를 이해하시면 나중에 3x3x3 LED CUBE를 만드는데 활용되는 원리니깐 꼭 이해해 주세요.

1. 스위치버턴을 이용한 KeyPad



스위치 버턴은 기본 눌렀을때 전류가 흐르잖아요. 이걸 16개의 버턴을 연결한 것이죠. 그리고 각 스위치 버턴의 입력을 내부풀업저항을 이용해서 스위치 누른 위치의 상태값을 통해서 몇번째 스위치가 눌러졌는지 알게 됩니다.


우선 선 4x4 스위치 버턴을 배치한뒤에 선을 그림과 같은 형태로 연결하세요. 그리고 스위치 버턴에 내부풀업저항을 이용한다고 가정해 봅시다. 여기서, 가로 라인을 1층으로 생각하시고 세로는 각 호실이라고 했을 때 그러면 1층 2호실을 눌렀을 경우를 생각해보세요.

어떤 경우에 1층 2호실이 눌러졌는지 알 수 있을까요.


하나만 쪼개서 다시 살펴보세요. 1층 2호실은 6번핀이 Gnd이고 4번핀은 LOW 상태일때 입니다. 좀 햇갈리 수 있는데 내부풀업저항 모드일대는 초기 스위치 값은 HIGH(1)인 상태가 되고 스위치를 누르면 전류가 흘러 가기 때문에 해당 4번핀은 LOW(0) 상태가 됩니다. 결론은 1층 2호실 스위치를 누르면 6번핀이 Gnd일때 4번핀의 입력핀이 LOW일때입니다.

이걸 어떻게 찾아내느냐 하면

  for(int y=0;y<rows;y++){
    digitalWrite(rowPins[y], LOW);   
    for(int x=0;x<cols;x++){     
        if(!digitalRead(colPins[x]))val = keys[y][x];
    }        
    digitalWrite(rowPins[y], HIGH);      
  
  }

2중 for문을 이용해서 각 층별에 대한 해당 호별로 순차적으로 체크하면 됩니다. 초기로 모든 층은 HIGH 상태로 세팅한 후에
1층(LOW)부터서 1호실, 2호실, 3호실, 4호실 체크해서 스위치가 눌러졌는지 확인한 뒤에 1층은 HIGH로 다시 잠궈집니다.
2층(LOW)부터서 1호실, 2호실, 3호실, 4호실 체크해서 스위치가 눌러졌는지 확인한 뒤에 2층은 HIGH로 다시 잠궈집니다.
...
이렇게 초기값으로 각 층은 HIGH로 해놓으면 LOW로 된 층의 각 호실만 체크할 수 있게 됩니다.

설명이 어려울 수 있는데 쉽게 말해서 HIGH은 각 층이 닫혀진 상태이고 LOW되면 LOW된 층이 개방되게 됩니다.

만약에 각 층이 전부다 LOW라고 생각해보세요. 가령 2층 1호실을 눌렀습니다. 그러면 5번 핀은 digitalRead(5)핀의 값은 LOW가 됩니다. 하지만 누른 위치가 1층 1호실인지 2층 1호실인지 알 수 없게 됩니다. 만약 for문이 1층부터 체크한다면 1층 1호실에서 출력값이 LOW되기 때문에 1층 1호실로 인식하게 되고 만약에 for문이 4층부터 순차적으로 체크했다면 4층 1호실에서 LOW가 되기 때문에 4층 1호실로 인식하게 됩니다.

각 층을 체크할때는 체크하는 층만 LOW가 되고 나머지 층은 HIGH로 잠궈놔야 됩니다.

이해가 안되시면은 그냥 HIGH일때 잠겨지고 LOW일때 열린다고 생각하시면 되겠습니다. 이게 스위치에 전류의 흐름을 이해하면 쉽게 이해되는데 약간 설명이 어렵네요.

만약 1층(6번)이 HIGH일때 1층 1호실을 누르나 안누르나 1층 1호실 digitalRead(5)의 출력값은 무조건 HIGH입니다. 스위치를 누른다고 LOW되지 않습니다. 1층(6번) 핀이 LOW일때 누른 호실의 digitalRead(호실)의 출력값이 그제서아 LOW 됩니다.
풀업저항스위치버턴의 기본 동작을 이해하셔야 이 원리를 이해하실 수 있습니다.

2. 회로도 구성


  • 준비물 : 스위치버턴 16개, 아두이노우노
  • 내용 : 키를 누르면 그 값을 시리얼모니터로 출력할거기 때문에 간단히 회로도 구성함.


선 연결만 주의하시면 그럽게 어렵지 않습니다.

3. 코딩


  • 사용함수 : Serial.begin(), Serial.println(), keypad.getKey()
  • 내용 : Keypad에 누른 값을 아두이노 IDE의 시리얼 모니터로 간단히 출력하자.
  • 참고출처 : http://playground.arduino.cc/Code/Keypad

복습

  • Serial.begin(9600) : 시리얼 통신 시작
  • Serial.println(출력값) : 출력값을 시리얼 모니터로 출력
  • keypad.getKey() : 키값 읽기

그외 함수들은 참고출처에 가셔서 한번 읽어보시길 바래요.

설계

  1. 스위치 버턴으로 읽기
  2. 아두이노 IDE 시리얼모니터로 키 값 출력한다.

코딩을 전체적으로 하면

#include <Keypad.h>

const byte rows = 4; //four rows
const byte cols = 4; //four columns

byte rowPins[rows] = {9,8,7,6};
byte colPins[cols] = {5,4,3,2};

char keys[rows][cols] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols);

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

void loop(){
  
   char key = keypad.getKey();

   if (key != NO_KEY){      //키값이 눌렀는지 확인문
      Serial.println(key);
   }
}

4. 라이브러리 없이 코딩


스위치버턴 Keypad에서 설명한 방식으로 코딩을 하면 제블러그에 있던 코딩인데 그대로 인용합니다. 로직을 자세히 살펴보시면 이해가 되실꺼에요.

const byte rows = 4; //4 rows
const byte cols = 4; //4 columns

byte rowPins[rows] = {9,8,7,6};
byte colPins[cols] = {5,4,3,2};

 char keys[rows][cols] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

void setup(){
  Serial.begin(9600);
  
  for(int i=0;i<rows;i++){
    pinMode(rowPins[i], OUTPUT);
    digitalWrite(rowPins[i], HIGH);   // 각층 초기화로 잠금
  }
  for(int i=0;i<cols;i++){
    pinMode(colPins[i], INPUT_PULLUP); //각호실 내부풀업저항모드로 읽기
  }  
}

void loop(){
  
  char val = '\0';
 
  for(int y=0;y<rows;y++){
    digitalWrite(rowPins[y], LOW);   //해당층 개방
    for(int x=0;x<cols;x++){     
        if(!digitalRead(colPins[x]))val = keys[y][x];
    }        
    digitalWrite(rowPins[y], HIGH);  //해당층 잠금
  
  }

 if(val!='\0'){
    Serial.println(val);
   delay(300);
 }      
}

5. 결과


선 배치가 제일 귀찮았네요.


마무리


오늘은 Keypad를 스위치버턴으로 대신 했습니다. 그리고 라이브러리 없이 직접 코딩을 사용하여 동작을 제어 했네요.

스위치버턴에서 층과 호실에 관한 전류 흐름의 체크에 대해서 꼭 이해해 주세요. 왜냐면 나중에 응용편에서 거론한 3x3x3 LED CUBE에 원리가 같습니다.

원래 LED 제어를 한뒤에 응용편으로 미리 다루어야 했는데 응용편이고 코딩쪽이여서 다소 어려울 수 있어서 스위치버턴에서 먼저 그 원리를 사용하게 되었네요.
꼭 층과 호실의 관계를 이해해 주세요. 입력을 층과 호실로 받았지만 반대로 출력을 층과 호실로 보낼 수 있습니다. 그게 바로 3x3x3 LED CUBE의 기본 원리입니다.

이 원리를 깨우치면 진짜 화려하고 재밌는 작품을 만들 수 있으니깐 꼭 이해해 주세요.

궁금하시면 구글검색으로 3x3x3 LED CUBE를 쳐보세요 만들고 싶은 충동이 느껴질꺼에요.


댓글()

[아두이노] Keypad 제어

IOT/아두이노|2019. 3. 7. 09:00

[아두이노] Keypad 제어



오늘은 간단히 KeyPad를 사용하는 법을 살펴보도록 하겠습니다. 아두이노 선 연결하는 것은 별로 어렵지 않습니다. 그리고 Keypad 라이브러리만 잘 연결하면 딱히 사용하는 것도 어렵지 않고 쉽게 사용 가능합니다. 이번 포스팅은 아주 간단한 내용입니다.

1. KeyPad



총 8개의 핀으로 구성되어 있으며 내부 구조는 다음 포스팅에 설명하겠지만 우선은 간단히 가로4핀 x 세로4핀으로 선이 연결되었다고 생각하시면 됩니다. 4x4로 행과 열을 나타낸다고 생각하시면 됩니다. 우선 배열로 KeyPad[4][4]로 생각하시면 될 듯요.
핀 연결은 아두이노 디지털핀 아무곳이나 8핀을 연결하시면 돼요.

2. 회로도 구성


  • 준비물 : KeyPad, 아두이노우노
  • 내용 : 키를 누르면 그 값을 시리얼모니터로 출력할거기 때문에 간단히 회로도 구성함.


진짜로 간단하지요. 그냥 순서대로 0부터 7핀으로 아두이노에 그냥 Keypad를 붙이여 연결되어 버립니다. 하지만 그렇게 하지 않고 2번핀부터 연결했습니다. 왜냐면 시리얼통신으로 그 결과를 출력하기 때문에 시리얼통신을 담당하는 0, 1핀 때문에 Keypad가 0, 1핀에 연결된 위치값이 가상시뮬레이터에서 작동을 하지 않습니다. 그래서 어쩔 수 없이 회로도 그림처럼 연결해야겠죠.

3. 코딩


  • 사용함수 : Serial.begin(), Serial.println(), keypad.getKey()
  • 내용 : Keypad에 누른 값을 아두이노 IDE의 시리얼 모니터로 간단히 출력하자.
  • 참고출처 : http://playground.arduino.cc/Code/Keypad

복습

  • Serial.begin(9600) : 시리얼 통신 시작
  • Serial.println(출력값) : 출력값을 시리얼 모니터로 출력
  • keypad.getKey() : 키값 읽기

그외 함수들은 참고출처에 가셔서 한번 읽어보시길 바래요.

설계

  1. KeyPad 읽기
  2. 아두이노 IDE 시리얼모니터로 키 값 출력한다.

KeyPad를 사용하기 위해서는 가상시뮬레이터에서는 제공됩니다

#include <Keypad.h>

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols);

이렇게 KeyPad 클래스의 변수를 선언합니다.

Keypad(makeKeymap(keys), rowPins, colPins, rows, cols);
Keypad(키값배열, 열핀, 행핀, 열수, 행수);

Keypad의 출력할 키값 keys[4][4]로 전부 사용할 예정입니다. 그 값을 생성자 함수를 통해서 사전에 미리 만들어 놓게 됩니다. 즉, Keypad를 누른다고 알아서 그 값을 찍어주는게 아니라 출력할 값을 미리 지정해놓은 출력값과 사용할 행과 열의 핀과 행과 열의 숫자를 Keypad 객체변수를 선언할때 인자로 넘겨주게 됩니다.

이렇게 함으로써 특정 행과열 위치의 key를 누르면 keys[4][4] 해당된 위치의 값을 출력으로 나오게 됩니다.

여러 함수들이 있는데 여기에서는

keypad.getKey();

이 함수를 통해서 눌러진 키 값을 읽어오게 됩니다.

코딩을 전체적으로 하면

#include <Keypad.h>

const byte rows = 4; //four rows
const byte cols = 4; //four columns

byte rowPins[rows] = {9,8,7,6};
byte colPins[cols] = {5,4,3,2};

char keys[rows][cols] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols);

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

void loop(){
  
   char key = keypad.getKey();

   if (key != NO_KEY){      //키값이 눌렀는지 확인문
      Serial.println(key);
   }
}

4. 결과


그냥 선만 대충 순서대로 연결하면 끝나고 그 결과는 위 코딩을 복사해서 시뮬레이터를 돌리면 아두이노 IDE 시리얼모니터에 키값이 출력되는걸 확인하실 수 있을꺼에요.

마무리


오늘은 Keypad를 라이브러리를 사용해서 Keypad 클래스 변수를 선언하고 이 클래스 안에 키를 읽어오는 함수를 이용해서 키가 눌렀을때 키값을 가져오게 됩니다.

그냥 선 연결을 잘하고 Keypad 클래스 변수 선언과 읽을 함수명만 기억하시면 쉽게 Keypad를 사용하실 수 있을꺼에요.

이걸 이용하면 가령 LCD16x2에 붙이면 키로 누른걸 LCD16x2로 출력시킬 수 있겠죠. 그러면 계산기를 만들고 싶은 충동이 안느껴지나요. LCD16x2 (LiquidCrystal) 제어(아두이노) 이걸 다시 보시고 회로도를 수정하시고 계산기를 한번 연구를 해보세요.


댓글()

[아두이노] GAS Sensor

IOT/아두이노|2019. 3. 2. 09:00

[아두이노] GAS Sensor



이제 다시 쉬운 주제로 Gas Sensor를 다루는 것을 배워 보도록 하겠습니다. 가상시뮬레이터에서 GAS Sensor는 좀 회로도 연결해야하는 핀이 많아서 불편할 수 있을꺼에요. 만약 실제로 MQ-2 같은 센서를 사용하면 핀 연결이 쉬울 수 있는데 가상시뮬레이터에서 실험해야하기 때문에 약간은 불편함을 감수해야 할 듯요. 암튼 회로도 구성만 좀 어려울뿐 나머지는 코딩은 쉬우니깐 간단히 원리만 이해하시고 넘어가시면 됩니다.

1. GAS Sensor



어느방향이든 상관없이 한쪽 방향이 Vcc이면 반대편 방향이 Gnd 쪽으로 연결하고 한개만 GAS 측정값을 출력하는 핀으로 사용하면 됩니다. 대충 실험에서는 그림에서 볼때 하단을 Vcc로 3핀을 전부 열결했으며 상단 왼쪽핀 저항을 붙여서 Gnd로 연결하고 가운데는 Gnd 연결 오른쪽을 출력핀으로 사용하였습니다. 햇갈리시면 회로도 구성을 살펴보시면 됩니다.


실제 MQ-2 모델을 구입해서 많이 사용하는데 위 그림을 자세히 살펴보면 Gnd, DO, AO, Vcc 핀으로 구성되어 있습니다. 출력핀이 2개인데 디지털 출력이나 아날로그 출력이냐로 구분되는데 디지털이든 아날로그든 하나를 선택하고 나머지 Vcc, Gnd를 사용하여 총 3핀만 아두이노우노에 연결해서 실험하시면 됩니다.

하지만 가상시뮬레이터에서는 총 6핀으로 구성되고 핀 연결을 어쩔 수 없이 복잡하지만 회로도를 만들어야 겠죠.

2. 회로도 구성


  • 준비물 : GAS Sensor, 저항 1K옴 1개, 아두이노우노, 뻥판
  • 내용 : 간단히 GAS 측정하여 IDE 시리얼 모니터로 측정된 값을 출력하도록 설계


회로도를 보면 간단합니다. 회로도에서 위아래가 반대로 연결해도 됩니다. 참가로 가운데 핀은 고정이고요 사이드 핀은 저항을 세번째로 옮기고 A0핀을 입력받는 핀을 GAS Sensor 첫번째로 핀으로 연결해도 됩니다. 쉽게 가운데 핀은 고정이고 위아래는 한쪽 방향으로 Vcc, Gnd로 나뉘고 양쪽 사이드 핀은 서로 바꿀 수 있다는 것만 알아 두시면 됩니다.

그리고 참고하셔야 할 것은 GAS Sensor에 연결된 저항r값에 따라서 측정되는 값이 달라집니다. 한번 나중에 시뮬레이터를 돌릴때 저항값을 바꿔 보세요.

3. 코딩


  • 사용함수 : Serial.begin(), Serial.println(), analogRead()
  • 내용 : GAS Sensor에서 측정된 값을 아두이노 IDE의 시리얼 모니터로 간단히 출력하자.

복습

  • Serial.begin(9600) : 시리얼 통신 시작
  • Serial.println(출력값) : 출력값을 시리얼 모니터로 출력
  • analogRead(아날로그핀) : 아날로그 값을 읽음

설계

  1. GAS Sensor의 값을 읽자.
  2. 아두이노 IDE 시리얼모니터로 GAS 값 출력한다.

코딩을 전체적으로 하면

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

void loop() {  
  Serial.println(analogRead(A0));
  delay(1000);
}

4. LCD16x2로 GAS Sensor 값을 출력해보자.



지난 시간에 다뤘던 LCD16x2로 출력을 한다면 어떻게 해야 할까요. 지난 포스팅한 참고자료에 보시면 됩니다. 여기서 필요한 것은 지난 코딩 소스에서 출력값을 analogRead(A0)만 삽입하면 그냥 간단히 해결 됩니다. 원래는 변수를 선언하고 변수에 저장된 값을 출력해야 하는데 정석이지만 최대한 코딩로직을 출이고 원리만 전달하는 목적으로 표현한 점을 이해해 주세요.

회로도 보면


LCD16x2 회로도와 오늘 포스팅한 GAS Sensor 회로도를 하나로 결합한 형태입니다.

코딩도 합치면

#include <LiquidCrystal.h>

//LiquidCrystal(rs, enable, d4, d5, d6, d7) 
LiquidCrystal lcd(3, 4, 9, 10, 11, 12);

void setup()
{
  lcd.begin(16,2);
  lcd.setCursor(0, 0);
  lcd.print("Hello World");
  delay(1000);
  lcd.clear();
}
void loop() {
  lcd.setCursor(0, 1);
  lcd.print("Sensor : ");
  lcd.setCursor(10, 1);
  lcd.print(analogRead(A0));   
  delay(20);      
  lcd.print("            ");   
}

간단히 이부분에 analogRead(A0) 함수만 추가되었네요. " "로 공백문자를 출력시킴으로 지우는 효과를 부여 했습니다. 가상시뮬레이터에서 lcd.clear()함수를 사용할때 좀 문제가 있어서 출력한 라인을 공백문자로 덮여서 지우는 효과를 대신했습니다.

  lcd.print(analogRead(A0));
  delay(20); 
  lcd.print("            ");  

참 쉽죠.

5. 결과


실험 영상은 GAS Sensor 실험과정의 내용만 담았습니다. 그리고 LCD16x2로 출력하는 실험결과를 마지막에 시뮬레이터 결과만을 영상에 포함 시켰습니다.


마무리


GAS Sensor는 거실이나 부엌 천장에 보면 가정집마다 다 붙어 있는 걸 확인 확인 할 수 있을꺼에요. 가스 검침원이 오면 측정기로 GAS가 새는지 확인하는 경우도 경험 하셨을꺼에요. 일상에서 자주 사용되는 부품이지요. 단순히 수치로 시리얼모니터로 출력을 하고 복습차원으로 LCD16x2로 출력 실험을 했습니다.

여기서 측정된 수치값을 기준을 정하여 위험수치가 되면 경보기가 울리게 한번 설계해 보세요. 피에조부저를 연결 하시면 경보기를 쉽게 만들 수 있겠죠.

[아두이노] 피에조부저 제어 포스팅의 내용을 오늘 배운 회로도에 추가하여 경보기를 만들어 보세요.


댓글()

[아두이노] 타이머를 이용한 인터럽트(Interrupt) 제어

IOT/아두이노|2019. 3. 1. 09:00

[아두이노] 타이머를 이용한 인터럽트(Interrupt) 제어



어제는 인터럽트 핀을 이용한 하드웨어 인터럽트에 대해 살펴보았습니다. 오늘은 타이머 함수를 이용한 인터럽트를 배워보도록 하죠. 위 참고자료의 해당된 링크로 가시면 MsTimer2 and FlexiTimer2 라이브러리가 있는데 그중에서 MsTimer2 라이브러리를 선택하여 실험해 보도록 하겠습니다.

1. 타이머를 이용한 인터럽트


  • MsTimer2 함수
MsTimer2::set(unsigned long ms, void (*f)()) //타이머 세팅
MsTimer2::start() //타이머 시작
MsTimer2::stop() //타이머 종료

대충 보시면 함수가 그렇게 어렵지 않죠

예)

MsTimer2::set(500, flash) 

0.5초 간격으로 flash 함수를 호출한다고 세팅합니다. start() 함수는 타이머를 시작하라는 명령이고 stop() 함수가 선언되지 않는 이상 아두이노는 0.5초 간격으로 무조건 flash()함수를 호출하게 됩니다.

FlexiTimer2 라이브러리도 비슷하니깐 한번 링크된 곳에 가셔서 보시면 대충 이해가 되실꺼에요.

2. 회로도 구성


  • 준비물 : LED 2개, 저항 220옴 2개, 아두이노우노, 뻥판
  • 내용 : 타이머 인터럽트를 발생시킨 결과를 Blue LED(발광다이오드)에 불이 들어오게 하고 기본 동작은 Red LED(발광다이오드)에는 불이 들오게 한다.


회로도를 보면 간단합니다. 12번 핀에 MsTimer2를 이용해서 0.5초 시간이 되면 5V의 전류를 보내 Blue LED를 제어하고 13번 핀은 loop()함수에서 1초 단위로 불이 깜박이게 할 예정입니다. 여기서 loop()함수의 1초 단위로 깜박이는 기본동작과 상관없이 강제적으로 인터럽트를 발생시켜서 12번핀에 전류 공급을 공급하게 됩니다.
쉽게 말해서 1초 단위로 깜박이는 로직이 있는데 어떤 라인의 명령이 수해되고 있던 상관없이 타이머 시간이 되면 강제적으로 특정 동작을 수행하게 만든다고 생각하시면 됩니다.

3. 코딩


  • 사용함수 : pinMode(), digitalWrite(), MsTimer2::set(), MsTimer2::start();
  • 내용 : 간단히 가변저항 조절기로 조절하면 흐를 전류를 조절할 수 있고 그 조절된 전류 값을 3색 LED에 출력값으로 해서 색을 자유롭게 만들어 낸다.
  • 참고 : MsTimer2

복습

  • pinMode(사용핀, OUTPUT) : 사용핀은 출력모드
  • digitalWrite(사용핀, 상태) : 디지털출력핀에 상태가 HIGH(5V) or LOW(0V)를 선택한다.
  • MsTimer2::set(호출시간, 호출함수명) : 타이머 세팅(500은 0.5초라는 것만 기억)
  • MsTimer2::start() : 타이머 시작
  • MsTimer2::stop() : 타이머 종료

설계

  1. Red LED은 loop()함수에서 기본동작으로 1초단위로 깜박이게 해야지
  2. Blue LED은 0.5초 간격으로 깜박이게 해야지

코딩을 전체적으로 하면

이 예제가 너무 잘 코딩 되어있는 거라서 따로 코딩 예제를 안만들고 인용을 하겠습니다. 메인 동작은 Red 핀이 1초 단위로 loop()함수 내에서 반복합니다. 타이머 함수의 경우는 setup() 함수에서 한번만 선언하면 됩니다. 왜냐면 loop()에 선언한다고 생각을 해보세요. 매번 타이먼을 세팅하고 타이머를 시작하고 거기다가 중요한것은 loop()에 넣으면 타이머가 loop() 반복순환문에 일부가 되어서 타이머 자체가 loop()의 기본동작이 되어 버리게 됩니다. 결론은 setup()함수에서 타이머를 작동시키고 loop()은 자신의 기본동작만 수행해서 타이머는 별거의 존재로 동작하도록 배치해야겠죠.

하지만 MsTimer2::start() or MsTimer2::stop() 함수는 loop()안에 넣을 수 있습니다. 타이머를 무조건적으로 동작시킨다면 setup()함수에서 선언하는게 맞지만 loop()함수내에서 타이머가 원하는 조건을 충족하기 전까지는 타이머를 동작안시키고 충족되면 타이머를 동작하게 할 수 있는 로직으로 표현 할 때 loop()함수 내에서 선언하게 됩니다.

#include <MsTimer2.h> //가상시뮬레이터 경우는 삭제하고 해당 라이브러리 파일을 여기에다 복사

int red= 13;
int blue = 12;
  
void flash() {  
  static boolean output = HIGH;
  digitalWrite(blue, output);
  output = !output;
}

void setup() {
  MsTimer2::set(500, flash); // 500ms period
  MsTimer2::start();
}

void loop() {
  digitalWrite(red, HIGH);  
  delay(1000);              
  digitalWrite(red, LOW);   
  delay(1000);     
} 

4. 가상시뮬레이터에서 외부라이브러리 함수를 사용하기


가상시뮬레이터에서는 기본 아두이노우노에서 제공되는 라이브러리들을 그냥 쓸 수 있습니다. 하지만 특수한 라이브러리는 제공하지 않습니다. 그러면 가상시뮬레이터에서는 어떻게 실험해야 할지 성멸을 드리도록 하겠습니다.

우선 라이브러리 파일은 두개의 파일로 구성됩니다. 예로 MsTimer2 라이브러리에는 MsTimer2.h, MsTimer2.cpp 파일이 있습니다. 이 두개를 실제로 아두이노 IDE에서 라이브러리 추가해야 사용이 가능합니다.

실제 라이브러리 추가는 LCD16x2 I2C(LiquidCrystal_I2C) 제어(아두이노)의 포스팅에 라이브러리 추가하기를 보시면 됩니다.

하지만 가상시뮬레이터에서는 라이브러리 자체를 추가할 수 없습니다. 라이브러리 자체를 코딩해서 사용해야 합니다. 그럼 사용하기 위해서는 두 파일을 가상시뮬레이터 코딩창에 다 복사해야 합니다.


MsTimer2.h 파일을 전체 복사해서 가상시뮬레이터 코딩창에 붙여넣기 한다음 다음 MsTimer2.cpp 파일에서 

#include <MsTimer2.h>
 라인을 삭제하고 나머지 전체를 복사해서 가상시뮬레이터 코딩창에 이여서 붙여넣기 하시면 됩니다. 그리고 setup(), loop()함수 코딩을 하시면 해결 됩니다.


대충 이런식으로 복사하시면 특수 라이브러리 파일을 연결해서 사용할 수 있습니다.

5. 결과


회로도 설계부터 외부라이브러리 파일을 복사해서 붙이고 코딩을 한 결과를 돌렸을때 결과를 자세히 보시면 대충 어떤식으로 해야할지 감이 잡히실꺼에요. 타이머 라이브러리 함수를 사용해서 LED에 불이 들어오는게 가상시뮬레이터에서 좀 약하게 들어와서 동영상을 보면 잘 안보일 수 있지만 자세히 보시면 두개의 LED들이 따로 독립적으로 움직이는 것처럼 보이실 꺼에요.


마무리


인터럽트는 끼어들기 함수라고 생각 하라고 했죠. 하드웨어 인터럽트에서는 인터럽트 핀에 특정한 조건이 만족했을때 호출 되었습니다. 하지만 오늘 배운 내용에서는 특정한 조건이 만족했을때라기 보다는 타이머로 강제적으로 일정 시간이 되면 호출하는 소프트웨어 인터럽트 였습니다. 둘 차이를 한번 잘 생각해보시고 나중에 어디에 사용할지 상상을 펼쳐 보세요.

한번 둘을 합쳐진 로직을 설계해보면 어떨지 생각해 보세요. 가령 하드웨어 인터럽트의 핀에 상황이 발생했을때 MsTimer2::start(), MsTimer2::stop() 함수가 실행 되게 설계를 한번 해보세요.


댓글()

[아두이노] 인터럽트(Interrupt) 제어

IOT/아두이노|2019. 2. 28. 09:00

[아두이노] 인터럽트(Interrupt) 제어



오늘은 인터럽트에 대해서 배워보도록 하겠습니다. 아두이노에서 인터럽트를 이용해서 예의치 못한 상황이 발생할때 중간에 개입하여 해결하는데 사용됩니다. 계속 센서값을 읽는 것에 대한 주제로 부품의 사용 설명을 해야 하는데 중간에 좀 코딩에 관련된 주제로 포스팅을 하게 되었었네요. 우선 이부분을 알고 계속 부품 사용을 살펴보는게 더 나을것 같다는 생각에 먼저 소개 합니다.

1. 인터럽트


인터럽트는 현재 프로그램이 수행 도중에 어떤 도출 상황이 발생하면 수행중인 프로그램을 일시 중단 시키고 도출 상황에 대한 특정 해결 동작을 수행하도록 하는게 인터럽트라고 생각 하시면 됩니다.

예를 들면은 요리사가 요리을 만들어 A라는 사람에게 대접하고 A는 맛있게 요리를 먹습니다.

  • 인터럽트 발생 : 갑자기 B라는 사람이 왔다.
  • 특정 동작 수행 : 요리사는 숟가락 하나를 B에게 건내서 같이 먹도록 하게 한다.
  • A는 계속 요리를 맛있게 먹고 B도 숟가락을 받은 후 A랑 같이 많있게 요리를 먹는다.

어떤 의미인지 아시겠지요. loop()함수에서 기본 아두이노 동작을 수행합니다. 그 동작을 수행할 때에 예기치 못한 상황이 발생하면 그 문제를 해결하기 위한 처리 동작이 필요합니다. 그래서 인터럽트 함수를 사용하게 되는 것이죠.

아두이노우노는 2.3번핀 인터럽트 핀이고 아두이노 종류에 따라서 인터럽트에 사용되는 핀의 갯수와 핀 번호가 각각 다릅니다 아두이노 종류에 따라서 잘 확인하시고 인터럽트 함수를 사용하시면 됩니다. 자세히 알고 싶으면 위에 링크한 참고자료에 가시면 아두이노 종류별 인터럽트 핀에 대해 잘 나와 있으니깐 참고하시면 됩니다.

표현형식

attachInterrupt(digitalPinToInterrupt(pin), ISR, mode); //권장
attachInterrupt(interrupt, ISR, mode); 
attachInterrupt(pin, ISR, mode) ; 

세가지 방법이 있는데 첫번째 권장 함수를 사용하세요. 물론 아두이노우노에서 다른 형식도 동작은 하는데 모드마다 제약이 따르기 때문에 권장함수로 표현을 하고 이해하시는게 편하실꺼에요.

함수

attachInterrupt(digitalPinToInterrupt(interruptPin), exchange, FALLING);

attachInterrupt(인터럽트핀, 호출함수, 상태조건)로 해서 setup()함수에 선언하기만 하면 알아서 인터럽트가 발생하면 호출이 됩니다. 동작은 해당 인터럽트핀이 특정 상태조건이 되면 호출함수가 호출됩니다. 이것만 이해하시면 오늘은 내용은 끝입니다.

상태조건은

LOW    :   LOW 상태일 때
CHANGE :  입력 값이 변할때
RISING   :  LOW -> HIGH로 변할때
FALLING :  HIGH -> LOW로 변할때

HIGH      :  HIGH 상태일 때 (rduino Due, Zero, MKR1000 만 허용)

실험에서는 내부풀업저항을 사용하는 스위치 버턴이 초기 상태값이 HIGH이니깐 스위치를 누르면 LOW가 됩니다. 고로 스위치를 누른 순간 HIGH->LOW로 FALLING 상태가 되는 것이죠. 인터럽트핀 2 or 3번 핀에 FALLING상태가 되면 exchange()함수가 호출되는 게 기본 동작입니다. 여기서 exchange라는 단어가 함수명으로 고정이 아니라 마음대로 test1이라고 해도 됩니다.

아래 예제처럼 함수를 표현하시면 됩니다.

attachInterrupt(digitalPinToInterrupt(interruptPin), test1, FALLING);
void test1()
{
  동착;
}

어떤 느낌인지 아시겠지요.

2. 회로도 구성


  • 준비물 : LED 3개, 저항 220옴 3개, 스위치버턴 2개, 아두이노우노, 뻥판
  • 내용 : 인터럽트 발생을 스위치버턴을 활용하고 그 결과를 LED(발광다이오드)에 불이 들어오게 한다.

회로도를 보면 스위치버턴에 저항이 붙어있지 않다. 즉, 내부풀업저항을 이용한다는 것이고 11,12,13번 핀으로 출력값으로 LED에 불이 들어오게 하는 기본 회로도 입니다. 복습차원으로 LED 제어와 스위치버턴 제어를 합쳐서 인터럽트 실험을 하기 위한 회로도 입니다.

3. 코딩


  • 사용함수 : pinMode(), digitalWrite, attachInterrupt(),analogRead()
  • 내용 : 간단히 가변저항 조절기로 조절하면 흐를 전류를 조절할 수 있고 그 조절된 전류 값을 3색 LED에 출력값으로 해서 색을 자유롭게 만들어 낸다.
  • 참고 : [아두이노] LED 제어(아두이노)

복습

  • pinMode(사용핀, OUTPUT) : 사용핀은 출력모드
  • pinMode(사용핀, INPUT_PULLUP) : 사용핀은 내부풀업저항을 이용한 입력모드
  • attachInterrupt(digitalPinToInterrupt(2~3번핀), exchange1, FALLING) : 인터럽트 2~3번핀이 HIGH->LOW로 바뀌면 exchange1 함수를 호출한다.
  • digitalWrite(사용핀, 상태) : 디지털출력핀에 상태가 HIGH(5V) or LOW(0V)를 선택한다.

설계

  1. Red LED은 loop()함수에서 기본동작으로 1초단위로 깜박이게 해야지
  2. Green LED은 2번 인터럽트가 누르면 불이 들어오고 다시 누르면 꺼지게 해야지
  3. Blue LED은 3번 인터럽트가 누르면 불이 들어오고 다시 누르면 꺼지게 해야지
  4. 인터럽트 2,3번 핀을 변수로 선언하고 RGB핀을 각각 변수로 우선 만들어 놔야지
  5. 인터럽트 호출함수를 setup()내에 선언하고 두개를 사용하니깐 두개의 호출될 함수를 만들어 놔야 겠군.
  6. loop()함수 안에다가 Red LED가 기본동작으로 1초 깜박이는 로직을 표현해야지
  7. 인터럽트 호출 함수 exchange1,2는 누를때마다 불이 들어왔다 나갔다 해야하니깐 상태변수를 하나 선언해서 True(1)일때 불이 들어오고 False(0)일때 불이 꺼지게 if문으로 상태변환을 시킬 수 있게 조건문 만들어야 겠다. 그러면 초기 상태변수는 False로 해 놔야지 우선 꺼진상태가 되겠군
  8. 이제 이 글을 코딩으로 바꿔야겠다.

인터럽트 호출시 호출함수내에서 상태변환

  if(state1==false){
    digitalWrite(blue, HIGH);
    state1=true;
  }else{
    digitalWrite(blue, LOW);
    state1=false;
  }

인터럽트 호출된 함수 내에서 state1은 초기값이 false이면 true로 변환 state1이 true이면 false로 변환을 if문으로 바꾸게 됩니다.

더 쉽게 표현을 하자면

state=!state;
digitalWrite(blue,state);

이렇게하면 state의 현재상태를 계속 반전 시키게 됩니다.

코딩을 전체적으로 하면

int red = 13; 
int blue = 12;
int green = 11;
int interruptPin1 = 2;
int interruptPin2 = 3;
boolean state1 = false;
boolean state2 = false;

void setup() {
  pinMode(red, OUTPUT);
  pinMode(blue, OUTPUT);
  pinMode(interruptPin1, INPUT_PULLUP);
  pinMode(interruptPin2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin1), exchange1, FALLING);
  attachInterrupt(digitalPinToInterrupt(interruptPin2), exchange2, FALLING);
}

void loop() {
  
  digitalWrite(red, HIGH);
  delay(1000);
  digitalWrite(red, LOW);
  delay(1000);
  
}

void exchange1() {
  if(state1==false){
    digitalWrite(blue, HIGH);
    state1=true;
  }else{
    digitalWrite(blue, LOW);
    state1=false;
  }
}
void exchange2() {
  if(state2==false){
    digitalWrite(green, HIGH);
    state2=true;
  }else{
    digitalWrite(green, LOW);
    state2=false;
  }
}

4. 결과


회로도와 코딩을 가상시뮬레이터에서 디자인하고 코딩을 복사해서 붙이고 실행 시켜보세요. 스위치 버턴을 누르면 인터럽트가 발생하하여 해당 LED에 불이 들어오는걸 확인하실 수 있을거에요. 여기수 주 동작인 Red LED는 인터럽트 발생과 상관없이 규칙적으로 1초 단위로 깜박입니다. 인터럽트가 어떤 느낌인지 잘 생각해보세요.

마무리


인터럽트는 끼어들기 함수라고 생각하시면 돼요. 기본 메인 동작을 수행하는 도중에 어떤 돌출된 상황이 발생하면 그 상황을 메인 동작에 영향을 안주는 선에서 끼여들어서 그 상황을 해결하는 특정 동작을 수행하고 그 뒤에 메인 동작은 연속해서 하고자 하는 일을 하게 됩니다.

이런 원리를 이용하여 아두이노에서 어떤곳에서 활용하면 좋을지 상상의 나래를 펼쳐보세요.


댓글()

[아두이노] 기울기센서(Tilt Sensor) 제어

IOT/아두이노|2019. 2. 27. 09:00

[아두이노] 기울기센서(Tilt Sensor) 제어



오늘도 쉬운 난이도로 기울기센서를 이용한 실험을 다루겠습니다. 기울기센서는 기울기에 따라서 연결되고 끊어지는 두가지 상태를 나타내고 그 상태값을 기준으로 스위치 역활로 수행 할 수 있습니다. 실험에서는 스위치 역활로 기울기에 따라서 Red LED에 불이 들어오고 꺼지는 동작 제어를 통해 Title Sensor를 이해하는 시간을 갖도록 하겠습니다.

1. 기울기센서(Tilt Sensor)



Tilt Sensor는 안에 들어있는 전류를 흐를 수 있는 물질이 들어 있어서 기울기에 따라 센서안에 두 단자를 연결하거나 끊어지게 할 수 있습니다. 가상시뮬레이터에서 수평과 기울기 모양을 나타내는 조절이미지가 있는데 이걸로 Tilt Sensor를 조절하고 출력은 스위치버턴과 동일하게 출력값을 얻으시면 됩니다.

2. 기울기센서(Tilt Sensor) 동작 모습


수평기울어질때

그림에서 보는것과 같이 기울어질때 값을 기준으로 Red LED에 불이 들어오게 됩니다. 스위치 버턴 제어(아두이노)의 실험예제에서 스위치 버턴위치에 Tilt Sensor가 바뀌었을뿐 동일한 예제입니다. 어떤 동작을 하는 센서인지 대충 감이 잡히셧을꺼에요.

1. 회로도 구성


  • 준비물 : LED 1개, 저항 220옴 1개, 저항 10k옴 1개, Tilt Sensor 1개, 아두이노우노, 뻥판
  • 내용 : Tilt Sensor를 통해 기울어지는 상태값을 Red LED로 출력하게 하자.

(1) 기본(2) 내부풀업모드

두가지 형태로 표현해 볼 수 있습니다. 스위치버턴 예제를 다시 보시고 한번 풀다운모드 형태로 회로도를 한번 디자인 해보세요.

3. 코딩



복습

  • pinMode(사용핀, OUTPUT) : 사용핀은 출력모드
  • digitalWrite(사용핀, 상태) : 디지털출력핀에 상태가 HIGH(5V) or LOW(0V)를 선택한다.
  • digitalRead(사용핀) : 전기신호를 입력받는다.

설계

  1. Tilte Sensor에서 수평과 기울어졌을때의 값을 읽어들인다. => digitalRead(입력핀)
  2. 그 읽은 신호값을 기준으로 Red led에 불이 들어오게 한다. =>
    if(입력값==LOW) digitalWirt(출력핀,HIGH);
    else digitalWirt(출력핀,LOW);

코딩을 전체적으로 하면

(1) 기본 코딩은

void setup()
{
  pinMode(13, OUTPUT);      
  pinMode(7, INPUT);    
}

void loop()
{     
   if(digitalRead(7)==LOW) digitalWrite(13, HIGH);       
   else digitalWrite(13, LOW);     
}

(2) 내부풀업 코딩은

void setup()
{
  pinMode(13, OUTPUT);      
  pinMode(7, INPUT_PULLUP);    
}

void loop()
{     
   if(digitalRead(7)==LOW) digitalWrite(13, HIGH);       
   else digitalWrite(13, LOW);     
}

둘 차이는 PinMode() 함수에서 입력모드만 변경해주면 된다. 코딩 로직은 따로 수정할 게 없다.

5. 결과


간단히 디자인 하는 모습과 실행 결과를 만들어 놨으니 한번 보시고 따라 해보세요.

마무리


Tilte Sensor의 동작 제어를 해보았습니다. PIR Sensor는 두가지 상태값으로 스위치 역활을 이번 실험에서 실행해 보았습니다. 여기에 피에조부저를 부착해서 소리를 만들어 내면 경보기도 될 수 있겠죠. 아니면 어떤 물체의 특정 대상이 될때 대상이 된 물체가 기울어졌는지 수평인지를 체크하는 용도로도 사용할 수 있겠죠.

한번 기울기센서를 이용해서 어디에 적용해 볼까 상상의 나래를 펼쳐보세요.


댓글()

[아두이노] 인체감지 센서(PIR Sensor) 제어

IOT/아두이노|2019. 2. 26. 09:00

[아두이노] 인체감지 센서(PIR Sensor) 제어



오늘은 좀 난이도 낮춰서 쉬운 인체감지 센서(PIR Sensor) 작동원리를 배워 보도록 하겠습니다. 흔히 아파트 통로나 현관문에 저녁때 들어서면 잠시 조명에 불이 들어오는걸 많이 경험해 보셨을 꺼에요. 그리고 자세히 보면 둥근 모양의 물체가 부착되어 있는 것을 아마 한번쯤은 보셨을 거라 생각됩니다. PIR Sensor는 사람이나 동물의 움직임을 감지하는 센서입니다. 이 감지된 신호를 가지고 아두이노에서 실험를 해보겠습니다.

1. 인체감지 센서(PIR Sensor)




가상시뮬레이터에서 제공되는 PIR Sensor에는 총 3개의 핀이 있습니다. 어떤 센서이든 전원을 담당하는 Vcc, Gnd핀과 출력을 담당하는 Signal 핀으로 대부분 구성되어 있습니다. 여기서 PIR Sensor에서 인체의 움직임을 감지하면 전기신호로 Signal핀을 통해 출력된다는 것을 기억해 두세요. 인체의 움직임 감지되면 전기신호값은 하나의 전류값으로 가상시뮬레이터에서 거의 5V의 가까운 신호값이 발생하고 움직임이 없으면 0V로 고정됩니다. 즉, 인체가 감지되면 전류가 흐르고 인체가 감지되지 않으면 전류가 흐르지 않는 2가지 상태값이 Signal에서 발생하는 것이죠.

2. 인체감지 센서(PIR Sensor) 동작 모습


작동중움직임감지



그림에서 보는 것 처럼 움직임이 감지 되면은 Red Led에 불이 들어오게 됩니다. 계속 들어오는게 아니라 일정시간동안 들어왔다가 다시 꺼지게 됩니다.

1. 회로도 구성


  • 준비물 : LED 1개, 저항 220옴 1개, PIR Sensor 1개, 아두이노우노
  • 내용 : PIR Sensor를 통해 움직임이 감지되면 그 결과를 LED로 출력


최대한 원리 실험을 목적으로 단축표현한 회로도 입니다. 위에서 인체 감지 센서 동작과 같은 표현입니다. 구지 아두이노를 사용할 필요는 없지만 중요한것은 이 신호값을 받으면 아두이노에서 LED로 특정 동작을 수행하게 했다는 것에 실험의 목적입니다. 즉, A라는 신호를 받아서 B라는 동작을 지시한다란 개념을 머리속에 넣어 두세요.

3. 코딩


  • 사용함수 : pinMode(), digitalWirte(), analogRead()
  • 내용 : PIR Seneor를 통해 움직임을 감지되면 Red LED에 불이 잠시 들어오게 한다.
  • 참고 : [아두이노]LED 제어

복습

  • pinMode(사용핀, OUTPUT) : 사용핀은 출력모드
  • digitalWrite(사용핀, 상태) : 디지털출력핀에 상태가 HIGH(5V) or LOW(0V)를 선택한다.
  • analogRead(사용핀) : 아날로그신호를 입력받는다.
  • delay(시간) : 1000은 1초

설계

  1. PIR Sensor에 움직임이 감지되면 아두이노에서 그 신호 값을 읽는다.
  2. 그 신호값이 입력되면 Red LED에 불이 들어오게 한다.

코딩을 전체적으로 하면

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

void loop()
{
  digitalWrite(13, analogRead(A0)); 
  delay(10); 
}

디지털핀은 입력/출력모드를 선언해야하지만 아날로그 입력은 구지 선언 안해도 됩니다. 입력전용으로 쓰기 때문에 그렇습니다. 만약에 출력모드로 아날로그 입력핀을 사용할 경우는 pinMode()에서 선언하여 디지털핀처럼 사용이 가능합니다.

여기서,

digitalWrite(13, analogRead(A0)); 

이렇게 표현한 이유는 digitalWrite(13,1) or (digitalWrite(13,1023) 이든 상관없이 0이 아닌 정수로 참임으로 전부다 5V가 출력이 됩니다. digitalWirte(13,0)이여야만 0V가 출력되고 그 외는 다 5V만 출력된다는 점을 이용해 코딩량을 대폭 줄였습니다. 즉, digitalWirte()출력은 0이 아니면 모든 숫자는 참으로 5V가 출력되고 0만 0V가 출력됩니다.
원래 이렇게 코딩하면 안됩니다. 그냥 억지로 끼워 맞춘 코딩입니다. 원래는 입력된 값을 기준으로 해서 if 조건문을 세우고 그 조건문을 기준으로 Red LED를 일정시간 동안 딜레이를 줘서 아파트 복도나 현관문처럼 좀 긴 시간을 줘서 비슷한 효과를 부여할 수 있지만 오늘 포스팅은 PIR Sensor의 원리를 이해하는게 목적임으로 최대한 줄여서 그 의미만 전달하고자 표현한 코딩이라는 점을 참고하세요.

5. 결과


이 실험을 할 당시 가상시뮬레이터 사이트에 아두이노 컴파일러가 버그가 발생해서 동영상 촬영을 포기했는데 올리기전에 다행히 사이트 복구가 되어 정상적으로 간단히 실험했네요. 최대한 코딩을 줄여서 복잡한 부분을 줄여 이해도를 높였네요. 간단한 코딩과 결과만 쉽게 실험했으니 이번 포스팅 내용은 쉬울꺼에요.


마무리


PIR Sensor의 동작 제어를 해보았습니다. PIR Sensor는 아파트 복도나 현관문에 조명을 제어하는 목적으로 사용할 수 있지만 여기에 피에조부저를 부착하여 소리를 발생시키면 어떻게 될까요. 바로 경보기로도 만들 수 있겠죠. 그렇다면 여러분들은 PIR Sensor로 인체 감지를 할 수 있으면 어떤걸 해보고 싶으신가요.

한번 상상의 나래를 펄쳐보세요.


댓글()