반응형

만능 리모컨을 만들기로 해놓고 정말 많은 시간이 흘렀습니다.

2019.11.10 - [DIY/Arduino] - [DIY] 아두이노로 만능 (통합) 리모콘 만들기 1/3

2019.11.10 - [DIY/Arduino] - [DIY] 아두이노로 통합(만능) 리모콘 만들기 2/3

2020.06.17 - [DIY/Arduino] - [DIY] 아두이노로 통합(만능) 리모콘 만들기 3/3

결국 이번에도 완성되지 못한채 프로젝트가 끝나는것이 안타까워 마무리를 해보기로 합니다.

(리모컨 신호 따기, 각 기능별 동작 확인하는 과정 등은 위 링크의 글을 참고해 주세요)

사실 로직도 다 만들었고 테스트도 어느정도 완료 했으니 하드웨어만 만들면 되는데요, 항상 그렇지만 적당한 하드웨어를 구성하는 것이 쉬운일은 아닙니다. 3D 프린터가 있긴 하지만 완전한 완성품을 만드는게 쉽지 않더군요.

저처럼 DIY 를 하는 경우에는 개조에 적당한 다른 하드웨어를 가지고 튜닝을 하는게 더 쉽고 완성도도 높다 보니 적당한 녀석을 찾는 것이 일이었습니다.

 

그러던 와중!

완전 적당한 녀석을 발견했지 뭡니까.

바로 알리에서 구입한 18650 배터리를 이용한 파워뱅크 케이스 입니다. 병렬로 2개의 18650 배터리가 들어가게 되는데 그 중 하나를 넣지 않아도 정상 동작하기에 다른 한쪽에 아두이노 나노와 필요한 부품을 넣으면 되겠다 싶더군요.

 

그냥 이전에 만들었던거 집어 치우고 새로 만들겁니다.

 

바로 작업에 들어갑니다.

아래 사진처럼 납작한 택트 스위치를 덮개 위에 얹으면 될 것 같았습니다.

파워뱅크 케이스 위에 택트 스위치를 배열해본 모습

음.. 그럴싸 하군요.

하지만 작업의 편의성을 위해 만능 기판 위에 올리기로 합니다.

만능기판 위에 올린 모습

네.. 요게 더 작업이 수월할 것 같군요.

요렇게 가기로 합니다.

표면에 스위치가 그대로 노출되면 예쁘지 않으니 덮개를 만들어야 겠습니다.

잽싸게 3D 모델링을 한 다음 바로 3D 프린터로 프린트 해 봅니다.

3D 프린터로 덮개를 출력중임

 

만능 기판 위에 덮어본 모습

네.. 잘 맞는 군요. (몇차례 수정이 있었습니다)

 

 

그럼 이제 만능 기판위에 스위치와 인디케이터용 LED 를 납땜해 봅니다.

양면으로 사용가능한 만능 기판이므로 위쪽에 버튼과 LED 를 모두 붙여 준 뒤 아래 쪽에서 실제 배선을 하기로 합니다.

지금 보이는 모든 점퍼 선은 그라운드에 연결할 예정이에요. 

부품 장착 및 윗면 배선을 모두 마친 모습
인디케이터용으로 작은 SMD LED 를 장착했다.

그리고 인디케이터 용으로 LED 를 장착 했는데요, 위쪽에 보이는 4개는 각각 제가 선택한 제품을 표시하도록 하고 아래쪽 한개는 동작 할 때마다 신호가 나가는지 확인하기 위한 용도로 사용하기로 하였습니다.

워낙 SMD 부품이 작아 삐뚤빼뚤 하긴 하지만 뭐 큰 문제는 없습니다.

 

버튼의 위치와 버튼별 기능 목록

실제 버튼별 기능 목록과 각 기능별로 전달될 신호는 위와 같습니다.

 

아래쪽에서 본 버튼별 위치

 

뒷면에서 보면 위와 같은 모양으로 버튼들이 배치되어 있습니다. 헷갈리지 않도록 저렇게 만들어 놓고 배선을 시작하기로 합니다.

 

반응형

 

이제 실제 아두이노와 연결을 해야 겠죠?

파워뱅크 덮개 중앙을 뚫어 배선이 내부로 들어갈 수 있도록 해 주었고요,

제작한 기판이 올라갈 위치에 맞게 구멍을 뚫어 주었다.

안쪽에 아두이노 나노를 넣어서 문제 없이 장착이 될 지 확인해 봅니다.

기판과 덮개를 덮어 본 모습

네.. 아주 딱 맞아 떨어집니다.

이제 살인적인 납땜이 남았군요.

 

 

배선 시작 

..

분노의 땜질 시작!!!!

..

 

 

 

네.

끝냈습니다.

살인적인 케이블 들..
아래쪽에는 나노의 USB 포트를 노출시켰다.

 

.

.

 

 

네.. 아두이노의 거의 모든 포트와 연결이 되어있기 때문에 정말 힘들었습니다.

신호를 전달 하는 케이블은 사용하지 않는 랜선 내부의 케이블을 이용해서 연결해 주었습니다.

그리고 아두이노 나노의 USB 연결 포트를 노출 시켜서 나중에라도 프로그래밍을 손쉽게 변경할 수 있도록 했고요, 구석쪽으로 IR LED 를 꺼내주었습니다.

 

배터리를 넣고 동작이 되는지 테스트 해보았습니다.

일단 불은 들어오는 것 확인

 

네 일단 동작은 되는 것 같군요.

TV 에 와서 동작하는지도 확인해 보았고,, 약간의 코드 수정이 필요해 보이는 것을 제외하면 버튼들은 모두 동작하는 것을 확인했습니다.

 

 

그리고 단순히 리모컨으로써의 기능 뿐만 아니라 간단한 플래쉬와 레이저 포인터 기능도 동작하도록 할 생각입니다.

리모컨은 항상 거실의 중심에 있으니 플래쉬가 필요할 때도 빠르게 찾아 사용할 수 있을 것입니다.

 

최종 마무리가 된 모습은 아래와 같습니다.

좌측 하단에 1W LED 를 넣어주고 아래쪽으로 푸쉬락 기능이 있는 스위치를 하나 달아 주었고요,

좌측 위쪽으로는 레이저 다이오드를 넣은뒤 택트 스위치를 연결해주었습니다.

배터리에서 아두이노로 전원을 공급하기 위해 5V 승압 보드를 연결해서 전원 공급을 해주게 됩니다. 물론 스압 보드와 배터리 사이에 푸쉬락 기능이 있는 스위치를 하나 달아 주었습니다.

 

이렇게 하면 일반 USB 충전기를 이용해서 리모컨을 충전할 수 있으며 만약 배터리가 방전되어 교체가 필요하더라도 18650 배터리만 교체하면 되므로 사용성이나 유지보수도 용이한 구성이 되겠습니다.

 

 

 

UI 디자인 올리기

이제 슬슬 마무리를 해야하는데요,

어떤 버튼이 어떤 동작을 하는지 알아야 하지 않겠습니까? 그래서 전면부 UI 디자인을 해보았습니다.

이래뵈도 디자이너 아뉘겠습니까?

리모컨에 사용될 UI 작업 중

일러스트로 버튼의 위치에 맞게 UI 를 그려보았습니다.

좌측에 뒤집혀 있는 그림은 뭘까요?

조금뒤에 알려드리겠습니다.

일단 프린트해서 크기와 위치, 배치 등이 적당한지 확인해 봅니다.

버튼의 위치와 크기 확인 중

또 실제 버튼의 모양, 위치, 기능이 모두 정상 동작하는지 최종 확인을 해봅니다.

 

그럼 이제 OHP 필름을 꺼내 프린트를 다시 합니다!

네. 바로 OHP 필름입니다.

OHP 필름에 뒤집힌 그래픽이 출력되었다.

 

자 OHP 필름에도 출력이 잘 되었습니다.

그럼 출력된 면에 

영일 락카 등장

띠용~ 락카가 등장했네요.

 

네 출력된 면에 백색 락카를 칠해주면 

OHP 필름 출력면에 락카 칠 하는 중

 

칠해주면~

 

그리고 뒤집으면 ~ 

 

쨔쟌~

 

요렇게 반짝 반짝 하는 예쁜 그래픽을 볼 수 있답니다. 손으로 눌르거나 해도 지워질 염려가 없지요.

요렇게 만들어진 전면부를 제작한 리모컨 위에 똭 붙여 주면

완 to the 성!

완성된 아두이노 만능리모컨

오예~

완성 되었습니다.

 

사실 처음 계획이랑 전원 스위치 와 제품 변경 기능이 뒤바뀌긴 했지만 뭐 괜찮습니다.

 

부가기능들을 볼까요? 

플래쉬 동작!
레이저 포인터 동작!

오~예! 오~ 예!

모두 잘 동작 합니다. ㅋㅋ

 

며칠에 한번씩 배터리 방전을 막기 위해 핸드폰 충전기로 배터리를 충전해 주면 끝

충전 중인 만능 리모컨

 

네.. 이렇게 마무리가 되었습니다.

 

어휴 생각보다 힘들었지만 이제 거실 테이블 위에 널브러져 있는 리모컨들을 치울 수 있다고 생각하니 후련 합니다.

 

끝으로 최종으로 적용한 아두이노 소스코드를 아래에 올립니다.

상당히 긴 코드가 되어 버렸네요. 

더보기
#include <boarddefs.h>
#include <IRremote.h>
#include <IRremoteInt.h>
#include <ir_Lego_PF_BitStreamEncoder.h>
#include <EEPROM.h>
/*
 * IRremote: IRsendDemo - demonstrates sending IR codes with IRsend
 * An IR LED must be connected to Arduino PWM pin 3.
 * Version 0.1 July, 2009
 * Copyright 2009 Ken Shirriff
 * http://arcfn.com
 */
/*
 * tv (NEC)
 * power :      0x2DF10EF, 32
 * input :      0x2DFD02F, 32
 * chnnel up :  0x2DF00FF, 32
 * chnnel dn :  0x2DF807F, 32
 * volumn up :  0x2DF40BF, 32
 * volumn dn :  0x2DFC03F, 32
 * OK :         0x2DF22DD, 32
 * sw up :      0x2DF02FD, 32
 * sw dn :      0x2DF827D, 32
 * sw left :    0x2DFE01F, 32
 * sw right :   0x2DF609F, 32
 * 
 * android tv (NEC)
 * power :      0x807F02FD, 32
 * sw up :      0x807F6897, 32
 * sw dn :      0x807F58A7, 32
 * sw left :    0x807F8A75, 32
 * sw right :   0x807F0AF5, 32
 * sw OK :      0x807FC837, 32
 * back :       0x807F9867, 32
 * volumn up :  0x807F18E7, 32
 * volumn dn :  0x807F08F7, 32
 * 
 * 
 * lonpoo speaker
 * power :      0x40BF807F, 32
 * volumn up :  0x40BF50AF, 32
 * volumn dn :  0x40BFD02F, 32
 * bt :         0x40BFA05F, 32
 * bt esc :     0x40BF906F, 32
 * opt :        0x40BF20DF, 32
 * 
 * 
 * LED
 * tv :     9
 * tv box : 10
 * btv :    11
 * spk :    12
 * 
 * input
 * s01 
 * 
 * 
 * 
 * 
 */

#include <IRremote.h>

const int s01 = 0;
const int s02 = 18;   // select sw 
const int s03 = 1;
const int s04 = 2;
const int s05 = 19;
const int s06 = 4;
const int s07 = 5;
const int s08 = 6;
const int s09 = 7;
const int s10 = 8;
const int s11 = 14;
const int s12 = 15;
const int s13 = 16;
const int s14 = 17;
//LED
const int L00 = 9;    // TV
const int L01 = 10;   // TV BOX
const int L02 = 11;   // BTV
const int L03 = 12;   // LONPOO SPEAKER

//
// define sw value
int sw_01;
int sw_02;
int sw_03;
int sw_04;
int sw_05;
int sw_06;
int sw_07;
int sw_08;
int sw_09;
int sw_10;
int sw_11;
int sw_12;
int sw_13;
int sw_14;

bool canWorking_01 = true;
bool canWorking_02 = true;
bool canWorking_03 = true;
bool canWorking_04 = true;
bool canWorking_05 = true;
bool canWorking_06 = true;
bool canWorking_07 = true;
bool canWorking_08 = true;
bool canWorking_09 = true;
bool canWorking_10 = true;
bool canWorking_11 = true;
bool canWorking_12 = true;
bool canWorking_13 = true;
bool canWorking_14 = true;
// 
int productIdx = 0; //0 : TV / 1: TV BOX / 2: BTV / 3: SPK

int addr = 0;
bool chkon = false;
IRsend irsend;

void setup()
{
  pinMode(s01, INPUT_PULLUP);
  pinMode(s02, INPUT_PULLUP);
  pinMode(s03, INPUT_PULLUP);
  pinMode(s04, INPUT_PULLUP);
  pinMode(s05, INPUT_PULLUP);
  pinMode(s06, INPUT_PULLUP);
  pinMode(s07, INPUT_PULLUP);
  pinMode(s08, INPUT_PULLUP);
  pinMode(s09, INPUT_PULLUP);
  pinMode(s10, INPUT_PULLUP);
  pinMode(s11, INPUT_PULLUP);
  pinMode(s12, INPUT_PULLUP);
  pinMode(s13, INPUT_PULLUP);
  pinMode(s14, INPUT_PULLUP);
  
  pinMode(L00, OUTPUT);
  pinMode(L01, OUTPUT);
  pinMode(L02, OUTPUT);
  pinMode(L03, OUTPUT);
  
  pinMode(13, OUTPUT);
  //EEPROM.write(0, productIdx);  
  productIdx = EEPROM.read(0);

  sw_01 = HIGH;
  sw_02 = HIGH;
  sw_03 = HIGH;
  sw_04 = HIGH;
  sw_05 = HIGH;
  sw_06 = HIGH;
  sw_07 = HIGH;
  sw_08 = HIGH;
  sw_09 = HIGH;
  sw_10 = HIGH;
  sw_11 = HIGH;
  sw_12 = HIGH;
  sw_13 = HIGH;
  sw_14 = HIGH;
  
  canWorking_01 = true;
  canWorking_02 = true;
  canWorking_03 = true;
  canWorking_04 = true;
  canWorking_05 = true;
  canWorking_06 = true;
  canWorking_07 = true;
  canWorking_08 = true;
  canWorking_09 = true;
  canWorking_10 = true;
  canWorking_11 = true;
  canWorking_12 = true;
  canWorking_13 = true;
  canWorking_14 = true;

  Serial.begin(9600);
  Serial.println(productIdx);

  
}

void loop() {

  digitalWrite(13, LOW);
  
  sw_01 = digitalRead(s01);
  sw_02 = digitalRead(s02);
  sw_03 = digitalRead(s03);
  sw_04 = digitalRead(s04);
  sw_05 = digitalRead(s05);
  sw_06 = digitalRead(s06);
  sw_07 = digitalRead(s07);
  sw_08 = digitalRead(s08);
  sw_09 = digitalRead(s09);
  sw_10 = digitalRead(s10);
  sw_11 = digitalRead(s11);
  sw_12 = digitalRead(s12);
  sw_13 = digitalRead(s13);
  sw_14 = digitalRead(s14);
  

  // change product
  if (sw_02 == HIGH) {
    chkon = true;
    ledON(productIdx);
  }else{
    if (chkon)
    {
      productIdx++;
      if (productIdx > 3) productIdx = 0;
      EEPROM.write(0, productIdx);
      chkon = false;
    }
    ledON(productIdx);
  }
  
  if (sw_01 == HIGH) {    
    canWorking_01 = true;
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_01)
    {
      press_S01(productIdx);
      canWorking_01 = false;
    }
  }
  if (sw_03 == HIGH) {   
    canWorking_03 = true; 
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_03)
    {
      press_S03(productIdx);
      canWorking_03 = false;
    }
  }
  if (sw_04 == HIGH) {
    canWorking_04 = true;     
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_04)
    {
      press_S04(productIdx);
      canWorking_04 = false;
    }
  }
  if (sw_05 == HIGH) {  
    canWorking_05 = true;    
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_05)
    {
      press_S05(productIdx);
      canWorking_05 = false;
    }
  }
  if (sw_06 == HIGH) {    
    canWorking_06 = true;  
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_06)
    {
      press_S06(productIdx);
      canWorking_06 = false;
    }
  }
  if (sw_07 == HIGH) {  
    canWorking_07 = true;    
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_07)
    {
      press_S07(productIdx);
      canWorking_07 = false;
    }
  }
  if (sw_08 == HIGH) {    
    canWorking_08 = true; 
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_08)
    {
      press_S08(productIdx);
      canWorking_08 = false;
    }
  }
  if (sw_09 == HIGH) {  
    canWorking_09 = true;   
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_09)
    {
      press_S09(productIdx);
      canWorking_09 = false;
    }
  }
  if (sw_10 == HIGH) {    
    canWorking_10 = true;   
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_10)
    {
      press_S10(productIdx);
      canWorking_10 = false;
    }
  }
  if (sw_11 == HIGH) {    
    canWorking_11 = true;   
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_11)
    {
      press_S11(productIdx);
      canWorking_11 = false;
    }
  }
  if (sw_12 == HIGH) {    
    canWorking_12 = true;  
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_12)
    {
      press_S12(productIdx);
      canWorking_12 = false;
    }
  }
  if (sw_13 == HIGH) {    
    canWorking_13 = true;  
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_13)
    {
      press_S13(productIdx);
      canWorking_13 = false;
    }
  }
  if (sw_14 == HIGH) {    
    canWorking_14 = true;  
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_14)
    {
      press_S14(productIdx);
      canWorking_14 = false;
    }
  }

	delay(10); //5 second delay between each signal burst
}

void press_S14 (int idx) // vol -
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DFC03F,32);   // tv vol -
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F06F9,32);  // tv box vol -
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE42BD,32);  // BTV box vol -
    break;
  case 3:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BFD02F,32);  //  spk vol -
    break;
  default:
    // statements
    break;
  }
}

void press_S13 (int idx) // chnl -
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF807F,32);   // tv chnl -
    break;
  case 1:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F18E7,32);  // tv box vol +
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE827D,32);   // Btv chnl -
    break;
  case 3:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF50AF,32);  //  spk vol +
    break;
  default:
    // statements
    break;
  }
}

