반응형

자 어느새 목표했던 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

 

반응형
반응형

뭔가 목표가 생기면 과정이 분명해 진다.

RC 카에 헤드트래킹을 이용한 FPV 를 구현하려는 목표가 생기고나니 과정을 잘 정리하는게 필요하겠다 싶어 포스트를 남깁니다.

우선 첫번째 스텝으로 2축으로 제어가 가능한 서보모터 마운트를 만들고 서보모터를 제어하는 것인데요, 이미 언젠가 사용하겠지 싶어서 구입해 둔 2축 서보모터 마운트가 있기에 이 부분은 간단히 해결될 거라 생각했습니다.

 

바로 중국에서 구입한 이런 제품이죠.

9g 서보모터를 이용하여 바로 연결이 가능하도록 나온 제품이고 약간 헷갈리기는 하지만 조립이 어렵지는 않습니다. 

그런데 너무 쉽게 생각한게 오산이었을까요?

일단 어떻게 연결해도 약간 제가 생각하는 머리의 움직임과 괴리감이 있었습니다. 

사람은 목이라는 놀라운 구조에 의해 상하좌우 회전이 거의 동일한 한 점에서 이루어 집니다. 심지어는 틸트까지 되죠, 3축이 하나의 구조에서 이루어지는 놀라운 구조가 아닐수 없습니다. 

제가 구입하였던 2축 서보 마운트는 그런 개념에서 완전히 빗나가 있더군요. 일단 x, y 축의 회전 축이 상당히 어긋나 있다는 점이 첫번째 문제점이었고 두번째로는 기본 각도가 수평에서 시작한다는 점 이었습니다.

위의 사진에서 보이는 것처럼 상단 평평한 면이 이미 최대 상향(y축) 각도인데요, 말하자면 더이상 고개를 들거나 또는 내리는건 불가능한 상태 입니다. 물론 90도를 꺽은 상태에서 카메라를 부착하는 방법도 있지만 그렇게 되면 축에서 더욱 멀어지게 되므로 이미 상당한 수준의 거북목이 진행된 것처럼 되어 버립니다. 무슨 목을 쭉 앞으로 뺀 거북이처럼 회전이 되게 되는 것이죠.

세번째 문제는 전체 부품의 부피가 너무 크다는 것인데요, RC 카 운전석 안에 장착을 해야 하는데 이런 저런 부품이 너무 자리를 많이 차지하더군요, 음...

반응형

 

2축 서보 연결을 위한 브라켓 제작하기

그래서 아주 간단한 구조에 부피도 아주 작은 2축 회전축을 만들어 보기로 하였습니다.

제 포스트를 보시는 분도 제가 만든 것처럼 구현하시면 축의 위치를 거의 같은 선상에 두고 사람의 머리처럼 제어되는 축을 만드실 수 있을 거에요.

 

서보모터2개를 연결해주는 부품 제작
최종 회전각도를 고려하여 수정

저는 3D 프린터를 이용하여 핵심 부품을 출력하였습니다만, 구조상 그냥 L 자 꺽쇠를 이용하여 만드시는 것도 가능합니다. 필요하신 크기로 가공만 하면 되는 것이죠. 

 

프린팅 중

저는 부품의 부피를 최소화 하기 위하여 서보모터의 고정 부품 안쪽으로 브라켓이 장착되도록 제작을 하였습니다.

 

출력된 프린트 물에 서보모터 부작을 위한 부품 장착

 

9g 서보모터에 부착된 상태
다른각도에서 본 모습

 

이렇게 되면 상단의 서보모터는 Y 축(상하회전)을 제어하고 하단의 서보모터는 X 축(좌우회전)을 제어하게 되는데요, 우리의 목의 구조를 봐도 보통 회전은 귀를 중심으로 머리통이 상하로 움직이고 좌우 회전은 목 전체가 회전되므로 상대적으로 제가 만든것과 유사한 구조로 움직이는 것을 알 수 있습니다. 물론 사람의 목은 대단히 유연하고 훌륭한 관절이어서 상하회전이 꼭 한군데서 이루어지는 것은 아닙니다만 유사한 움직임이 구현되기는 합니다.