void press_S12 (int idx) //vol +
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF40BF,32);   // tv vol +
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F18E7,32);  // tv box vol +
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FEC23D,32);   // Btv vol +
    break;
  case 3:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF50AF,32);  //  spk vol +
    break;
  default:
    // statements
    break;
  }
}

void press_S11 (int idx) // chnl +
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF00FF,32);   // tv chnl +
    break;
  case 1:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F58A7,32);  // tv box
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE02FD,32);   // Btv chnl +
    break;
  case 3:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF906F,32);  //  spk bt esc
    break;
  default:
    // statements
    break;
  }
}

void press_S10 (int idx) // down
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF827D,32);   // tv down
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F58A7,32);  // tv box down 
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE52AD,32);   // Btv down
    break;
  case 3:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF20DF,32);  //  
    break;
  default:
    // statements
    break;
  }
}

void press_S09 (int idx) // right
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF609F,32);   // tv right
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F0AF5,32);  // tv box right 
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE32CD,32);   // Btv right
    break;
  case 3:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF20DF,32);  // tv box up 
    break;
  default:
    // statements
    break;
  }
}
void press_S08 (int idx) // ok
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF22DD,32);   // tv ok
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807FC837,32);  // tv box ok 
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE629D,32);   // Btv ok
    break;
  case 3:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF20DF,32);  // tv box up 
    break;
  default:
    // statements
    break;
  }
}

void press_S07 (int idx) // arrow left
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DFE01F,32);   // tv left
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F8A75,32);  // tv box left 
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FED22D,32);   // Btv left
    break;
  case 3:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF20DF,32);  // tv box up 
    break;
  default:
    // statements
    break;
  }
}
void press_S06 (int idx)
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF02FD,32);   // tv back
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F9867,32);  // tv box back 
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE728D,32);   // Btv back
    break;
  case 3:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF20DF,32);  // spk OPT 
    break;
  default:
    // statements
    break;
  }
}

void press_S05 (int idx) // arrow up
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF02FD,32);   // tv up
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F6897,32);  // tv box up 
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE926D,32);   // Btv up
    break;
  case 3:
    //irsend.sendNEC(0x40BFA05F,32);  
    break;
  default:
    // statements
    break;
  }
}

void press_S04 (int idx)
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DFD02F,32);   //외부입력
    break;
  case 1:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF10EF,32);   
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE40BF,32);   // Btv home
    break;
  case 3:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BFA05F,32);   // blue tooth
    break;
  default:
    // statements
    break;
  }
}

void press_S03 (int idx) // power
{

  irsend.sendNEC(0x2DFD02F,32); // tv 외부입력
  // S03 번은 편의를 위해 항상 외부입력으로 동작한다
  /*
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF10EF,32);   // tv power
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F02FD,32);   // tv box power
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE807F,32);   // Btv power1 
    break;
  case 3:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF807F,32);   
    break;
  default:
    // statements
    break;
  }*/
}
void press_S01 (int idx) //  S01
{
  Serial.println(productIdx);
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF10EF,32);   
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F02FD,32);   
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE807F,32);   
    break;
  case 3:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF807F,32);   
    break;
  default:
    // statements
    break;
  }
}
void ledON (int idx)
{
  digitalWrite(L00, LOW);
  digitalWrite(L01, LOW);
  digitalWrite(L02, LOW);
  digitalWrite(L03, LOW);
  switch (idx) {
  case 0:
    digitalWrite(L00, HIGH);
    break;
  case 1:
    digitalWrite(L01, HIGH);
    break;
  case 2:
    digitalWrite(L02, HIGH);
    break;
  case 3:
    digitalWrite(L03, HIGH);
    break;
  default:
    // statements
    break;
  }
}

 

 

이상으로 아두이노 만능 리모컨 만들기를 마칩니다. 

 

반응형
반응형

자 어느새 목표했던 RC headtracking FPV 만들기의 종착역을 다가가고 있습니다.

앞서 소개해 드렸던 2축 서보마운트 제작 및 서보컨트롤과 mpu6050 을 이용하여 2축 서보모터를 제어하는 내용은 잘 보셨나요? 아직 못보신분은 아래 글을 참고 부탁드립니다

2022.01.31 - [DIY/Arduino] - [아두이노]2축 서보모터 초간단 제어하기, 간단한 프레임 제작까지

 

[아두이노]2축 서보모터 초간단 제어하기, 간단한 프레임 제작까지

뭔가 목표가 생기면 과정이 분명해 진다. RC 카에 헤드트래킹을 이용한 FPV 를 구현하려는 목표가 생기고나니 과정을 잘 정리하는게 필요하겠다 싶어 포스트를 남깁니다. 우선 첫번째 스텝으로 2

diy-dev-design.tistory.com

2022.01.31 - [DIY/Arduino] - [아두이노]mpu6050 을 이용하여 2축 서보모터 제어하기

 

[아두이노]mpu6050 을 이용하여 2축 서보모터 제어하기

아두이노와 같은 마이크로 칩을 이용하여 무엇인가를 해보다 보면 정말 놀라운 경험들을 많이 하게 됩니다. 그 중 하나가 바로 mpu6050 같은 센서가 될 수 있겠습니다. 요 조그만 칩을 이용하여 기

diy-dev-design.tistory.com

 

반응형

 

오늘은 앞에서 소개해드린 mp6050 을 이용하여 측정한 회전 정보를 이용하여 서보모터를 제어하는 것을 응용하여 무선으로 서보모터를 제어하는 과정을 소개해 드릴 예정인데요, 여기에서는 아주아주 놀랍고 멋진 무선 송수신 칩인 nrf24L01 칩을 이용하여 구현을 할 계획입니다.

nrf24L01 칩의 경우 저렴하면서도 놀라운 성능을 보여주는 아주아주 애정하는 부품입니다.

실제로 해당 칩을 이용하여 RC 카 조종장치와 수신 장치를 만들기도 한적이 있습니다. 제어되는 범위, 거리도 아주 훌륭하고 신호 수준도 좋으며 중요한 것은 1개의 송신부에서 여러개의 수신장치를 제어할 수 있는 멋진 칩 입니다. 게다가 송신과 수신을 하나의 칩에서 지원하고 있으므로 양방향 통신을 구현하는데에도 어려움이 없습니다.

일단 오늘 도전할 목표는 앞서 말씀 드렸던것 처럼 mp6050 자이로 센서에서 측정된 회전 값을 NRF24L01 칩을 이용하여 보내고 또다른 NRF24L01 칩에서 이 신호를 받아 2축 서보모터를 제어하는 과제가 되겠습니다.

이전 포스팅에서 소개해 드린 것에서 무선 송수신이 추가만 되었을 뿐 크게 달라지는 건 없습니다.

무선 송수신을 위한 라이브러리는 웹상에도 많이 있고요, 아래 첨부해둔 라이브러리를 받으셔서 바로 내문서-아두이노 폴더에 넣으셔도 됩니다.

RF24-master.zip
0.39MB
SPI.zip
0.01MB

 

자 이젠 무선 송수신 칩이 들어오면서 배선이 조금 복잡해 집니다.

차근차근 따라 오시면 됩니다.

 

NRF24L01 칩에 대하여

여기서 NRF24L01 칩 외에 추가 준비물이 필요한데요, 준비물은 바로 10uf 전해콘덴서 입니다. 이 전해 콘덴서를 nrf24l01 의 전원 (3.3v) 단에 연결을 해 주셔야 문제 없이 잘 동작합니다. 용량은 가능하면 10uf 로 준비해 주세요, 제가 처음 테스트 할 때 전해 콘덴서 용량이야 뭔들 중요하랴... 싶어 용량이 안 맞는 부품을 적당히 연결해서 해보았지만 잘 안되서 정말 골머리를 썩었는데요, 10uf 콘덴서를 달면 정말 거짓말처럼 동작이 잘 됩니다. 

물론 소스코드나 배선에 실수는 없어야 겠지요.

 

한가지더, nrf24l01 칩은 몇가지 타입이 있는데요, 크기가 작은 SMD 타입과 일반 핀이 달려있는 Dip 타입, 그리고 원거리 송수신이 가능한 PA LN 타입이 있습니다. 핀 구성은 모두 동일하고 배열만 약간 차이가 있으며 코드는 모두 동일하게 지원 합니다. 저는 소형화된 부품이 좋아 보통은 SMD 타입 부품을 사용하고요, 배선이 일렬로 되어 있어 브래드 보드 등에 테스트 하기도 편리한 점이 장점이라 할 수 있습니다. 해외 어떤 사용자 분은 성능도 SMD 타입이 좋다고 하시는데.. 실제로 그런지는 잘 모르겠습니다. (- -)>;;

DIP 타입을 이용하실 경우 확장 보드가 있으면 조금더 편리하게 사용하실 수 있지만 부피가 조금 커진다고 보시면 되고, PA-LNA 보드는 DIP 타입의 확장보드와 호환이 가능하며 아래쪽에 SMD 타입과 같은 보드 실장을 위한 연결 단자가 있으므로 보드에 바로 연결이 가능합니다. PA-LNA 보드의 경우 NRF24L01 칩이 기본 탑제되어 있고 위에서 말씀드린것과 같이 코드 및 배선은 동일하게 하시면 되며 원거리까지 송수신이 가능하므로 무선으로 멀리까지 신호를 보내고 받아야 하는 경우 사용하시면 되겠습니다.

 

배선 시작

자 이제 배선을 해보겠습니다.

 

먼저 제가 사용할 SMD 타입은 만능기판에 바로 핀을 꼽기가 어려운데요. 그래서 생각해낸 것이 아래와 같은부품을 이용하는 것 입니다.

14pin smd 부품을 만능기판에 사용하기 위한 pcb 보드

가운데 있는 보드를 가운데를 잘라주고 반쪽만 사용하게 되는데요, 이 pcb 를 이용하면 NRF24L01 SMD 칩의 핀 간격과 정확히 일치 하게 되며 실제로 사용하지 않는 가장 오른쪽 IRQ 는 무시하고 왼쪽부터 맞추어 납을 흘려넣어 납땜을 해주면 됩니다.

하단의 구멍에는 1.24mm pin 이 정확히 맞으므로 일렬로 배열된 핀을 만능기판에 꼽아 사용할 수 있게 됩니다.

위에서 말씀드린 10uF 전해 콘덴서까지 연결하게 되면 아래와 같은 모양이 됩니다.

만능기판용으로 제작된 SMD 타입의 NRF24L01 칩

 

NRF24L01 칩은 아래와 같은 핀 구성을 하고 있고요, 각각 아두이노와 그림처럼 연결하시면 됩니다.

NRF24L01 SMD PINOUT

 

아두이노 핀 연결방법

 

CE, CSN 은 각각 7번 8번에 연결하여야 하는데 이 두핀은 위치가 바뀌어도 상관 없습니다. 코드상에서 정의해준 핀의 번호와 일치하기만 하면 되고요, 나머지 핀은 지정된 핀에 연결하시면 됩니다.

저처럼 SMD 타입을 사용할 경우 전원은 아두이노의 반드시 3.3V 에 연결해 주셔야 하고 dip 타입을 사용하시는 경우 확장보드를 이용하시면 5V 를 바로 연결하셔도 되며 10uf 전해 콘덴서는 필요 없습니다.

 

특별 초대손님이 있다고?

자~ 오늘의 특별 초대손님이 계신데요...

두구두구

두구

두구

 

 

아두이노 RC 카 수신기

짜잔~

네~ 바로 아두이노로 제작한 RC 카 수신기 입니다. 물론 송신기도 아두이노로 만들었었습니다.

해당 보드는 왼쪽 상단에 3핀 소켓을 통해 ESC 로부터 전원을 입력 받고 신호 선을 통해 모터스피드(속도)를 제어하게 되고요, 우측 아래 두개의 3핀 소켓을 통해 조향서보와 2-speed gear 의 조작용 서보에 연결하게 제작되었습니다. 오늘은 해당 두개의 서보모터 핀을 이용하여 x,y 축을 제어해 보도록 할 계획입니다. 보드 중간에 가로로 보이는 구멍이 NRF24L01 보드를 장착하기 위한 소켓입니다.

무선 모듈을 연결하면 아래와 같은 모양이 됩니다.

NRF24L01 보드가 장착된 아두이노 RC 카 수신기

한동안 즐겁게 가지고 놀았었는데요, 일반적인 RC 카 수신기에 비하면 덩치가 조금 크기는 하지만 4개의 2pin 포트를 통해 헤드라이트나 후미등, 좌우측 깜박이 등을 이 보드 하나로 제어할 수 있어 멀펑 보드가 별도로 필요하지 않으므로 나름 쓸만하다고 할 수 있겠습니다. 

언제 시간이 나면 핀 사진의 커넥터 대신 일반 적인 RC 카에 많이 사용하는 후타바 짹이나 JR 커넥터 등을 이용하여 부피를 줄여볼 계획입니다. 나중에 작업하게 되면 소개해 드리겠습니다.,

 

배선은 송신부, 수신부 두개의 아두이노에 동일하게 해주시면 되고 송신부와 수신부는 코드에서 정의를 해주게 됩니다.

 

배선이 완료된 송신부 입니다.

아두이노나노, NRF24L01, MPU6050 이 연결되어 있다.

네 사진상으로는 조금 복잡하지만 기존 설명드린데로 잘 연결 하셨다면 어려움 없이 잘 하실 수 있으리라 믿습니다.

 

수신기 쪽을 볼까요?

아두이노 나노, NRF24L01, 2개의 서보모터가 연결되어 있다

네 수신부에는 예상하시다 시피 두개의 서보모터와 NRF24L01 보드가 연결되어 있습니다.

 

 

코딩을 해보자

코드를 볼까요?

 

먼저 송신부 입니다. 기존 작성해 놓은 mp6050 코드에 무선 송수신 코드만 추가할 예정입니다.

// init for nrf24L01 
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(8,7);  //  CE, CSN 
const byte address[6] = "00001"; //송신기와 수신기 동일한 주소 사용
int msg[8]; 


//pinout smd version
//3.3v  --- 3.3v
//GND  ---  GND
//CE   ---  8
//CSN  ---  7
//SCK  ---  13
//MOSI ---  11
//MISO ---  12
//IRQ  ---  none

// init for mpu6050
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps_V6_12.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif

MPU6050 mpu;
#define OUTPUT_READABLE_YAWPITCHROLL
#define INTERRUPT_PIN 2  // use pin 2 on Arduino Uno & most boards
#define LED_PIN 13 // (Arduino is 13, Teensy is 11, Teensy++ is 6)
bool blinkState = false;

// MPU control/status vars
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 gy;         // [x, y, z]            gyro sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector

volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
  mpuInterrupt = true;
}

void setup() {

  // join I2C bus (I2Cdev library doesn't do this automatically)
  #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    Wire.begin();
    Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
  #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
    Fastwire::setup(400, true);
  #endif
  
  Serial.begin(115200);
  while (!Serial); // wait for Leonardo enumeration, others continue immediately

 
  // initialize device
  Serial.println(F("Initializing I2C devices..."));
  mpu.initialize();
  pinMode(INTERRUPT_PIN, INPUT);

  // verify connection
  Serial.println(F("Testing device connections..."));
  Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

  // wait for ready
  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

  // load and configure the DMP
  Serial.println(F("Initializing DMP..."));
  devStatus = mpu.dmpInitialize();

  // supply your own gyro offsets here, scaled for min sensitivity
  mpu.setXGyroOffset(51);
  mpu.setYGyroOffset(8);
  mpu.setZGyroOffset(21);
  mpu.setXAccelOffset(1150);
  mpu.setYAccelOffset(-50);
  mpu.setZAccelOffset(1060);
  // make sure it worked (returns 0 if so)
  if (devStatus == 0) {
    // Calibration Time: generate offsets and calibrate our MPU6050
    mpu.CalibrateAccel(6);
    mpu.CalibrateGyro(6);
    Serial.println();
    mpu.PrintActiveOffsets();
    // turn on the DMP, now that it's ready
    Serial.println(F("Enabling DMP..."));
    mpu.setDMPEnabled(true);

    // enable Arduino interrupt detection
    Serial.print(F("Enabling interrupt detection (Arduino external interrupt "));
    Serial.print(digitalPinToInterrupt(INTERRUPT_PIN));
    Serial.println(F(")..."));
    attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
    mpuIntStatus = mpu.getIntStatus();

    // set our DMP Ready flag so the main loop() function knows it's okay to use it
    Serial.println(F("DMP ready! Waiting for first interrupt..."));
    dmpReady = true;

    // get expected DMP packet size for later comparison
    packetSize = mpu.dmpGetFIFOPacketSize();
  } else {
    // ERROR!
    // 1 = initial memory load failed
    // 2 = DMP configuration updates failed
    // (if it's going to break, usually the code will be 1)
    Serial.print(F("DMP Initialization failed (code "));
    Serial.print(devStatus);
    Serial.println(F(")"));
  }

  // Radio setup
  //setupRadio();
  
  msg[0] = 0; 
  msg[1] = 0; 
  msg[2] = 0; 
  msg[3] = 0;
  msg[4] = 0; 
  msg[5] = 0; 
  msg[6] = 0; 
  msg[7] = 0;
  
  radio.begin();
  radio.openWritingPipe(address); //이전에 설정한 5글자 문자열인 데이터를 보낼 수신의 주소를 설정
  radio.setPALevel(RF24_PA_MIN); //전원공급에 관한 파워레벨을 설정합니다. 모듈 사이가 가까우면 최소로 설정합니다.
  radio.stopListening();  //모듈을 송신기로 설정
}

void loop() {

  // if programming failed, don't try to do anything
  if (!dmpReady) return;
  // read a packet from FIFO
  if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) { // Get the Latest packet 

  #ifdef OUTPUT_READABLE_YAWPITCHROLL
    // display Euler angles in degrees
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
    
    float angle_x = (ypr[0] * 180 / M_PI) * -1 + 90 ; //* -1 + 90;
    float angle_y = (ypr[1] * 180 / M_PI) + 90 ; //* -1 + 30;    
   
    if (angle_y < 60) angle_y = 60;
    if (angle_y > 120) angle_y = 120;
    
    if (angle_x < 30) angle_x = 30;
    if (angle_x > 150) angle_x = 150;

    msg[0] = int(angle_x);
    msg[1] = int(angle_y);
    
    radio.write(&msg, sizeof(msg)); //해당 메시지를 수신기로 보냄
    
    Serial.print("ypr\tx:");
    Serial.print(int(angle_x));
    Serial.print("\ty:");
    Serial.print(int(angle_y));
    Serial.print("\t");
    
    

  #endif
  }
  Serial.println(".");
  delay(10);
  
}

아무래도 셋업해주는 부분이 약간 생소할 수 있겠습니다만 저도 인터넷에서 긁어 모은 코드를 조합하였을 뿐 상세하게는 모른답니다. 중요한 것은 노드의 이름을 정해주었는데요, 수신부에서도 동일한 이름을 지정해 주어야 한다는 점과, 다른 분들과의 혼선을 막기 위하여는 해당 이름을 유니크한 이름으로 정해주는 것이 좋다는 것 정도 입니다.

제가 사용한 코드는 (존경하는)새다리 님의 코드를 참고 하였고 NRF24L01 로 검색하시면 해외 여러 개발자 분들이 올려주신 멋진 코드들이 많이 있으므로 참고하시면 좋을 것 같습니다.

 

이번에는 수신부 입니다.

 #include <SPI.h> 
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(8,7); //  CE, CSN 
const byte address[6] = "00001"; //송신기와 수신기 동일한 주소 사용
int msg[8]; 

//pinout smd version
//NRF24L01    ARDUINO
//3.3v    ---   3.3v
//GND     ---   GND
//CE      ---   8
//CSN     ---   7
//SCK     ---   13
//MOSI    ---   11
//MISO    ---   12
//IRQ     ---   none

#include <Servo.h>

Servo myservo_LR;   
Servo myservo_UD; 

int pin_servo_LR = 9;
int pin_servo_UD = 10;

void setup() {
  radio.begin();
  radio.openReadingPipe(1, address);
  radio.setPALevel(RF24_PA_MIN); //
  // RF24_PA_MIN / RF24_PA_LOW / RF24_PA_HIGH / RF24_PA_MAX
  radio.startListening(); //수신기로 설정
  
  //setting 2 servo
  myservo_LR.attach(pin_servo_LR);
  myservo_LR.write (90);
  myservo_UD.attach(pin_servo_UD);
  myservo_UD.write (90);
}

void loop() {

  if (radio.available()) {
    radio.read(&msg, sizeof(msg));
    for(int i = 0; i < 8 ; i ++)
    {
      Serial.print(msg[i]);
      Serial.print("\t");
    }

    int angle_x = int(msg[0]);
    int angle_y = int(msg[1]);
    
    if (angle_x < 150 && angle_x > 30)
    {
      myservo_LR.write (angle_x);          
    }
    if (angle_y < 120 && angle_y > 60)
    {
      myservo_UD.write(angle_y);
    }
  }
}

수신부는 서보모터 제어코드와 무선 수신 코드가 있는데요, 송신부에 비하면 간단하게 구성되어 있습니다.

무선 패킷이 들어오면 동작하도록 되어 있으므로 수신에 실패하면 아무런 동작도 하지 않도록 되어 있으며 만약을 대비하여 잘못된 값이 수신되더라도 서보모터에 전달되지 않도록 최대, 최소 범위를 제한하여 서보에 입력되도록 하였습니다.

 

과연 잘 동작 할런지???

동작하는 모습을 볼까요?

 

 

ㅋㅋ 네 잘 되는군요. 이제 뭐 거의 다 온 것 같습니다.

다음 편에는 카메라를 연결하고 차량안에 장착할 준비를 한번 해봐야 겠습니다.

머릿속에 아주 좋은 그림이 있거든요~ ㅎㅎ

기대해 주세요 ~ ^^

2022.01.31 - [DIY/Arduino] - [아두이노]2축 서보모터 초간단 제어하기, 간단한 프레임 제작까지

 

[아두이노]2축 서보모터 초간단 제어하기, 간단한 프레임 제작까지

뭔가 목표가 생기면 과정이 분명해 진다. RC 카에 헤드트래킹을 이용한 FPV 를 구현하려는 목표가 생기고나니 과정을 잘 정리하는게 필요하겠다 싶어 포스트를 남깁니다. 우선 첫번째 스텝으로 2

diy-dev-design.tistory.com

2022.01.31 - [DIY/Arduino] - [아두이노]mpu6050 을 이용하여 2축 서보모터 제어하기

 

[아두이노]mpu6050 을 이용하여 2축 서보모터 제어하기

아두이노와 같은 마이크로 칩을 이용하여 무엇인가를 해보다 보면 정말 놀라운 경험들을 많이 하게 됩니다. 그 중 하나가 바로 mpu6050 같은 센서가 될 수 있겠습니다. 요 조그만 칩을 이용하여 기

diy-dev-design.tistory.com

 

2020.10.29 - [DIY] - [DIY] 오래된 다목적 캠핑용 랜턴 ▶ LED 램프, 충전식으로 교체하기

 

[DIY] 오래된 다목적 캠핑용 랜턴 ▶ LED 램프, 충전식으로 교체하기

처갓집 구석에서 재미있는 물건을 하나 발견했습니다. 마침 캠핑 전용 랜턴이 하나 있었으면 하는 차에 이게 왠 떡인가요 ㅋ 언제부터 처갓집에 있었는지는 모르겠지만 사용하지 않는 것이 분

diy-dev-design.tistory.com

 

2019.10.13 - [DIY] - [DIY] 아이방 수면등 만들기 (feat. 아이그림)

 

[DIY] 아이방 수면등 만들기 (feat. 아이그림)

아이들은 창의적이다. 시키지도 않았던 생각치도 않았던 아무 많은 그림을 시도 때도 없이 시간과 장소 불문, 심지어는 종이를 불문하고 마구마구 그려댄다. 그래서 A4 용지를 내주었더니 그럴

diy-dev-design.tistory.com

 

반응형
반응형

카테고리를 c# 으로 해야 할지 Arduino 로 해야할지 조금 고민이 되는 포스트  입니다.

음....

arduino 로 하는게 좋겠네요. 따지고 보면 C# 으로 만든 어플이 중요한게 아니라 아두에노에서 시리얼 통신으로 메시지를 받는게 중요한거니까요.

일단 한번 해보겠습니다. 자 시작하죠.

 

우선 이번 포스트에서 다룰 주제는 아래와 같습니다.

  • C# 에서 시리얼 통신 사용하기
  • 시리얼 통신으로 문자열 송신하기
  • 아두이노에서 문자열 수신하기
  • LCD 에 수신된 문자열 표시하기

간단하죠?

내용도 간단하니까 걱정 붙들어 매시고 따라오시면 됩니다.

 

 

C#에서 시리얼 통신 사용하기

먼저 비주얼 스튜디오를 열고 c# .net 플랫폼을 선택하신 후 window form 프로젝트를 하나 만들어 줍니다.

window forms 앱 을 선택합니다.
적당히 이름을 만들어 주세요. 닷넷 버전은 그냥 건드리지 않았습니다.

 

이렇게 하시고 만들기를 누르시면 윈도우 창이 나올텐데요.

간단하게 label, combobox, button 을 하나씩 만들어주고 사이즈를 적당히 조절합니다.

간단한 메뉴 구성 완료

 

이제 폼이 실행되면 combobox 에 연결 가능한 시리얼 포트를 띄워 주겠습니다. 그리고 버튼을 누르면 해당 포트로 메시지를 전달하도록 할 예정입니다.

만들어진 UI 상단에 Form1 이라고 써있는 타이틀바를 더블클릭해 줍니다.

그럼 아래와 같이 Load 이벤트와 함께 함수가 하나 만들어 집니다.

이제 이번 포스트에 필요한 지시문과 콤보박스에 시리얼을 연결하는 방법을 소개해 드리겠습니다.

먼저 맨윗줄 지시문이 있는 그룹에 아래와 같이 추가해 줍니다.

using System.IO.Ports;

 

그런 다음 이번 예제에서 사용할 시리얼 포트를 만들어 주고요 

public partial class Form1 : Form
{
  private SerialPort mySerial; //<-- 요 부분이죠

  public Form1()
  {
  	InitializeComponent();
  }

 

아까 만들어 진 Form1_Load 함수에 아래와 같이 시리얼포트를 생성해주고 콤보박스에는 데이터를 담습니다.

private void Form1_Load(object sender, EventArgs e)
{
  mySerial = new SerialPort();
  comboBox1.DataSource = SerialPort.GetPortNames();

}

 

이제는 콤보박스의 선택정보가 변경될 때 시리얼 포트가 열려있지 않다면 포트이름과 설정을 지정해 주고 열도록 하겠습니다. 

UI 메뉴에서 콤보 박스를 더블클릭하면 함수가 자동으로 생성되는데요. 만들어진 함수에 아래와 같이 코드를 작성하시면 됩니다.

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
  if (mySerial.IsOpen) // 특정 포트가 이미 열려 있다면 설정이 되지 않기 때문에 우선 닫는다.
  {
  	mySerial.Close();
  }
  if (!mySerial.IsOpen)  //시리얼포트가 닫혀있을 때만
  {
    mySerial.PortName = comboBox1.Text;  // 선택된 combobox 의 이름으로 포트명을 지정하자
    mySerial.BaudRate = 9600;  //아두이노에서 사용할 전송률를 지정하자
    mySerial.DataBits = 8;
    mySerial.StopBits = StopBits.One;
    mySerial.Parity = Parity.None;

    mySerial.Open();  //시리얼포트 열기
  }
  else
  {
    MessageBox.Show("해당포트가 이미 열려 있습니다.");
  }
}

 

 

 

 

시리얼 통신으로 문자열 송신하기

뭐 거의 다 되었습니다. 이제 버튼을 누르면 아두이노로 메시지를 전달하기만 하면 됩니다.

문자열을 전송하기 위한 byte 배열로 바꾸어주는 함수를 하나 만들어 보겠습니다.

private byte[] StringToByte(string _str)
{
  byte[] tmpBytes = Encoding.UTF8.GetBytes(_str);
  return tmpBytes;
}

요렇게 만들어 주었다면 이제 UI 생성하는 페이지에서 만들어 두었던 버튼을 두번 클릭하여 버튼을 눌렀을때 실행할 함수를 하나 만들어 줍시다.

그런다음 아래와 같이 메시지를 보내보겠습니다.

private void button1_Click(object sender, EventArgs e)
{
  byte[] datas = StringToByte("Hello Arduino?\n"); // 줄바꿈 기호인 \n 은 끝에 꼭 들어가야 합니다.
  mySerial.Write(datas, 0, datas.Length);
}

 

자 C# 어플리케이션은 완성이 되었습니다. 

 

벌써요?

네 벌써 완료 되었습니다. 정상적으로 동작한다면 아두이노 LCD 에 Hello Arduino? 가 뜰 것입니다.

제 포스트를 보시면서 천천히 하셔도 아마 15분이면 충분할만한 분량이지요. 

 

전체 코드도 올립니다.

더보기

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;

namespace csharp2arduino_serial
{
    public partial class Form1 : Form
    {
        private SerialPort mySerial;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            mySerial = new SerialPort();
            comboBox1.DataSource = SerialPort.GetPortNames();

        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
        	if (mySerial.IsOpen)
            {
                mySerial.Close();
            }
            if (!mySerial.IsOpen)  //시리얼포트가 닫혀있을 때만
            {
                mySerial.PortName = comboBox1.Text;  // 선택된 combobox 의 이름으로 포트명을 지정하자
                mySerial.BaudRate = 9600;  //아두이노에서 사용할 전송률를 지정하자
                mySerial.DataBits = 8;
                mySerial.StopBits = StopBits.One;
                mySerial.Parity = Parity.None;

                mySerial.Open();  //시리얼포트 열기
            }
            else
            {
                MessageBox.Show("해당포트가 이미 열려 있습니다.");
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // 텍스트 박스 추가한 버전
            string myMsg = textBox1.Text;
            if (myMsg != "")
            {
                myMsg += "\n";
            }
            else
            {
                myMsg = "Hello Arduino?\n";
            }
            byte[] datas = StringToByte(myMsg);
            mySerial.Write(datas, 0, datas.Length);
        }

        private byte[] StringToByte(string _str)
        {
            byte[] tmpBytes = Encoding.UTF8.GetBytes(_str);
            return tmpBytes;
        }
    }
}

 

 

 

아두이노에서 시리얼 메시지 받기

아두이노에서 시리얼 메시지를 받는것은 사실 매우 쉽습니다. 아두이노 기본 예제 중에 Serial 을 사용하는 것을 많이 보셨죠? 아두이노는 Serial 을 사용하기 위하여 별도로 설정할것이 거의 없습니다.

아두이노를 열어 주시고 아래와 같이 코드를 작성합니다.

String myStr = "";

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {

  if(Serial.available()){//시리얼에 데이터가 있다면

    char data = (char)Serial.read();//한 문자씩 읽어 문자형 변수에 담고
    
    if((int)data != 10){ //개행문자 '\n' -> newline 이 아니라면 스트링에 계속 저장
      myStr += data;
      
    }else{
      // \n 을 만나면 여기로 와서 문자열을 출력하면 된다.

      // LCD 에 문자를 여기서 출력하면 됨

      //그리고 문자열은 다시 초기화를 해주겠습니다.
      myStr = "";
    }
  }
}

 

완전 간단하죠? 시리얼 신호가 들어오면 \n 이라는 줄바꿈 문자열이 오기 전까지 계속해서 한글자씩 문자열 변수에 추가를 해주었다가 \n 을 만나면 필요한 결과를 수행하고 원래 문자열 변수를 초기화 하는 것이죠.

 

 

물론 아직 LCD 를 붙이지 않았으므로 시리얼을 받아도 뭔가 표시는 안되겠지요?

그래서 이제 LCD 를 붙여 보겠습니다.

 

 

 

 

아두이노 OLED LCD 에 시리얼 통신 결과 출력하기

 

지난 LCD 예제를 참고하시면 되며 이번에는 바로 시리얼을 출력해 보겠습니다.

2020/05/26 - [DIY/Arduino] - 0.96 inch OLED 디스플레이 구동하기

 

0.96 inch OLED 디스플레이 구동하기

가끔 아두이노로 무엇인가를 만들어 보려고 하다보면 디스플레이가 있으면 하는 생각이 들때가 있습니다. 아두이노는 작은 컴퓨터이기는 하지만 모름지기 컴퓨터라 한다면 입력장치 - 중앙처��

diy-dev-design.tistory.com

 

상단에 필요한 요소들을 추가해주고 기본적인 셋팅을 해준뒤 제 포스트의 주제인 DIY DEV DESIGN 을 출력하고 시작하겠습니다. ㅋ

(작성자의) 편의상 그냥 전체 코드블럭을 올립니다. 라이브러리나 LCD 구동 관련된사항은 위 포스트에서 확인하시면 됩니다.

#include <Wire.h> 
#include <Adafruit_SSD1306.h>
#include <splash.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SPITFT.h>
#include <Adafruit_SPITFT_Macros.h>
#include <gfxfont.h>
#include <Fonts/FreeMono9pt7b.h>


#define OLED_ADDR   0x3C

// reset pin not used on 4-pin OLED module
Adafruit_SSD1306 display(-1);  // -1 = no reset pin

//#if (SSD1306_LCDHEIGHT != 64)
//#error("Height incorrect, please fix Adafruit_SSD1306.h!");
//#endif

String myStr = "";
int linegap = 2;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);

  display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
  display.setFont();
  display.clearDisplay();
  display.display();
  display.setTextSize(1);

  draw_text(2, 25, "DIY DEV DESIGN", false); // 폰트 표시를 쉽게 하기 위해 제가 만든 함수 입니다.
  draw_text(3, 4, "C# -> Arduino Serial", false);

  display.display();
  delay(3000);
  
}

void loop() {

  if(Serial.available()){//시리얼에 데이터가 있다면

    char data = (char)Serial.read();//한 문자씩 읽어 문자형 변수에 담고
    
    if((int)data != 10){ //개행문자 '\n' -> newline 이 아니라면 스트링에 계속 저장
      myStr += data;
      
    }else{
      // \n 을 만나면 여기로 와서 문자열을 출력하면 된다.

      // LCD 에 문자를 여기서 출력하면 됨
      display.clearDisplay();
      draw_text(2, 10, myStr, false);
      display.display();
      //그리고 문자열은 다시 초기화를 해주겠습니다.
      myStr = "";
    }
  }
}

void draw_text (int line, int xPos, String txt, bool isSelected)
{
   int cHeight = (line-1) * 8 + ((line-1) * linegap);
   if(isSelected)
   {
      display.fillRect(xPos - 2, cHeight, 127, 8, WHITE);
      display.setTextColor(BLACK);
   }else{
      display.setTextColor(WHITE);
   }
   display.setCursor(xPos, cHeight);
   display.print(txt);
}

 

음 뭐 간단하다면 간단한 내용이죠?

 

자 아두이노를 컴파일하고 올려야 하는데요. 아까 c#에서 만들어놓은 APP 을 실행해 놓으면 소스가 업로드 되지 않습니다. App 을 열어두었다면 끄고 업로드를 해야 합니다.

 

 

 

 

결과 확인하기

이제 결과를 확인해 보겠습니다.

먼저 아두이노를 업로드 해보면 아래와 같은 화면이 나오게 됩니다.

ㅎㅎ 저렇게 작은 글씨가 보이는게 귀엽네요. 

 

만들어 두었던 시리얼 통신용 어플을 실행해 볼까요?

간단한 화면

이제 버튼을 눌러보면 아래와 같이 아두이노와 연결된 OLED LCD 에 전송한 글자가 표시됩니다.

성공!

 

전송한 메시지가 잘 표시가 되네요.

우하하하핫

이때가 기분이 제일 좋은 순간이지요. 

 

 

끝으로..

C# 으로 개발하기 귀찮은 분들을 위해 테스트용 앱을 만들어 보았습니다.

요렇게 하단에 메시지 박스를 이용해서 원하는 메시지를 보낼 수 있는 툴입니다.