제가 만든 것처럼 브라켓의 상하 길이를 작게 만들게 되면 상하 회전 움직임에 제약이 있기는 합니다. 서보모터에 부딪힐 수 있기 때문인데요, 사람의 목이 상하로 180도를 움직이지 않는 것을 고려해보면 크게 문제될 것은 없어 보입니다.

만약 서보모터의 동작 범위 전체를 커버하기를 원하시면 브라켓의 상하 길이를 서보모터의 회전 반경보다 크게 제작하면 문제 없이 동작하게 됩니다.

 

아두이노와 연결을 해보자

자 이제 아두이노로 잘 제어가 되는지 확인해 보도록 하겠습니다.

2축 제어를 위하여 간단하게 2축 아날로그 조이스틱을 연결하고 제어를 해봅니다.

연결은 아래와 같이 하시면 됩니다.

소스코드는 아주 간단합니다.

아날로그 신호를 받아 서보모터를 움직이는 기본 소스코드 그대로 약간만 응용하면 구현이 가능합니다.

다만 저처럼 상하 움직임에 제한이 있는 경우는 서보로 가는 신호에 제한을 주어야 합니다.

#include <Servo.h>

Servo myservo_LR;   // streering servo
Servo myservo_UD;  // 2speed gear box servo

int pin_servo_LR = 9;
int pin_servo_UD = 10;

int pin_x = A3;
int pin_y = A4;
int angle_x = 512 ;
int angle_y = 512 ;
void setup() {

  myservo_LR.attach(pin_servo_LR);
  myservo_LR.write (90);
  myservo_UD.attach(pin_servo_UD);
  myservo_UD.write (90);
}

void loop() {
  angle_x = analogRead(pin_x);
  angle_y = analogRead(pin_y);
  
  angle_x = map(angle_x, 0, 1024, 30, 150);
  angle_y = map(angle_y, 0, 1024, 60, 120);

  myservo_LR.write (angle_x);  
  myservo_UD.write (angle_y);

  delay(10);
  
}

90도를 기준 각도로 하여 상하는 60~120도, 좌우는 30~150도의 움직임을 범위로 하였습니다.

자 이제 테스트를 해볼까요?

 

 

 

잘 동작하네요.

 

일단 첫번째 관문은 통과한 셈 입니다.

다음 편에는 mp6050 을 이용하여 회전한 각도 만큼 서보모터를 움직이는 방법을 알아보도록 하겠습니다.

아래 링크된 글을 이용하여 바로 보실 수 있습니다.

 

그럼 이만~

 

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

 

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

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

diy-dev-design.tistory.com

 

2022.01.31 - [DIY/Arduino] - [아두이노]NRF24L01을 이용하여 무선으로 mpu6050 신호 전달하기

 

[아두이노]NRF24L01을 이용하여 무선으로 mpu6050 신호 전달하기

자 어느새 목표했던 RC headtracking FPV 만들기의 종착역을 다가가고 있습니다. 앞서 소개해 드렸던 2축 서보마운트 제작 및 서보컨트롤과 mpu6050 을 이용하여 2축 서보모터를 제어하는 내용은 잘 보

diy-dev-design.tistory.com

 

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

 

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

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

diy-dev-design.tistory.com

 

2020.10.28 - [DIY/Arduino] - 아두이노로 아이방 스탠드 개조하기 - 자동 꺼짐 기능

 

아두이노로 아이방 스탠드 개조하기 - 자동 꺼짐 기능

아이가 유치원에서 너무나 예쁜 스탠드를 만들어 왔습니다.. AAA 건전지가 들어가는 예쁜 스탠드. 스텐드 옆면에 예쁘게 그림을 그려서 본인이 자는 방에 가져다 두고는 "오늘부터 얘가 나를 지

diy-dev-design.tistory.com

 

반응형
반응형

아이가 유치원에서 너무나 예쁜 스탠드를 만들어 왔습니다.. AAA 건전지가 들어가는 예쁜 스탠드.

스텐드 옆면에 예쁘게 그림을 그려서 본인이 자는 방에 가져다 두고는

"오늘부터 얘가 나를 지켜줄꺼야~"

ㅎㅎ

너무나 귀엽지 않습니까?

 

저녁이 되어 불을 켜보니 

ㄷㄷㄷ 오던 잠도 깰것 같은 블루라이트가 펑펑

헐랭

정말 오던 잠도 달아날 것 같은 푸르스름한 조명이더군요.

그래서 바꿔주기로 하였습니다.

  • 일단 조명 색상은 따뜻한 색상으로 
  • 밤새 켜져 있지 않도록 자동으로 소등될 것

요렇게 간단한 요구사항을 작성한뒤에 바로 실행에 들어갑니다.

 

필요한 재료는 간단합니다.

  • Attiny85 (아두이노를 사용하기는 아깝잖아요?)
  • 1W LED (5000K 미만)
  • 50uf 전해 콘덴서 (작은걸로)
  • 전선 조금.

 

간단하죠?

우선 자동으로 소등이 되어야 하고 소등이 될때는 서서히 어두워 지는 것이 좋을 것 같아 먼저 테스트를 해보기로 하였습니다.

우선 Attiny 에 대하여 간단히 알아 보아야 하는데요. attiny 는 아주 작은 마이크로프로세서로 칩안에 부트로더, 디지털핀, 아날로그핀, PWM 핀을 모두 갖춘 환상적인 부품입니다. 전압만 적당히 주면 칩하나로도 왠만한 동작이 가능한 녀석입니다. 나중에 이녀석을 이용한 작품들을 좀 소개해 드리겠습니다.

 

소스코드를 업로드 하기 위하여 별도의 프로그래머가 필요하긴 하지만 아두이노를 이용하여 간단하게 프로그래머로 사용할 수 있는 방법이 있으니 걱정마시고 진행하시면 됩니다.

저는PWM 핀에 일정 시간이 지나면 서서히 전압을 감소 시킨뒤 0V 까지 떨어지면 Attiny85를 sleep 모드로 진입시켜 초절전상태가 되도록 할 계획입니다. 

 

 

아두이노 ISP 만들기

아두이노를 ISP 와 같이 사용하여 Attiny85 를 프로그래밍 할 건데요. 간단합니다.

아두이노 예제에 있는 ArduinoISP 를 열어 주세요.

아두이노 기본 예제에 들어있는 ArduinoISP 소스

해당 소스를 본인의 아두이노 설정에 맞게 세팅한 뒤 업로드를 합니다. 

그런 다음 10uf 전해 콘덴서를 하나 준비해주시고

Arduino reset --  콘덴서의 + ,
Arduino GND -- 콘덴서의 - 를

연결한 뒤 Arduino 와 Attiny85의 각 핀을 아래와 같이 연결합니다.

  • Arduino 5V   --  ATTiny85 Pin 8 (Vcc)
  • Arduino GND -- ATTiny85 Pin 4 (GND)
  • Arduino Pin 13 -- ATTiny85 Pin 8 (SCK)
  • Arduino Pin 12 -- ATTiny85 Pin 7 (MISO)
  • Arduino Pin 11 -- ATTiny85 Pin 6 (MOSI)
  • Arduino Pin 10 -- ATTiny85 Pin 1 (Slave Reset)

attiny85 핀 맵

전 브래드 보드에 매번 연결하는 것이 귀찮아서 간단한 보드를 하나 만들었습니다.

arduino isp for attiny85

위 사진과 같이 만들어서 소켓에 아두이노 나노와 attiny85 를 끼웠다 뺐다 할 수 있게 했습니다.

참고로 사진에 보이는 attiny85 는 smd 타입으로 dip 타입보다 작고 가격도 조금 저렴합니다. 저는 smd 타입에 8핀 보드를 붙여서 사용하고는 합니다.

 

일단 이러면 코드 테스트를 위한 준비가 완료 되었습니다.

 

 

코딩을 해보자!

// this code for enter sleep mode
#include <avr/sleep.h>
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

//setting variables
int ledPin = 0;    // LED connected to digital pin 9
float timer = 2400;
float maxBrightness = 255.0;
float a = 0;
float curX = 0;
float curY = 0;

void setup() {
  // 그래프의 기울기 값 계산
  a = maxBrightness / (timer*timer);
}