아두이노 LCD 에는 이렇게 받아지죠.

시리얼 통신으로 받은 메시지들

 

시리얼 메시지 테스터는 아래 파일을 다운로드 받으셔서 압축을 해제하신 후 실행하시면 됩니다.

csharp2arduino_serial.7z
0.00MB

 

바이러스 따위는 없으니 안심하셔도 됩니다. 

 

이렇게 해서 간단하게 아두이노로 PC 에서 메시지를 보내는 방법을 설명해 드렸습니다.

만약 다른 정보일지라도 문자열로 바꾸어서 보내준뒤 아두이노 코드에서 해석해서 받으면 어떤 정보든 전달하는 것이 가능하겠죠? 어렵지 않으리라 생각됩니다.

 

C# 레퍼런스는 정말 어마어마 하게 많기 때문에 우리가 생각하는 거의 모든 것들을 만들어 내는 것이 가능할 것입니다. 

아두이노같은 작은 컴퓨터에서 우리가 PC 로 하는 많은 것들을 알려주고 동작하게 하는 것. 아두이노 만큼 손쉽게 실제 동작하는 기구를 만들 수 있는 도구가 또 어디 있겠습니까? 

시작이 어렵지 잘만들어진 레퍼런스 하나면 바로 첫 삽을 뜰 수 있답니다.

일단 통신만 뚫었다면 그 다음은 일사 천리아니겠습니까? 

 

예를 들면 제가 아두이노를 이용해서 TV 리모컨 신호를 해킹한 적이 있는데요. 적외선 기능이 없는 노트북에서 TV 를 켜고 채널을 바꾸는것도 간단한 프로젝트로 해볼만할 것 같습니다. 

 

 

그럼 이만~

뎃글, 공감 은 블로그 작성자에게 큰 힘이 된답니다. 
도움이 되었다 생각되시면  클릭!!  부탁드려요~

 

 

2020/06/17 - [DIY/Arduino] - [DIY] 아두이노로 통합(만능) 리모콘 만들기 3/3

2020/05/26 - [DIY/Arduino] - 0.96 inch OLED 디스플레이 구동하기

2020/03/23 - [DIY/Arduino] - Arduino(아두이노) 무작정 시작하기

2019/06/27 - [DIY/Arduino] - 아두이노를 이용한 간단한 화분 자동 물주기 시스템

 

반응형
반응형

아두이노로 통합(만능) 리모콘 만들기 3/3 이라 쓰고 왠지 이번 포스트에서 끝나지 않을 것 같은 느낌이 강하게 듭니다. 지난 포스트에서 아두이노 IR 센서를 이용하여 신호를 해킹하고 또 필요한 신호를 따서 기록하는 것까지 소개해 드렸는데요.  해당 포스트는 아래 링크를 참고해 주세요.

2019/11/10 - [DIY/Arduino] - [DIY] 아두이노로 만능 (통합) 리모콘 만들기 1/3

 

[DIY] 아두이노로 만능 (통합) 리모콘 만들기 1/3

도대체 영화 한편 보려면 몇개의 리모콘을 사용하는지... TV 전원을 켜기위해 TV 리모컨을 찾아야 하고 안드로이드 TV 셋톱을 켜기위해 안드로이드 TV 리모콘을 역시 찾아야 하며 막상 틀었더니 소

diy-dev-design.tistory.com

2019/11/10 - [DIY/Arduino] - [DIY] 아두이노로 통합(만능) 리모콘 만들기 2/3

 

[DIY] 아두이노로 통합(만능) 리모콘 만들기 2/3

먼저 포스트에서 아두이노를이용하여 다양한 리모콘의 신호를 해킹하는 방법을 알아 보았습니다. https://diy-dev-design.tistory.com/65 [DIY] 아두이노로 만능 (통합) 리모콘 만들기 1/3 도대체 영화 한편 �

diy-dev-design.tistory.com

 

이번 포스트에서는 본격적으로 SW 를 설계하고 HW 를 만들어가는 과정을 소개해 드릴까 합니다.

먼저 조사했던 여러가지 기능을 모두 소화하기에 아두이노의 입력핀은 너무 적기 때문에 기기 전환 버튼같은 기능을 두어서 버튼은 동일한 레이아웃을 가지고 아두이노의 동일한 핀으로 입력 신호가 들어가더라도 실제 출력해주는 IR  신호는 다르게 보내주는 방식으로 구현을 할 계획입니다.

 

필요한 재료

  • 3cm x 7cm 양면 만능기판 (2.54mm 규격) x 2EA
  • 아두이노 나노
  • Attiny85
  • SW-200D 진동/틸트 스위치 양쪽 다리형
  • Push switch (2pin) x 14 EA
  • LED (red, SMD) x 4EA
  • LED (green, SMD) x 1EA
  • 5V DCDC Converter, Step Up Power Supply 보드
  • TP4056 18650 charge module
  • 0.1F 전해콘덴서 (슈퍼콘덴서??) 꼭 필요한지 모르겠음
  • 대충 33옴 정도되는 저항, 낮아도 별 상관 없음 (SMD LED 연결용)
  • IR LED (송신용) x 1EA
  • 18650 배터리 1EA

 

 

 

SW 를 대충 설계해보자.

먼저 아두이노의 인풋, 아웃풋 키와 함께 각각의 기능을 매칭하는 테이블을 만들어야겠습니다. 아무래도 여러가지 기능이 동작되어야 하므로 나중에 실수하지 않으려면 각각의 입력핀에 연결될 버튼과 기능을 정의하는게 중요합니다.

4개의 제품의 동작별로 기록한 신호와 기능명칭들 가장 오른쪽은 아두이노의 핀번호

위와 같이 각각의 기능을 테이블로 만들어 보았습니다. 가장 우측의 비고에 해당하는 열이 아두이노의 핀번호가 되겠으며 9~12번은 4개의 제품에 해당되는 LED 를 켜주기 위한 출력핀입니다. 18번은 제품전환용 버튼 입력핀이 됩니다.

제일 좌측에 S01~S14까지 번호가 있는데요. 이번호들은 각각 아래와 같은 형태로 배치할 계획입니다.

리모컨 버튼부 레이아웃

저는 양면 만능기판을 사용할 계획이고요. 만능기판의 끝부분에 단자용도로 사용할 수 있는 납이 붙어있어 그부분까지 일단 납땜을 하여 연결해 보기로 합니다. 중앙 상단의 네게의 흐릿한 동그라미가 LED 1234 와 연결이 되는 것이죠.

14개의 버튼은 모두 1개의 그라운드핀과 연결되며 나머지 한쪽 다리를 아래쪽 단자에 순차적으로 매칭을 시켜줍니다.

LED 역시 그라운드를 모두 한꺼번에 연결하고 아래쪽 LED 핀은 아두이노 디지털 출력핀과 연결해주어 선택된 제품에 불이 켜지도록 하겠습니다.

 

만능기판에 버튼을 실장한 사진은 아래와 같습니다.

총 14개의 버튼이 위와같이 배치된다.

총 14개의 버튼이 위와 같이 아름답게 배치됩니다. 만능기판의 크기를 생각해보면 아주 아담한 리모컨이 만들어 질 것 같네요.

 

 

 

 

아두이노 나노와 전원 공급 모듈

전원 입력은 3.7V 리튬이온 18650 배터리를 이용할 계획인데요. 아두이노가 5V 이상의 전압이 필요하기 때문에 5V 로 승압을 해주는 보드를 하나 장착이 필요하고요, 이 제품을 연속으로 사용하지 않을 경우 자동으로 전원 공급이 중단되도록 할 필요가 있을듯 하여 attiny85 를 이용하여 일정 시간이 지나면 전원 공급이 중단되도록 할 계획입니다. atiny는 sleep 모드라는 것을 지원하는데 sleep 모드로 들어가면 거의 전기를 먹지않는 놀라운 기능을 수행하게 됩니다.

다시 동작할때는 reset 핀에 GND 를 입력해주면 다시 동작을 시작하게 되는데 틸트 스위치를 이용하여 움직임을 감지하여 물리적으로 움직임이 발생되면 attiny85의 reset핀으로 신호를 보내줌으로써 attiny 가 전원 공급을 시작하는 것이죠. 이번 제작하는 리모컨 자체가 전류를 거의 사용하지 않기 때문에 문제는 없을 듯 하지만 그래도 attiny 의 디지털 출력핀이 충분한 전기를 공급할 수 있을지는 미지수입니다.

그래서 0.1F 정도의 콘덴서를 전원 공급용으로 하나 달아 보았습니다. ( 사실 전기/전자 지식이 매우 얕아서 저렇게 하면 되는지는 잘 모르겠어요)

 

그렇게 만들어진 보드가 바로 아래와 같이 만들어졌습니다.

 

위에서 설명드린 것처럼 우측 상단에 attiny85 가 자리잡고 있고요. (SMD 버전입니다) 중간에 검은 통이 틸트 스위치입니다. 약간 기울여서 달아주어야 평소에 눕혀져 있을때 신호가 끊어지기 때문에 기울여서 부착을 했고요, 아래 검은 네모박스가 0.1F 전해 콘덴서 입니다. 어디서 떼어낸건지는 기억이 안나는데 버리는 가전제품에서 떼어낸 부품입니다.

실제 아두이노 나노로 입력되는 전원은 전해콘덴서와 연결이 되어 있어 attiny 가 sleep 모드로 들어가더라도 일정시간 전원을 공급하여 주는 역할을 하게 됩니다.

우측의 단자부분은 이미 납땜이 마구 되어 있는데요, 위에 소개해드린 버튼부분과 연결이 되게 됩니다.

 

 

 

 

 

고난의 시작 ! 인정사정 없이 납땜시작

정말 납땜하다가 정신병 걸리는 줄 알았습니다.

왼쪽은 아두이노 나노를 포함한 메인 컨트롤 부, 오른쪽은 버튼 조작부와 LED 되겠습니다. LED 는 우측 끝에 매달려 있는데 잘 안보이시죠? 작은 회로기판 같은데 신호용으로 아주아주 작은 SMD 타입의 LED가 붙여 있는 것을 보셨을 텐데요, 고 녀석들을 떼어내서 부탁을 해주었습니다. 일부러 구입할일은 없으니까요.

각각의 선들은 위에서 설계한 것처럼 연결을 해주었는데 설계할때는 이게 이렇게나 복잡할거라고는 생각을 못했습니다.

위쪽에 보이는 작은 초록색 기판이 3.7 --> 5V 승압회로 , 그사이에 아주 작게 보이는 파란색 보드가 USB 전원을 이용하여 3.7V 배터리를 충전할 수 있는 충전 보드 입니다. 

이렇게 하면 완전한 하나의 제품에 해당되는 부품을 모두 제작한 셈이 되겠네요.

두 보드는 이렇게 접어서 조립할 예정입니다.

위에서 보았던 두개의 보드는 위 사진처럼 접어서 조립할 계획입니다. 안쪽역시 매우 지저분하네요. 저렇게 만들어진 배선이 과연 잘 붙어있을까 궁금합니다.

두개의 보드 중간에는 절연을 위하여 얇은 실리폰 패드를 하나 넣어줄 계획입니다.

 

전원 입력!

전원이 들어오게 되면 위사진과 같이 불이 켜집니다. 계획에는 LED가 4개였지만 메인 전원이 들어왔는지 여부를 판단하기 위하여 연두색 SMD LED를 하나 추가하였습니다. 

 

 

 

코딩 시작!

자 순서가 좀 바뀐것 같지만 이제부터는 지루한 SW 코딩 부분입니다.

SW 구현할때 다음의 것들이 동작해야 하는 것이 중요합니다.

  • 하나의 버튼에 의해 주어진 신호는 4개의 제품에 대한 신호를 보낼 수 있어야 한다.
  • 마지막으로 사용한 제품을 기억했다가 다시 동작할때 세팅해준다.
  • 신호가 한번에 입력이 안될 경우를 대비하여 연속으로 n번 보낼 수 있도록 구성한다.

뭐 별건 없습니다만 두번째에 해당되는 부분은 제가 처음해보는 관계로 저도 공부를 좀 했습죠.

 

 

 

EEPROM 을 사용해보자!

아두이노 나노에 사용되는 Atmega328 칩에는 무려 1kb 라는 비 휘발성 저장공간이 존재합니다. EEPROM 이라고 부르는데요, 프로그램이 저장되는 공간 외에 사용자가 데이터를 저장할 수 있는 공간말입니다.

무려 1kb 요.

-_-;;

1kb 라니.. 장난하냐..

 

 

 

싶겠지만 있는게 어딥니까?

이 저장공간을 이용하여 우리는 마지막 동작했던 제품의 정보를 기억할 건데요. 따지고 보면 차고도 넘치는 양이지요.

자 그럼 EEPROM 에 데이터는 어떻게 저장하고 읽어 들이는지 볼까요?

#include <EEPROM.h>

int sw_02;
int productIdx = 0; //0 : TV / 1: TV BOX / 2: BTV / 3: SPK
bool chkon = false;

//LED
const int L00 = 9;    // TV
const int L01 = 10;   // TV BOX
const int L02 = 11;   // BTV
const int L03 = 12;   // LONPOO SPEAKER

void setup()
{
    pinMode(s02, INPUT_PULLUP);
    productIdx = EEPROM.read(0); // EEPROM 에 저장된 제품의 번호를 불러오는 부분
}

void loop() {

  sw_02 = digitalRead(s02);

   // change product
  if (sw_02 == HIGH) {
    chkon = true; // 연속으로 눌리는 것을 방지하기 위하여 on 상태일때만 기기 변경이 되도록 한다.
    ledON(productIdx);
  }else{
    if (chkon)
    {
      productIdx++;
      if (productIdx > 3) productIdx = 0;
      EEPROM.write(0, productIdx); // 이부분이 EEPROM 에 현재 제품의 번호를 저장하는 부분
      chkon = false;
    }
    
    ledON(productIdx);
}

void ledON (int idx) // 선택된 인덱스에 맞게 LED 를 켜주는 함수
{
  digitalWrite(L00, LOW);
  digitalWrite(L01, LOW);
  digitalWrite(L02, LOW);
  digitalWrite(L03, LOW);
  switch (idx) {
  case 0:
    digitalWrite(L00, HIGH);
    break;
  case 1:
    digitalWrite(L01, HIGH);
    break;
  case 2:
    digitalWrite(L02, HIGH);
    break;
  case 3:
    digitalWrite(L03, HIGH);
    break;
  default:
    break;
  }
}

어렵지 않죠? 제일 위에 해당 라이브러리를 불러들여 주고요,

Setup 시에 저장되어 있는 값을 불러옵니다.

버튼은 눌렀을때 LOW 가 되도록 하고 항상 HIGH 한 상태를 만들어 줍니다. HIGH 한 상태가 되면 chkon 이 true 가되고 버튼이 눌렸을때 기기의 인덱스를 변경한 뒤 EEPROM 에 값을 저장하고 chkon 을 false 로 만들어 다음 신호를 기다리게 됩니다. 이렇게 하면 버튼을 연속으로 누르고 있어도 신호는 한번만 가게 되겠죠. 마지막으로 사용한 제품도 저장이 됩니다.

 

 

 

 

버튼 하나로 4개의 제품은 어떻게 제어를 할까요?

//setup, 변수 선언 등은 생략합니다.

void loop() {

if (sw_03 == HIGH) {    //s03 은 전원 버튼입니다.
  }else{    
    digitalWrite(13, HIGH);
    press_S03(productIdx);
  }
  // 생략
}


void press_S03 (int idx) // power
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF10EF,32);   // tv power
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F02FD,32);   // tv box power
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE807F,32);   // Btv power1 
    break;
  case 3:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF807F,32);   
    break;
  default:
    // statements
    break;
  }
}

바로 Switch 문을 이용하여 입력되는 idx 에 따라 각각 다른 신호를 보내주는 것이지요. 

아래 press_S03 버튼이 바로 전원을 켜고 끄는기능을 담당하는 함수가 되는 것이죠.

저런 식으로 버튼 개수만큼 함수를 만들어 주면 됩니다. 좀더 효율적으로 코딩할수도 있겠지만 초보인 저에게는 저렇게 쉽게 코딩하는게 나중에 뜯어 고치지도 좋을 것 같아 이란 저런 방식으로 작성을 하였습니다.

 

 

 

포스트가 점점 길어지네요.. 

자 이제 전체 소스 갑니다.

중요한 내용은 위에 다 설명 드렸고 이전 포스트를 참고하시면서 내용 확인해 보시면 되고요. 꼭 저와 동일하게 만들 필요는 없으니 참고만 해주시면 됩니다.

 

길이가 대단하니 조심하세요~ ㅋ

#include <boarddefs.h>
#include <IRremote.h>
#include <IRremoteInt.h>
#include <ir_Lego_PF_BitStreamEncoder.h>
#include <EEPROM.h>
/*
 * IRremote: IRsendDemo - demonstrates sending IR codes with IRsend
 * An IR LED must be connected to Arduino PWM pin 3.
 * Version 0.1 July, 2009
 * Copyright 2009 Ken Shirriff
 * http://arcfn.com
 */
/*
 * tv (NEC)
 * power :      0x2DF10EF, 32
 * input :      0x2DFD02F, 32
 * chnnel up :  0x2DF00FF, 32
 * chnnel dn :  0x2DF807F, 32
 * volumn up :  0x2DF40BF, 32
 * volumn dn :  0x2DFC03F, 32
 * OK :         0x2DF22DD, 32
 * sw up :      0x2DF02FD, 32
 * sw dn :      0x2DF827D, 32
 * sw left :    0x2DFE01F, 32
 * sw right :   0x2DF609F, 32
 * 
 * android tv (NEC)
 * power :      0x807F02FD, 32
 * sw up :      0x807F6897, 32
 * sw dn :      0x807F58A7, 32
 * sw left :    0x807F8A75, 32
 * sw right :   0x807F0AF5, 32
 * sw OK :      0x807FC837, 32
 * back :       0x807F9867, 32
 * volumn up :  0x807F18E7, 32
 * volumn dn :  0x807F08F7, 32
 * 
 * 
 * lonpoo speaker
 * power :      0x40BF807F, 32
 * volumn up :  0x40BF50AF, 32
 * volumn dn :  0x40BFD02F, 32
 * bt :         0x40BFA05F, 32
 * bt esc :     0x40BF906F, 32
 * opt :        0x40BF20DF, 32
 * 
 * 
 * LED
 * tv :     9
 * tv box : 10
 * btv :    11
 * spk :    12
 * 
 * input
 * s01 
 * 
 * 
 * 
 * 
 */