void loop() {
  curX++;
  curY = a * (curX*curX);
  
  analogWrite(ledPin, maxBrightness - curY);
  
  if (maxBrightness - curY < 1 )
  {
    analogWrite(ledPin, 0);
    system_sleep(); // sleep 모드 진입
  }
  delay(1000);
}

  void system_sleep()
  {
    cbi(ADCSRA, ADEN);                    // switch Analog to Digitalconverter OFF
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // sleep mode is set here
    sleep_enable();
    sleep_mode();                         // System sleeps here
    sleep_disable();                      // System continues execution here when watchdog timed out 
    sbi(ADCSRA, ADEN);                    // switch Analog to Digitalconverter ON
  }

 

코드 위아래에 좀 눈에 익지 않은 코드가 있죠? 그 부분이 바로 sleep 모드 진입을 시키기 위한 코드입니다. attiny85 는 슬립 모드에 진입하면 놀라울 만큼 전기를 먹지 않기 때문에 아주 유용한 기능이 아닐 수 없습니다. sleep 모드에서 깨우기 위하여는 reset pin 에 GND 를 스위치 등으로 순간 연결해 주거나 전원을 껏다가 켜면 다시 최초 상태로 실행이 됩니다.

위에 타이머 부분이 전체 조명이 켜져있는 시간이되겠습니다. 다만 정확한 시간 조정은 안되고 대충 감으로 잡아야 하더군요. 현재 설정은 2400 초 인데요. 대략 40분 내외의 시간이 흐르면 소등됩니다. (한 30분 지나면 점점 어두워집니다)

loop 의 아래쪽에 현재 아날로그 출력값이 1보다 작아지면 sleep 모드로 진입하는 코드가 있고요.

그게 전부입니다. ㅋ

이제 attiny85 에 업로드를 해야하는데요. 

먼저 라이브러리가 없다면 추가를 해주어야 겠죠?

구버전용 라이브러리는 직접 받으셔야 하고요. 신버전은 json 을 추가해주고 설치를 하면 됩니다.

아래 경로에서 라이브러리 설치에 대한 내용을 확인하시면 됩니다.

highlowtech.org/?p=1695

 

Programming an ATtiny w/ Arduino 1.6 (or 1.0)

This tutorial shows you how to program an ATtiny45, ATtiny85, ATtiny44 or ATtiny84 microcontroller using the Arduino software. These are small, cheap ($2-3) microcontrollers that are convenient for…

highlowtech.org

 

보드는 attiny > attiny85 1Mhz 로 선택해 주시고요.

ISP 는 Arduino as ISP 를 선택해 주시면 됩니다.

그리고 업로드 고고

 

 

Hardware 개조 타임

이제 조명을 뜯어서 기존의 LED 를 떼어내고 Attiny 와 LED 를 부착해줄 차례 입니다.

먼저 배터리 를 제거한다.

당연한 이야기지만 먼저 배터리를 제거해 줍니다.

 

상단 뚜껑 및 케이스 분해

상단의 흰색 갓은 위로 잡아 당기면 그냥 빠집니다. 꽉 끼워져 있기는 한데 본드로 붙어있는 것은 아니더군요. 

아래쪽 나사를 모두 풀어주고 분리하면 위와 같은 상태입니다. 심플하네요.

스프링이 도망가지 않게 살살 열어 줍니다.

 

거의 8000K 인 듯한 LED. 덩치도 크다.

어쨌든 문제의 LED는 제거합니다.

크기도 엄청 크네요.

 

LED 를 잘라낸 모습

스위치에 붙어 있는 저항도 필요 없으니 떼어낼겁니다.

 

Attiny 에 콘덴서를 하나 달아주었다.

Attiny 에 배터리 (약 4.5v) 를 연결하니 전압이 불안정한지 동작이 원하는데로 되지 않아 굴러다니는 전해 콘덴서를 하나 붙여 주었습니다. 저는 50uf 를 달아 주었는데요. 적당히 아무거나 달아도 될 듯 합니다. 크기가 작아 소켓보드 아래에 쏙 들어가네요. ㅎㅎ

 

 

LED 는 1W(??) cree 제품. 

예전에 사두었던 LED 가 적당해 보여 달아주었습니다. 불빛도 너무 노랗지도 않고 방열판도 있으니 적당하다 생각되었습니다.