#include <IRremote.h>

const int s01 = 0;
const int s02 = 18;   // select sw 
const int s03 = 1;
const int s04 = 2;
const int s05 = 19;
const int s06 = 4;
const int s07 = 5;
const int s08 = 6;
const int s09 = 7;
const int s10 = 8;
const int s11 = 14;
const int s12 = 15;
const int s13 = 16;
const int s14 = 17;
//LED
const int L00 = 9;    // TV
const int L01 = 10;   // TV BOX
const int L02 = 11;   // BTV
const int L03 = 12;   // LONPOO SPEAKER

//
// define sw value
int sw_01;
int sw_02;
int sw_03;
int sw_04;
int sw_05;
int sw_06;
int sw_07;
int sw_08;
int sw_09;
int sw_10;
int sw_11;
int sw_12;
int sw_13;
int sw_14;

// 
int productIdx = 0; //0 : TV / 1: TV BOX / 2: BTV / 3: SPK

int addr = 0;
bool chkon = false;
IRsend irsend;

void setup()
{
  pinMode(s01, INPUT_PULLUP);
  pinMode(s02, INPUT_PULLUP);
  pinMode(s03, INPUT_PULLUP);
  pinMode(s04, INPUT_PULLUP);
  pinMode(s05, INPUT_PULLUP);
  pinMode(s06, INPUT_PULLUP);
  pinMode(s07, INPUT_PULLUP);
  pinMode(s08, INPUT_PULLUP);
  pinMode(s09, INPUT_PULLUP);
  pinMode(s10, INPUT_PULLUP);
  pinMode(s11, INPUT_PULLUP);
  pinMode(s12, INPUT_PULLUP);
  pinMode(s13, INPUT_PULLUP);
  pinMode(s14, INPUT_PULLUP);
  
  pinMode(L00, OUTPUT);
  pinMode(L01, OUTPUT);
  pinMode(L02, OUTPUT);
  pinMode(L03, OUTPUT);
  
  pinMode(13, OUTPUT);
  
  productIdx = EEPROM.read(0);
}

void loop() {

  digitalWrite(13, LOW);
  
  sw_01 = digitalRead(s01);
  sw_02 = digitalRead(s02);
  sw_03 = digitalRead(s03);
  sw_04 = digitalRead(s04);
  sw_05 = digitalRead(s05);
  sw_06 = digitalRead(s06);
  sw_07 = digitalRead(s07);
  sw_08 = digitalRead(s08);
  sw_09 = digitalRead(s09);
  sw_10 = digitalRead(s10);
  sw_11 = digitalRead(s11);
  sw_12 = digitalRead(s12);
  sw_13 = digitalRead(s13);
  sw_14 = digitalRead(s14);
  
  // change product
  if (sw_02 == HIGH) {
    chkon = true;
    ledON(productIdx);
  }else{
    if (chkon)
    {
      productIdx++;
      if (productIdx > 3) productIdx = 0;
      EEPROM.write(0, productIdx);
      chkon = false;
    }
    
    ledON(productIdx);
  }
  
  if (sw_01 == HIGH) {    
  }else{    
    digitalWrite(13, HIGH);
    press_S01(productIdx);
  }
  if (sw_03 == HIGH) {    
  }else{
    digitalWrite(13, HIGH);
    press_S03(productIdx);
  }
  if (sw_04 == HIGH) {    
  }else{
    digitalWrite(13, HIGH);
    press_S04(productIdx);
  }
  if (sw_05 == HIGH) {    
  }else{
    digitalWrite(13, HIGH);
    press_S05(productIdx);
  }
  if (sw_06 == HIGH) {    
  }else{
    digitalWrite(13, HIGH);
    press_S06(productIdx);
  }
  if (sw_07 == HIGH) {    
  }else{
    digitalWrite(13, HIGH);
    press_S07(productIdx);
  }
  if (sw_08 == HIGH) {    
  }else{
    digitalWrite(13, HIGH);
    press_S08(productIdx);
  }
  if (sw_09 == HIGH) {    
  }else{
    digitalWrite(13, HIGH);
    press_S09(productIdx);
  }
  if (sw_10 == HIGH) {    
  }else{
    digitalWrite(13, HIGH);
    press_S10(productIdx);
  }
  if (sw_11 == HIGH) {    
  }else{
    digitalWrite(13, HIGH);
    press_S11(productIdx);
  }
  if (sw_12 == HIGH) {    
  }else{
    digitalWrite(13, HIGH);
    press_S12(productIdx);
  }
  if (sw_13 == HIGH) {    
  }else{
    digitalWrite(13, HIGH);
    press_S13(productIdx);
  }
  if (sw_14 == HIGH) {    
  }else{
    digitalWrite(13, HIGH);
    press_S14(productIdx);
  }
	
	delay(75); //5 second delay between each signal burst
}

void press_S14 (int idx) // vol -
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DFC03F,32);   // tv vol -
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F08F7,32);  // tv box vol -
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE42BD,32);  // BTV box vol -
    break;
  case 3:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BFD02F,32);  //  spk vol -
    break;
  default:
    // statements
    break;
  }
}

void press_S13 (int idx) // chnl -
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF807F,32);   // tv chnl -
    break;
  case 1:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F18E7,32);  // tv box vol +
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE827D,32);   // Btv chnl -
    break;
  case 3:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF50AF,32);  //  spk vol +
    break;
  default:
    // statements
    break;
  }
}

void press_S12 (int idx) //vol +
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF40BF,32);   // tv vol +
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F18E7,32);  // tv box vol +
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FEC23D,32);   // Btv vol +
    break;
  case 3:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF50AF,32);  //  spk vol +
    break;
  default:
    // statements
    break;
  }
}

void press_S11 (int idx) // chnl +
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF00FF,32);   // tv chnl +
    break;
  case 1:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F58A7,32);  // tv box
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE02FD,32);   // Btv chnl +
    break;
  case 3:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF906F,32);  //  spk bt esc
    break;
  default:
    // statements
    break;
  }
}

void press_S10 (int idx) // down
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF827D,32);   // tv down
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F58A7,32);  // tv box down 
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE52AD,32);   // Btv down
    break;
  case 3:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF20DF,32);  //  
    break;
  default:
    // statements
    break;
  }
}

void press_S09 (int idx) // right
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF609F,32);   // tv right
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F0AF5,32);  // tv box right 
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE32CD,32);   // Btv right
    break;
  case 3:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF20DF,32);  // tv box up 
    break;
  default:
    // statements
    break;
  }
}
void press_S08 (int idx) // ok
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF22DD,32);   // tv ok
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807FC837,32);  // tv box ok 
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE629D,32);   // Btv ok
    break;
  case 3:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF20DF,32);  // tv box up 
    break;
  default:
    // statements
    break;
  }
}

void press_S07 (int idx) // arrow left
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DFE01F,32);   // tv left
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F8A75,32);  // tv box left 
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FED22D,32);   // Btv left
    break;
  case 3:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF20DF,32);  // tv box up 
    break;
  default:
    // statements
    break;
  }
}
void press_S06 (int idx)
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF02FD,32);   // tv back
    break;
  case 1:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F9867,32);  // tv box back 
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE728D,32);   // Btv back
    break;
  case 3:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF20DF,32);  // spk OPT 
    break;
  default:
    // statements
    break;
  }
}

void press_S05 (int idx) // arrow up
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF02FD,32);   // tv up
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F6897,32);  // tv box up 
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE926D,32);   // Btv up
    break;
  case 3:
    //irsend.sendNEC(0x40BFA05F,32);  
    break;
  default:
    // statements
    break;
  }
}

void press_S04 (int idx)
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DFD02F,32);   //외부입력
    break;
  case 1:
    //irsend.sendNEC(0x2DF10EF,32);   
    break;
  case 2:
    //irsend.sendNEC(0x2DF10EF,32);   
    break;
  case 3:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BFA05F,32);   // blue tooth
    break;
  default:
    // statements
    break;
  }
}

void press_S03 (int idx) // power
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF10EF,32);   // tv power
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F02FD,32);   // tv box power
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE807F,32);   // Btv power1 
    break;
  case 3:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF807F,32);   
    break;
  default:
    // statements
    break;
  }
}
void press_S01 (int idx) //  S01
{
  switch (idx) {
  case 0:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF10EF,32);   
    break;
  case 1:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F02FD,32);   
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE807F,32);   
    break;
  case 3:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF807F,32);   
    break;
  default:
    // statements
    break;
  }
}
void ledON (int idx)
{
  digitalWrite(L00, LOW);
  digitalWrite(L01, LOW);
  digitalWrite(L02, LOW);
  digitalWrite(L03, LOW);
  switch (idx) {
  case 0:
    digitalWrite(L00, HIGH);
    break;
  case 1:
    digitalWrite(L01, HIGH);
    break;
  case 2:
    digitalWrite(L02, HIGH);
    break;
  case 3:
    digitalWrite(L03, HIGH);
    break;
  default:
    // statements
    break;
  }
}

 

자 이렇게 해서 프로그램까지 마쳤습니다.

원래 3편에 걸쳐 포스팅을 완료하려고 하였으나 너무 길어져서 최종 케이스제작과 동작 테스트는 다음 포스트로 미루도록 하겠습니다. 죄송합니다.

또 얼마가 걸릴지 모르겠네요. 

 

어쨌든 핵심이 되는 내용은 이번 편까지인것 같고 하드웨어 구성은 여러분께서 마음껏 하셔도 될 것 같습니다. 기능도 변경해보시고요, 좀 고민해보면 더 많은 기능을 구성하는 것도 가능할 수도 있습니다.

이번 프로젝트로 무려 4개의 리모컨을 1개로 통합하는 작업을 해보았는데요, 사실 제가 생각했던것과 아주 유사한 제품이 판매되는 제품도 있습니다. 가격이 아주 비싼것도 아니고요. 다만 이런것을 직접 해봄으로 해서 우리는 또 많은 것을 배울 수 있지 않겠습니까?

저는 이번 프로젝트에서 EEPROM 과 IR 신호를 제어하는 것을 배울 수 있는 좋은 기회였던 것 같습니다.

 

자 그럼 케이스 까지 완성되는 그날을 기대하며 ~

 

완성된 리모컨 구경하려면?

2022.08.16 - [DIY/Arduino] - [DIY] 아두이노로 통합(만능) 리모콘 만들기 (최종)

 

[DIY] 아두이노로 통합(만능) 리모콘 만들기 (최종)

만능 리모컨을 만들기로 해놓고 정말 많은 시간이 흘렀습니다. 2019.11.10 - [DIY/Arduino] - [DIY] 아두이노로 만능 (통합) 리모콘 만들기 1/3 2019.11.10 - [DIY/Arduino] - [DIY] 아두이노로 통합(만능) 리모콘..

diy-dev-design.tistory.com

 

뎃글, 공감 은 블로그 작성자에게 큰 힘이 된답니다. 
도움이 되었다 생각되시면  클릭!!  부탁드려요~

 

IR 리모컨 제작 관련 링크는?

2019/11/10 - [DIY/Arduino] - [DIY] 아두이노로 만능 (통합) 리모콘 만들기 1/3

 

[DIY] 아두이노로 만능 (통합) 리모콘 만들기 1/3

도대체 영화 한편 보려면 몇개의 리모콘을 사용하는지... TV 전원을 켜기위해 TV 리모컨을 찾아야 하고 안드로이드 TV 셋톱을 켜기위해 안드로이드 TV 리모콘을 역시 찾아야 하며 막상 틀었더니 소

diy-dev-design.tistory.com

2019/11/10 - [DIY/Arduino] - [DIY] 아두이노로 통합(만능) 리모콘 만들기 2/3

 

[DIY] 아두이노로 통합(만능) 리모콘 만들기 2/3

먼저 포스트에서 아두이노를이용하여 다양한 리모콘의 신호를 해킹하는 방법을 알아 보았습니다. https://diy-dev-design.tistory.com/65 [DIY] 아두이노로 만능 (통합) 리모콘 만들기 1/3 도대체 영화 한편 �

diy-dev-design.tistory.com

 

다른 아두이노 예제는?

 

2020/05/26 - [DIY/Arduino] - 0.96 inch OLED 디스플레이 구동하기

 

0.96 inch OLED 디스플레이 구동하기

가끔 아두이노로 무엇인가를 만들어 보려고 하다보면 디스플레이가 있으면 하는 생각이 들때가 있습니다. 아두이노는 작은 컴퓨터이기는 하지만 모름지기 컴퓨터라 한다면 입력장치 - 중앙처��

diy-dev-design.tistory.com

2019/10/29 - [DIY/Arduino] - [DIY] 아두이노를 이용한 수경재배 시스템

 

[DIY] 아두이노를 이용한 수경재배 시스템

아두이노는 정말 놀라운 하드웨어가 아닐수 없죠. 우리가 생각하는 이런건 자동으로 해주는거 없나? 이런게 자동으로 되면 좋을텐데... 이런걸 왜 자동으로 안하는거야?? 같은 대부분의 자동화 �

diy-dev-design.tistory.com

2019/06/27 - [DIY/Arduino] - 아두이노를 이용한 간단한 화분 자동 물주기 시스템

 

아두이노를 이용한 간단한 화분 자동 물주기 시스템

얼마뒤면 베트남으로 가족 여행을 떠날 예정입니다. 7박이나 하고 올 예정이므로 집을 비우기 전 준비해야 할 것들이 많습니다. 이것저것 여행준비를 하던 찰나 베란다에 내어 둔 화분이 걱정이

diy-dev-design.tistory.com

2020/03/23 - [DIY/Arduino] - Arduino(아두이노) 무작정 시작하기

 

Arduino(아두이노) 무작정 시작하기

아두이노, Arduino 여기저기서 많이 들었을 겁니다. 이걸 해야 된다는 말이 귀에 못이 박히도록 이야기 합니다. 대충 뭔지는 알겠는데 개발자도 아닌 내가 과연 이걸로 뭘 할 수 있을까 싶기도 하��

diy-dev-design.tistory.com

2020/06/29 - [DIY/Arduino] - C# 에서 아두이노로 시리얼 통신 하기

 

C# 에서 아두이노로 시리얼 통신 하기

카테고리를 c# 으로 해야 할지 Arduino 로 해야할지 조금 고민이 되는 포스트 입니다. 음.... arduino 로 하는게 좋겠네요. 따지고 보면 C# 으로 만든 어플이 중요한게 아니라 아두에노에서 시리얼 통신

diy-dev-design.tistory.com

 

반응형
반응형

가끔 아두이노로 무엇인가를 만들어 보려고 하다보면 디스플레이가 있으면 하는 생각이 들때가 있습니다. 아두이노는 작은 컴퓨터이기는 하지만 모름지기 컴퓨터라 한다면 입력장치 - 중앙처리장치 - 출력장치가 모두 있어야 비로소 컴퓨터라 할수 있겠지요. 센서나 버튼 등은 간단한 입력장치라 볼수 있겠으나 아두이노 기판에 붙어있는 LED 는 출력장치라고 하기에는 조금 부족한 면이 있습니다.

 

그래서 오늘은 아두이노 나노 에 OLED 디스플레이를 붙여 볼 생각입니다.

오늘도 역시 알리익스프레스에서 구입한 부품으로 시작합니다.

 

 

오늘 필요한 부품은

  • 아두이노 나노  Arduino Nano (우노가 있으신 분은 그냥 우노로 하시면 됩니다)
  • 0.96 inch OLED display (128x64)

이게 전부 입니다.

간단하죠?

 

참고로 제가 구입한 OLED 보드는 아래의 제품입니다.

Arduino OLED 테스트를 위하여 구입한 제품

I2C 통신이 가능한 모델로 코딩을 간단하게 할수 있고 연결해야 하는 선이 적어 손쉽게 구현이 가능하다는 장점이 있습니다. (4pin)

 

 

먼저 OLED 에 무엇인가를 손쉽게 표시하기 위하여는 라이브러리를 받아야 합니다. 

아래의 첨부파일을 받아주세요.

Adafruit_SSD1306-master.zip
0.03MB
Adafruit-GFX-Library-master.zip
0.32MB

받은신 라이브러리는 압축을 해제하여 내문서> Arduino> Library 폴더안에 아래와 같이 복사해서 넣어주세요.

 

그럼 기본적인 준비는 끝났습니다. 

아두이노 나노와 OLED 를 아래와 같이 연결해 주세요.

  • A4 --> SDA
  • A5 --> SCK
  • 5V --> VDD
  • GND --> GND

요렇게 연결하면 연결도 끝 입니다.

매우 간단하죠?

 

 

자 코딩을 해볼까요? 간단하게 텍스트를 출력하는 예제를 짜 보았습니다.

#include <Wire.h> 
#include <Adafruit_SSD1306.h>
#include <splash.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SPITFT.h>
#include <Adafruit_SPITFT_Macros.h>
#include <gfxfont.h>

#define OLED_ADDR   0x3C

// reset pin not used on 4-pin OLED module
Adafruit_SSD1306 display(-1);


void setup() {
  // put your setup code here, to run once:

  display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);

  display.setFont();
  display.clearDisplay();
  display.display();

  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(2, 25);
  display.print("DIY DEV DESIGN");
  display.display();
}

void loop() {
  // put your main code here, to run repeatedly:

}

 

setup 에서 한번만 호출해주는 코드 입니다. 나중에 loop 에서 다른 화면을 보여주는 걸로 한다면 인트로 정도가 될 수 있겠습니다.

출력되는 화면을 한번 볼까요?

DIY DEV DESIGN 을 출력해 보았다.

캬~ 아주 깔끔하고 예쁩니다. 디스플레이 크기가 작은 만큼 글자 크기도 좀 작게 설정을 하였는데요. 선명하고 예쁩니다. 

우리는 이 디스플레이에 무엇인가를 그리거나 표시하기 위하여 위와 같은 방식으로 계속 직접 코딩을 하는 것은 비 효율 적이기 때문에 뭔가 좀저 효율적으로 그릴 수 있도록 코딩을 해볼 계획입니다.

 

 

만들어 볼 기능은 아래와 같습니다. (사실 RC 카 조종기에 넣을 기능임)

  • 텍스트 표시하기
  • 프로그래스 바 표시하기
  • 좌우 밸런스 바 표시하기

 

 

텍스트 표시하기

// 위쪽은 동일하므로 생략합니다.

void loop() {
  display.clearDisplay();
  draw_text(1, 2, "[ DIY DEV DESIGN ]", false);
  draw_text(2, 2, "This is selected menu", true);
  draw_text(3, 2, "This is 2nd menu", false);
  draw_text(4, 6, "Sub menu 1 line", false);
  draw_text(5, 6, "Sub menu 2 line", false);
  draw_text(6, 2, "This is 3rd menu", false);
  display.display();
  delay(1000);
}

void draw_text (int line, int xPos, String txt, bool isSelected)
{
  
   int cHeight = (line-1) * 8 + ((line-1) * 2);
   if(isSelected)
   {
      display.fillRect(xPos - 2, cHeight, 127, 8, WHITE);
      display.setTextColor(BLACK);
   }else{
      display.fillRect(xPos - 2, cHeight, 127, 8, BLACK);
      display.setTextColor(WHITE);
   }
   display.setCursor(xPos, cHeight);
   display.print(txt);
}

아래쪽에 draw_text 라는 함수를 하나 만들어 주었습니다. 원하는 줄에 원하는 텍스트를 쉽게 표시할 수 있도록 했는데요, 특징으로는 메뉴 등의 기능으로 사용할 수 있도록 해당 라인이 선택 상태인지를 입력할 수 있도록 했습니다.

구현된 화면을 보면 아래와 같죠. 로터리 스위치나 푸쉬스위치의 입력을 받아 선택상태를 바꾸어 주는 방식으로 메뉴를 개발할 수 있을 것 같습니다.

아두이노 OLED 텍스트 메뉴 표시 테스트

 

 

 

 

프로그래스 바 만들어 보기

void loop() {
  
  for(int i = 2; i < 127; i++)
  {
    display.clearDisplay();
    draw_text(1, 2, "[ DIY DEV DESIGN ]", false);
    draw_text(2, 2, "progress 1", false);
    draw_progressBox(3, i, false);
    draw_text(4, 2, "progress 2", false);
    draw_progressBox(5, 126-i, true);
    display.display();
    delay(20);
  }
  
}

void draw_progressBox(int line, int val, bool isSelected)
{
  
  int cHeight = (line-1) * 8 + ((line-1) * 2);
  if(isSelected)
    {
      display.fillRect(0, cHeight, 127, 8, WHITE);
      display.drawRect(1,cHeight+1, 126, 6, BLACK );
      display.fillRect(1,cHeight+1, val, 6, BLACK );
    }else{
      display.drawRect(1,cHeight+1, 126, 6, WHITE );
      display.fillRect(1,cHeight+1, val, 6, WHITE );
    }
}

void draw_text (int line, int xPos, String txt, bool isSelected)
{
  
   //생략
}

이번에는 draw_progressBox 라는 함수로 프로그래스 바를 표현해 봤습니다. 역시 라인의 위치를 간단히 설정할 수 있도록 하였으며 val 값을 입력 받도록 하였습니다.

그리고 텍스트와 마찬가지로 선택상태를 주게 되면 색상이 반전되어 보이도록 기능을 주었습니다. 총 2~126 까지의 값이 표현 가능합니다. 깔끔하게 100개만 입력 받도록 넓이를 조정해보는 것도 좋을 것 같네요.

구현하면 아래와 같이 표시 됩니다.

아두이노, OLED 프로그래스바 코드 테스트

 

 

 

 

 

아날로그 발란서 만들어보기

다음은 아날로그 발란서 입니다. 센터를 정해 놓고 좌우로 얼마나 기울었는지 등을 설정하거나 보여주는 기능에 적합한 메뉴 입니다.

void loop() {
  
  for(int i = 2; i < 127; i++)
  {
    display.clearDisplay();
    draw_text(1, 2, "[ DIY DEV DESIGN ]", false);
    draw_text(2, 2, "progress 1", false);
    draw_analogBalancer (3, i, false);
    display.display();
    delay(20);
  }
  
}
void draw_analogBalancer(int line, int val, bool isSelected)
{
  int cHeight = (line-1) * 8 + ((line - 1) * 2);  

  if (isSelected)
  {
    display.fillRect(0, cHeight, 127, 8, WHITE);
    display.drawLine(0, cHeight+3, 127, cHeight+3, BLACK);
    display.drawLine(val, cHeight+1, val, cHeight+5, BLACK);
    display.fillTriangle(64, cHeight+5, 62, cHeight+7, 66, cHeight+7, BLACK);
    display.drawPixel(val-1, cHeight+3, WHITE);
    display.drawPixel(val+1, cHeight+3, WHITE);
  }else{
    display.drawLine(0, cHeight+3, 127, cHeight+3, WHITE);
    display.drawLine(val, cHeight+1, val, cHeight+5, WHITE);
    display.fillTriangle(64, cHeight+5, 62, cHeight+7, 66, cHeight+7, WHITE);
    display.drawPixel(val-1, cHeight+3, BLACK);
    display.drawPixel(val+1, cHeight+3, BLACK);
  }
}

코드가 좀 길죠? 아무래도 표현해야 할 요소가 많기 때문인데요, 역시 선택상태를 표시하기 위하여 두벌로 코딩이 되었습니다. 선택 상태를 만드실 필요가 없다면 else 아래쪽만 코딩을 해주셔도 표현이 가능합니다.

 

구현된 화면을 한번 보실까요?

아두이노 OLED 밸런스 바 코드 테스트

가운데 세모가 중심의 위치를 나타내고 세로로 얇은 막대가 현재 입력 받는 val 에 해당되는 위치가 됩니다. 물론 반대로 표시하는 것도 조금만 코드를 수정하면 가능하겠죠?

 

 

이렇게 하여 간단하게 아두이노를 이용하여 초소형 OLED 를 구동하는 것을 보여드렸습니다. 전혀 어렵지 않으니 바로 도전해 보시기 바랍니다. 

OLED 가 각종 정보를 표시하는데 생각보다 속도가 많이 느리므로 아두이노 자체 기능이 지연될 수 있습니다. realtime clock 과 같은 실제 시계 등을 만들기 위하여는 위에 표시한 방식과는 다르게 구현을 해야 될 것 같습니다.

 

궁금하신 것은 뎃글로 문의 해 주시기 바랍니다.

뎃글, 공감 은 블로그 작성자에게 큰 힘이 된답니다. 
도움이 되었다 생각되시면  클릭!!  부탁드려요~


 

 

2020/03/23 - [DIY/Arduino] - Arduino(아두이노) 무작정 시작하기

2019/12/23 - [DIY/RC] - 아두이노를 이용한 RC 카 만들기 2

2019/11/10 - [DIY/Arduino] - [DIY] 아두이노로 만능 (통합) 리모콘 만들기 1/3

2019/06/27 - [DIY/Arduino] - 아두이노를 이용한 간단한 화분 자동 물주기 시스템

2019/10/29 - [DIY/Arduino] - [DIY] 아두이노를 이용한 수경재배 시스템

반응형
반응형

아두이노, Arduino 여기저기서 많이 들었을 겁니다. 이걸 해야 된다는 말이 귀에 못이 박히도록 이야기 합니다. 대충 뭔지는 알겠는데 개발자도 아닌 내가 과연 이걸로 뭘 할 수 있을까 싶기도 하죠. 

아두이노에 대해 설명을 해드릴 필요가 있을만큼 인터넷에는 너무나 많은 아두이노에 대한 글들이 널리고 널렸습니다. 게다가 저처럼 적당한 수준의 지식만 가지고 있는 사람이 설명을 할 수 있을 만큼 실제로 단순한 장치도 아니지요.

오늘 저는 이 포스트에서 딱 한가지만 전달 드릴 생각입니다.

아두이노를 갖고 싶다는 마음 말이죠.

 

아두이노를 제가 정의한다면 다음과 같습니다.

"오직 내손에 의해 만들어지는 세상에서 가작 작은 컴퓨팅 프로세서"

아두이노는 정말 사용자가 프로그래밍을 해서 프로세서 칩에 데이터를 넣은뒤 동작을 시켜 주어야 동작하는 장치입니다. 다른 전자제품처럼 모든게 세팅되어 있지도 않고 만들어져 있지도 않습니다. 오직 나에 의해 만들어지는 것이죠.

그런데 어렵냐고요?

어렵다고 말하면 어렵고 쉽다고 말하면 이렇게 쉬운게 없습니다.

인터넷에는 너무나 많은 예제 프로그램들이 돌고 있고 아두이노 개발 환경을 설치하는 것만으로도 기본적으로  자체에서도 엄청난 양의 예제 코드를 제공합니다. 대충 코드의 흐름만 읽을 줄 알아도 개발을 시작할 수 있다는 이야기죠. 심지어 많은 경우에 전체 코드를 짜지 않고 변수나 값을 일부만 바꾸어도 내가 원하는 코드가 완성되는 경우가 대부분이니 코딩을 잘 몰라도 걱정 마시고 시작하셔도 됩니다.

 

 

우선 ARDUINO 를 사라!

 

자 그럼 아두이노를 처음 시작하는 분은 어떤 과정을 거쳐야 할까요?

전자제품(부품) 쇼핑몰에서 아두이노 패키지를 사야 할까요? 저는 비추입니다. 비싸고 불필요한 것들이 많습니다. 우선 아두이노 자체를 구입하세요. 제일 만만한건 아두이노 나노 또는 기본형인 아두이노 우노 입니다. 아두이노 나노와 우노는 생김새에는 차이가 많지만 사실상 차이가 없는 동일한 제품이라 생각하셔도 됩니다. 모든 코드가 호환되며 입출력 핀 수도 같기 때문에 브래드 보드 없이 바로 테스팅을 진행하려면 우노를, 브래드 보드에서 테스트가 끝나면 바로 자신만의 제품에 넣으려면 나노를 구입하시면 됩니다. 알리 같은 해외 쇼핑몰을 이용하시면 배송시간은 좀 걸리지만 3000원도 안되는 금액에 나만의 컴퓨팅 시스템을 만들수 있는 보드를 구입할 수 있답니다.

어짜피 이것 못해서 안달난 것 아니자나요? 주문해놓고 잊을 만할때 똭! 도착하여 마음을 다시 설레게 만들죠. 로또 사놓고 즐거워하는 시간은 길어야 일주일이지만 아두이노를 주문해놓고 몇주 동안 내손으로 만드는 기계장치를 상상하는 맛도 로또 못지 않답니다.

 

땅콩보다 조금 큰 Arduino Nano

둘다 컴퓨터에 USB 로 바로 연결이 가능하고 USB 전원을 이용하여 동작이 가능하며 LED가 달려 있어 기본적인 동작 테스트가 가능하고 모든 연결 핀이 소켓으로 되어 있거나 표준 규격의 핀으로 구성되어 브래드 보드에 바로 장착하여 테스트를 할수 있기 때문입니다.

 

아두이노로 뭔가를 해보겠다면 필요한 것들

만약 제가 처음 시작하는 분께서 구입하셔야 할 것들을 안내 해 드린다면 아래와 같을 겁니다.

  • 아두이노 NANO (필수)
  • 브래드 보드 (NANO 제품을 구입한 경우)
  • 점퍼 케이블 (필수, 하지만 없으면 만들어도 됨)
  • 가변저항 (볼륨)
  • 푸쉬 버튼, 토글 스위치, 슬라이드 스위치
  • 저항 (1.4k, 4.7k, 100)
  • LED (다리 있는것, 없는 것)

아마 이 정도가 기본일 것 같습니다. 추가로 좀더 재미있는 것들을 해보고 싶다면

  • 서보모터 (9g, 3pin)
  • 부저
  • DC 모터 드라이버 (ESC 라고 불리우는 것)
  • 무선 데이터 송수신 보드 (2.4ghz RF 또는 bluetooth) 
  • 조이스틱
  • 스텝모터
  • IR LED (송신 , 수신)
  • 터치패드
  • 초음파 거리 센서

 

요렇게 부품들이 있으시면 정말 할 수 있는것이 많죠. 사실 위의 부품들 전체를 알리에서 다 구입하더라도 1만원~2만원 정도면 구입할 수 있다는 것이 믿어 지시나요? ㅋㅋ 하지만 사실입니다. 물론 잘 구매 해야 겠고 중국에서 구입하면 배송되는 시간도 꽤 걸릴 거라서 시간을 두고 천천히 해나가시면 됩니다.

 

자 아두이노를 구입하셨고 수중에 아두이노가 있다면 다음 단계는 개발 환경을 설치하는 일 입니다. 아두이노 공식 사이트에서 다운 받으시면 되는데 오픈소스 개발환경이다 보니 사실 최신 버전을 꼭 받아야 할 필요는 없습니다. 또 어떤 부품은 이전 버전에서만 테스트 되었으며 특정 버전에서만 동작이 되는 제품도 있습니다. 개인적으로는 1.6.x 버전과 1.0.x 버전중에 자신에게 맞는 (튜토리얼 등을 참고하고 있는) 버전을 설치하시면 될 것 같습니다.

아두이노 예전 버전 받는 링크

 

Arduino - OldSoftwareReleases

Arduino IDE that can be used with any Arduino board, including the Arduino Yún and Arduino DUE. Refer to the Getting Started page for Installation instructions. See the release notes. Windows Installer Windows ZIP file for non admin install Mac OS X 10.8 M

www.arduino.cc

전 별다른 이유는 없지만 1.6.7 버전을 설치하였습니다.

아두이노 1.6.7 버전 설치 직후

 

실제로 최신 버전을 타겟으로 개발되지 않은 라이브러리가 동작하지 않는 문제가 있으므로 반드시 최신 버전을 설치해야 할 필요는 없습니다. 1.0.x 대 버전에서만 동작하는 라이브러리가 있을 수도 있으므로 라이브러리 다운로드시 어떤 버전에서 동작했는지 확인이 필요합니다.

개발하는 순서는 다음과 같습니다.

코딩을 하고 (또는 예제를 열고) 현재 자신의 보드를 선택한 뒤 프로그래머를 선택하고 빌드하시면 됩니다.

참고로 잘못 선택한다고 고장나는 경우는 거의 없으니 두려워 하실 필요 없어요. 뭔가 원하는 데로 동작이 안된다면 처음부터 다시 확인해 보시면 됩니다. 프로그래밍 상의 오류는 아두이노 IDE 에서 컴파일 할때 다 잡아 줍니다. 장비와의 매칭 문제는 컴파일된 소스를 업로드 할때 잡히게 되어 있죠. 클럭 수 등은 보통 구입할때 알수 있지만 일반적인 경우가 대부분이므로 일반적인 값을 선택해 주면 됩니다. 예를 들면 아두이노 나노라면 16Mhz 가 일반 적인 옵션이죠.

 

 

 

아두이노 코딩의 기본 규칙은 아래와 같습니다.

처음에 현재 개발하고 있는 코드에서 사용할 라이브러리를 선언합니다. 그리고 각종 변수를 선언 해 줍니다. 

void setup 이라는 함수가 나오는데 이 함수는 최초 딱 1번 실행이 되는 함수 입니다. 변수들의 초기값을 세팅해 주거나 시작하자마자 동작해야 하는 함수 등을 호출 해줄 수 있습니다.

그다음 void loop() 이라는 함수가 나오는데요. 이 함수가 실제로 아두이노를 동작시키는 함수 입니다.

loop 는 말그대로 반복하는 동작을 말하죠? loop 안의 내용이 계속해서 반복된다고 보시면 됩니다.

유사하고 같은 동작이 반복적으로 사용된다면 함수를 만들어 빼내고 loop 에서는 해당함수를 호출해 주는 방식으로 사용하게 됩니다. 이렇게 필요한 기능들을 잘 생각해서 함수를 아래로 쭉쭉 추가해 나갈 수 있습니다.

 

 

라이브러리 설치는 어떻게 하는 것인가!!!

인터넷에 궁금한 것을 찾다 보면 라이브러리를 설치하라고 나오면서 경로를 링크해놓은 경우가 많습니다. 들어가 보면 github 같은 사이트에 올라온 경우도 있고 직접 라이브러리를 압축 파일로 올려놓는 경우도 많이 있습니다. 일단 기본 예제 외에 무선 통신이나 좀 신기한거를 하려면 대부분 누군가 개발해놓은 라이브러리를 이용해야 하는 경우가 많은데요. 처음 할때는 마냥 햇갈립니다. 어떻게 하라는 건지 말이죠.

최신 버전은 라이브러리를 개발 환경 (개발 툴) 에서 직접 검색하여 설치 할 수 있도록 되어 있는데요. 예전 방식으로 그냥 복사해서 붙여넣는 것도 간단하고 쉽습니다.

제가 설치한 1.6.7 버전 기준으로 설명을 드릴께요

다운로드 받은 압축파일 , github의 경우 download zip 과 같은 버튼이 있습니다. 일단 다운로드 받으신 뒤 압축을 해제하여 주세요.

다운로드 받은 압축 파일을 해제한 상태

이렇게 압축 해제된 폴더들 (압축파일이 아닙니다)을 복사해서 내문서>arduino>library 폴더에 복사해서 붙여 넣으시면 끝! 입니다.

라이브러리 폴더를 내문서>arduino>libraries 폴더에 붙여 넣었다.

 

참쉽죠?

 

요렇게 하신 뒤에 arduino sw 를 재실행 하시면

예제 카테고리 안에 해당 라이브러리에서 제공하는 예제가 보인다.

요렇게 설치된 라이브러리의 예제가 보이는 것을 확인할 수 있답니다.

 

예제 파일을 열어보시면 라이브러리를 불러들이는 방법이나 간단한 사용방법 등이 코드로 설명이 되어 있으니 그냥 복사해서 새로운 ino 창에 붙여 넣고 사용하면 되는 것이죠.

보신것처럼 간단합니다.

 