본딩을 해서 붙인뒤 고정을 위해 일단 테이프로 붙여 둡니다.

 

최종 단계 진입. 스위치에 저항을 떼어내고 결선을 위한 전선을 매달기 직전

자 이제 거의 다 되었습니다.

스위치에 있는 저항을 테어내고 전선을 연결할 겁니다. 스위치로 - 가 연결되고 스위치의 다른 한쪽이 attiny 의 GND 에 연결되면 됩니다.

빨간선은 + 이므로 attiny 의 5V 에 연결합니다.

테스트 성공

쨔잔~ 네 예쁘게 잘 들어옵니다.

Attiny 는 3M 양면 테이프로 붙여주면 완성입니다.

 

배선 확인용 사진

참고로 위와 같이 배선하시면 안됩니다. 잘못 배선한 사진인데요. LED 로 가는 굵은 빨간선이 사진상 오른쪽 맨 아래 핀에 연결이 되어야 합니다. ㅜㅜ

아래 사진에는 다시 제자리에 납땜을 했습니다.

납땜까지 완료된 모습

자 이제 완성입니다.

배선에 약간 오류가 있기는 했지만 간단한 문제로 바로 해결하였습니다.

 

최종 결과물은?

DIY 가 완료 되었으나 처음과 똑같은 모습

ㅋㅋ 처음과 똑같네요. 다시봐도 그림이 너무 예쁩니다. 

어쨌든 이제 아이방에 놔주기만 하면 됩니다.

 

따뜻하고 예쁜 조명으로 다시 태어나다.

음~ 마음에 드는군요.

아이도 좋다며 매일 켜고 자고 있습니다. ㅎㅎ

자동으로 소등이 되니 배터리도 많이 먹지 않고요. 

만약 사용해보고 배터리를 많이 먹는다 싶으면 저항을 하나 달아줄 계획입니다. 저항은 아주 작은 용량의 저항이면 됩니다. 

 

 

스탠드 조명 튜닝 완료

자 간단한 작업으로 싸구려 같은 장난감에서 고급스러운 조명 색상에 타이머 기능까지 있는 멋진 조명으로 탈바꿈 하였습니다. 여러분도 한번 도전해 보시면 어떨까요?

 

그럼 오늘의 포스팅을 마칩니다. ~

 

2022.01.31 - [DIY/Arduino] - [아두이노]NRF24L01을 이용하여 무선으로 mpu6050 신호 전달하기

 

[아두이노]NRF24L01을 이용하여 무선으로 mpu6050 신호 전달하기

자 어느새 목표했던 RC headtracking FPV 만들기의 종착역을 다가가고 있습니다. 앞서 소개해 드렸던 2축 서보마운트 제작 및 서보컨트롤과 mpu6050 을 이용하여 2축 서보모터를 제어하는 내용은 잘 보

diy-dev-design.tistory.com

2019/10/13 - [DIY] - [DIY] 아이방 수면등 만들기 (feat. 아이그림)

 

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

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

diy-dev-design.tistory.com

 

2020/05/17 - [DIY] - [재활용] 고장난 LED 바 수리하기 > 화장대 조명 만들기

 

[재활용] 고장난 LED 바 수리하기 > 화장대 조명 만들기

저의 보물창고 바로 폐 배터리, 형광등 수거함 입니다. 지나는 길에 기웃거려 보니 LED 바가 버려진게 있더군요. LED 바 금액 자체가 비싼건 아니지만 사실 일부러 돈주고 살만큼 효과적인 조명기

diy-dev-design.tistory.com

 

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

 

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

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

diy-dev-design.tistory.com

 

2020/10/29 - [DIY] - [DIY] 오래된 다목적 랜턴 LED 램프, 충전식으로 교체하기

 

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

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

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

 

반응형
반응형

아두이노, 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 가 될 수 있습니다. 하루하루 지루해진 일상에 단물 같은 취미를 한번 선물해 보시는건 어떨까요?

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

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