아두이노는 이렇게 SW 를 구성하는 것으로 끝이 아니라 점퍼 케이블과 브래드 보드 등을 이용하여 원하는 동작이 되는지 확인하는 과정과 최종 제품으로 만들어가는 과정이 모두 완료 되어야 하나의 프로젝트가 마무리 되었다고 볼 수 있습니다. 

원하는 결과대로 잘 동작 할 경우 그 만큼 만족도로 아주 높습니다.

손톱만큼 작은 OELD 에 내가 만들고 있는 프로그램의 화면을 출력할 수도 있죠.

0.96inch OLED 스크린을 이용하여 글자를 출력한 모습

(OELD 스크린을 연동하는 예제는 따로 포스팅 하도록 하겠습니다)

 

대표적으로 저는 아두이노로 RC카 송수신기를 만들어 가지고 놀았습니다. 말하자면 RC 카의 무선 조종 장치를 만들고 차량에 그 신호를 받아 차량을 컨트롤 할 수 있도록 하는 장치를 만들었습니다. 아주 간단(??)하지만 재미있는 작업이었습니다.

직접 제작한 RC 카 송수신기로 동작하는 RC 카 WPL-C24

 

조종기(송신기) 와 차량 (수신기)에 각각 arduino nano 보드를 이용하여 송수신 장치를 만들었다.

 

제가 아두이노로 그동안 만들었거나 만들수 있는 재미있는 것들은 아래와 같습니다.

위의 것들 중 제가 직접 만든 것들은 링크를 할테니 한번 둘러보시는 것도 좋겠네요 ^^

 

 

자 수중에 아두이노가 이미 있다면 망설일게 무엇이 있겠습니까? 

아두이노를 이용하여 단순한 조명을 만들수도, 장난감을 만들수도, 어쩌면 좀 복잡한 기계 장치를 만들수도 있겠으며 홈 네트워크 같은 미래기술도 내손으로 직접 만들어 볼 수 있답니다. 

 

누구나 창조자, MAKER 가 될 수 있습니다. 하루하루 지루해진 일상에 단물 같은 취미를 한번 선물해 보시는건 어떨까요?

돈 몇푼이면 토니스타크 같은 멋진 컴퓨팅 시스템 개발자가 될 수 있답니다.

자 ~ 바로 시작해 보실까요?

반응형
반응형

먼저 포스트에서 아두이노를이용하여 다양한 리모콘의 신호를 해킹하는 방법을 알아 보았습니다.

https://diy-dev-design.tistory.com/65

 

[DIY] 아두이노로 만능 (통합) 리모콘 만들기 1/3

도대체 영화 한편 보려면 몇개의 리모콘을 사용하는지... TV 전원을 켜기위해 TV 리모컨을 찾아야 하고 안드로이드 TV 셋톱을 켜기위해 안드로이드 TV 리모콘을 역시 찾아야 하며 막상 틀었더니 소리가 어마무시하..

diy-dev-design.tistory.com

 

그럼 이제 신호를 TV, 안드로이드 TV BOX, 블루투스 사운드바 에 각각 보내는 테스트를 해볼 차례 입니다.

앞선 포스트에서 확인이 가능하겠지만 제가 테스트를 통해 모은 신호는 아래와 같습니다.

 * tv (NEC)
 * power :      0x2DF10EF, 32
 * input :      0x2DFD02F, 32
 * chnnel up :  0x2DF00FF, 32
 * chnnel dn :  0x2DF807F, 32
 * volumn up :  0x2DF40BF, 32
 * volumn dn :  0x2DFC03F, 32
 * OK :         0x2DF22DD, 32
 * sw up :      0x2DF02FD, 32
 * sw dn :      0x2DF827D, 32
 * sw left :    0x2DFE01F, 32
 * sw right :   0x2DF609F, 32
 * 
 * android tv (NEC)
 * power :      0x807F02FD, 32
 * sw up :      0x807F6897, 32
 * sw dn :      0x807F58A7, 32
 * sw left :    0x807F8A75, 32
 * sw right :   0x807F0AF5, 32
 * sw OK :      0x807FC837, 32
 * back :       0x807F9867, 32
 * volumn up :  0x807F18E7, 32
 * volumn dn :  0x807F08F7, 32
 * 
 * 
 * lonpoo speaker
 * power :      0x40BF807F, 32
 * volumn up :  0x40BF50AF, 32
 * volumn dn :  0x40BFD02F, 32
 * bt :         0x40BFA05F, 32
 * bt esc :     0x40BF906F, 32
 * opt :        0x40BF20DF, 32
 * 
 */

 

먼저 몇가지 확인이 쉬운 기능들을 테스트 할 예정입니다.

전원 ON/OFF 로 해보죠.

준비물은 아래와 같습니다.

  • 아두이노 나노
  • IR LED (송신용, 투명한 재질임)
  • 10 k 정도 되는 저항, 저는 4.7k * 3 개
  • push button 3개
  •  

간단하죠?

IR LED 는 전에 사 두었던 거리감지 쉴드에서 적출 하였습니다. 일정 거리 미만이 되면 신호를 보내는 쉴드 인데요. 마땅히 사용할 일이 없어 놀고 있던 보드에서 추출하였습니다.

거리 감지 센서, 아래쪽의 투명한 LED 가 IR 송신부 LED 이다.

버튼 역시 앞서 포스트에 등장했던 비디오 테이브 플레이어의 보드에서 적출 하였습니다. 다리 4개 짜리 버튼이 브래드보드에서 테스트 하기가 좋지 않기 때문에 뜯는 김에 버튼들도 추출을 진행하였습니다.

혹시 몰라 TR 도 하나 뜯어 보았는데 동작이 잘 안되면 연결해볼까 합니다.

버려진 가전제품에서 적출해낸 부품들

 

일단 테스트 코드를 만들어 보겠습니다.

미리 준비하였던 신호들을 본인의 제품에 맞는 명령으로 보내면 되는데요. 일단 예제 중에 IRremote --> IRsendDemo 예제를 기본으로 시작을 하도록 하겠습니다.

 

해당 예제에 라이브러리를 추가(스케치--> 라이브러리 포함하기--> IRremote) 하고 아래와 같이 코딩을 합니다.

#include <boarddefs.h>
#include <IRremote.h>
#include <IRremoteInt.h>
#include <ir_Lego_PF_BitStreamEncoder.h>

/*
 * IRremote: IRsendDemo - demonstrates sending IR codes with IRsend
 * An IR LED must be connected to Arduino PWM pin 3.
 * Version 0.1 July, 2009
 * Copyright 2009 Ken Shirriff
 * http://arcfn.com
 */
/*
 * tv (NEC)
 * power :      0x2DF10EF, 32
 * input :      0x2DFD02F, 32
 * chnnel up :  0x2DF00FF, 32
 * chnnel dn :  0x2DF807F, 32
 * volumn up :  0x2DF40BF, 32
 * volumn dn :  0x2DFC03F, 32
 * OK :         0x2DF22DD, 32
 * sw up :      0x2DF02FD, 32
 * sw dn :      0x2DF827D, 32
 * sw left :    0x2DFE01F, 32
 * sw right :   0x2DF609F, 32
 * 
 * android tv (NEC)
 * power :      0x807F02FD, 32
 * sw up :      0x807F6897, 32
 * sw dn :      0x807F58A7, 32
 * sw left :    0x807F8A75, 32
 * sw right :   0x807F0AF5, 32
 * sw OK :      0x807FC837, 32
 * back :       0x807F9867, 32
 * volumn up :  0x807F18E7, 32
 * volumn dn :  0x807F08F7, 32
 * 
 * 
 * lonpoo speaker
 * power :      0x40BF807F, 32
 * volumn up :  0x40BF50AF, 32
 * volumn dn :  0x40BFD02F, 32
 * bt :         0x40BFA05F, 32
 * bt esc :     0x40BF906F, 32
 * opt :        0x40BF20DF, 32
 * 
 */

#include <IRremote.h>

const int pw_tv = 4;
const int pw_and = 5;
const int pw_spk = 6;

IRsend irsend;

void setup()
{
  pinMode(pw_tv, INPUT_PULLUP);
  pinMode(pw_and, INPUT_PULLUP);
  pinMode(pw_spk, INPUT_PULLUP);
  pinMode(13, OUTPUT);
}

void loop() {

  int s_pw_tv = digitalRead(pw_tv);
  int s_pw_and = digitalRead(pw_and);
  int s_pw_spk = digitalRead(pw_spk);

  // tv 전원 역할 스위치 (평상시는 HIGH 입니다)
  if (s_pw_tv == HIGH) {
    digitalWrite(13, LOW);
  }else{
    digitalWrite(13, HIGH);
    for (int i = 0; i < 3; i++) {
     irsend.sendNEC(0x2DF10EF,32);     
      delay(40);
    }
  }
  
  // 안드로이드 tv box 전원 역할 스위치 (평상시는 HIGH 입니다)
  if (s_pw_and == HIGH) {
    digitalWrite(13, LOW);
  }else{
    digitalWrite(13, HIGH);
    for (int i = 0; i < 3; i++) {
     irsend.sendNEC(0x807F02FD,32);     
      delay(40);
    }
  }
  
  // 사운드 바 전원 역할 스위치 (평상시는 HIGH 입니다)
  if (s_pw_spk == HIGH) {
    digitalWrite(13, LOW);
  }else{
    digitalWrite(13, HIGH);
    for (int i = 0; i < 3; i++) {
     irsend.sendNEC(0x40BF807F,32);     
      delay(40);
    }
  }
	
	delay(50); //5 second delay between each signal burst
}

3개의 버튼을 사용할 것이므로 3개의 입력핀을 설정한뒤 버튼이 눌렸을 때 LED (13번 핀, 아두이노 기본 LED) 를 켜주면서 3차례에 걸쳐 신호를 보내도록 코딩하였습니다.

저는 모든 신호의 제품이 NEC 로 확인 되었기 때문에 라이브러리의 sendNEC 라는 명령을 통해 신호를 보내 주었습니다.

보드는 아래와 같이 구성하였습니다.

3개의 버튼은 각각 4,5,6 번에 연결, IR LED 는 3번에 연결 하였다.

코드를 보면 알겠지만 버튼은 모두 GND 에 연결하고 PULLUP 상태를 만들어 준 뒤 LOW 일때 동작하도록 하였습니다. 이렇게 하면 아두이노 내부 저항에 의해 별도의 외부 저항 없이 버튼 연결이 가능하게 됩니다.

IR LED 의 경우 방향이 정해져 있는데 어짜피 다이오드 이므로 신호가 나오는지 체크해서 신호가 안나오면 방향을 반대로 뒤집어 주면 됩니다. 

신호가 나오는지는 핸드폰 카메라로 확인이 가능한데요, 신호가 나올때는 보라색으로 깜박깜박 합니다. 아래 영상을 참고하시면 됩니다.

 

IR 신호가 정상적으로 나온다면 불이 깜빡이는 것을 확인할 수 있다. 

 

해당 보드로 TV 와 안드로이드 TV box, 사운드바가 정상적으로 동작하는 것을 확인할 수 있습니다.

실제 LED 는 TR 없이 쇼파에서 문제 없이 동작하였으니 이제 실제 결과물을 만들어야 할 시간입니다.

동작할 기능은 많은데 모든 기능에 대하여 버튼을 만들어 넣을 수 는 없을 것 같으니 머리를 좀 써야 할 시간입니다. 이제부터 본격적인 개발이 시작된다고 할까요?

 

이거 뭐 껌이네요. 여러분도 한번 도전해 보시기 바랍니다.

 

자 대망의 만능 리모콘 만들기 3편은 다음 포스트에서 소개해 드리겠습니다.

 

그럼 이만~

 

2020/06/17 - [DIY/Arduino] - [DIY] 아두이노로 통합(만능) 리모콘 만들기 3/3

 

[DIY] 아두이노로 통합(만능) 리모콘 만들기 3/3

아두이노로 통합(만능) 리모콘 만들기 3/3 이라 쓰고 왠지 이번 포스트에서 끝나지 않을 것 같은 느낌이 강하게 듭니다. 지난 포스트에서 아두이노 IR 센서를 이용하여 신호를 해킹하고 또 필요한

diy-dev-design.tistory.com

2020/05/26 - [DIY/Arduino] - 0.96 inch OLED 디스플레이 구동하기

 

0.96 inch OLED 디스플레이 구동하기

가끔 아두이노로 무엇인가를 만들어 보려고 하다보면 디스플레이가 있으면 하는 생각이 들때가 있습니다. 아두이노는 작은 컴퓨터이기는 하지만 모름지기 컴퓨터라 한다면 입력장치 - 중앙처��

diy-dev-design.tistory.com

2019/06/27 - [DIY/Arduino] - 아두이노를 이용한 간단한 화분 자동 물주기 시스템

 

아두이노를 이용한 간단한 화분 자동 물주기 시스템

얼마뒤면 베트남으로 가족 여행을 떠날 예정입니다. 7박이나 하고 올 예정이므로 집을 비우기 전 준비해야 할 것들이 많습니다. 이것저것 여행준비를 하던 찰나 베란다에 내어 둔 화분이 걱정이

diy-dev-design.tistory.com

2022.03.26 - [DIY] - [DIY]무민양품 USB 데스크팬 수리하기

 

[DIY]무민양품 USB 데스크팬 수리하기

사무실을 이사하다보면 각 종 쓰레기가 쏟아져 나오는데 간혹 쓸만한 것들이 나오는 경우가 있습니다. 오호~ 이건 무민 양품의 USB 선풍기! 뭐 고장 났으니 버렸겠지 싶어 지나쳐 가다가 ... 고치

diy-dev-design.tistory.com

 

만능 리모컨 만들기 최종 완성본은 아래에서 확인하세요~

2022.08.16 - [DIY/Arduino] - [DIY] 아두이노로 통합(만능) 리모콘 만들기 (최종)

 

[DIY] 아두이노로 통합(만능) 리모콘 만들기 (최종)

만능 리모컨을 만들기로 해놓고 정말 많은 시간이 흘렀습니다. 2019.11.10 - [DIY/Arduino] - [DIY] 아두이노로 만능 (통합) 리모콘 만들기 1/3 2019.11.10 - [DIY/Arduino] - [DIY] 아두이노로 통합(만능) 리모콘..

diy-dev-design.tistory.com

 

반응형
반응형

도대체 영화 한편 보려면 몇개의 리모콘을 사용하는지... 

TV 전원을 켜기위해 TV 리모컨을 찾아야 하고 안드로이드 TV 셋톱을 켜기위해 안드로이드 TV 리모콘을 역시 찾아야 하며 막상 틀었더니 소리가 어마무시하게 커서 블루투스 사운드 바 소리를 줄이기 위해 사운드바 리모콘을 찾아서 겨우 소리를 줄였더니 TV 기본 사운드가 너무 큰 탓인지 사운드바의 볼륨 1에서도 소리가 커서 하나를 줄이면 소리가 아얘 나지 않는 상황이어서 다시 TV 리모콘을 집어 들고 TV 볼륨을 줄였으나 알고보니 영상의 기본 사운드가 너무 크게 인코딩 되어 있어 안드로이드 TV 에서 볼륨 레벨을 낮추기 위하여 다시 안드로이드 TV 리모콘을 집어들어야 하는 상황이 거짓말처럼 하루건너 발생하는 우리집이다.

아놔..

끌때도 TV 끄고, 안드로이드 TV 끄고 사운드바 끄고... 

이걸 한번에 통합하는 건 불가능 한 것일까..

 

그래서 한번 만들어 보기로 하였습니다.

아두이노를 이용하여 IR 리모콘 신호를 해킹하는 예제를 언젠가 본적이 있어 구글링을 하여 보았습니다.

제가 참고한 블로그는 아래 블로그 입니다.

https://blog.naver.com/opusk/220984753138

 

[Application] 아두이노로 리모컨 IR신호 확인하기 - IRremote Library를 활용한

적외선 통신 (Infrared Ray Communication)먼저, IR통신에 대해 알아보면 말그대로 적외선 통신입니다...

blog.naver.com

좀 어려운 말들이 있기는 하지만 복잡할 것 없이 그냥 따라 해보시면 됩니다.

 

일단 신호를 따서 필요한 신호를 모두 모은 뒤에 버튼을 달고 버튼을 누를 때 마다 필요한 신호를 보내면 끝.

 

일단 신호를 따야 겠죠?

필요한 준비물은 아래와 같습니다.

  • 아두이노 나노
  • IR 리모콘 수신부 LED (저는 고장난 비디오 데크 에서 뜯어냈습니다. 재활용품 버리는날 주워왔었죠)
  • 10 k 정도의 저항 (얼마인지는 모르겠네요)
  • IR 라이브러리 (링크)

 

요렇게 하고 시작해 보겠습니다.

먼저 위에 링크 라고 되어있는 페이지로 가봅시다.

https://github.com/z3t0/Arduino-IRremote

가서보면 아래와 같은 페이지가 보이는데요. 우측에 'clone or download' 를 누르시게 되면 압축 파일로 다운로드가 됩니다.

 

 

이렇게 다운로드 받은 zip 파일을 압축을 해제한 뒤 아두이노 library 폴더에 넣으시면 되는데요. library 폴더는 아두이노가 설치된 폴더에도 있고 내문서에 Arduino 폴더에도 있습니다. 

저는 설치폴더 찾아 들어가기가 귀찮아서 그냥 내문서에 있는 라이브러리 폴더에 넣었습니다.

 

이제 아두이노 IDE 를 실행해 봅니다. (아두이노 IDE가 켜진 상태에서 라이브러리를 추가 하였다면 모두 종료하고 재실행하면 라이브러리 및 예제가 추가되어 있는 것을 알 수 있습니다)

그런다음 예제에서 IRremote 로 들어가고 그 하위에 있는 IRrecvDumpV2 를 선택해 줍니다.

 

그러면 아두이노 IDE 에 예제 파일이 보이게 되는데요.

여기에서 추가로 설정해주어야 하는 것이 입력 핀 번호 수정입니다.

아래 그림과 같이 int recvPin 을 2번으로 변경해 줍니다. 2번은 나노의 D2 에 해당됩니다.

 

끝으로 IR 라이브러리를 추가해주어야 하는데요.

요렇게 해주면 준비가 완료 됩니다.

이제 브래드 보드에 리모콘 수신부를 연결하도록 하겠습니다.

아래 사진과 같이 연결해 줍니다.

 

적외선 수신부의 가장 좌측 다리를 D2 에 연결하고 10k 저항을 통해 5v 를 인가합니다.

가운데 다리는 GND 에 우측 다리는 5v 에 직접 연결합니다.

준비 완료.

 

아두이노 IDE 에 작성해놓은 코드를 업로드 합니다. 

혹시 처음 하시는 분은 아래와 같이 설정을 하면 됩니다.

COM 포트는 사용자마다 다를거에요. 어떤 USB 포트에 연결했느냐에 따라 다른 번호가 나타납니다. 

USB 를 연결한뒤에 새로 나타나는 포트가 아두이노가 연결된 포트입니다.

 

이제 신호를 테스트 해보겠습니다.

USB 를 연결하고 시리얼 모니터를 켠 후 리모콘의 버튼을 눌러 필요한 신호를 보내봅니다.

무려 세개의 리모콘이 이번 통합 대상이다.
리모콘을 수신부를 향해 눌러준다.

그럼 시리얼 모니터에 아래와 같이 출력이 됩니다.

시리얼 모니터를 통하여 입력된 정보

중요한 것은 여기 빨간색 네모 박스의 내용입니다.

입력된 신호는 NEC 라는 회사의 프로토콜이며 0x807FAA55 라는 HEX 코드가 바로 입력된 신호인 것이죠. 위에 rawData 라는 정보가 보이는데 이는 NEC, LG 등과 같이 프로토콜의 제조사를 알 수 없는 리모콘신호인 경우 rawData 를 직접 보내기 위하여 사용되는 정보 입니다. 

저의 경우에는 NEC 라는 제조사의 신호임이 확인되었기 때문에 해당 제조사에서 약속한 0x807FAA55 라는 값을 보내면 됩니다. 

가끔 시리얼 모니터에 제조사 정보에 UNKNOWN 이라고 나오는 경우가 있습니다. 실제로 제품 사용중에 리모콘을 눌러도 신호가 전달이 되지 않는 경우가 있죠? 이렇게 신호가 불분명하게 입력되는 경우입니다. 다시 눌러보면 정확히 들어오게 되는데 이때 들어온 정보를 사용하면 됩니다.

이렇게 3개의 리모콘을 이용하여 제가 모아본 신호는 아래와 같습니다.

 * tv (NEC)
 * power :      0x2DF10EF, 32
 * input :      0x2DFD02F, 32
 * chnnel up :  0x2DF00FF, 32
 * chnnel dn :  0x2DF807F, 32
 * volumn up :  0x2DF40BF, 32
 * volumn dn :  0x2DFC03F, 32
 * OK :         0x2DF22DD, 32
 * sw up :      0x2DF02FD, 32
 * sw dn :      0x2DF827D, 32
 * sw left :    0x2DFE01F, 32
 * sw right :   0x2DF609F, 32
 * 
 * android tv (NEC)
 * power :      0x807F02FD, 32
 * sw up :      0x807F6897, 32
 * sw dn :      0x807F58A7, 32
 * sw left :    0x807F8A75, 32
 * sw right :   0x807F0AF5, 32
 * sw OK :      0x807FC837, 32
 * back :       0x807F9867, 32
 * volumn up :  0x807F18E7, 32
 * volumn dn :  0x807F08F7, 32
 * 
 * 
 * lonpoo speaker
 * power :      0x40BF807F, 32
 * volumn up :  0x40BF50AF, 32
 * volumn dn :  0x40BFD02F, 32
 * bt :         0x40BFA05F, 32
 * bt esc :     0x40BF906F, 32
 * opt :        0x40BF20DF, 32
 * 
 */

저는 위의 신호들을 하나의 리모콘을 통하여 보낼 수 있도록 만들어볼 생각입니다.

그럼 다음 포스트에서 실제 신호를 보내는 예제를 만들어 보겠습니다.

 

그럼 이만~

 

그 다음 이야기는 아래 포스트에서 확인해 주세요

https://diy-dev-design.tistory.com/66

 

[DIY] 아두이노로 통합(만능) 리모콘 만들기 2/3

먼저 포스트에서 아두이노를이용하여 다양한 리모콘의 신호를 해킹하는 방법을 알아 보았습니다. https://diy-dev-design.tistory.com/65 [DIY] 아두이노로 만능 (통합) 리모콘 만들기 1/3 도대체 영화 한편 보려..

diy-dev-design.tistory.com

 

2020/06/17 - [DIY/Arduino] - [DIY] 아두이노로 통합(만능) 리모콘 만들기 3/3

 

[DIY] 아두이노로 통합(만능) 리모콘 만들기 3/3

아두이노로 통합(만능) 리모콘 만들기 3/3 이라 쓰고 왠지 이번 포스트에서 끝나지 않을 것 같은 느낌이 강하게 듭니다. 지난 포스트에서 아두이노 IR 센서를 이용하여 신호를 해킹하고 또 필요한

diy-dev-design.tistory.com

2020/05/26 - [DIY/Arduino] - 0.96 inch OLED 디스플레이 구동하기

 

0.96 inch OLED 디스플레이 구동하기

가끔 아두이노로 무엇인가를 만들어 보려고 하다보면 디스플레이가 있으면 하는 생각이 들때가 있습니다. 아두이노는 작은 컴퓨터이기는 하지만 모름지기 컴퓨터라 한다면 입력장치 - 중앙처��

diy-dev-design.tistory.com

2020/07/01 - [DIY] - 미니 테슬라코일 만들기 - 알리 DIY KIT

 

미니 테슬라코일 만들기 - 알리 DIY KIT

어렸을적 위대한 발명가, 과학자 하면 단연 손꼽는 일인자가 있었는데 누군가 물어본다면 100 이면 100 다 토마스 에디슨을 떠올릴 것입니다.. 그런데 커보니 에디슨은 발명가나 과학자라기보다��

diy-dev-design.tistory.com

 

만능 리모컨 만들기 최종 완성본은 아래쪽을 참고해 주세요

2022.08.16 - [DIY/Arduino] - [DIY] 아두이노로 통합(만능) 리모콘 만들기 (최종)

 

[DIY] 아두이노로 통합(만능) 리모콘 만들기 (최종)

만능 리모컨을 만들기로 해놓고 정말 많은 시간이 흘렀습니다. 2019.11.10 - [DIY/Arduino] - [DIY] 아두이노로 만능 (통합) 리모콘 만들기 1/3 2019.11.10 - [DIY/Arduino] - [DIY] 아두이노로 통합(만능) 리모콘..

diy-dev-design.tistory.com

 

반응형
반응형

아두이노는 정말 놀라운 하드웨어가 아닐수 없죠. 우리가 생각하는 이런건 자동으로 해주는거 없나? 이런게 자동으로 되면 좋을텐데... 이런걸 왜 자동으로 안하는거야?? 같은 대부분의 자동화 장치, 기계장치를 원하면 직접 만드는것을 가능하게 끔 만드는 것을 아두이노는 가능하게 합니다.

사실 아주 많은 부분에서 자동화 또는 그런 류의 장치들은 간단한 로직만을 필요로 합니다. 실제 상용화된 제품에서는 보다 많은 기능과 안정성, 수익성, 또 제품화 되기 위한 여러가지 복합적인 문제들을 안고 있기 때문에 제품화 되지 않는 것일 뿐이죠.

오늘 포스트에서 올릴 내용은 바로 "아두이노를 이용한 수경재배" 에 대한 내용입니다.

회사에서 DIY 동호회를 운영중인데 이번 프로젝트가 바로 수경재배가 되시겠습니다.

수경재배를 하기 위하여 알아야 할 것들이 많이 있는데 사실 원천적으로는 복잡하지 않습니다. 식물이 생장에 필요로 하는 물과 양분을 공급해주는것. 바로 이게 핵심인데 그 과정에서 발생되는 인간의 개입 정도를 좀 줄여보는게 이번 프로젝트의 핵심이라 할 수 있겠습니다.

양액은 인터넷에서 저렴하게 구입이 가능하므로 일정 농도로 희석된 양액을 통에 담아두고 수경 재배용 통에 항상 부족하지 않게 공급하는 장치를 만들어 볼 계획입니다.

실제 수경재배를 위하여는 양액의 Ph 농도와 전해질의 양의 측정이 매우 중요하지만 이번 프로젝트를 위하여 엄청난 수확량을 기대한다거나 하는 것이 아니기 때문에 우리는 자동으로 부족한 물 (양액)을 보충해 주어 인간의 개입을 최소화 하는데에 의의를 두기로 하였습니다.

뎃글, 공감 은 블로그 작성자에게 큰 힘이 된답니다. 
도움이 되었다 생각되시면 클릭!!  부탁드려요~

 

 

먼저 필요한 구조를 고민해 보았습니다.

- 식물이 자라기 위한 pot 이 필요할 것이고 pot을 잠기에하여 양액을 공급할 수 있는 양액통이 필요할 것입니다. 그리고 보조 양액통을 만들어 주 양액통에 양액이 부족해지면 보조 양액통으로 부터 양액을 보충해주는 역할을 하도록 만들어 볼 계획입니다. 그리고 양액통 내 용존 산소량을 확보하기 위하여 일정 수준의 공기 주입을 지속적으로 해주는 장치 정도를 만들어 볼 계획입니다.

필요한 재료는 아래와 같은데 이전에 소개했던 자동으로 식물에 물을 공급하는 장치와 거의 유사합니다.

  • 아두이노 나노
  • DRV8883 듀얼 모터 드라이버
  • 미니 DC 펌프 x 2 (양액 공급용, 공기 펌프용)
  • 수위 측정용 스위치 (물주기에 사용했던 센서는 사용불가... 나중에 설명하겠습니다)
    • 일반적인 리미트 스위치
  • 어항용 공돌 (다이소 500원 짜리)
  • 어항용 호스, 링거 호스
  • 양액통 2개,
  • 싱크대 수채구멍용 거름망

 

전체적으로 보면 아래와 같은 구조입니다. 배선은 제외한 그림이기는 한데요.

두개의 DC 모터를 컨트롤 하기 위한 모터 드라이버와 아두이노 나노, 중간에 수량 체크를 위한 스위치 (탁구공의 부력을 이용합니다) 그리고 각각의 식물의 재배를 위한 pot (하수구 거름망), 두개의 양액 통이죠.

 

 

스위치에 의하여 상단 박스의 수량이 부족하면 계속해서 우측 펌프가 물을 끌어 올릴 겁니다. 물이 차오르고 필요한 높이가 되면 탁구공을 매단 수량 체크용 스위치가 신호를 보낼것이고 해당 신호를 받아 필요한 양이 보충되게 되면  양액 공급펌프는 멈출것입니다.

좌측의 에어 공급 펌프는 계속해서 상단 양액내 공기를 뿜어 넣음으로써 산소량을 높이는 역할을 하게 됩니다.

반응형

이론적으로는 간단하죠?

 

네 실제로 간단 합니다. 아래와 같은 형태로 제작이 되었다고 보시면 됩니다. 

중간에 CDS 로 보이는 부품은 네..  CDS 가 맞습니다. CDS가 필요한 이유는 뒤에 설명을 드리도록 하겠습니다.

수경재배 양액 / 산소 공급장치 구조도

 

 

프로토 타입으로 제작한 이미지를 보시죠

탁구공을 접착하여 제작한 부력스위치

탁구공의 부력이 스위치를 작동시키이에 약간 부족함이 있어 날개 길이를 조금 연장하였습니다. 

 

탁구공을 이용하여 제작한 부력스위치를 장착한모습
부력스위치와 워터펌프 테스트 중인 모습

 

 

양액내 산소공급을 위한 공돌, 다이소에서 500원에 구입하였다.

 

 

씨앗으로 부터 시작된 모종. 동그라미 친 두개의 모종이 재배용으로 결정 되었다.

 

통에 양액을 채우고 모종도 pot 에 옮겨 담아 준뒤 시스템을 가동하였습니다.

물과 공기가 예상데로 잘 공급이 되는 것을 확인하였으나 모터 구동시 소음이 좀 있는 편이고 에어의 경우 하루종일 동작하게 되어 있어 모터에 열이 좀 나는 것이 문제가 될 것 같았습니다. 워터 쪽 모터도 물을 끌어올려야하는 상황이다보니 부하가 걸려 모터에서 열이 많이 발생되었습니다. 

기본적인 수경재배 시스템이 완성된 모습

나중에 브래드보드 대신 만능기판에 보드를 옮길 계획인데 해당 작업이 완료되면 별도의 포스팅을 진행할 예정입니다.

 

그리고 위에 잠깐 소음에 대하여 언급을 했었는데요. 아무래도 사무실에서 키우는 것이다 보니 소음으로 인한 민원이 바로 올라오더군요. 시끄럽다고 꺼달라고 하더라구요. - -;;

 

그래서 긴급 조치로 CDS 를 하나 달아서 사무실 조명이 모두 꺼지면 동작하도록 간단한 기능을 추가하였습니다. 이제 아무도 없는 밤에 열심히 산소를 공급해주게 될겁니다.

빨간 동그라미 부분이 CDS 되시겠다.

 

앞으로 진행할 내용은 아래와 같습니다.

  • 만능기판에 기자재 납땜으로 부착
  • 현재 상태는 보기에 좋지 않으므로 케이스를 제작
  • 식물이 자라는것을 타임랩스로 보여주기 위한 카메라 장착 (안쓰는 블랙박스 활용)

준비가 되면 포스팅을 하도록 하겠습니다.

 

아두이노 소스코드는 아래를 참고해 주세요.


const int waterLevelCheckerSW = 2;
const int airPumpSpeedControlPin = A3;
const int motor_waterpump = 9; // pin 9 (D9) is waterpump action
const int motor_airpump = 10; // pin 10 (D10) is waterpump action
const int lightSensor_pin = A4;

int airPumpSpeed = 0;
int buttonState = 0;
int count = 0;
int lightTotal = 0;

void setup() {

  pinMode(13, OUTPUT); // status LED
  pinMode(motor_waterpump, OUTPUT);  // water pump pin
  pinMode(motor_airpump, OUTPUT); // air pump pin
  pinMode(waterLevelCheckerSW, INPUT_PULLUP); // water level checker switch pin
  
  Serial.begin(9600);
}


void loop() {

  count++;
  
  //air pump speed control
  airPumpSpeed = analogRead(airPumpSpeedControlPin); // 
  int pumpSpeed = map(airPumpSpeed, 0, 1023, 0, 128);
  
  
  int lightValue = analogRead(lightSensor_pin);
  
  lightTotal += lightValue;
  
  if (count == 10)
  {
    // the air pump run when dark
    if (lightTotal/10 < 1 )
    {
      analogWrite(motor_airpump, pumpSpeed);
    }else{      
      analogWrite(motor_airpump, 0);
    }
    lightTotal = 0;
    count = 0;
  }
  
  
  Serial.print(airPumpSpeed);
  Serial.print("/");
  Serial.print(pumpSpeed);
  Serial.print("///");
  Serial.println(lightValue);
    
  // water pump control
  buttonState = digitalRead(waterLevelCheckerSW);
  if (buttonState == HIGH ) 
  { 
    digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW       
    // stop water pumping
    digitalWrite(motor_waterpump, LOW);

  } 
  else 
  {    
    digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)    
    // start water pumping when dark
    if (lightValue < 2 )
    {
      analogWrite(motor_waterpump, 128);           
    }
  }
  
  delay(10);              // wait for a second

}

 

 

 

끝으로 수위 측정 센서관련하여 

참고로 물주기에 사용하였던 수분 측정 센서(알리익스프레스에서 구입)의 경우 양극단에서 흐르는 전류에 의해 물의 전기분해가 이루어 진 탓인지 센서 표면에 수많은 기포가 발생되었습니다.

센서 측정부에 발생된 기포. 전기분해가 이루어 졌다면 산소와 수소일 것으로 판단 된다.

결국 센서 표면중 한쪽면이 심하게 부식이 되며 양액을 변질 시키는 것을 보고 사용할 수 없겠다 판단 하였습니다.

하루정도 시간이 흐르자 양액의 색깔이 심하게 변하고 센서 표면도 변질 되었다.

결국 수위 측정은 위 사진에서 보여드린 탁구공을 이용하는 방법을 사용해야 했습니다.

토양의 수분 측정시에도 유사한 현상이 있어 결국 센서를 빼버렸었는데요. 해당 센서를 사용한다면 측정 주기를 매우 길게 하고 측정 시간을 짧게 여러번 하여 결과값을 사용하는 방식을 이용하는 것이 좋을 것으로 판단됩니다. 해당 센서를 이용하여 토양의 수분을 측정하려는 목적이시라면 연속해서 측정을 진행하지 마시고 일정 주기를 두고 실제 측정 시간은 짧게 측정을 하는 것이 센서의 수명에 도움이 될것으로 생각됩니다. 참고하세요~

 

뎃글, 공감 은 블로그 작성자에게 큰 힘이 된답니다. 
도움이 되었다 생각되시면 클릭!!  부탁드려요~

 

 

참고로 이전에 사용하였던 해당 센서를 이용한 토양 수분 측정을 통한 자동으로 물주는 시스템 관련 포스트

https://diy-dev-design.tistory.com/16

 

아두이노를 이용한 간단한 화분 자동 물주기 시스템

얼마뒤면 베트남으로 가족 여행을 떠날 예정입니다. 7박이나 하고 올 예정이므로 집을 비우기 전 준비해야 할 것들이 많습니다. 이것저것 여행준비를 하던 찰나 베란다에 내어 둔 화분이 걱정이 되었습니다. 출발..

diy-dev-design.tistory.com

2020/06/29 - [DIY/Arduino] - C# 에서 아두이노로 시리얼 통신 하기

 

C# 에서 아두이노로 시리얼 통신 하기

카테고리를 c# 으로 해야 할지 Arduino 로 해야할지 조금 고민이 되는 포스트 입니다. 음.... arduino 로 하는게 좋겠네요. 따지고 보면 C# 으로 만든 어플이 중요한게 아니라 아두에노에서 시리얼 통신

diy-dev-design.tistory.com

 

반응형

+ Recent posts