반응형
반응형
이번에는 트랙사스 UDR 의 라이트 셋을 만들어 볼 계획입니다. 무선으로 켜거나 끌수 있도록 할 예정이며 전면 헤드라이트 1쌍. 후면 브레이크등 1쌍. 운전석 내 실내등 및 주요 장비에 LED 등을 만들어볼 계획입니다. 그리고 이번 DIY 의 꽃은 운전석과 조수석에 디지털 디스플레이를 장착해볼 계획입니다. 그동안 어떤 트랙사스 UDR 에서도 볼수 없었던 튜닝이죠.

먼저 필요한 장비를 소개하겠습니다.
Arduino Nano x2
NRF24L01 x2
SSD1306 0.96inch OLED (i2c) x2
DC2DC voltage ragulater
Push 스위치
Toggle switch
LED (white SMD, 0.25w) x 28
LED (white DIP, 0.25w) x 8
LED (green DIP) x1
LED (RED DIP) x10
PCB (17x9, 0.25mm) x2

모든 부품은 알리익스프레스에서 구입하였으며 배터리는 버려진 무선 진공청소기에서 적출하였습니다.
먼저 가장 기본이 되는 하드웨어 부분 부터 만들어 보겠습니다.

헤드라이트 모듈 만들기
우선 디자인은 UDR 고유의 헤드라이트 모양을 살려서 만들기로 결심하였습니다.
시중에 판매하는 라이트 셋은 UDR 만의 특징을 살린 디자인이라고 보기는 어려웠기 때문에 이번 DIY 가 더 특별한것 같습니다.
먼저 도면을 만들어봤습니다. 그리고 LED 실장 위치도 배치해봤습니다.
배터리를 7.4v 2s 로 사용할 계획이기 때문에 LED 2개씩 직렬로 연결하였고 입력부는 5.5~6v 사이로 전압을 강하항 입력해 줄 계획입니다. 전원 입력은 알리에서 주문한 직류 전원 장치인데 입력 출력이 모두 DC 이며 중간에 가변저항을 돌리면 출력 전압이 변경됩니다. 저는 5.5v 에서 6v 정도로 맞출 계획입니다. 아무래도 방열을 하기 어려운 구조이기 때문에 정격 전압이 가해졌을때 열 때문에 문제가 될 수도 있겠다 싶었습니다.
중요한것은 LED 는 극성이 있기 때문에 부착하는 방향을 잘 맞추어 부착을 해야 합니다. SMD led 의 경우 위에서 봤을때 거무튀튀한 부분이 한쪽으로 쏠려 있는데요. 검은 점이 있는 쪽이 -극이 되겠습니다.

우선 만능기판에 LED 를 부착하여 보았습니다. 만능기판은 17x9 크기로 잘라 주었습니다.

SMD 부품의 경우 손으로 잡고 납땜을 하는 것이 불가능하기 때문에 연고처럼 짜서 사용할 수 있는 납을 이용하여 먼저 부착할 위치에 짜준후 LED 를 핀셋을 이용하여 살짝 얹어 놓으면 됩니다. 그런다음 열풍기를 200도 정도로 하여 가열해주면 납이 녹아 붙게 됩니다. 열풍기가 없으면 인두로 끝부분을 살짝 대고 있으면 납이 부글부글 끓으며 녹아 달라붙게 됩니다.
자 부착이 완료 되었고 배선도 끝냈습니다.

그럼 전원을 연걀하고 테스트를 해봐야겠습니다.

쨔잔

와 예쁘네요.
그런데 빛이 너무쎄서 주변까지 다 빛이 퍼지네요. 라이트 주변에 케이스를 만들어 빛이 옆으로 퍼지는것을 좀 막아 주어야 하겠습니다.
그리고 발열 테스트 하다가 문제가 발생되었습니다. 세번째 사진을 보면 유독 led 하나가 푸르고 밝게 빛나는게 보이시나요? LED 가 불량인지 해당 지점에서 열이 상당히 많이 나는지 시간이 지나면서 연기가 피어오르며 LED 가 사망하는 사건이 발생하였습니다. 아무래도 해당 LED 가 불량인것도 같고 직렬로 연결하면서 그라운드 선을 골고루 연결해주지 않아 전류가 과하게 인가된게 아닌가 추측을 해봅니다. LED를 교체하고 그라운드를 골고루 뿌려볼 예정입니다.

무선 송수신기 연결은 다음 포스트에서 

 

반응형

+ Recent posts