[아두이노] 아두이노 RC카 자율주행(장애물피하기)

IOT/아두이노|2019. 5. 29. 09:00

[아두이노] 아두이노 RC카 자율주행(장애물피하기)



오늘은 몇일동안 포스트한 장애물을 피하는 자율주행 테스트 해보는 시간으로 내용을 채우고자 합니다. 좀 더 정교한 자율주행 코딩을 할까도 생각 했지만 처음은 단순한 장애물을 피하는 자율주행을 보여드리는 것이 좋을 것 같아서 종합 코딩은 간단하게 표현하여 자율주행의 의미를 전달하고자 합니다.


우선 초음파 아두이노 RC카를 사진으로만 보면 정확히 구조를 이해할 수 없으니 간단히 fritzing으로 회로도를 보여드리고 간단히 장애물피하기 자율주행 코딩을 보여드리겠습니다.

1. 초음파센서 아두이노 RC카 회로도


  • 준비물 : L293D Motor Shield, DC Motor 2개, Servo Motor 1개, 초음파센서 1개, 외부전원 2개, 아두이노우노
  • 내용 : A4(Trig), A5(Echo)로 초음파센서에 연결하고 Servo Motor는 왼쪽 상당애 Servo Pin(-,+,Sig)에 연결한다. DC Motor 2개는 M3, M4에 연결합니다.



위 사진의 아두이노 RC카의 회로도는 아래 그림과 같이 구성되어 있습니다.


위 그림에서 외부 전원을 두개로 분리해서 공급합니다. 참고포 점퍼핀 덮개를 빼주면 아두이노와 Motor Shield의 전원을 나눌 수 있습니다. 그림이 좀 복잡해 보일 수 있지만 내용에 자세히 핀 연결 설명이 되어 있으니깐 해당 위치에 부품의 선을 연결해주시면 됩니다.

2. 코딩



이전 post에서 장애물을 감지하는 방법과 장애물을 피하는 패턴을 살펴 보았습니다. 여러개를 설명했는데 그중에 각각 한개씩 간단한 표현으로 코딩을 하겠습니다.

[기본소스]

#include <AFMotor.h>

AF_DCMotor motor1(3);
AF_DCMotor motor2(4);

void setup() {
  motor1.setSpeed(200);
  motor2.setSpeed(200);
  motor1.run(RELEASE);
  motor2.run(RELEASE);
}

void loop() {  

  초음파센서 장애물 감지;

  motor1.run(FORWARD); //전진
  motor2.run(FORWARD);
  delay(1000);
}

1) 서보모터+초음파센서 장애물 감지


[코딩 순서]

  • 40~140도 사시를 10도씩 회전하면서 초음파센서로 장애물를 측정한다.
  • 장애물 감지 거리를 15cm 미만일 때 감지로 간주한다.
void loop() {
  //초음파센서 회전
  servo.write(angle);
  delay(50);
  
  int distance = sonar.ping_cm();

  //장애물 감지
  if(distance>0 && distance<15){
     movePattern(); 
     //Serial.println(distance);
     motor1.run(FORWARD);
     motor2.run(FORWARD);  
  }   
  //회전 각도
  if (angle == 140) state = -10;    
  else if (angle == 40) state = 10; 
  angle += state;
}

2) 장애물 피하기 패턴


[코딩 순서]

  • 장애물 감지시 0.5초 동안 후진한다.
  • 전방 90도를 기준으로 90이상이면 우회전 90미만 좌회전 시킨다.
  • 장애물 피하기 회전이 끝나면 다시 전진 주행한다.
  • 초음파센서는 방향은 90도로 위치시킨다. (90도 기준으로 다시 10도씩 변화를 시키기 위해서)
void movePattern(){
     motor1.run(BACKWARD);
     motor2.run(BACKWARD);
     delay(500);   
    if(angle>=90){
      motor1.run(FORWARD);
      motor2.run(BACKWARD);
      delay(500);   
    }
    else{
      motor1.run(BACKWARD);
      motor2.run(FORWARD);
      delay(500);   
    }
    angle=90;
    servo.write(angle);
    delay(100);
}

3) 기본소스+장애물감지소스+장애물피하기 패턴소스


위 세가지 소스를 합치면 다음과 같습니다. 참고로 몇가지 다듬어서 코딩을 했습니다.

#include <AFMotor.h>
#include <Servo.h> 
#include <NewPing.h>

AF_DCMotor motor1(3);
AF_DCMotor motor2(4);

Servo servo;
const int servoPin = 10;
const int TRIG = A4;
const int ECHO = A5;
const int MAX_DISTANCE = 100;
NewPing sonar(TRIG, ECHO, MAX_DISTANCE);

int speed=200;
int state = 10;
int angle = 90;

void setup() {
//  Serial.begin(9600);
  motor1.setSpeed(speed);
  motor2.setSpeed(speed);
  motor2.run(RELEASE);
  motor2.run(RELEASE);

  servo.attach(servoPin);
  servo.write(angle);
  delay(1000);

  //Start
  motor1.run(FORWARD);
  motor2.run(FORWARD);  
}

void loop() {
  //초음파센서 회전
  servo.write(angle);
  delay(50);
  
  int distance = sonar.ping_cm();

  //장애물 감지
  if(distance>0 && distance<15){
     movePattern(); 
     //Serial.println(distance);
     motor1.run(FORWARD);
     motor2.run(FORWARD);  
  }   
  //회전 각도
  if (angle == 140) state = -10;    
  else if (angle == 40) state = 10; 
  angle += state;
}
void movePattern(){
     motor1.run(BACKWARD);
     motor2.run(BACKWARD);
     delay(500);   
    if(angle>=90){
      motor1.run(FORWARD);
      motor2.run(BACKWARD);
      delay(500);   
    }
    else{
      motor1.run(BACKWARD);
      motor2.run(FORWARD);
      delay(500);   
    }
    angle=90;
    servo.write(angle);
    delay(100);
}

setup()함수에서 초기화 작업을 하는데 젤 먼저 DC기어모터의 속도를 200으로 세팅하고 각 Motor를 RELEASE(해제 or 정지)상태로 둔다. 그리고 나서 주행전 Servo Motor의 초기 위치는 90도 회전 시켜 초음파센서가 전방90도를 바라보게 한다. 그 다음 1초동안 준비 상태로 있다가 FORWARD(전진)명령으로 아두이노 RC카가 출발하게 됩니다.

loop()함수는 angle(각도)에 위치로 회전을 0.05초 간격으로 합니다. 회전이 될 때 초음파센서(sonar)의 거리를 측정하여 distance에 저장합니다. 이 값이 0보다 크거나 15cm보다 작은 값일 때 장애물을 감지한 걸로 감주합니다. 0의 값은 MAX_DISTANCE의 제한최대거리 값을 넘게 되면 0으로 반환되기 때문에 0의 값이 주행 중에 일정 간격으로 발생하게 됩니다. 그렇기 때문에 0보다 크거나 15cm보다 작은 값으로 if문으로 체크하는 조건식을 만든 것이죠. 이렇게 15cm 미만일 때 장애물 감지 판정을 내리게 되면 if문 이하 문장을 수행 하게 됩니다. 이때 장애물 피하기 패턴을 코딩하면 됩니다 movePattern()함수로 장애물을 피하는 동작을 코딩해 놨는데 이 방식으로 피하고 나면 다시 motor1, motor2은 FORWARD(전진)으로 게속 자율주행을 하게 됩니다.

여기서, movePattern()함수를 살펴보면 먼저 0.5초 동안 후진을 하고 후진 한 후에 Servo Motor가 회전한 현재 각(angle)의 값이 90보다 큰거 아니면 작은 가로 나누게 되는데 이때 90도보다 크면 좌측에 장애물을 감지했기에 우회전 명령문을 0.5초 동안 90도 우회전을 하게 되고 90도보다 작으면 우측에 장애물을 감지했기에 좌회전 명령문으로 0.5초 동안 90도 좌회전을 하게 됩니다. 그리고 다시 전진 주행을 하기 전에 초음파센서의 방향 위치를 초기화 상태로 전방 90도 위치로 회전시켜놓습니다.

대충 전체의 소스에 대해 설명을 했는데 이해하셨는지 모르겠네요.

만약, 다른 방식으로 접근하고 싶다면 장애물 감지 코딩 부분을 다른 감지 주행패턴을 선택해서 붙여 넣으시면 되고요. 장애물 피하기 패턴도 movePattern()함수 내부의 코딩을 원하는 패턴으로 만들어서 넣으시면 됩니다.

3. 결과


그냥 전방의 장애물을 손으로 표현해서 손으로 막을 때 이 손을 아두이노 RC카가 장애물로 감지하고 초음파센서가 감지한 각도에 따라 좌/우회전을 하는 짧게 촬영한 영상입니다.


이렇게 간단하게 장애물 피하기로 자율주행을 출발합니다.

위 코딩을 좀 더 정교하게 하려면 다음과 같이 해야 합니다.

void loop(){
  //장애물 감지
  if(distance>0 && distance<15){
     movePattern(); 
  }  
}   
void movePattern(){
     motor1.run(BACKWARD);
     motor2.run(BACKWARD);
     delay(500);   
    if(angle>=90){
      motor1.run(FORWARD);
      motor2.run(BACKWARD);
      delay(회전각도시간값);   
    }
    else{
      motor1.run(BACKWARD);
      motor2.run(FORWARD);
      delay(회전각도시간값);   
    }
    motor1.run(FORWARD);
    motor2.run(FORWARD);    
        
    angle=90;
    servo.write(angle);
    delay(100);
}

여기서, 회전각도시간값은 여러분들이 가지고 있는 아두이노 RC카의 바퀴힐과 속도를 기준으로 1초(1000)에서 delay값 100을 기준으로 몇도 회전되는지 체크했다가 정확하게 회전각도시간값을 넣어주세요. 그리고 회전을 시킨 후 바로 전진 주행으로 바꿔 주시면 좀 더 정확하게 회전 후 전진 주행이 됩니다. 사실 촬영할 때는 loop()함수에다가 넣어서 간단히 movePattern()함수에서는 회전 주행만 표현했는데 마지막 초음파센서의 90 회전 초기화에서 0.1초 동안의 회전이 더 일어나는 단점을 가지게 되더군요. 그러면, 회전각도시간값으로 정확히 회전을 시키더라도 초기화 0.1초동안 회전이 더 발생하는 문제가 생길 수 있습니다. 단순하게 장애물 피하기 코딩에서는 0.1초 차이는 티도 안나기 때문에 무시하고 촬영을 했지만 post을 작성하면서 이 코딩부분을 수정해서 올릴까도 했지만 촬영은 이전 소스를 기반으로 주행 시켜서 그냥 약간 부족한 상태로 수정 없이 올리게 되었네요. 하지만 이렇게 추가로 이야기 하고 넘어가야 할 것 같아서 좀 더 내용을 채우게 되었네요.

motor1.run(FORWARD);
motor2.run(FORWARD);   

아무튼 이 명령이 어느 위치에 있느냐에 따라 결과는 달라지게 됩니다. 단순한 코딩은 무시 할 정도로 표현해도 되지만 정교한 코딩에서는 명령문의 위치에 따라 딜레이 0.1초 차이도 회전 오류각을 발생할 수 있기 때문에 코딩을 완료되더라도 완료된 코딩의 흐름을 머리속에서 상상하면서 문제가 없는지 체크를 꼭 하셔야 합니다.

마무리


위 코딩을 보면 그렇게 어려운 코딩이 아닙니다. 재밌는 것은 이 원리를 이해하시면 또다른 다양한 표현을 할 수 있습니다. 장애물을 피한다면 아두이노 RC카가 도망다니는 RC카로 표현 할 수 있습니다. 가령, 전방의 특정 각도의 장애물이 감지되면 그 장애물의 각도을 기준으로 180도 회전해서 뒤로 도망가는 RC카를 만들 수 있습니다. 인간과 RC카가 이 원리로 접근하게되면 인간이 아두이노 RC카를 추적하고 아두이노 RC카는 도망자가 됩니다.

아니면, 주행을 규칙적으로 지그재그로 표현을 한다면 지그재그 주행을 할 때 전방의 초음파센서가 장애물을 감지하면 진행 방향에 있는 장애물을 피하기 위해서 진행 라인의 주행을 포기하고 다음 라인의 주행을 이여간다면 로봇청소기와 같은 주행을 할 수 있게 됩니다.

또 다른 상상을 하면 아두이노 RC카를 랜덤 주행을 일정 범위안에서 수행되게 해놓고 있다가 초음파센서의 장애물 감지 범위에 인간이 다가갔을 경우 일정 거리까지 아두이노 RC카가 따라오도록 주행패턴을 만들게 되면 어떤 느낌의 아두니오 RC카가 될까요. 바로 애완동물 아두이노 RC카로 표현이 가능해 집니다.

간단히 장애물 감지와 장애물 피하는 동작의 원리를 곰곰히 생각해보니깐 방금 이야기 했던 상상들이 떠오르더군요. 위에서 설명한 주제들의 원리는 전부 동일한 원리 입니다. 장애물 감지와 장애물 피하기에서 장애물 피하기를 역발상으로 장애물 따라오기로 생각의 관점을 바꾸면 이렇게 또 다른 결과가 나올 수 있습니다. 재밌는 아두이노 RC카를 만들 수 있겠죠.

여러분들도 한번 이 원리를 기반으로 나는 어떤 상상을 할 수 있을지 상상의 나래를 펼쳐보셨으면 합니다.


댓글()

[아두이노] 아두이노 RC카 초음파센서로 장애물 피하기 패턴

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

[아두이노] 아두이노 RC카 초음파센서로 장애물 피하기 패턴



지난 시간에는 초음파센서로 장애물을 감지하는 방법들에 대해서 간단히 살펴 보았습니다. 오늘은 감지 했을 때 피하는 방법에 대해 한번 이야기 하고자 합니다. 아두이노 RC카는 주행 중 전방에 장애물이 감지하면 후진을 할 것인지 좌회전을 할 것인지 아니면 우회전을 할 것인지를 결정해야 합니다. 여러분들은 어떤 선택을 하실 건지 한번 머리속에서 상상을 해보세요. 현재 여러분들이 서 있는 위치에서 앞으로 걷다가 앞에 벽이 나타난다면 나는 어느 방향으로 벽을 피해 이동할지를 상상하며 한번 걸어보세요. 벽이 나타나고 벽을 피해서 방향전환하고 나서 다시 걷는 상상을 하거나 아니면 실제 걸어보시면서 걷는 과정을 기록해 주세요. 이 행동의 기록이 아두이노 RC카의 주행 패턴이 됩니다. 아두이노 RC카는 이 주행 패턴을 통해서 자율주행을 하게 됩니다. 어떤 느낌인지 아시겠지요.


지금부터 장애물 감지 센서인 초음파센서를 지난 시간에 배운 방식 중에 하나를 선택해서 실험해야 하는데 위 사진을 보면 서보모터로 회전되는 초음파센서로 전방에 하나 배치했기 때문에 이 방식을 통해 장애물을 감지 하고 장애물을 피하는 패턴들을 만들겠습니다.

1. 초음파센서로 장애물 감지 시 피하기 패턴



위의 선행 학습을 꼭 하시고 오셔야 합니다. 그래야 아래 내용을 쉽게 이해 하실 수 있습니다.

1) 전방 장애물 피하는 기본 방향 패턴


지난 시간에 몇가지 초음파센서로 장애물 감지에 대해서 살펴 보았습니다. 장애물 감지 중 첫번째 회전 초음파센서로 장애물 감지를 기준으로 한번 패턴을 만들어 보도록 할까요.

주행 중 회전 초음파센서로 장애물을 감지 상황은 다음과 같습니다.


주행 중에 전방에 장애물이 감지하는 한다고 상상해 봅시다. 이 상황에서 여러분들은 어떤 주행 패턴을 만드시겠습니까?


좌회전 아니면 우회전 그것도 아니면 후진 중 어떤 주행패턴을 하실지 머리속에서 그려 보세요.

#include <AFMotor.h>

AF_DCMotor motor1(3); //왼쪽 모터
AF_DCMotor motor2(4); //오른쪽 모터

두개의 DC기어모터를 제어할 Motor가 있을 때

  • motor.run(FORWARD) : 전진
  • motor.run(BACKWARD) : 후진
  • motor.run(RELEASE) : 해제

모터의 기본 동작함수인데 이 함수를 통해서 장애물을 피하는 패턴을 만듭니다.

if(장애물 감지){
  장애물 피하기 패턴;
}

장애물 감지 시 제자리에서 좌/우 회전을 시킨 후 회전 된 방향으로 주행을 계속 진행하도록 주행패턴을 만들어 보도록 합시다. 단, 90도 회전은 0.5초동안 회전하고 180도 회전은 1초동안 회전이라고 가정하고 상상 코딩을 합니다. Motor의 속도에 따라 시간에 따른 회전각은 달라집니다. 개념을 잡기 위해서 0.5초은 90도 이고 1초가 180도라고 상상한 것이기 때문에 이대로 실제 코딩하시면 안됩니다. Motor 속도와 시간에 대한 회전각도를 수학적으로 계산하시거나 단순하게 속도에 따른 시간별 각도를 일일히 체크해서 각도를 가늠하시거나 둘 중 하나를 선택하셔서 나중에 실제 만들어 보세요.


[90도 우회전 후 전진]

motor1.run(FORWARD);
motor2.run(BACKWARD);
delay(500);   
motor1.run(FORWARD);
motor2.run(FORWARD);

[90도 좌회전 후 전진]

motor1.run(BACKWARD);
motor2.run((FORWARD););
delay(500);   
motor1.run(FORWARD);
motor2.run(FORWARD);

이렇게 하면, if문으로 장애물 감지되면 좌/우로 90도 회전 시킨 후 그 방향으로 전진하게 됩니다. 장애물 피하기 주행 패턴이 단순하죠.

다음으로, 뒤로 회전 한다면 좌/우로 회전 방향은 상관 없이 180도 회전을 시키면 됩니다.


[180도 뒤로 회전 후 전진] 우방향을 기본으로 잡았을 경우

motor1.run(FORWARD);
motor2.run(BACKWARD);
delay(1000);   
motor1.run(FORWARD);
motor2.run(FORWARD);

대충 이런 느낌이 됩니다. 1초가 정확히 180도 회전이 아닙니다. 가정한 것이고 DC기어모터 회전 속도에 따라서 회전각은 달라집니다. 가정하에서 코딩한 것이기 때문에 감안하시고 보세요.

이렇게 해서 간단히 장애물을 피해서 주행을 할 수 있게 되었습니다.

2) 전방 장애물을 피할 때 문제점


방금 세가지 피하는 방식에서 문제가 발생할 수 있습니다. 제자리에서 아래 그림처럼 DC기어모터가 회전하면 바퀴를 기준으로 몸체가 돌아가게 됩니다. 몸체가 돌아갈 때 장애물과의 충돌이 발생 할 수 있습니다.


제자리 좌/우회전을 시키거나 뒤로 180도 회전 시킬때 장애물 거리 감지가 짧을 경우 아래와 같은 상황이 발생 할 수 있습니다.

[우회전]


[180도 회전]



위 그림같은 상황 때문에 주행 중 진행 방향에 장애물이 감지 되었을 때 다음 장애물 피하기 동작 패턴을 만들려면 감지한 거리과 아두이노 RC카의 몸체의 크기를 고려해야 합니다. 그리고 피하는 행동을 할 때에도 주변 환경의 장애물과의 충돌 상황도 고려해야 합니다.

[해결 방법]

  • 첫번째, 장애물 감지 거리를 좀 더 길게 잡으면 해결 됩니다. 아두이노 RC카가 충분히 회전 할 수 있는 거리만큼을 장애물 감지 거리로 설정해 놓으면 장애물 감지 후 바로 회전을 하더라도 정상적으로 회전 할 수 있습니다.
  • 두번째, BACKWARD(후진)을 일정 거리만큼 시킨 뒤 회전을 시키면 됩니다. 하지만, 이 경우에는 또 다른 문제점으로 후진 후 회전시 아래 그림처럼 측면에 장애물이 있을 경우 충돌 가능성이 있습니다.

위 그림처럼 문제가 생기면 후면에 초음파센서를 부착하여 후진 시 뒷면에 장애물을 감지하면 해결 할 수 있겠죠. 그런데 이부분은 코딩화 하지 않겠습니다 계속 상황을 만들어 가면 이야기가 끝도 없기 때문에 의미만 전달하기 위해서 이정도로 마무리 합니다.

장애물을 피하는 주행 패턴을 만들 때 처음에 제가 설명한 세가지 주행 패턴을 기반으로 상상코딩을 한 뒤에 주행 패턴을 변화시키십시오. 만약에, 자신이 만든 주행패턴에 문제가 생기면 그 문제 해결을 위한 주행 패턴을 만들고 기존 소스에 덧붙여가며 코딩을 늘려가시면 문제를 충분히 해결 할 수 있게 됩니다. 처음은 단순한 주행패턴으로 머리속에서 정리하고 그 주행 패턴에서 하나씩 새로운 주행 패턴을 만들어가면 좀 더 정교한 장애물 피하기 패턴을 만들 어 가면 자율주행하는 재밌는 아두이노 RC카를 만들 수 있습니다.

2. 회전 초음파센서로 장애물 감지 후 피하기 패턴 - I



위 그림처럼 초음파센서는 전방에 일정 각도로 좌/우 회전을 하면서 각도마다 거리를 측정하게 됩니다. A, B, C로 세부분으로 나누었지만 만약, 40~140도 사이의 각도를 10도 간격으로 하면 11개의 각도의 장애물 감지 거리를 측정하게 됩니다. 11개의 각도가 나올 때 전방 중앙을 90도 각도로 하면 좌/우 5개의 각도로 장애물 감지 거리가 측정 할 수 있습니다.

이렇게 각도별로 장애물이 감지 되었을 때 어떻게 상상코딩을 할까요. 정중앙을 90도기준으로 장애물이 감지된 초음파센서 각도에서 반대 방향으로 회전시켜 장애물을 피하는 간단한 방법이 있습니다.

위 그림에서 B가 90도 기준으로 A은 140도 방향이고 C은 40도방향이라고 한다면 A방향은 좌측이고, C방향은 우측으로 나눌 수 있습니다. 이때, A방향의 장애물이 감지 되었을 때 우측으로 진로 C방향으로 진행하도록 합니다. C방향 우측에서 장애물이 감지되면 좌측으로 진로 C방향으로 진행하게 하면 간단한 피하기 패턴을 만들 수 있습니다.

[A방향 장애물 발견 시 90도 우회전 후 전진]

motor1.run(FORWARD);
motor2.run(BACKWARD);
delay(500);   
motor1.run(FORWARD);
motor2.run(FORWARD);

[B방향 장애물 발견 시 90도 좌회전 후 전진]

motor1.run(BACKWARD);
motor2.run((FORWARD););
delay(500);   
motor1.run(FORWARD);
motor2.run(FORWARD);

이렇게 진로 방향을 바꾼다면 장애물 감지는 아래와 같이 코딩을 할 수 있겠죠.

if(장애물거리<15){
  if(초음파각도>90){
      [A방향 장애물 발견 시 90도 우회전 후 전진];
  }
  else{
      [B방향 장애물 발견 시 90도 좌회전 후 전진];
  }
}

이렇게 단순하게 표현 할 수 있습니다.

추가로

전방, 좌/우측 방향의 장애물이 나타 났을 때 각도별 상황 구간을 잡아 놓고 아두이노 RC카를 좀 더 세부적으로 아래와 같이 if문으로 나누어서 각도별로 회전 주행 패턴을 만들 수 있습니다. 아래는 3구간으로 나눴지만 더 나눌 수 있고 또는 각 나눈 구간에서 다시 if조건문으로 해당 구간의 각도 범위에서 좀 더 각도를 나눠서 제어 방식을 변경할 수 도 있습니다. 이것은 여러분들의 상상력에 달려 있으니깐 한번 상상해 보세요.

if(A상황일때) 우회전;
else if(B상황일때) 좌/우회전 결정;
else if(C상황일때) 좌회전;

3. 회전 초음파센서로 장애물 감지 후 피하기 패턴 - II


위에서는 단순하게 장애물이 발견 된 시점에서 제자리에서 방향전환을 시키고 그다음 주행을 진행 했습니다. 뭔가 주행 중에 멈춰서 회전을 하기 때문에 끊기는 느낌으로 주행이 됩니다.


위 그림처럼 부드럽게 반원을 그리 듯 곡선 주행을 시키는 주행 패턴을 만들고 싶지 않으신가요. 이 방식은 어떤 주행패턴 일까요.

지난 [아두이노] 아두이노 2륜 RC카 주행 패턴 실험에서 주행패턴을 여러가지를 설명했는데 다음 주행패턴 코딩으로 표현을 할 수 있습니다.

motor1.setSpeed(130);
motor2.setSpeed(200);
motor1.run(FORWARD);
motor2.run(FORWARD);

바로, 이 주행패턴 코딩으로 곡선 주행을 하게 됩니다. 좌회전을 하고 싶다면 좌측 DC기어모터의 속도를 낮추게 되면 좌회전을 위 그림처럼 하게 되고 우회전을 하겠다면 우측 DC기어모터의 속도를 낮추면 그 방향으로 회전이 이뤄집니다. 실험에 사용하는 2륜 아두이노 RC카의 방향전환은 해당 DC기어모터의 속도에 따라서 방향을 전환하면서 주행할 수 있습니다. 도로주행과 같은 느낌의 주행이 되겠죠.

하지만, 아두이노 RC카는 이 방식만 있는게 아닙니다. 앞바퀴에 핸들식으로 서보모터를 부착해서 좌/우회전을 시킬 수 있는 방식이 있습니다. RC카 완제품을 사시면 앞바퀴 핸들식으로 무선조정하는 제품들을 많이 보셨을 꺼에요. 이방식에서는 서보모터가 45도가 수평일 때 90도는 좌회전이고 0도는 우회전으로 방향으로 조정하고 뒷바퀴는 그냥 RC카의 속도와 전진/후진 기능을 담당하면 됩니다.


servo.write(90);
motor1.run(FORWARD);
motor2.run(FORWARD);

이렇게 코딩을 바꾸면 됩니다.

마무리


대충 장애물 감지 했을 때 몇가지 패턴들을 만들어 보았습니다. Post [아두이노] 아두이노 2륜 RC카 주행 패턴 실험 의 내용에서 별로 바뀐 부분은 없습니다. 그냥 장애물 감지했을 때 상황을 두고 몇가지 패턴만 만들었을 뿐입니다. 상황을 그림으로 그리고 나서 그 그림을 기반으로 DC기어모터의 바퀴 회전을 어떤 형태로 회전 시킬 건지만 여러분들이 결정해서 패턴을 만들어 내면 됩니다.

  • motor.run(FORWARD) : 전진
  • motor.run(BACKWARD) : 후진
  • motor.run(RELEASE) : 해제

이 함수 3개를 이용하여 초음파센서로 장애물이 감지되면 각 DC기어모터를 FORWARD, BACKWARD, RELEASE 중 하나를 선택하여 장애물를 피하는 주행 패턴을 만들면 됩니다.

처음에는 이렇게 단순하게 장애물에 대해서 난 이렇게 피할꺼야! 아니면 저렇게 피할꺼야! 하면서 쉽게 만들면 됩니다. 처음부터서 정교하게 실제 차처럼 주행시켜야하지 하면 단순하 RC카 주행도 못시킬 가능성이 큽니다. 처음은 단순하게 무조건 쉽게 아두이노 RC카 주행을 시키면서 거기서 문제점이 발견되면 그 문제에 대해 다시 또 다른 주행 패턴을 만들고 하면서 조금씩 살을 붙여가면서 하셔야 정교한 주행이 되는 아두이노 RC카를 만들어 낼 수 있습니다.

오늘 Post도 처음에는 기조척으로 90도 기준으로 해서 좌/우 방향 회전하여 주행 패턴을 만들었는데 만들고 나서 Post를 쓰다보니깐 쓰는 도중에 상상코딩을 하게 되면서 주행 패턴에 대해 내용이 추가되었네요. 몇가지 더 내용이 있었는데 너무 길게 쓰는 것은 좀 그래서 중간에 멈췄네요. 다른 상황과 주행 패턴이 있었는데 대충 이런식으로 장애물을 피하는 주행패턴을 만들 수 있다는 의미를 전달하고 여러분들의 상상력에 맞기는게 나을 것 같아서 이정도로 마무리 했네요.

다음 Post에서 장애물 감지와 장애물 감지 했을 때 어떤 주행 패턴을 선택할지는 결정을 못했네요. 90도 기준으로 좌/우 방향 회전이 가장 유력하지만 너무 복잡한 주행 코딩은 안하고 단순하게 의미전달하는 주행 코딩으로 소개할 것 같습니다. 아무튼 여러분들은 오늘 제가 상상한 주행 패턴을 보시고 여러분들도 직접 장애물이 이렇게 배치 되어 있다면 난 이런 주행을 시켜봐야지 하고 상상을 하는 시간을 가졌으면 합니다.

댓글()

[아두이노] 초음파센서 아두이노 RC카 장애물 감지 방법

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

[아두이노] 초음파센서 아두이노 RC카 장애물 감지 방법



오늘은 초음파센서로 전방에 장애물을 감지하는 방법에 대해서 이야기를 할까 합니다. 우선, 주행하는 전방에 장애물이 등장 했을 때 여러분들이 제작한 RC카가 어떤 주행을 시킬 것인지 상상을 한번 해봅시다. 주행과 장애물 감지에 대해 잘 상상이 안될 수 있습니다. 그런데 주행과 장애물 감지 원리를 알면 그렇게 어렵지 않습니다. 주행과 장애물 감지에서 주행은 그냥 전진주행을 하게 하고 전진 주행 중 장애물에 대해 일정시간 단위로 초음파센서가 측정하게 하면 됩니다. 이 두가지 과정을 loop()함수에서 반복하게 하면 아두이노가 전진 주행중에 실시간으로 계속 장애물을 감지하게 되겠죠. 그리고 장애물이 감지 되었을 때 주행 방향을 바꿔주는 코딩을 하면 아두이노 RC카 자율주행이 됩니다. 복잡하게 출발하지 말고 쉽게 전진 중행을 하면서 초음파센서로 거리를 측정하여 장애물을 감지한다는 개념만 잡고 출발하시면 됩니다.


그러면, 간단히 상황을 설정하고 장애물 감지에 대해 살펴보도록 합시다.

1. 아두이노 RC카의 기본 주행



참고 자료를 보시면 주행 패턴에 대한 내용입니다. RC카가 주행을 하기 위한 기본 소스 코딩이 있는데 주행 패턴에 대해 사전학습을 하셨으면 합니다. 우선 아두이노 RC카의 움직임을 먼저 머리속에서 개념을 잡아놓고 출발하시면 쉽게 코딩 할 수 있습니다.

자율 주행은 전진 주행을 기본 베이스로 합니다.

#include <AFMotor.h>

AF_DCMotor motor1(3);
AF_DCMotor motor2(4);

void setup() {
  motor1.setSpeed(200);
  motor2.setSpeed(200);
  motor1.run(RELEASE);
  motor2.run(RELEASE);
}

void loop() {  

  초음파센서 장애물 감지;

  motor1.run(FORWARD); //전진
  motor2.run(FORWARD);
  delay(1000);
}

이렇게 기본 베이스 코딩이 끝났습니다. 실행을 하면 무조건 전진 주행을 하게 됩니다. 지난 RC카 post에서 위 소스에 대한 설명을 했기 때문에 간단히만 재정리 차원으로 설명하면 motor1(3), morot2(4)로 모터쉴드에서 M3, M4의 위치의 핀으로 DC기어모터 두개를 제어하는데 속도는 setSpeed(200)으로 스피드가 200이고 FORWARD(전진), BACKWARD(후진), RELEASE(해제) 중에서 loop()함수에서 무한 반복 FORWARD(전진)을 한다는 로직입니다.

딱 한줄로 정리하자면 전진만 하는 RC카라고 생각하시면 됩니다.

자율주행 기본 베이스 코딩은 끝났습니다. 이 소스를 기반으로 초음파센서로 장애물을 감지하는 방법들을 살펴 볼까요.

2. 초음파센서로 장애물 감지


초음파센서로 장애물을 감지하는 방식은 엄청 많습니다. 지금 소개하는 방식 외에도 더 많지만 생각나는 것만 몇개 간단히 설명을 할까 합니다.

1) 회전 초음파 센서로 장애물 감지



위 그림처럼 직진으로 주행하다가 장애물이 나타났을 때 초음파센서가 장애물을 어떤 식으로 감지 해야 할까요. 초보적 접근 방법으로는 두가지 방식으로 나누어 살펴 볼 수 있습니다. 주행 중 전방, 좌/우측 방향의 장애물 감지하는 방식과 주행 중에는 전방 장애물만 감지하다가 장애물 발견시 주행 중단 후 좌/우측 방향 장애물 감지하는 방식으로 나누어 살펴 볼 수 있습니다.

주행 중 전방, 좌/우측 방향의 장애물 감지



위 그림처럼 아두이노 RC카는 전방으로 진행하면서 서보모터가 부착된 초음파센서가 전방 일정 각도 범위로 왔다 갔다 회전하면서 장애물을 감지합니다. 이 방식은 전방, 좌/우측 방향으로 3가지 방향을 실시간으로 측정하여 장애물이 감지했을 때 이 3가지 방향의 값을 기준으로 장애물을 피하기 위한 RC카 주행 패턴을 만들 수 있습니다. 주행 중에 초음파센서가 장애물 감지하기 위해 거리 측정을 하고 장애물이 감지 된 측정 각도에 따라 진행 방향을 바꿀 수 있기 때문에 장애물이 감지된 각도에 따라 적절하게 방향을 바꿀 수 있게 할 수 있습니다.

void loop() {
  //회전
  servo.write(angle);
  delay(50);  

  //거리측정
  int distance = sonar.ping_cm();
  
  //장애물 감지
  if(distance<15){
      각도에 따른 장애물 피하기 동작;  
  }
  
  //회전 각도
  if (angle == 140) state = -10;    
  else if (angle == 40) state = 10; 
  angle += state;

  전방주행;
}

위 소스는 지난시간에 정상적으로 초음파센서가 0~180도 회전되는지 테스트한 코딩을 약간 수정한 소스입니다. 이 코딩을 기반으로 0.05초 간격으로 회전하면서 거리를 측정하면 됩니다. 회전 각도는 10도씩하여 40~140도 사이를 11번을 측정하게 됩니다. 1도씩 안하고 10도씩 한 이유는 사실 장애믈을 측정하기 위해서 1도씩 하면 110번을 측정해야하고 왕복 220번을 0.05초 간격으로 측정을 한다면 꽤 긴시간이 걸리기 때문에 실시간 대응이 어렵습니다. 짧게 돌면서 원하는 동작을 수행하기에는 10도정도가 가장 이상적이네요. 더 큰 각도로 제어해도 됩니다, 그냥 90도, 135도, 45도 이렇게 세각도만 측정해도 됩니다. 선택은 여러분의 마음입니다.

주행 중에는 전방 장애물만 감지하다가 장애물 발견시 주행 중단 후 좌/우측 방향 장애물 감지



아두이노 RC카가 직진 주행하는 중 전방 방향의 장애물만 측정합니다. 그러다가 장애물이 발견 되면 위 그림에서 오른쪽 그림처럼 A, B 각도의 장애물을 있는지 측정해서 둘 중 안전한 방향을 찾아 진행 방향을 선택하는 방식입니다.

void loop() {
  delay(50);  

  //거리측정
  int distance = sonar.ping_cm();
  
  //장애물 감지
  if(distance>0 && distance<15){
    주행정지
      angle=135;
        servo.write(angle);
        delay(500);
        int distanceA = sonar.ping_cm();
        angle=45;
        servo.write(angle);
        delay(500);
        int distanceB = sonar.ping_cm();
        
    if(distanceA<distanceB){
         B방향으로 피하기 주행;  
    }
    else{
         A방향으로 피하기 주행;
    }
  }
  전방주행; 
}

위 소스는 0.05초 간격으로 전방 장애물을 감지하다가 장애물이 발견되면 if문으로 들어가서 먼저 주행이 정지되고 그다음 135도와 45도의 방향으로 장애물을 감지합니다. 그리고 둘 중 거리값이 긴 쪽으로 장애물을 피하는 동작을 만들 수 있습니다.

2) 고정 초음파센서 장애물 감지



고정 형태로 한개의 초음파센서로 전방 장애물만 감지합니다. 전방 장애물을 감지 할 때 장애물을 피하기 동작 패턴을 만들기가 애매합니다. 그냥 랜덤 방향으로 할지 아니면 무조건 우방향으로 피할지는 여러분의 마음에 달렸습니다.

void loop() {
  delay(50);  

  //거리측정
  int distance = sonar.ping_cm();
  
  //장애물 감지
  if(distance<15){
    장애물 감지 방향선택;
  }
  전방주행; 
}

코딩은 가장 단순합니다. if문으로 전방 장애물이 발견 되면 그냥 후진, 좌/우 방향으로 진로 변경 할지는 여러분들의 선택으로 하나의 방향을 만들어 주면 됩니다.

3) 양쪽 두개의 고정 초음파 센서 장애물 감지



두개의 고정된 초음파 센서로 장애물을 감지할 경우 A, B 두지점으로 좌/우 방향 위치의 장애물을 감지하게 됩니다. 이 경우는 위에서 전방 장애물을 감지 되었을 때 주행을 정지 한 상태에서 좌/우 방향 위치의 장애물을 감지 했던 코딩과 유사 합니다. 차이점은 주행 중에 실시간으로 좌/우 방향의 장애물을 감지 한다는 점만 차이가 있습니다.

void loop() {
  //거리측정
  delay(50);  
  int distanceA = sonar[0].ping_cm();  
  delay(50);  
  int distanceB = sonar[1].ping_cm();
    
  //장애물 감지
  if(distanceA<15 || distanceB<15){
    if(distanceA<distanceB){
         B방향으로 피하기 주행;  
    }
    else{
         A방향으로 피하기 주행;
    }
  }
  전방주행; 
}

이렇게 두 지점의 거리를 측정 한 뒤에 if문에서 조건식을 체크하는데 '||'로 두 조건식 중 하나라도 만족하면 참인 조건문으로 체크하게 됩니다. 둘중 하나라도 만족하면 진로 방향에 장애물이 있다고 간주하는 것이죠. 즉, A or B의 거리가 15cm 이하이면 참이되어 장애물을 감지된 걸로 간주 합니다. 여기서, 두 지점을 비교하여 A지점이 거리가 더 짧으면 B방향으로 주행하게 만들고 A지점의 거리가 더 길면 A방향으로 주행하도록 만들 수 있습니다.

이외에도 장애물 감지 방식은 여러분들이 상상하는 방식에 따라서 다양하게 표현이 가능합니다. 순간 떠오른 생각들을 몇가지 정리한 것이 위의 장애물 감지 표현입니다. 여러분들도 이 방식 말고 다른 방식으로 장애물을 감지할 수 있는 방법을 찾아보셨으면 합니다. 그래야 상상코딩 능력을 키울 수 있습니다.

마무리


오늘은 초음파센서를 이용하여 장애물을 잠지하는 방법들을 살펴 보았습니다. 초음파센서를 어떤 형태로 배치하느냐에 따라서 감지하는 방식이 달라집니다. 어떤 방식이 정도라도 말 할 수 없으며 여러분들이 원하는 방식으로 표현하시는게 바로 정도입니다. 그리고, 위에서 열거한 방식으로 구지 안하셔도 됩니다. 여러분들의 상상력으로 감지 방식을 만들면 됩니다.

장애물 감지를 위해 실험에 사용한 초음파센서를 이용하셔도 되고 다른 거리측정 센서를 이용하셔도 됩니다. 아니면 전혀 다른 방식으로 주변을 감지할 수 있는 부품을 사용하여 표현하셔도 됩니다. 저는 가장 쉽게 접근할 수 있는 방식이 초음파센서라서 초음파센서를 사용한 것 뿐입니다. 여러분들이 다른 방식으로 하기 어려우시다면 1200원 짜리 초음파센서를 이용하여 실험하셔도 됩니다. 초음파센서를 어떻게 배치하고 사용할지는 여러분의 상상력에 달려 있음으로 한번 상상의 나래를 펼쳐보세요.

댓글()

[아두이노] 초음파센서 아두이노 RC카 자율주행 준비 입문자용

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

[아두이노] 초음파센서 아두이노 RC카 자율주행 준비 입문자용



아두이노 2륜 RC카를 가지고 초음파센서와 결합하여 장애물을 감지하면서 자율주행을 할 수 있도록 하는 간단한 실험을 할 예정입니다. 오늘 Post는 초음파센서를 이용한 자율주행 RC카를 만들기 위한 몸체 조립과 조립에 대한 테스트 동작을 통해 아무 문제가 없는지 확인하는 단계의 내용으로 구성되어 있는데 천천히 배워보도록 하죠.

진짜로, 정교한 자율주행 RC카를 만든다면 영상처리 부분까지 하셔야 합니다. 입문자분들이 바로 영상처리까지 하기는 무리가 따릅니다. 저도 영상처리 부분은 어렵고 실시간 영상을 가지고 주변환경 영상의 에지를 추출하여 그 값을 기반으로 주행 처리를 해야하는데 대충 코딩 방향은 아는데 실시간 영상에서 에지 검출이 좀 힘들어서 설명드리기가 어렵네요. 에지 검출이 영상처리책 보면 대표적으로 canny, sobel 에지 검출 알고리즘이 있습니다. 이 알고리즘으로 정지 영상에서 에지를 추출하는 것은 쉽지만 실시간 영상에서는 저도 알고리즘 로직이 잘 감이 안와서 설명을 못 드리겠네요. opencv로 얻은 영상에서 에지를 검출하고 그 영상을 기반으로 주행을 시키는 것 같은데 고급 자율주행은 사실 깊게 이쪽 분야로 공부를 안해서 아쉽게 실험을 못해 봤습니다. 나중에 라즈베리파이로 공부할 때 한번 도전은 해보고 싶긴 하네요.

사설은 그만하고 오늘 post의 주제는 지난 시간에 만든 아두이노 RC카 정면에 초음파센서를 부착하는 조립하는 단계입니다. 초음파센서에 서보모터를 통해 0~180도 사이각의 회전을 시킬 수 있는 형태로 조립을 하게 되는데 사실 뼈대가 부품가격과 거의 비슷하기 때문에 그냥 테이프로 부착시켰습니다. 테이프로 부착하다보니 정교한 측정이 되지 않지만 그래도 근사값 측정을 통해서 원하는 동작을 충분히 실험할 수 있기 때문에 아무런 문제는 없습니다. 여러분들은 뼈대를 구매하거나 3D 프린트로 직접 만들시면 좋겠죠. 저처럼 테이프로 고정시키는 실험은 보기 흉하니깐 뼈대를 사셔서 이쁘게 만들어 보세요.


이제 본격적으로 아두이노 RC카 자율주행을 도전 할까요.

1. 아두이노 RC카 준비



사전학습 post로 가셔서 아두이노 RC카에 대한 학습을 먼저 하셔야 합니다. 대충 사전학습에서 RC카 조립을 끝낸 상태면 이렇게 완성되어 있겠죠.


조립이 완성된 상태에서 다음 준비 부품이 필요합니다.

2. 아두이노 RC카에 추가 부품 조립


  • 준비물 : 아두이노 RC카 1대, 초음파센서 1개, ServoMotor 1개


위 사진에서는 뼈대가 있으면 좋은데 간단히 실험을 하기 위해서 그냥 테이프로 붙였습니다. 아두이노 RC카 정면에 Servo Motor와 초음파 센서를 정면을 바라보도록 배치하시면 됩니다. 혹시 뼈대를 개인적으로 구매하신다면 안정적으로 배치 할 수 있겠죠. 저는 테이프로 붙여서 각도도 안맞고 약간 아래로 쳐져 있어서 좀 불편하게 실험 했네요.


위에서 바라보는 모습입니다. 보시면 A은 초음파센서핀으로 연결합니다. 초음파센서 핀은 A4(Trig), A5(Echo)핀입니다. 그리고 B는 Servo Motor 핀을 연결합니다. 바깥쪽부터 (-,+,sig) 이렇게 순서대로 있는데 3핀을 순서대로 연결하시면 됩니다. 제가 실험한 모터쉴드는 2개의 Serovo Motor 핀을 제공하는데 상단 첫번째 줄은 아두이노 디지털핀 10번이고 두번째 줄은 9번핀이네요. 10번핀을 사용하였습니다.

좀 더 자세히 아래 사진의 L293D Motor Shield의 모습을 보시고 연결하세요.


해당 위치에 핀을 꼽아주고 아두이노 RC카 정면에 Servo Motor와 초음파 센서를 부착하시면 조립은 완성입니다.

2. 간단히 장애물 감지센서 시범 회전 코딩



Servo Motor

#include <Servo.h> 
  • Servo servo : 서보모터 객체변수 선언
  • servo.attach(servoPin) : 서보모터 시작하는데 사용되는 핀은 servoPin(10) 임
  • servo.write(angle) : angle 값으로 서보모터를 회전 시킨다.

NewPing

#include <NewPing.h>
  • NewPing sonar(TrigPin, EchoPin, MaxDistance) : TrigPin과 EchoPin과 최대제한거리(MaxDistance)의 값을 선언합니다.
  • sonar.ping_cm() : 센서 거리를 'cm'로 계산된 값을 출력한다.

1) Servo Motor 회전

Servo Motor가 정상적으로 회전이 되는지 테스트 코딩을 해야 겠지요.

Servo servo;
const int servoPin = 10;
int state = 1;
int angle = 90;

이렇게 우선 서보모터 객체변수을 하나 만들고 서보모터를 제어할 아두이노 디지털 핀 10을 선언하고 초기 각도(angle) 값을 90도로 표현 했습니다. 그리고 state은 초기값이 1인데 이 값은 1이면 1씩 증가 -1이면 1씩 감소하는 변수로 사용하기 위해 만든 변수입니다.

void setup() {
  servo.attach(servoPin);
  servo.write(angle);
  delay(1000);
}

setup()함수에서 초기화 작업을 하는데 서보모터를 시작하고 바로 servo.write()함수를 사용하여 angle(90)각도로 회전합니다. 딜레이 시간은 1초를 줬네요. 초기값이라서 원하는 시간값을 여러분들이 마음대로 결정하세요. 서보모터를 회전시킬 만큼의 딜레이시간을 줘야하는데 1초은 꽤 긴시간이고 더 짧게 딜레이시간을 주고 90도까지 회전 시켜도 됩니다. loop()함수로 진입하기 전에 충분히 대기시간을 주기 위해서 1초정도 딜레이를 줬네요.

void loop() {
  //회전
  servo.write(angle);
  delay(50);  

  //회전 각도
  if (angle == 180) state = -1;    
  else if (angle == 0) state = 1; 
  angle += state;
}

loop()함수는 0.05초 간격으로 서보모터를 angle(각)으로 회전시키게 됩니다. 어떻게 회전될 까요 회전 각도 로직을 살펴보시기 바랍니다.

  if (angle == 180) state = -1;    
  else if (angle == 0) state = 1; 
  angle += state;

보시면 angle이 180도면 state은 -1이고 angle이 0도이면 state 값은 1이다. 이 의미를 이해하기 위해서는 아래 문장을 보시면 쉽게 이해가 되실 꺼에요.

angle = angle + state;

이 문장은 0.05초 간격으로 loop()함수가 무한 반복하는데 기존에 angle값에다가 state을 계속 더해주는 문장입니다. 여기서, state가 1이면 1씩 증가하고 state가 -1이면 1씩 감소하게 됩니다. 의미를 잘 이해해 주세요. 이렇게 되면 초기값이 state가 1이기 때문에 초기각도 angle이 90도니깐 1도씩 증가하게 됩니다.

서보모터는 0~180도까지 제어가 가능합니다 그러면 1씩 증가하더라도 180도를 넘을 수 없게 됩니다 180도가 되면 state값을 -1로 변경하면 1도씩 감소하게 되고 역방향으로 회전하게 됩니다. 그러면, 다시 역방향으로 1도씩 감소하는데 angle은 0이하로 떨어질 수 없습니다. 이때 state 값을 1로 변경하고 다시 정방향으로 1도씩 증가하게 됩니다. 이렇게 표현하면 0~180도 사이를 1도씩 왔다 갔다 회전하게 됩니다.

loop()함수 로직의 동작은 0.05초 간격으로 0~180도 사이를 무한 반복 회전을 하게 됩니다. 이 움직임을 보시면 초음파레이더가 생각 나시는 분이 있을 지 모르겠네요. 초음파레이더의 기본 동작 코딩입니다. 이 코딩으로 초음파레이더를 만들 수 있습니다. 현재 우리가 초음파레이더를 만드는 것이 목적이 아니기 때문에 서보모터 회전에 대한 테스트 동작으로만 이해해 주셨으면 합니다.

2) 초음파센서 거리 측정


이제는 서보모터가 회전할 때마다 초음파센서로 거리를 측정해야 겠죠.

NewPing sonar(A4, A5, 100);

trig(A4), Echo(A5), 제한거리1미터(100)으로 초음파센서 객체변수를 만듭니다.

  int distance = sonar.ping_cm();

distance 변수에 초음파센서에서 측정한 거리(cm)값이 저장됩니다. 이렇게 해서 거리 측정이 끝났네요.

그러면 이 코딩은 전체소스 안에서 어느 위치에 삽입하는 것이 좋을까요. 회전 시마다 초음파센서로 측정한다고 했죠. 서보모터는 어떻게 회전한다고 했죠. 0.05초 간격으로 회전이 이뤄집니다. 그러면, 0.05초 간격으로 회전할 때 초음파센서로 측정하면 되겠죠. 즉, 서보모터를 angle(각)으로 회전시킨 후 초음파센서로 거리를 측정하게 코딩하면 간단히 해결 됩니다.

void loop() {
  //회전
  servo.write(angle);
  delay(50);  
    
  int distance = sonar.ping_cm();
    
  //회전 각도
  if (angle == 180) state = -1;    
  else if (angle == 0) state = 1; 
  angle += state;
}

이렇게 하면, 0.05초 간격으로 서보모터를 회전시키고 distance 변수에 회전 시킨 각도에서 거리측정한 값이 저장됩니다.

그러면, 이 값을 시리얼모니터로 정상적으로 출력되는지 Serial 함수로 표현하면 되겠죠.


  • Serial.begin(9600) : 시리얼통신 시작
  • Serial.print(distance) : 시리얼모니터로 출력

3) 종합 코딩


좀 더 깔끔하게 각도와 거리값을 동시에 시리얼모니터로 출력시켜서 확인할 수 있도록 소스를 수정하였습니다. Serial함수는 너무 많이 설명을 드렸기 때문에 구지 설명을 안드려도 아실꺼라 믿고 아래 변경된 부분만 잘 확인해 주세요.

참고로, 완성된 소스는 초음파레이더 소스와 동일합니다. 이 소스로 processing으로 시각화 하면 초음파레이더가 됩니다. 초음파레이더로 보시지 마시고 그냥 앞에 장애물을 감지하는 센서로 보셨으면 합니다. 서보모터의 회전이 정상적으로 이뤄지고 거리측정이 정상적으로 이루어지는지 테스트하는 용도로 코딩을 바라 보시기 바랍니다.

[소스]

#include <Servo.h> 
#include <NewPing.h>

const int TRIG = A4;
const int ECHO = A5;
const int MAX_DISTANCE = 100;
NewPing sonar(TRIG, ECHO, MAX_DISTANCE);

Servo servo;
const int servoPin = 10;
int state = 1;
int angle = 90;

void setup() {
  Serial.begin(9600);
  servo.attach(servoPin);
  servo.write(angle);
  delay(1000);
}

void loop() {
  //회전
  servo.write(angle);
  delay(50);  

  //거리측정
  int distance = sonar.ping_cm();
  Serial.print(angle);        
  Serial.print(" : ");        
  Serial.print(distance);    
  Serial.println(" cm");         

  //회전 각도
  if (angle == 180) state = -1;    
  else if (angle == 0) state = 1; 
  angle += state;
}

newPing 라이브러리를 혹시 안깔고 코딩하신분들이 있을 것 같아서

라이브러리 매너저 창에서 "newPing"이라고 검색하시면 자동으로 검색되고 해당 라이브러리를 설치하시면 됩니다.


3. 결과


촬영이 좀 깔끔하게 되지 못했네요. 폰으로 찍어서 하다 보니깐 공간적 제약도 따르고 해서 좀 보기 불편하시더라도 대충 어떤식으로 Servo Motor가 회전 되고 초음파센서를 통해 거리가 측정되는지 영상으로 살펴보시기 바랍니다. 참고로 위쪽 상단는 모니터가 있어서 거리값이 10~11정도로 측정됩니다. 오류값이 아니라 모니터와의 거리 때문에 나온 값이니깐 감안하시고 보세요.


마무리


오늘은 아두이노 RC카가 준비된 상태에서 두개의 부품 Servo Motor와 초음파센서를 조립에 대해 간단히 살펴보았습니다. 그리고 정상적으로 동작하는지 실험 코딩으로 테스트까지 해보았습니다. 이렇게 해서 초음파센서를 통해 아두이노 RC카는 정면의 장애물 감지까지 할 수 있게 되었습니다. 다음 단계는주행 도중에 장애물이 감지된 정보에 대해 어떻게 반응 할 것인지가 남아 있습니다.

사실 위에서 정상 작동하는지 테스트한 코딩을 이해하셨다면 조금만 상상을 더하면 장애물을 피해 자율주행을 시키는 코딩은 어렵지 않게 하실 수 있을 겁니다. 이미 이 post로 아두이노 RC카 자율주행은 끝난 거나 다름 없습니다. 그런데, 이걸로 어떻게 자율주행이 되냐고 생각하시는 분들도 많을 꺼에요. 잘 연상이 안되실 꺼에요. 상상을 많이하고 자기 자신이 RC카라고 상상하면서 내 이마에 서보모터가 부착되어 회전하고 있고 회전하면서 초음파센서로 거리를 측정하고 있다고 상상하면서 한번 제자리에서 걸어 다녀보세요. 전방에 벽이 나타나면 여러분들은 걷다가 멈추게 되겠죠. 그리고, 그 벽을 피해서 어떤 행동을 하겠죠. 벽 앞에서 다시 뒤로 되돌아 간다거나 오른쪽 길이 있으면 오른쪽으로 걸음을 옮기게 되겠죠. 이렇게 어떤 행동을 취하게 됩니다. 그 행동을 코딩화 하면 그 코딩이 아두이노 RC카의 자율주행 코딩이 됩니다. 즉, 어떤 상황이 되면 그 상황을 피하기 위해서 어떤 행동을 취하고 그 행동이 자율 주행 명령 코드가 되어 실제로 아두이노 RC카가 자율주행을 하게 됩니다. 코딩은 모르더라도 대충 어떤 느낌이신지 아시겠지요.

한번 자기자신이 아두이노 RC카라고 상상하고 제자리에서 주변을 걸어다녀보시면서 상황을 만들고 그 상황을 기록했다가 상상 코딩을 해보셨으면 합니다.


댓글()

[아두이노] newPing 라이브러리로 초음파센서 제어

IOT/아두이노|2019. 5. 25. 09:00

[아두이노] newPing 라이브러리로 초음파센서 제어



이제까지 초음파센서를 이용하여 거리를 구하기 위해서 직접 공식에 대입하여 코딩을 했었습니다. 직접 코딩하는 것이 불편하신 분들을 위해 newPing이라는 라이브러리를 소개할까 합니다. newPing이라는 라이브러리를 통해서 초음파센서를 쉽게 제어하여 원하는 거리측정 값을 얻을 수 있습니다. newPing 라이브러리를 이야기하기전에 우선 초음파센서 거리측정에 대해서 복습해야겠죠.

초음파센서 거리를 구하는 공식은 다음과 같습니다.

duration = 초음파 센서를 통해 읽은 거리 시간값
distance = ((float)(340 * duration) / 10000) / 2;

위 공식은 복습차원으로 봐주세요. duration을 구하는 코딩 로직은 참고 자료 post를 한번 읽고 와주세요.


여기까지 복습이 완료 된 상태라면 newPing 라이브러리로 실제 실험을 해봅시다.

1. 초음파센서 회로도



공개회로도는 3핀 초음파센서로 직접 측정 로직을 코딩하고 직접 측정한 시간값을 거리를 구하는 공식에 대입하여 구한 예제입니다. 실제 실험에서는 4pin으로 구성된 초음파 센서를 실험하기 때문에 회로도를 표현하면 아래 회로도와 같습니다.


2. newPing 라이브러리 설치


라이브러리 매너저 창에서 "newPing"이라고 검색하시면 자동으로 검색되고 해당 라이브러리를 설치하시면 됩니다.


3. 코딩



NewPing

#include <NewPing.h>
  • NewPing sonar(TrigPin, EchoPin, MaxDistance) : TrigPin과 EchoPin과 최대제한거리(MaxDistance)의 값을 선언합니다.
  • sonar.ping_cm() : 센서 거리를 'cm'로 계산된 값을 출력한다.
  • 다수의 초음파센서 사용 :
NewPing sonar[센서수] = { 
  NewPing(trigPin1, echoPin1, 최대제한거리1), 
  NewPing(trigPin2, echoPin2, 최대제한거리2), 
    ...
};
  • 다수의 초음파센서 접근 :
  • sonar[0].ping_cm() : 0번째 sonar에서 측정된 거리를 'cm'로 계산된 값을 출력한다.

그외 함수는 newPing 라이브러리가 링크된 아두이노 공식 사이트에 가시면 함수들에 대해 자세히 설명되어 있으니깐 가셔서 한번 읽어 주시기 바랍니다.

[소스] : NewPingExample 소스(출처: newPing 라이브러리 예제)

#include <NewPing.h>

//sonar(TrigPin, EchoPin, MaxDistance);
NewPing sonar(2, 3, 200);

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

void loop() {
  delay(50);          
  Serial.print("Ping : ");
  Serial.print(sonar.ping_cm());
  Serial.println("cm");
}

sonar.ping_cm() 함수를 통해서 초음파 센서의 거리를 'cm'로 출력된 값을 시리얼모니터로 출력하는 코딩입니다. 딱 한줄입니다. sonar.ping_cm() 함수 이 한줄의 명령을 실험하는 내용입니다. 그걸 시리얼모니터에 출력하기 위해서 3줄의 시리얼 출력문을 코딩했을 뿐 실제 초음파센서를 제어하는 명령은 딱 한줄입니다.

void loop() {
 delay(50);          
 sonar.ping_cm();
}

이렇게 0.05초 간격으로 초음파센서는 거리측정 값을 'cm'을 반환합니다. 어렵지 않죠.

라이브러리를 이용하면 이 함수 한개로 복잡하게 코딩을 생각하지 않고 거리를 'cm'로 만들 수 있지만 직접 거리를 계산하는 공식 코딩은 단순하기 때문에 구지 라이브러리를 사용 안해도 됩니다.

그런데, 왜! 라이브러리를 사용하는지 궁금하실 꺼에요. 거리 측정하는 거리 공식도 별로 어렵지 않은데 그냥 코딩하면 되지 라이브러리를 사용할 필요가 있냐고 생각하실 꺼에요. 그 이유는 초음파 센서를 한개 사용할 때는 상관 없는데 다중 초음파센서를 사용할 때 newPing 라이브러리 사용하면 효율적으로 코딩을 할 수 있습니다. 표현도 배열로 간단히 하나의 묶음으로 표현 할 수 있고, 여러개를 측정하려면 시간 관련한 문제도 고려해서 코딩해야 하는데 라이브러리를 사용하면 좀 더 정교하게 코딩이 가능하기 때문에 사용합니다. 위에 링크 된 아두이노 공식 홈페이지에서 소개하는 newPing 라이브러리 관련 내용을 보시면 15개 초음파센서를 한번에 제어하는 예제가 있는데 가셔서 살펴 봐주세요. 물론 라이브러리를 설치하면 아두이노 IDE에 예제로 나와 있어 설치하고 나서 예제를 열어보시고 어떻게 코딩되어 있는지 살펴보셔도 됩니다.

3. 결과


4. 2개 초음파 센서 사용


이제 2개의 초음파를 한번에 측정하여 그 결과를 출력해 보는 실험을 해보겠습니다.

1) 2개 초음파 센서 회로도


  • 준비물 : 초음파센서 2개, 아두이노우노
  • 내용 : 왼쪽 초음파센서는 tragPin 2, echoPin 3 에 연결하고, 오른쪽 초음파센서는 tragPin 4, echoPin 5 에 연결하시오.


2) 코딩


배열로 초음파센서 객체를 선언합니다.

NewPing sonar[2] = { 
  NewPing(2, 3, 200), 
  NewPing(4, 5, 200), 
};

이렇게 선언하시면 초음파센서 측정은 sonar[0].ping_cm(), sonar[1].ping_cm() 함수로 해서 2개의 초음파 센서의 거리를 측정하여 'cm'로 출력하게 됩니다. 초음파 센서들 간의 시간 딜레이는 0.05 초로 간격을 두어 측정하게 됩니다. 너무 빠르게 두개의 초음파센서의 값이 시리얼모니터로 출력되기 때문에 딜레이를 loop()함수 안에 마지막 라인에 0.5초의 딜레이를 줌으로서 좀 천천히 2개의 초음파센서 값이 시리얼모니터로 출력되게 코딩했네요.

코딩은 따로 설명할 필요 없이 방금 앞에서 코딩한 소스에서 한번 더 초음파센서 sonar[위치].ping_cm()함수로 출력하는 명령만 추가한 것 뿐이니 구지 설명하지 않겠습니다.

[소스]

#include <NewPing.h>

//sonar(TrigPin, EchoPin, MaxDistance);
NewPing sonar[2] = { 
  NewPing(2, 3, 200), 
  NewPing(4, 5, 200), 
};

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

void loop() {
  delay(50);          
  Serial.print("A Ping : ");
  Serial.print(sonar[0].ping_cm());
  Serial.println("cm");
  
  delay(50);          
  Serial.print("B Ping : ");
  Serial.print(sonar[1].ping_cm());
  Serial.println("cm");

  delay(500);
}

3) 결과



위 사진을 보시면 A초음파센서와 B초음파센서로 나뉩니다. 여기서, B초음파센서는 Servo Motor에 테이프로 감겨 있고 Servo Motor 같이 아두이노 RC카에 테이프로 부착되어 있어서 어쩔 수 없이 RC카에 부착된 B초음파센서랑 A초음파센서를 실험하다 보니깐 보시는 것처럼 좀 불편하게 실험 되었습니다. 감안하시고 동영상을 보시기 바랍니다.


마무리


오늘 post는 초음파센서를 직접 코딩하는 방법도 있지만 이렇게 라이브러리를 이용하여 쉽게 코딩할 수 있는 것을 보여드리기 위해서 다뤘습니다. 그리고, 왜! 갑자기 초음파센서를 이 시점에 다시 post를 했냐면 아두이노 RC카에서 거리측정을 통해 장애물을 감지하는데 사용하기 위해서 사전 학습으로 거론하게 되었네요. 직접적으로, 측정 로직을 코딩하는 것도 좋지만 새로운 것을 소개하는 것이 좋을 것 같아서 초음파센서에 대한 post를 하면서 newPing에 대해 이야기 하면 좋을 것 같아 이렇게 이야기를 하게 되었네요.

오늘 이야기한 post를 보기전 사전학습으로 직접 거리를 측정하는 방법에 대한 링크된 post 글을 찾아가셔서 복습을 한 뒤에 newPing을 사용해 보셨으면 합니다.


댓글()

[아두이노] 2륜 RC카 원리를 가상시뮬레이터로 실험

IOT/아두이노|2019. 5. 24. 09:00

[아두이노] 2륜 RC카 원리를 가상시뮬레이터로 실험



지난 시간에 기본 아두이노 2륜 RC카 조정에 대해서 마무리 했습니다. post로만 아두이노 RC카 설명을 끝내기가 아쉬워서 가상시뮬레이터에서 동일하게 표현하고 코딩도 유사하게 코딩해서 간접적으로나마 체험 할 수 있게 하면 좋을 것 같아서 간단히 가상시뮬레이터에서 회로도를 만들어 보았습니다.

구체적으로 어떻게 표현했는지 설명 하겠습니다.

1. 아두이노 2륜 RC카 회도로


  • 준비물 : L293D 칩 1개, DC Motor 2개, 외부전원, 아두이노우노
  • 내용 : 아두이노 위에 L293D 칩 pin 들을 아두이노우노에 원하는 위치에 연결한다.
  • 참고 : [아두이노] L293D + DC MOTOR 제어

회로도가 복잡해 보일 꺼에요. 회로도가 이해가 안가신다면 위에 링크 걸린 참고 자료에 가셔서 복습하시면 됩니다.

참고로, [아두이노] 2륜 RC카 Bluetooth를 통해 스마트폰(무선) 조정하기 post의 동작과 동일합니다. 실제로 만들 경우에 동일한 결과가 나오겠지요.

그리고, 이 회로도에서 Bluetooth은 0,1 핀을 사용한다는 가정하에서 진행됩니다. 저번에 설명했듯이 아두이노 내부 시리얼통신 핀을 사용할 경우는 SoftwareSerial 라이브러리가 필요 없습니다. 그냥 상상으로 0,1번핀에 Bluetooth가 연결되었다고 상상하신 후 보시기 바랍니다.

Bluetooth Tx = arduino Pin0
Bluetooth Rx = arduino Pin1

3. Motor 라이브러리 만들기



원래는 Adafruit-Motor-Shield-library 를 인용해서 코딩해야 하는데 해당 부분만 편집하면 좀 문제가 생길 수 있고 해서 지난시간에 이 라이브러리로 실험했던 느낌을 계속 유지하기 위해서 라이브러리에 사용한 함수명만 그대로 인용해서 새롭게 Motor 라이브러리를 만들어 보았습니다.


  • AF_DCMotor motor(3) : M3핀을 DC Motor 제어용으로 사용.
  • motor.setSpeed(200) : 모터 속도
  • motor.run(FORWARD) : FORWARD, BACKWARD, RELEASE 회전 명령

기본 구조가 이렇게 위처럼 되어 있는데 혼동을 피하고 같은 느낌을 계속 유지하기 위해서 위 표현을 그대로 인용 합니다.

#define FORWARD  1
#define BACKWARD 2
#define RELEASE  3

class DCMotor{
 public:
   DCMotor(uint8_t pinA, uint8_t pinB, uint8_t pinC);
   void run(uint8_t);
   void setSpeed(uint8_t);
   private:
   uint8_t motorA, motorB,speedPin;
};

위 처럼 우선 생성자 DCMotor() 함수를 표현 했습니다. 어떤 핀을 Motor로 제어할지 Motor 객체변수를 선언과 동시에 지정해주기 위해서 입니다. 그리고, run(), setSpeed() 함수명을 그대로 인용했네요.

함수들의 로직 코딩을 볼까요.

DCMotor::DCMotor(uint8_t pinA, uint8_t pinB, uint8_t pinC) {
  motorA = pinA;
  motorB = pinB;
  speedPin = pinC;
  pinMode(motorA,OUTPUT);
  pinMode(motorB,OUTPUT);
  pinMode(speedPin,OUTPUT);
}

DCMotor()함수는 private 형으로 선언한 클래스 내부 변수들에 대한 초기화 작업을 수행합니다. 각 pin의 사용 모드도 여기서 초기화 합니다. 사실 클래스 형태로 표현할려면 이런 코딩이 아니라 아두이노 주소번지로 직접 접근하여 좀 더 하드웨어적 코딩을 해야하는데 복잡해 보일 것 같아서 아두이노 함수로 간단히 클래스를 표현했네요. 느낌만 표현한 클래스 입니다. 진짜 만든다면 이렇게 하면 안되고요. 좀 더 하드웨어적 코딩을 해야 합니다. 우리는 즐기기 위해서 하는 것이기 때문에 쉽게 코딩하는게 좋겠죠.

void DCMotor::run(uint8_t key) {
  switch (key) {
  case 1:
    digitalWrite(motorA,HIGH); //전진
    digitalWrite(motorB,LOW);
    break;
  case 2:
    digitalWrite(motorA,LOW); //후진
    digitalWrite(motorB,HIGH);
    break;
  case 3:
    digitalWrite(motorA,LOW); //정지
    digitalWrite(motorB,LOW);
    break;
  default:
    return;
  }
}

run()함수는 key 값은 FORWARD, BACKWARD, RELEASE 값을 말합니다. define으로 정의한 1, 2, 3값에 해당되는 switch()함수를 통해서 해당된 pin을 digitalWrite()함수로 High or Low로 결정하게 됩니다. 이 코딩은 이미 지난 Post L293D + DC MOTOR 제어 (아두이노) 에서 소개 했습니다. 혹시 잘 모르시겠다면 해당 post에 가셔서 한번 읽고 오시기 바랍니다.

void DCMotor::setSpeed(uint8_t speed) {
   analogWrite(speedPin, speed);
}

setSpeed()함수는 Enable Pin으로 값을 출력하는데 PWM Pin을 사용하여 0~255 사이의 아날로그 신호를 출력합니다. speed의 값을 analogWrite()함수로 아날로그 신호를 출력하면 되기 때문에 딱 한줄 명령으로 표현 됩니다.

이렇게 해서 DCMotor 클래스를 간단히 표현 했습니다.

3. 코딩


만든 DC Motor 클래스

  • DCMotor motor1(7,6,5);) : In1, In2, EnablePin
  • motor.setSpeed(200) : 모터 속도
  • motor.run(FORWARD) : FORWARD, BACKWARD, RELEASE 회전 명령

[소스]

#define FORWARD  1
#define BACKWARD 2
#define RELEASE  3

class DCMotor{
 public:
   DCMotor(uint8_t pinA, uint8_t pinB, uint8_t pinC);
   void run(uint8_t);
   void setSpeed(uint8_t);
   private:
   uint8_t motorA, motorB,speedPin;
};

DCMotor::DCMotor(uint8_t pinA, uint8_t pinB, uint8_t pinC) {
  motorA = pinA;
  motorB = pinB;
  speedPin = pinC;
  pinMode(motorA,OUTPUT);
  pinMode(motorB,OUTPUT);
  pinMode(speedPin,OUTPUT);
}
void DCMotor::run(uint8_t key) {
  switch (key) {
  case 1:
    digitalWrite(motorA,HIGH);
    digitalWrite(motorB,LOW);
    break;
  case 2:
    digitalWrite(motorA,LOW);
    digitalWrite(motorB,HIGH);
    break;
  case 3:
    digitalWrite(motorA,LOW);
    digitalWrite(motorB,LOW);
    break;
  default:
    return;
  }
}

void DCMotor::setSpeed(uint8_t speed) {
   analogWrite(speedPin, speed);
}
                 
DCMotor motor1(7,6,5); //L293D Motor Pin과 Enable Pin
DCMotor motor2(9,8,3);

int speed = 200;
                 
void setup() {
  Serial.begin(9600);   
  motor1.setSpeed(speed);
  motor2.setSpeed(speed);
  motor1.run(RELEASE);
  motor2.run(RELEASE); 
}
 
void loop() {
  if (Serial.available()){
    char ch = Serial.read();

    switch(ch){
      case 'w':
            motor1.run(FORWARD);
            motor2.run(FORWARD);
            break;
      case 's':
            motor1.run(BACKWARD);
            motor2.run(BACKWARD);
            break;
      case 'a':
            motor1.run(BACKWARD);
            motor2.run(FORWARD);
            break;
      case 'd':
            motor1.run(FORWARD);
            motor2.run(BACKWARD);
            break;
      case 'z':
            motor1.run(RELEASE);
            motor2.run(RELEASE);
            break;
      case 'm':
            speed+=10;
            if(speed>=250) speed=250;
            motor1.setSpeed(speed);
            motor2.setSpeed(speed);
            break;
      case 'n':
            speed-=10;
            if(speed<=0) speed=0;
            motor1.setSpeed(speed);
            motor2.setSpeed(speed);
            break;
    }         
  }
}

만든 Motor 클래스만 추가되고 setup(), loop()함수는 실제 제작했던 Post [아두이노] 2륜 RC카 Bluetooth를 통해 스마트폰(무선) 조정하기 와 동일한 소스 입니다.

이렇게 해서 가상시뮬레이터로 회로도를 만들고 코딩도 동일하게 코딩해서 실험할 수 있도록 세팅이 끝났네요.

위 소스에서 실제로 L293D Motor Shield로 제어한다면 해당 라이브러리를 다운 받은 후 객체 선언부분만 형식에 맞춰서 하신 후 setup(), loop()함수는 수정 없이 그대로 사용하면 실제로 스마트폰으로 조정이 가능 합니다.

5. 결과


가상시뮬레이터를 싱행 시킨 후 위 그림처럼 시리얼모니터에서 회전 명령 키값을 전송하면 됩니다. 스마트폰의 역활을 대신하는 것이죠. 0,1번 핀에 Bluetooth Pin을 연결하면 바로 스마트폰으로 제어가 가능합니다.

전진(w)/후진(s), 좌(a)/우회전(d), 정지(z), 속도 증가(m)/감소(n) 의 명령으로 세팅 되어 있고 가상시뮬레이터가 스마트폰으로 상상하고 키값을 입력하여 전송하면 거기에 맞게 가상시뮬레이터에 DC기어모터가 회전을 하게 됩니다.

아래는 테스트 결과입니다.


위 영상이 제대로 식별이 안되신다면 위에 공개회로도를 링크 걸어 놓았어요. 가셔서 실제로 실행 시켜보셔서 확인하시면 됩니다. 시뮬레이더 실행 키 누르고 코딩창 열고 코딩창 하단에 시리얼모니터 아이콘을 눌러서 창을 개방시키고 조정 키값을 입력하시면 회로도의 DC기어모터가 조정 키값에 따른 회전명령을 수행합니다.

마무리


실제로 아두이노 RC카 부품을 구매해서 직접 제작하지 않더라도 간접적으로 이렇게 가상시뮬레이터에서 실험을 할 수 있습니다. 실제로 주행하는 모습은 볼 수 없지만 DC Motor 회전만으로도 간접적으로 회전의 원리를 체험할 수 있습니다.

실험을 하실 때 Post [아두이노] 아두이노 2륜 RC카 주행 패턴 실험 에서 그림으로 주행 패턴을 설명했는데 그 그림을 보시고 가상시뮬레이터의 DC기어모터 회전을 합쳐진 이미지를 머리속에서 상상하시면서 오늘 post의 내용을 보시면 대충 상상속에서 주행하는 모습이 이미지로 그려질 거라 생각합니다.

이렇게 해서 아두이노 RC카를 만들고 Bluetooth로 무선 조정하고 마무리로 가상시뮬레이터로 표현까지 해서 꽤 긴 Post를 마무리 합니다. 사실 간단하게 이렇게조립하세요. 소스코딩은 이렇게하면 Bluetooth로 조정이 돼요. 하면 1~2일 post 주제로 끝낼 수 있었지만 아두이노 RC카의 만드는 것보다 만드는 과정을 보여드리고 싶었기 때문에 post가 길어졌네요.

마지막으로 아두이노 RC카의 주행 패턴도 알았고 조정하는 방법까지 알았습니다. RC카의 움직임을 원하는 형태로 여러분들은 할 수 있게 되었습니다. 즉, 어떤 상황이 되었을 때 여러분들이 아두이노 RC카를 특정한 패턴의 움직임을 보이도로 설계가 가능하다는 이야기가 됩니다. 쉽게 말해서, 이 의미는 아두이노 RC카를 상황에 맞게 주행할 수 있도록 자율주행 설계가 가능하다는 의미가 됩니다. 예를들면, 아두이노 RC카 앞에 장애물이 발견되면 그 장애물을 피해서 주행을 자동으로 할 수 있게 할 수 있다는 것이죠.

여러분들이 상상력을 어디까지 끌어 올리느냐에 따라서, 아두이노 RC카 자율주행을 단순하게 또는 정교하게 표현을 가능해 집니다. 한번 아두이노 RC카 자율주행에 대해서 상상의 나래를 펼쳐 보세요.

댓글()

[아두이노] 아두이노 RC카를 조정하는 다양한 코딩 접근법

IOT/아두이노|2019. 5. 23. 09:00

[아두이노] 아두이노 RC카를 조정하는 다양한 코딩 접근법


지난시간까지 해서 간단히 아두이노 2륜 RC카를 조립하고 주행패턴을 분석한 뒤에 Bluetooth를 연결하여 스마트폰으로 조정하는 것 까지 살펴 보았습니다. 오늘은 지난시간에 Bluetooth로 RC카를 조정하는 소스를 가지고 다양한 접근을 시도하고자 합니다. 코딩은 딱 하나의 정해진 로직에 의한 코딩으로 이루어지지 않습니다. 비슷하면서도 다양한 코딩이 존재 합니다. 바로 프로그래머의 주관적 상상에 의해 코딩의 로직이 표현 됩니다. 제가 코딩하는 방식은 제 나름대로의 상상의 의한 코딩입니다. 그렇기 때문에 RC카를 조정하는 코딩은 여러분들이 직접 상상하여 코딩하시면 제가 했던 방식과 다르게 표현 될 수 있습니다. 어떤 회전을 하고 어떤식으로 명령어를 처리해서 DC Motor를 회전 시킬지는 여러분들의 상상력에 달린 것이죠.

오늘은 제가 어떤 상상을 하고 코딩을 했을 때 그 하나의 코딩에 머물지 않고 다양한 방식으로 접근하는 코딩을 설명할까 합니다. 제가 처음 코딩을 입문했을 때 부터 고수해 온 코딩법 입니다. 하나의 결과물을 만들어 내면 거기서 멈추지 않고 유사한 표현이나 다른 접근법을 통해 둘 이상의 결과물이 만들어지도록 코딩 합니다. 그래서인지 대학시절에 조별 프로젝트를 만들면 꼭 2개이상의 결과물을 만들어 제출 했던 기억이 나네요. 저는 할수 있는 코딩만 하고 그 코딩을 통해 표현할 수 있는 최대의 방향으로 코딩을 표현 합니다.

아무튼, 이야기가 삼천포로 좀 빠졌지만 RC카를 조정하는 코딩을 다양하게 접근해 보겠습니다.


1. 2륜 RC카 + Bluetooth 조정 기본 코딩(Switch문)


void loop() {
  if (mySerial.available()){
    char ch = mySerial.read();
        처리문;
  }
}

우선 Bluetooth로 통신을 받는 기본 틀 소스입니다. 처리문은 통신을 통해서 읽은 값을 통해 RC카를 처리하는 위치입니다. 그 위치에서 Switch문을 통해서 명령을 수행하는 소스가 지난시간에 만든 아래 소스입니다.

#include <AFMotor.h>
#include <SoftwareSerial.h>

const int rxPin = A0;
const int txPin = 2;

SoftwareSerial mySerial(rxPin, txPin); // RX, TX

AF_DCMotor motor1(3);
AF_DCMotor motor2(4);
int speed =200;

void setup() {
  mySerial.begin(9600);   
  motor1.setSpeed(speed);
  motor2.setSpeed(speed);
  motor1.run(RELEASE);
  motor2.run(RELEASE);
}

void loop() {
  if (mySerial.available()){
    char ch = mySerial.read();

    switch(ch){
      case 'w': //전진
            motor1.run(FORWARD);
            motor2.run(FORWARD);
            break;
      case 's': //후진
            motor1.run(BACKWARD);
            motor2.run(BACKWARD);
            break;
      case 'a': //제자리 좌회전
            motor1.run(BACKWARD);
            motor2.run(FORWARD);
            break;
      case 'd': //제자리 우회전
            motor1.run(FORWARD);
            motor2.run(BACKWARD);
            break;
      case 'z': //정지
            motor1.run(RELEASE);
            motor2.run(RELEASE);
            break;
      case 'o': //좌회전
            motor1.run(RELEASE);
            motor2.run(FORWARD);
            break;
      case 'p': //우회전
            motor1.run(FORWARD);
            motor2.run(RELEASE);
            break;            
      case 'm': //속도 증가
            speed+=10;
            if(speed>=250) speed=250;
            motor1.setSpeed(speed);
            motor2.setSpeed(speed);
            break;
      case 'n': //속도 감소
            speed-=10;
            if(speed<=0) speed=0;
            motor1.setSpeed(speed);
            motor2.setSpeed(speed);
            break;
    }      
  }
}

지난시간의 코딩한 이 소스에서 간단히 전진/후진, 좌/우회전, 정지 이렇게 5가지 동작만을 코딩으로 간단히 표현 한뒤에 그 소스를 기반으로 연습하셔도 됩니다. 저는 그냥 지난 소스를 기반으로 다른 방식으로 접근하는 코딩을 하겠습니다.

2. IF~Else IF 문


IF~Else IF문은 기본적으로 Switch문과 유사한 동작을 수행합니다.

IF (조건식1) 명령1;
Else IF(조건식2) 명령2;
Else IF(조건식3) 명령3;
...

이런 구조로 되어 있습니다. Bluetooth 통신으로 읽은 값이 조건식1에 만족하면 명령1의 RC카 주행을 하고 만약 조건식1에 만족하지 않으면 다음 라인 조건식2에 만족하는지 체크하고 만족하면 명령2의 RC카 주행을 합니다. 이런식으로 하나씩 비교해서 만족하는 명령의 RC카 주행을 하게 됩니다.

Switch문을 IF~Else IF으로 수정만 하시면 됩니다.

#include <AFMotor.h>
#include <SoftwareSerial.h>

const int rxPin = A0;
const int txPin = 2;

SoftwareSerial mySerial(rxPin, txPin); // RX, TX

AF_DCMotor motor1(3);
AF_DCMotor motor2(4);
int speed =200;

void setup() {
  mySerial.begin(9600);   
  motor1.setSpeed(speed);
  motor2.setSpeed(speed);
  motor1.run(RELEASE);
  motor2.run(RELEASE);
}

void loop() {
  if (mySerial.available()){
    char ch = mySerial.read();

    if (ch == 'w') {
      motor1.run(FORWARD);
      motor2.run(FORWARD);
    } 
    else if (ch == 's') {
      motor1.run(BACKWARD);
      motor2.run(BACKWARD);
    } 
    else if (ch == 'a') {
      motor1.run(BACKWARD);
      motor2.run(FORWARD);
    } 
    else if (ch == 'd') {
      motor1.run(FORWARD);
      motor2.run(BACKWARD);
    }
    else if (ch == 'z') {
      motor1.run(RELEASE);
      motor2.run(RELEASE);
    }
    else if(ch =='o'){
      motor1.run(RELEASE);
      motor2.run(FORWARD);
    }
        else if(ch == 'p'){
      motor1.run(FORWARD);
      motor2.run(RELEASE);
    }
    else if (ch == 'm') {
      speed+=10;
      if(speed>=250) speed=250;
      motor1.setSpeed(speed);
      motor2.setSpeed(speed);
    }
    else if (ch == 'n') {
      speed-=10;
      if(speed<=0) speed=0;
      motor1.setSpeed(speed);
      motor2.setSpeed(speed);
    }
  }
}

이렇게 IF~Eles IF문으로 표현을 합니다.

3. 배열로 동작 명령 제어


다음으로는 배열로 동작 명령을 등록해 놓고 Bluetooth로 수신한 명령 키값을 등록된 배열 명령 키값과 비교하여 일치한 키값에 대한 명령을 수행하는 코딩입니다. 중복된 코딩을 줄이고자 표현한 방식입니다.

이전 시간에 주행 패턴을 만들 때 배열로 했던 기억이 나실지 모르겠네요. 유사합니다. Bluetooth 앱에 조정버턴의 등록된 키 값을 문자열로 등록 합니다.

char stringVal[]="wsadzop"

전진/후진(w/s), 제자리 좌/우회전(a/d), 정지(z), 좌회전/우회전(o/p), 속도 증가/감소(m/n) 입니다. 참고로 속도는 배열에서 제외했습니다.

다음으로는 Motor 패턴 배열입니다. 즉, 두개의 Motor 회전 명령 FORWARD(1), BACKWARD(2), RELEASE(4)의 값을 위의 키값의 순서대로 배열에 위치에 Motor 회전 명령값을 저장합니다.

byte movePattern1[7]={1,2,1,2,4,1,4}; //motor1 Pattern
byte movePattern2[7]={1,2,2,1,4,4,1}; //motor2 Pattern

이렇게 하면, 배열 키값의 index랑 Motor 회전의 index은 같은 위치가 됩니다. 즉, Bluetooth로 수신된 키값이 StringVal[]의 값과 일치한 index(위치)값을 알게 되면 해당 index(위치)값의 Motor 패턴으로 회전 시키면 간단히 키값에 따른 회전 명령을 내릴 수 있게 됩니다.

  if (mySerial.available()){
    char ch = mySerial.read();
        처리문;
  }

에서,

      indexVal = 4;
      for(int i=0;i<7;i++){         
         if(ch==stringVal[i]) indexVal=i;
      }

이렇게 ch값이 순차적으로 StringVal[i]와 비교해서 일치하면 indexVal에 해당 일치한 i값을 저장하게 됩니다. 일치한 키값에 Motor 회전 명령을 수행하기 위해서 index(위치)를 찾는 문장이라고 생각하시면 됩니다. 참고로, for문에 들어가기 전 초기값이 "index=4"인 이유는 4는 정지 명령입니다. 일치한 값이 없을 경우 초기값으로 0을 두면 무조건 전진을 하겠죠. 일치한 동작만 수행하기 위해서는 일치하지 않았을 때 정지해야 겠죠. 그렇게 때문에 정지 위치인 4가 초기값으로 세팅됩니다. 만약 스마트폰에서 다른 알파펫이 들어왔을 때는 일치하지 않기 때문에 정지상태로 머물게 됩니다. 왜! 이렇게 선언했는지 아시겠지요.
만약, index(위치)의 0의 위치에 정지 명령을 넣어두시면 "index=0"으로 초기값을 선언해도 됩니다.

      indexVal = 4;
      for(int i=0;i<7;i++){      
         if(ch==stringVal[i]) indexVal=i;
      }
       motor1.run(movePattern1[indexVal]);
       motor2.run(movePattern2[indexVal]);

이렇게 표현하면, Bluetooth로 수신된 ch값과 일치한 StringVal[i]값의 index를 찾고 그 index의 위치인 movePattern1[index]와 movePattern2[index]의 Motor 회전 명령을 수행하면 키값에 회전 명령을 수행하게 됩니다.

여기까지, 내용은 Bluetooth로 수신된 키값을 회전 비교문이였다면 이제는 속도부분을 처리할 코딩을 작성해야 합니다. 속도는 키값등록 배열에서 제외 했습니다. 그렇기 때문에 Bluetooth에서 수신한 할때 개별적으로 체크해야 합니다. 다음 아래와 같은 형식으로 코딩을 하면 됩니다. Bluetooth 통신에 의해 수신된 ch값이 'm'과 'n' 키와 일치하냐고 묻고 일치하지 않으면 else 이하문으로 해서 방금 위에서 코딩한 방향키 indexVal를 찾는 로직을 수행하면 됩니다.

    if (ch == 'm') {
      speed+=10;
      if(speed>=250) speed=250;
      motor1.setSpeed(speed);
      motor2.setSpeed(speed);
    }
    else if (ch == 'n') {
      speed-=10;
      if(speed<=0) speed=0;
      motor1.run(BACKWARD);
      motor2.run(FORWARD);
    }
    else{
      indexVal = 4;
      for(int i=0;i<7;i++){      
         if(ch==stringVal[i]) indexVal=i;
      }
       motor1.run(movePattern1[indexVal]);
       motor2.run(movePattern2[indexVal]);
     }

속도 내부 로직은 지난시간에 설명을 했기 때문에 생략합니다.

종합해보면,

#include <AFMotor.h>
#include <SoftwareSerial.h>

const int rxPin = A0;
const int txPin = 2;

SoftwareSerial mySerial(rxPin, txPin); // RX, TX

AF_DCMotor motor1(3);
AF_DCMotor motor2(4);

int speed = 200;
int indexVal = 4;
char stringVal[]="wsadzop"
byte movePattern1[7]={1,2,1,2,4,1,4}; //motor1 Pattern
byte movePattern2[7]={1,2,2,1,4,4,1}; //motor2 Pattern

void setup() {
  mySerial.begin(9600);   
  motor1.setSpeed(speed);
  motor2.setSpeed(speed);
  motor1.run(RELEASE);
  motor2.run(RELEASE);
}

void loop() {
  if (mySerial.available()){
    char ch = mySerial.read();
    if (ch == 'm') {
      speed+=10;
      if(speed>=250) speed=250;
      motor1.setSpeed(speed);
      motor2.setSpeed(speed);
    }
    else if (ch == 'n') {
      speed-=10;
      if(speed<=0) speed=0;
      motor1.run(BACKWARD);
      motor2.run(FORWARD);
    }
    else{
      indexVal = 4;
      for(int i=0;i<7;i++){      
         if(ch==stringVal[i]) indexVal=i;
      }
       motor1.run(movePattern1[indexVal]);
       motor2.run(movePattern2[indexVal]);
     }
  }
}

코딩이 엄청 간소화 되었죠. 위에 배열부분은 원하는 형태로 수정하면은 loop()함수를 건들 필요가 없어 수정이 가능합니다.

4. 스마트폰으로 방향키를 누른 상태에서만 동작


스마트폰 앱에서 누르고 있는 동안에만 그 방향으로 회전하고 손을 때면 RC카가 멈추는 것으로 표현하고 싶다면 어떻게 해야 할까요. 키가 누르고 있는 동안에는 그 키 방향으로 RC카가 회전해야 하고 키를 누르지 않으면 정지해 있어야 한다면 어떤식으로 코딩을 하면 좋을까요.

지난시간에 Bluetooth 통신을 통한 조정을 하면서 문득 이런 경우에는 어떻게 제어를 하지 하고 떠오른 상상을 통해서 코딩을 수정해 보았습니다.

예전에 RFID 리더기 코딩을 하면서 카드를 RFID에 대기 전까지 계속 if문으로 무한 체크를 하는 문장이 떠오르더군요.

 if (mySerial.available()){
   처리문;
   return ;
 }
  motor1.run(RELEASE);
  motor2.run(RELEASE);

그냥 이런표현을 하면 되지 않을까 해서 코딩을 해 보았습니다. 즉, 키가 입력되면 처리문을 수행하고 loop()함수문을 빠져나오고 다시 if문으로 Bluetooth의 수신 명령이 있는지 체크하게 하면 되지 않을까 하고 상상을 했습니다. 이렇게 Bluetooth 통신을 통해 수신된 데이터가 없으면 DC 기어모터는 RELEASE로 정지 상태가 되고 수신데이터가 있으면 처리문으로 해당 회전 명령을 수행하게 하면 된다는 상상을 하게 되었네요.

하지만 이렇게 하면 너무 짧은 시간의 찰라에 전진/후진, 좌/우회전을 하고 바로 정지해버리기 때문에 사실 아무런 동작을 수행하지 않게 됩니다. 그래서, 약간의 DC 기어모터가 회전할 딜레이 시간을 주었습니다. 즉, Bluetooth를 통해 전송되는 키값의 딜레이 시간이라 아두이노에서 수신해서 DC 기어모터를 회전 시키는 딜레이시간을 적절이 조절하여 계속 회전 명령을 키를 누를때마다 수행되게 하고 누르지 않으면 정지상태로 되도록 변경했습니다.

참고로, 제가 깐 Bluetooth 앱은 누르고 있으면 연속해서 해당 키값을 전송하지 않기 때문에 일부러 딜레이 시간을 100으로 주고 테스트 했습니다.

#include <AFMotor.h>
#include <SoftwareSerial.h>

const int rxPin = A0;
const int txPin = 2;

SoftwareSerial mySerial(rxPin, txPin); // RX, TX

AF_DCMotor motor1(3);
AF_DCMotor motor2(4);
int speed =200;

void setup() {
  mySerial.begin(9600);   
  motor1.setSpeed(speed);
  motor2.setSpeed(speed);
  motor1.run(RELEASE);
  motor2.run(RELEASE);
}

void loop() {
  if (mySerial.available()){
    char ch = mySerial.read();

    switch(ch){
      case 'w':
            motor1.run(FORWARD);
            motor2.run(FORWARD);
            break;
      case 's':
            motor1.run(BACKWARD);
            motor2.run(BACKWARD);
            break;
      case 'a':
            motor1.run(BACKWARD);
            motor2.run(FORWARD);
            break;
      case 'd':
            motor1.run(FORWARD);
            motor2.run(BACKWARD);
            break;
    }      
    delay(100);
    return ;
  }
  motor1.run(RELEASE);
  motor2.run(RELEASE);
}

이렇게 키를 한번 누를때마다 0.1초동안 회전하게 되는 것이죠. 이것은 Bluetooth 앱이 연속 키를 전달하지 못하기 때문에 회전의 시간을 좀 길게 잡기 위해서 0.1초동안으로 회전을 강제적으로 했지만 연속으로 보낼 수 있는 앱이나 조이스틱이면 delay 시간값을 조정해 주세요. 0.1초도 긴 시간입니다. 1초동안에 300번의 'w'키값이 들어왔다면 1초동안 회전하는게 아니라 3초동안 회전하게 됩니다. 그러면 조정에 문제가 있겠죠. 보내는 시간과 읽는 시간에 대한 회전 딜레이를 조정하시면 원하는 RC카 조정이 이뤄집니다.

[ 결과 ]


키를 누를때 0.1동안 회전시키게 한 RC카 조정하는 영상입니다.


마무리


오늘은 코딩 이야기만 했네요. 하나의 코딩을 했다면 거기서 멈추지 말고 이렇게 상상을 계속 하시면 여러 방법으로 동일한 표현을 할 수 있습니다. 구지 동일한 표현을 다른 방식으로 코딩 할 필요가 있냐고 생각하실 수 있지만 이것은 상상코딩에서 무척 중요한 부분입니다.
코딩하는 사람들은 하나의 상상으로 결과를 얻게 되면 더이상 상상하려 하지 않습니다. 그렇게 되면 하나의 틀에 갇히게 됩니다. 코딩에서는 상상은 무척 중요합니다. 하나의 상상의 틀에 갇히면 발전은 없고 단지 엔지니어가 될 뿐이죠. 계속 이방식으로 코딩해보고 저방식으로 코딩하면서 코딩의 영역을 넓혀가시고 자신만의 색체을 지닌 코딩을 만들어 내셔야 코딩의 성장을 이룹니다.
제가 post하는 소스 코딩은 그냥 따라만 하면 제 코딩 스타일에 여러분들은 갇히게 됩니다. 자신의 코딩 스타일을 찾지 못하게 되는 것이죠. 이방법, 저방법을 시도하면서 자신만의 코딩 스타일을 만들어 내는 것이 무척 중요하니깐 꼭 여러가지 방법으로 접근해 보시면서 자신만의 스타일을 만드셨으면 합니다.


댓글()

[아두이노] 2륜 RC카 Bluetooth를 통해 스마트폰(무선) 조정하기

IOT/아두이노|2019. 5. 22. 09:00

[아두이노] 2륜 RC카 Bluetooth를 통해 스마트폰(무선) 조정하기



지난시간에는 아두이노 2륜 RC카의 주행 패턴을 실험을 해 보았습니다. 오늘은 실험한 주행 패턴을 가지고 Bluetooth로 한번 조정하는 실험을 하겠습니다. 스마트폰에서 Bluetooth 앱을 미리 깔아놓고 그 앱을 통해서 아두이노 2륜 RC카에 부착된Bluetooth로 명령을 내리고 RC카를 조정하게 됩니다. 아래 사진은 아두이노 RC카에 Bluetooth를 연결한 모습입니다.


1. 2륜 RC카 + Bluetooth 회로도


  • 준비물 : Bluetooth, DC 기어모터 2개, L293D Motor Shield, AAx4개 배터리 케이스 배터리 홀더(6V), 아두이노우노

지난시간의 2륜 RC카를 회로도에서 Bluetooth 부품만 추가 연결은 아래와 같습니다.


제가 사용한 L293D Motor Shield에는 따로 Bluetooth를 연결할 수 있는 핀이 없습니다. 대부분 0,1번핀을 이용하여 Serial 통신을 합니다. 전선으로 0, 1번핀을 아두이노와 쉴드 사이에 선을 중간에 묶거나 납탬하는 방법뿐이 없습니다. 대부분 이런식으로 실험을 하지만 자세히 L293D Motor Shield의 기판을 보시면 2번 핀에 구멍이 있고 A0~A5라는 위치에 구명이 뚫려 있습니다. 즉, 핀을 꼽을 수 있는 위치에 핀구멍이 있는데 이 핀구멍을 이용하면 Bluetooth를 쉽게 이용할 수 있습니다.

BlueTooth은 Rx, Tx 핀으로 구성되는데 읽는 핀과 출력 핀으로 나뉩니다. 즉, 2번핀은 디지털핀으로 출력이 가능하고 A0~A5핀은 아날로그핀으로 입력이 가능합니다.

BlueTooth Tx -> Motor Shield A0
BlueTooth Rx -> Motor Shield 2

이렇게 핀 구멍을 활용하면 Bluetooth 통신을 따로 선을 납땜을 할 필요 없이 연결 할 수 있습니다.


2, A0 Pin 위치는 위 사진에서 확인하시고 어느 위치인지 잘 기억해 두세요. Vcc, Gnd pin 위치도 기판에 써있으니깐 보시면 쉽게 찾을 수 있을 꺼에요.

2. 코딩


DC Motor

#include <AFMotor.h>
  • AF_DCMotor motor(3) : M3핀을 DC Motor 제어용으로 사용.
  • motor.setSpeed(200) : 모터 속도 200으로 설정
  • motor.run(FORWARD) : FORWARD, BACKWARD, RELEASE 회전 명령 중 하나를 선택해서 실행 시킴.

SoftwareSerial 통신

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

복습으로, Adafruit Industries에서 제공해주는 Motor 라이브러리 함수를 다시 post에 담았습니다. 중요한 부분이니깐 다시 살펴봐 주세요. 그리고 아두이노 내부 시리얼통신 0,1번 핀으로 실험하는게 아니라 다른 핀을 활용하기 때문에 SoftwareSerial 통신 부분도 다시 복습해 주세요.

1) Bluetooth 연결

#include <SoftwareSerial.h>

const int rxPin = A0;
const int txPin = 2;

SoftwareSerial mySerial(rxPin, txPin); // RX, TX

이렇게 해서 SoftwareSerial 통신 객체를 만들었습니다.

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

setup()함수안에다 begin()함수로 9600 통신속도로 통신을 시작합니다.

void loop() {
  if (mySerial.available()){
    char ch = mySerial.read();
  }
}

이렇게 loop()함수에서 bluetooth을 통해 들어온 데이터을 읽게 됩니다. available() 함수를 통해 수신 데이터가 있는지 체크합니다. 수신 데이터가 있게 되면 read()함수로 1byte을 읽게 되는데 char 자료형으로 한 문자를 읽어서 저장하게 됩니다.

이미 예전에 다뤘던 내용인데 혹시 잊으신 분들이 있을 수 있기 때문에 다시 설명을 드립니다.

[0,1번 시리얼통신을 할 경우]
따로, SoftwareSerial 라이브러리를 이용하실 필요는 없습니다.

void setup() {
  Serial.begin(9600);   
}
void loop() {
  if (Serial.available()){
    char ch = Serial.read();
  }
}

이렇게 기본 Serial 통신을 이용하시면 됩니다. 0,1번 Pin을 이용할 경우에 이렇게 하고 다른 Pin을 이용할 경우는 SoftwareSerial 라이브러리를 이용하셔야 합니다.

2) 스마트폰에 Bluetooth 앱 설정


안드로이드폰이면 구글스토어에서 Bluetooth 앱을 치시면 아무거나 적당한 것을 다운로드 받아서 설치하시면 됩니다.

앱에서 Controller mode를 선택합니다.



위 그림처럼 버턴에 대한 키값을 지정합니다. 오른쪽 상단에 환경설정 아이콘을 누르시면 아래 창이 뜹니다.


빈칸에 키값을 위에 표시한 알파벳을 등록하시면 됩니다. 그러면 스마트폰에서의 모든 설정은 끝납니다.

3) Bluetooth를 통해 읽은 데이터 값을 통한 동작 제어

Bluetooth를 통해 수신된 데이터 값을 통해서 RC카를 조정하도록 하겠습니다.

전진/후진, 좌/우 명령을 수행한다면 스마트폰 등록된 키값 알파벳 문자들이 수신되면 그 값에 따라서 동작명령을 내려야 합니다.

void loop() {
  if (mySerial.available()){
    char ch = mySerial.read();
  }
}

위 로직에서 if문 안에 수신된 키값에 대한 Motor를 제어하는 명령을 표현 해야 겠죠.

지난 시간 post에서 주행 패턴을 전부 삽입했습니다. 전진/후진과 좌/우만 넣어도 되지만 그냥 실험한 김에 전부 넣었습니다. 여러분들은 전진/후진과 좌/우와 정지 명령만 표현하셔서 실험하세요. 구지 아래처럼 전부 다 넣어서 실험하실 필요는 없습니다.

    switch(ch){
      case 'w':
            motor1.run(FORWARD); //전진
            motor2.run(FORWARD);
            break;
      case 's':
            motor1.run(BACKWARD); //후진
            motor2.run(BACKWARD);
            break;
      case 'a':
            motor1.run(BACKWARD); //제자리 좌회전
            motor2.run(FORWARD);
            break;
      case 'd':
            motor1.run(FORWARD); //제자리 우회전
            motor2.run(BACKWARD);
            break;
      case 'z':
            motor1.run(RELEASE); //정지
            motor2.run(RELEASE);
            break;
      case 'o':
            motor1.run(RELEASE); //좌회전
            motor2.run(FORWARD);
            break;
      case 'p':
            motor1.run(FORWARD); //우회전
            motor2.run(RELEASE);
            break;            
      case 'm':                  //속도증가
            speed+=10;
            if(speed>=250) speed=250;
            motor1.setSpeed(speed);
            motor2.setSpeed(speed);
            break;
      case 'n':                  //속도감소
            speed-=10;
            if(speed<=0) speed=0;
            motor1.setSpeed(speed);
            motor2.setSpeed(speed);
            break;
    }      

위 코딩을 보시면 속도 증가와 감소가 있는데 이 부분은 실제 변화하는 수치는 실험에 사용하는 앱에서는 확인할 수 없어서 불편합니다. 따로 앱인벤터로 전용 Bluetooth를 만들면 되는데 그냥 생략 합니다.

코딩 명령부분만 살펴보면은

speed+=10;
if(speed>=250) speed=250;

예전 post에 표현했던 방법인데 speed를 무조건 10씩 증가시키고 그다음 if문에서 max값으로 해서 그 이상 값이 나오면 max로 무조건 speed 값으로 고정시키는 표현입니다. 그러면 반대로 10씩 감소하면 if문으로 min값 이하 값이 되면 무조건 speed값을 min값으로 고정시켠 되겠죠.

speed-=10;
if(speed<=0) speed=0;

이렇게 어떤 값의 범위을 벗어날려고 하면 min or max 값으로 고정화 시키는 표현은 자주 사용하는 표현이니깐 잘 기억해 두세요. 다른 곳에서 이 표현을 사용할 수 있으니깐요.

4) 종합 소스

지난 시간의 주행 패턴을 Bluetooth 통신을 한다면 다음과 같습니다.

#include <AFMotor.h>
#include <SoftwareSerial.h>

const int rxPin = A0;
const int txPin = 2;

SoftwareSerial mySerial(rxPin, txPin); // RX, TX

AF_DCMotor motor1(3);
AF_DCMotor motor2(4);
int speed =200;

void setup() {
  mySerial.begin(9600);   
  motor1.setSpeed(speed);
  motor2.setSpeed(speed);
  motor1.run(RELEASE);
  motor2.run(RELEASE);
}

void loop() {
  if (mySerial.available()){
    char ch = mySerial.read();

    switch(ch){
      case 'w':
            motor1.run(FORWARD);
            motor2.run(FORWARD);
            break;
      case 's':
            motor1.run(BACKWARD);
            motor2.run(BACKWARD);
            break;
      case 'a':
            motor1.run(BACKWARD);
            motor2.run(FORWARD);
            break;
      case 'd':
            motor1.run(FORWARD);
            motor2.run(BACKWARD);
            break;
      case 'z':
            motor1.run(RELEASE);
            motor2.run(RELEASE);
            break;
      case 'o':
            motor1.run(RELEASE);
            motor2.run(FORWARD);
            break;
      case 'p':
            motor1.run(FORWARD);
            motor2.run(RELEASE);
            break;            
      case 'm':
            speed+=10;
            if(speed>=250) speed=250;
            motor1.setSpeed(speed);
            motor2.setSpeed(speed);
            break;
      case 'n':
            speed-=10;
            if(speed<=0) speed=0;
            motor1.setSpeed(speed);
            motor2.setSpeed(speed);
            break;
    }      
  }
}

지난시간의 패턴을 전부 키에 등록시켜서 실험하기 때문에 switch()문이 꽤! 길게 코딩이 되었네요.

간단히, 동작 FORWARD, BACKWARD, RELEASE 기준으로 Bluetooth 통신으로 읽은 ch(키값)에 해당된 모터 동작 명령을 내리면 됩니다. 주행 패턴이 지난시간에 실험한 것들과 속도까지 컨트롤 하다보니깐 코딩이 길어졌을 뿐 원리는 아래 코딩이 전부입니다.

switch(ch){
 case '키값1':  
            motor1.run(동작1);
            motor2.run(동작1);
            break;
 case '키값2':  
            motor1.run(동작2);
            motor2.run(동작2);
            break;  
}

이 코딩만 이해하시면 됩니다. switch문에서 인수 ch값과 같은 case을 찾고 그 케이스의 라인아래로 명령문들이 수행되는데 break 문을 만나면 switch문을 빠져나오는 로직입니다. 즉, ch값이 키값1이면 motor1.run(동작1)과 motor2.run(동작1) 함수를 수행한뒤에 다음 라인 break문을 만나 switch문을 빠져나오게 됩니다. 만약 동작1을 수행한 뒤에 break문이 없으면 두번째 케이스의 동작2을 수행하게 됩니다. break문이 switch문에서 꼭 필요하니깐 switch문에서 실수하지 말아주세요.

3. 결과


스마트폰으로 조정하면 이렇게 주행을 할 수 있게 됩니다. 이 주행이 정석은 아닙니다. 여러가지 표현 중 하나이고 그 하나를 그냥 소개하는 것일 뿐 다른식으로 조정을 하고 싶다면 한번 코딩에 도전 해보는 것도 괜찮습니다.


마무리


지난 시간의 주행패턴을 전부 사용하다 보니깐 코딩이 길어졌을 뿐 전진/후진, 좌/우 키와 Stop 키로 구성된 5개 키값 만으로 테스트 하셔도 됩니다. 제가 코딩한 것처럼 전부 하실 필요는 없습니다. 좌/우회전 패턴이 2종류인데 원하는 한 종류만 조정값으로 선택하셔도 됩니다.
참고로, speed 코딩이 추가되었는데 어떤식으로 코딩했는지 기억하셨다가 이 원리는 여기뿐 아니라 다른곳에서도 활용이 자주 되는 코딩이라서 원리를 꼭 기억해 주세요.

그렇게까지 어렵지 않습니다. Bluetooth 앱도 구글 검색에서 "앱인벤터 Bluetooth" 키워드로 찾으시면 유튜브 동영상 강좌나 블로그 같은 곳에서 튜토리얼로 잘 나와 있으니깐 따라서 만드시면서 자신만의 Bluetooth 앱으로 개조하셔도 됩니다.

http://ai2.appinventor.mit.edu

이곳에 가셔서 구글계정으로 로그인하면 됩니다. 그리고, 검색하셔서 튜토리얼을 따라서 하시면 됩니다.

이제 여기까지 해서 Bluetooth로 아두이노 RC카를 무선 조정까지 하였습니다. 이제 아두이노 RC카에 어떤 부품을 연결하여 재밌는 것을 만들지 한번 상상의 나래를 펼쳐보세요.


댓글()

[아두이노] RFID-RC522+Bluetooth+초음파 센서 실험

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

[아두이노] RFID-RC522+Bluetooth+초음파 센서 실험



지난 시간에 RFID-RC522 + Servo Motor를 제어하는 실험을 하였습니다. 오늘은 몇가지 상황을 정하고 그 상황에 맞게 아두이노를 제어하는 실험을 하고자 합니다.

첫번째, 실험 내용은 RFID-RC522는 카드를 읽는 동작만 수행하는 아두이노와 Servo Motor를 회전 시키는 아두이노로 나누고 싶은 때가 있을 수 있습니다. 역할 분담으로 설계를 하고 싶을 때 그 문제를 해결 하기 위해서 두 아두이노 사이의 통신을 통해서 메세지를 주고 받으면 됩니다. 통신 중의 무선 통신 모듈인 Bluetooth 통신을 접목하여 실험을 하고 싶었습니다. 실제 실험의 내용은 Card가 저장된 UID랑 일치하면 Bluetooth를 통해 스마트폰에 메세지를 전송하는 과정을 실험 하였습니다. 이렇게 메세지를 전송할 수 있다면 다른 아두이노가 있으면 그 아두이노가 블루투스로 메세지를 수신하고 그에 따른 회전 명령을 수행하면 간단히 역할을 나눠서 동작할 수 있게 됩니다. 실제로 역할 분담 실험을 하기에는 부품이 부족하여 아쉽게 실험을 할 수 없고 해서 무선 통신으로 스마트폰에 메세지를 전송하는 데 까지만 실험을 해도 충분히 실험의 목적을 달성 할 수 있기 때문에 이정선에서 실험을 하기로 결정 했습니다.

두번째, 실험 내용은 RFID-RC522 리더기로 Card를 읽고 Servo Motor가 회전 되었을 때 다시 원위치로 되돌아 가게 하는 방법을 실험하고자 합니다. 실험에 쓰인 부품은 초음파센서 입니다. Servo Motor를 출입문이라고 가정하고 Card가 인식되면 180(Open)도 회전 후 사람이 출입문을 지나가고 나면 출입문을 통과 된 상황을 설정하면 자동으로 Servo Motor가 다시 0(Close)도 회전 되어 문이 닫히게 하면 좋을 것 같아서 상상해 보았습니다. 그 역할을 초음파센서를 이용할 예정입니다. 상상을 더하면 초음파센서가 인간이 지나기 전에 가지고 있는 거리측정값에서 인간이 지나가면 초음파센서의 거리측정값이 작아집니다. 인간를 감지한 값이 됩니다. 이때 초음파 센서값을 작아졌을 때 Servo Motor를 0(Close)도 회전 시켜서 문이 닫힌다는 설정입니다. 초음파센서의 위치는 안전위치에 문이 닫혀도 되는 위치에 설치 해야 겠지요.

1. RFID-RC522의 Bluetooth 통신



실험은 RFID-RC522 리더기로 Card를 인식하면 그 인식한 값을 Bluetooth를 이용하여 스마트폰에 전송하는 실험을 하겠습니다. 본 실험의 목적은 RFID-RC522 모듈을 사용하면서 이 모듈을 통해서 얻은 데이터를 외부 다른 기기로 전송하기 위해서 입니다. 지금 하고자 하는 실험은 Bluetooth 통신으로 외부로 기기(스마트폰)으로 RFID-RC522 리더기의 결과를 보낼 수 있다면 나중에 아두이노 두대로 나눠서 Bluetooth 통신으로 하나는 RFID-RC522 리더기 제어하고 하나는 Servo Motor제어를 하게 할 수 있게 됩니다.


1) RFID-RC522 + ServoMotor+피에조부저+Bluetooth 회로도



  • 준비물 : RFID-RC522, Servo Motor, 피에조부저, Bluetooth, 아두이노우노
  • 내용 : SPI 통신을 할 수 있게 핀은 연결하고 Servo Pin 7번, 피에조부저 6번으로 연결해 보자. 2,3번 핀을 Bluetooth핀으로 사용.
  • 지난시간 회로도 참고 : [아두이노] RFID-RC522 제어

  • BlueTX - arduinoRx - 2번
  • BlueRX - arduinoTx - 3번

출처 : Fritzing

지난 시간의 회로도에서 Bluetooth 모듈이 추가로 더 연결된 회로도 입니다. 2,3번 핀만 Bluetooth로 추가로 연결하시면 됩니다.

2) 코딩



MFRC522

#include <SPI.h>
#include <MFRC522.h>
  • MFRC522 mfrc522(SS_PIN, RST_PIN) : MFRC522 인스턴스화
  • SPI.begin() :SPI bus 초기화
  • mfrc522.PCD_Init() : MFRC522 card 초기화
  • mfrc522.PICC_IsNewCardPresent() : 새카드 확인
  • frc522.PICC_ReadCardSerial() : 하나의 카드읽기 확인
  • rfid.uid.uidByte[] : 읽은 카드 키값이 들어 있음

피에조 부저

  • tone(tonePin ,음,음길이) : 음 시작
  • noTone(tonePin ) : 음 중지

Servo Motor

#include <Servo.h>
  • Servo servo : 서보모터 객체 선언
  • servo.attach(서보핀) : 서보 모터에 출력을 담당할 핀 선언
  • servo.write(회전각) : 회전각 만큰 서보모터를 회전 시킴

시리얼 통신

  • Serial.begin(9600) : 시리얼 통신 시작(9600 통신속도)
  • Serial.println(값) : 시리얼모니터 출력
  • Serial.available() : 시리얼통신으로 데이터가 들어놨는지 상태 확인
  • Serial.parseInt() : Int형으로 데이터를 읽음

Bluetooth(SoftwareSerial 통신)

#include <SoftwareSerial.h>

SoftwareSerial mySerial (rx, tx) : 소프트시리얼 객체선언(rx(수신), tx(전송))
mySerial.begin(9600) : 시리얼 통신 시작(예로 9600 통식속도를 사용해 봤네요.)
mySerial.println(값) : 데이터 전송


지난 시간 소스에서 Bluetooth를 다음과 SfotwareSerial 명령들을 추가하면 됩니다. 간단히 Serial 부분을 SoftwareSerial 형태로 변경만 해주시면 됩니다.

SoftwareSerial mySerial (rx, tx) : 소프트시리얼 객체선언(rx(수신), tx(전송))

이렇게 통신객체변수 mySerial로 선언을 해주면 다음과 같이 표현만 해주시면 됩니다.

Serial.begint(9600) => mySerial.begin(9600)
Serial.println("Open") => mySerial.println("Open")
Serial.println("Close") => mySerial.println("Close")

이렇게 SoftwareSerial 통신을 하면 됩니다.

Bluetooth 부분을 추가하여 수정 하면 다음과 같습니다.

[소스]

#include <SPI.h>
#include <MFRC522.h>
#include <Servo.h>
#include <SoftwareSerial.h>

const int RST_PIN = 9;
const int SS_PIN = 10;

MFRC522 mfrc522(SS_PIN, RST_PIN);   // MFRC522 인스턴스

byte cardkeyByte[4] = {0x55, 0xAF, 0x07, 0x88}; //card UID 
boolean state = false; //Servo Motor 상태값

Servo servo;
const int SERVO_PIN = 7;
const int TONEPIN = 6;

const int rxPin = 2;
const int txPin = 3;
SoftwareSerial mySerial(rxPin, txPin); // RX, TX

void setup() {
  Serial.begin(9600);  
  while (!Serial);  
  mySerial.begin(9600);     
  SPI.begin();         //SPI 시작
  mfrc522.PCD_Init();  //MFRC522 card 초기화
  Serial.println(F("Warning: this example clears your mifare UID, use with care!"));

  servo.attach(SERVO_PIN);
  servo.write(0);
  delay(50);
}

void loop() {
  // 새 카드 확인
  if ( ! mfrc522.PICC_IsNewCardPresent()) return; 

  // 카드 읽기
  if ( ! mfrc522.PICC_ReadCardSerial()) return;

  //카드 확인 메세지 음
  tone(TONEPIN ,523,1000/8);     // 도음으로 8분음표(음길이)
  delay(1000/4*1.30);             
  noTone(TONEPIN); 

  //읽은 Card UID와 등록된 Card UID가 일치하는 확인
  if (mfrc522.uid.uidByte[0] == cardkeyByte[0] && mfrc522.uid.uidByte[1] == cardkeyByte[1] &&
        mfrc522.uid.uidByte[2] == cardkeyByte[2] && mfrc522.uid.uidByte[3] == cardkeyByte[3] ){
   
         state=!state;    
         if(state == true){
          Serial.println("Open");
          mySerial.println("Open");
          servo.write(180);
          delay(1000);
   }
   else{          
          Serial.println("Close");
          mySerial.println("Close");
          servo.write(0);
          delay(1000);
         }
         delay(2000);
   }  
}

2) 결과


아두이노 IDE 시리얼모니터로 "Open" or "Close" 메세지 출력


실제 블루투스로 스마트폰에 깔리 블루투스 앱에 "Open" or "Close" 메세지 출력


작동 동영상 결과는 다음과 같습니다.


2. RFID-RC522 + 초음파 센서



RFID-RC522 모듈로 출입문이 열리고 닫히는 동작을 한다고 상상을 해보세요. 이때 출입문에 RFID-RC522 Card를 대면 Servo Motor를 180도 회전 시켜서 "Open" 상태가 되고 출입문를 지나가면 자동으로 Servo Motor가 0도로 회전 시켜서 "Close" 상태를 만든 다면 좀 더 그럴싸 해지겠죠. 이 표현을 하기 위해서 여러 Sensor 중 초음파 Sensor를 사용하여 그 느낌을 표현 하고자 합니다. 즉, 출입문을 지나고 출입문이 닫혀도 되는 안전지점에 초음파 Sensor를 배치하고 인간 감지를 통해 출입문을 자동으로 닫히게 한다는 설정으로 상상을 해 보았습니다. 물론 이 경우는 상황 조건 변수들을 다 고려한다면 좀 복잡해지지만 최대한 단순하게 의미만 전달하는 실험을 하는게 목적임으로 단순하게 접근하고 단순한 동작만 수행되도록 실험 하겠습니다.
실제로 모형을 만들어서 실험을 하면 좋은데 손재주가 없어서 못 만들었네요. 대충 그 의미만 전달하는 실험으로 Card를 RFID-RC522모듈에 대면 Servo Motor가 180도 회전을 하고 초음파 Sensor가 측정한 거리값을 일정 수치 이하면 인간을 감지한 걸로 판정하고 Servo Motor를 0도로 회전시키는 실험으로 그 느낌을 대신 전달하고자 합니다.


1) RFID-RC522 + 초음파 센서 회로도



부품이 하나씩 늘어날 때마다 회로도가 지져분 해지네요. 처음에는 초음파센서 두개로 연결해서 상황을 하나 더 만들려고 했는데 엄청 지져분 해져서 한개의 초음파 센서로 단순하게 초음파센서로 읽은 거리값으로 동작하게 표현하여 최대한 코딩량을 줄였네요.


2) 코딩


  • 내용 : RFID-RC522 리더기로 읽은 Card가 인식되면 Servo Motor를 180도 회전 시키고 초음파 센서로 일정거리값이하면 Servo Motor를 0도로 회전 시킨다.(거리값=10cm 실험)

위 소스를 약간만 변경하겠습니다.

  //읽은 Card UID와 등록된 Card UID가 일치하는 확인
  if (mfrc522.uid.uidByte[0] == cardkeyByte[0] && mfrc522.uid.uidByte[1] == cardkeyByte[1] &&
        mfrc522.uid.uidByte[2] == cardkeyByte[2] && mfrc522.uid.uidByte[3] == cardkeyByte[3] ){
   
         state=!state;    
         if(state == true){
          Serial.println("Open");
          mySerial.println("Open");
          servo.write(180);
          delay(1000);
   }
   else{          
          Serial.println("Close");
          mySerial.println("Close");
          servo.write(0);
          delay(1000);
         }
         delay(2000);
   }  

위 소스에서 필요한 동작은 Card가 일치하면 문만 열기게 하면 되니깐 else 이하문은 필요 없습니다. 이 부분은 초음파센서 코딩으로 넘기게 됩니다.

<
  if (mfrc522.uid.uidByte[0] == cardkeyByte[0] && mfrc522.uid.uidByte[1] == cardkeyByte[1] &&
        mfrc522.uid.uidByte[2] == cardkeyByte[2] && mfrc522.uid.uidByte[3] == cardkeyByte[3] ){
   
         state=true;
                 
         Serial.println("Open");
         mySerial.println("Open");
         servo.write(180);
         delay(1000);
    }

이렇게 해서 Card가 읽치하면 무조건 Servo Motor는 180도로 향하게 됩니다. 여기서 state 상태변수를 그냥 남겨둿는데 이것은 초음파센서 진입 변수로 재활용 할 예정입니다. 즉, Card를 대고 문이 열렸을 때만 초음파센서를 작동시킨다고 보시면 됩니다.

초음파 센서핀은 Trig, Echo핀이 있는데 Trig은 초음파를 쏘는 핀이고 Echo은 초음파를 읽는 핀입니다.

const int TRIG_PIN = 4;
const int ECHO_PIN = 5;

남는 핀중에 4,5번핀을 그냥 선택했습니다.

void setup(){
  pinMode(TRIG_PIN,OUTPUT); //초음파센서핀(TRIG)
  pinMode(ECHO_PIN,INPUT); //초음파센서핀(ECHO)
}

그리고 Trig(OUTPUT), Echo(INPUT)핀을 어떤 모드로 사용할 것인지 선언해야 합니다.

if(state == true){   
     float distance = UltrasonicDistance();
     if(distance<10){
          state=false;
          Serial.print("Close : "); 
          Serial.println(distance);          
          mySerial.println("Close");
          servo.write(0);
          delay(500);
      }
}   

state로 Card가 일치하면 문이 열리잖아요 그때 state가 true가 되니깐 초음파센서는 그 때부터서 초음파 센서가 동작해야 하기 때문에 이렇게 if문으로 "state==true"일 때 UltrasonicDistance()함수로 초음파 센서를 측정하겠다는 로직입니다. UltrasonicDistance()함수는 사용자정의함수로 직접 만든함수명입니다.

float UltrasonicDistance(){  
  digitalWrite(TRIG_PIN, LOW); 
  delayMicroseconds(2); 
  digitalWrite(TRIG_PIN,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(TRIG_PIN,LOW); 

  float duration = pulseIn(ECHO_PIN, HIGH);  
  return duration / 57.5;  
}

초음파 측정 로직인데 이 로직은 기본 아두이노홈페이지에 가시면 측정하는 코딩 로직을 예제로 소개하고 있습니다. 그 부분을 묶어서 캡슐화 해서 사용자정의 함수로 분리해 냈습니다. 이 코딩을 loop()안에다가 전부 코딩하면 오히려 가독성이 떨어지기 때문에 사용자정의함수로 분리를 한 것이죠.

     if(distance<10){
           거리가 10cm이하면 참;
             state = false;
             servo.write(0);
             delay(500);
     }

거리가 10cm이하면 문이 닫힌다는 설정문입니다. 그러면 문이 닫히면 더이상 초음파센서 측정이 필요 없으니깐 state=false로 바꾸어 주면 다시 문이 열리기 전까지는 초음파 측정을 할 필요가 없겠죠. state가 초음파센서 작동 진입 락이라고 생각하시면 됩니다.

초음파센서 로직의 대한 코딩 배치를 하면 종합 코딩은 다음과 같습니다. 새카드확인과 카드읽기 전에 초음파센서를 동작시킬건지 말건지를 먼저 수행되도록 배치 했습니다. 그 이유는 새카드확인과 카드읽기가 만족하지 않으면 거기서 return 되기 때문에 새카드확인과 카드읽기 문장 아래에 배치하면 카드를 대기 전까지 초음파센서는 측정하지 못하게 됩니다. 그래서 먼저 배치 한 것이죠. 그리고, state 상태변수로 초음파센서를 작동 상태값으로 초음파센서를 제어하게 됩니다.

[소스]

#include <SPI.h>
#include <MFRC522.h>
#include <Servo.h>
#include <SoftwareSerial.h>

const int RST_PIN = 9;
const int SS_PIN = 10;

MFRC522 mfrc522(SS_PIN, RST_PIN);   // MFRC522 인스턴스

byte cardkeyByte[4] = {0x55, 0xAF, 0x07, 0x88}; //card UID 
boolean state = false; //초음파 센서 작동 상태값

Servo servo;
const int SERVO_PIN = 7;
const int TONEPIN = 6;

const int rxPin = 2;
const int txPin = 3;
SoftwareSerial mySerial(rxPin, txPin); // RX, TX

const int TRIG_PIN = 4;
const int ECHO_PIN = 5;


void setup() {
  Serial.begin(9600);  
  while (!Serial);  
  mySerial.begin(9600);     
  SPI.begin();         //SPI 시작
  mfrc522.PCD_Init();  //MFRC522 card 초기화
  Serial.println(F("Warning: this example clears your mifare UID, use with care!"));

  pinMode(TRIG_PIN,OUTPUT); //초음파센서핀(TRIG)
  pinMode(ECHO_PIN,INPUT); //초음파센서핀(ECHO)

  servo.attach(SERVO_PIN);
  servo.write(0);
  delay(50);
}

void loop() {

  if(state == true){   
     float distance = UltrasonicDistance();
     if(distance<10){
          state=false;
          Serial.print("Close : "); 
          Serial.println(distance);          
          mySerial.println("Close");
          servo.write(0);
          delay(500);
      }
   }    
  // 새 카드 확인
  if ( ! mfrc522.PICC_IsNewCardPresent()) return; 

  // 카드 읽기
  if ( ! mfrc522.PICC_ReadCardSerial()) return;

  //카드 확인 메세지 음
  tone(TONEPIN ,523,1000/8);     // 도음으로 8분음표(음길이)
  delay(1000/4*1.30);             
  noTone(TONEPIN); 

  //읽은 Card UID와 등록된 Card UID가 일치하는 확인
  if (mfrc522.uid.uidByte[0] == cardkeyByte[0] && mfrc522.uid.uidByte[1] == cardkeyByte[1] &&
        mfrc522.uid.uidByte[2] == cardkeyByte[2] && mfrc522.uid.uidByte[3] == cardkeyByte[3] ){
   
          state = true;    
          Serial.println("Open");
          mySerial.println("Open");
          servo.write(180);
          delay(500);
   }  
}
float UltrasonicDistance(){  
  digitalWrite(TRIG_PIN, LOW); 
  delayMicroseconds(2); 
  digitalWrite(TRIG_PIN,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(TRIG_PIN,LOW); 
    
  float duration = pulseIn(ECHO_PIN, HIGH);  
  return duration / 57.5;  
}

3) 결과


아두이노 IDE 시리얼모니터로 "Open" or "Close" 메세지 출력과 함께 초음파센서가 측정된 거리(Cm)을 동시에 출력 합니다.



스마트폰에는 이전 소스와 동일하게 "Open" or "Close" 메세지 출력 합니다.


동작 동영상은 다음과 같습니다.


마무리


RFID-RC522 모듈에 부품이 하나씩 늘어나면서 코딩량이 좀 많이 늘어 났습니다. 만약, 처음부터 이렇게 회도도를 만들고 코딩량이 길었다면 아마도 RFID-RC522 모듈에 대해 이해하기가 힘들었을 꺼에요. 그리고, 이전 post를 읽지 않는다면 오늘 post가 어려울 수도 있으니깐 꼭 읽고 이 post를 보셨으면 합니다.

최근의 post를 계속 중복된 실험이 이루어지고 있는데 그 중복되 실험 의미를 잘 이해하시고 따라오셔야 합니다. 대개 개별적인 부품 실험은 쉽게 이해하시는데 2개 이상의 부품이 결합하면 어떻게 코딩해야 할지 이해하지 못하는 경우가 많습니다. 어디에 어느 명령 코딩을 삽입해야 하는지를요. RFID-RC522 모듈을 이렇게 작동합니다. 하고 끝내면 사실 이 모듈로 상상을 하더라도 다른 부품과 연결 하기는 초보분들은 쉽지 않습니다. 여러개의 부품을 연결하면 코딩에 멘붕이 발생하기도 합니다. 제가 실험한 과정을 잘 보시고 어떤 과정으로 회로도를 만들고 코딩을 했는지 이해하셨으면 합니다.

RFID-RC522 부품을 하나에서 출발해서 여기까지 왔습니다. 부족한 부분이 많고 추가해야 할 부품은 많지만 상상의 상상을 더하면 위 회로도가 아닌 여러분만의 회로도를 만들 수 있으니깐 RFID-RC522 모듈 하나에서 다시 처음부터 여러분들이 회로도를 만들고 상황을 설정하고 그 상황의 회로도를 만들고 그 상황의 코딩을 한번 해보셨으면 합니다.


댓글()

[아두이노] 조도센서 + Bluetooth 통신 제어

IOT/아두이노|2019. 4. 23. 09:00

[아두이노] 조도센서 + Bluetooth 통신 제어



오늘은 지난시간에 배운 Bluetooth를 이용하여 Servo Motor를 제어 해볼려고 했지만 스마트폰의 데이터를 아두이노로 읽었으면 그 반대로 보내는 실험을 하는 것이 더 좋을 것 같아서 반대로 보내는 실험을 해볼까 합니다. 조도센서에서히 조도 값을 아두이노에서 측정하여 Bluetooth을 이용하여 스마트폰으로 보내는 실험을 하겠습니다.


1. 조도센서



링크 된 곳은 예전에 포스트 한 내용입니다. 회로도를 공개 모드로 변경해서 올려 놨습니다. 포스트에서 두가지 타입이 있는데 아래 타입으로 조도 센서를 배치 했습니다.


2. Bluetooth 연결


링크 : [아두이노] Bluetooth 통신 제어



  • Bluetooth Rx -> Arduino txPin - pin3
  • Bluetooth TX -> Arduino rxPin - pin2

지난 시간에 설명했기 때문에 설명은 생략하겠습니다. 혹시, 기억이 안나면 위에 걸어 놓은 링크 주소로 가셔서 다시 살펴보시기 바랍니다.

3. 회로도 구성


  • 준비물 : - Bluetoooth HC-06 1개, 조도센서 1개, 저항 10k옴, 아두이노우노
  • 내용 : A0핀은 조도센서 입력핀으로 사용하고 2,3번 Bluetooth 통신핀으로 사용한다.


여기서, 제어를 담당하는 세개의 핀이 있는데 조도센서 입력 받을 핀과 Bluetooth rx, tx 핀만 정확히 연결하시면 됩니다.

4. 코딩


  • 내용 : 조도센서를 아두이노에서 측정한 데이터를 Bluetooth로 스마트폰에 보내고 그 결과를 출력하도록 하자.

SoftwareSerial 통신

#include <SoftwareSerial.h>
  • SoftwareSerial mySerial (rx, tx) : 소프트시리얼 객체선언(rx(수신), tx(전송))
  • mySerial.begin(9600) : 시리얼 통신 시작(예로 9600 통식속도를 사용해 봤네요.)
  • mySerial.println(값) : 데이터 전송

따로 코딩을 새로 만들지 않았습니다. 기존에 조도센서를 시리얼모니터로 출력 했던 소스를 그대로 bluetooth통신에 적용하겠습니다.

[조도센서+시리얼출력] : 조도센서 제어(아두이노)

int greenpin = 13;
int cdspin = A0;

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

void loop()
{
  int m_cds = map(analogRead(cdspin),0,1023,0,255);
  
  Serial.print("CDS =  ");
  Serial.println(m_cds);
  
  if(m_cds<100) { 
     digitalWrite(greenpin, HIGH);  
  }
  else {
      digitalWrite(greenpin, LOW);  
  }
  
}

위 소스에서 필요한 부분만 뽑아내면은

int cdspin = A0;
void setup()
{
    Serial.begin(9600);
}

void loop()
{
    int m_cds = map(analogRead(cdspin),0,1023,0,255);
 
    Serial.print("CDS =  ");
    Serial.println(m_cds);
}

이렇게 이 소스만 필요합니다. 여기서 Serial 개체변수명만 여러분이 선언한 블루투스객체명으로만 바꾸면 끝납니다.

수정을 하면.

#include <SoftwareSerial.h>

const int rxPin = 2;
const int txPin = 3;

SoftwareSerial mySerial(rxPin, txPin);

int cdspin = A0;

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

void loop()
{
  int m_cds = map(analogRead(cdspin),0,1023,0,255);
  
  mySerial.print("CDS =  ");
  mySerial.println(m_cds);  
  delay(1000);
}

이렇게 수정이 완료 되었네요. Serial 이름을 블루투스 통신에 사용할 객체명 mySerial로 "my"라는 단어가 앞에 추가되고 변경된 부분은 없네요.

간단하게 코딩을 완료 했습니다. 어렵게 생각하시지 마시고 평소 시리얼 모니터로 출력하는 식으로 코딩을 표현하시면 됩니다.

5. Bluetooth 통신을 하기 위한 세팅


지난 시간에는 "Controller mode"에서 실험 했다면 오늘은 "Terminal mode"에서 실험하시면 됩니다. 실행하시면 자동으로 아두이노에서 조도값을 측정하면 그 값을 바로 스마트폰으로 출력이 되어 나옵니다. "Terminal mode"에서는 스마트폰에서 데이터를 아두이노로 보낼 수 있고 아두이노에서 읽어온 데이터를 바로 출력해주는 모드입니다. 채팅모드라고 생각하시면 됩니다.

6. 결과


아래 동영상은 실행 과정을 PC에서 녹화한 장면입니다.


마무리


원래는 앱인벤터로 직접 만들어서 자신이 원하는 스타일로 앱을 만드는게 가장 좋습니다. 이미 만들어진 앱들이 구글스토어에 많지만 여러분들이 원하는 기능으로 딱 맞게 세팅 되어 있지 않습니다. 오늘 실험은 단순히 출력 데이터를 수치 상으로 보여 주기만 하면 되기 때문에 구글스토어에 등록된 앱을 사용했지만 그래프와 같은 시각적 표현을 하시고 싶다면 직접 블루투스 앱을 만드셔야 합니다.

혹시 스마트폰과 특정 센서를 연동한 뭔가를 제작 싶다면 앱인벤터를 구글에서 검색하시면 강좌가 많기 때문에 한번 제대로 공부해 보셨으면 해요.

블루투스는 사용범위가 넓기 때문에 상상를 어떻게 하냐에 따라 재밌는 작품들을 많이 만드실 수 있을거에요. 실제로 어떤 분은 자외선 센서를 이용했는데 소형으로 만들어서 스마트폰과 연동하여 실시간으로 자외선 관련 데이터 모니터링하면서 서버와 연동해서 자외선 정보를 알려주는 어플을 만드신 분도 있으시더군요. 이처럼 스마트폰과 연동하면 재밌는 것들을 만들 수 잇습니다.

여러분들도 상상력을 발휘해 보세요.


댓글()

[아두이노] Bluetooth 통신 제어

IOT/아두이노|2019. 4. 22. 09:00

[아두이노] Bluetooth 통신 제어



오늘은 Bluetooth 통신을 주제로 이야기를 할가 합니다. 원래는 나중에 할 예정이였는데 Servo Motor를 조이스틱으로 제어하고 스위치 버턴으로 제어하는 포스프를 쓰다 보니깐 나중에 Bluetooth를 포스트하고 응용 예제로 LED나 Servo Motor를 복습차원을 포스팅을 할 것 같아서 이참에 한번에 해버리는게 좋을 것 같아서 오늘의 포스트 주제로 선정했네요. 이제 본격적으로 Bluetooth 통신으로 들어 가겠습니다.


1. Bluetooth HC-06 모듈



  • Rx - 데이터 읽기
  • Tx - 데이터 보내기

두 개의 Rx, Tx만 무슨 핀인지만 아시면 됩니다. 두 핀만 잘 아두이노에 연결하면 무선 통신을 할 수 있게 됩니다.

하지만 이 Bluetooth HC-06 모듈을 아두이노에 연결할 때 잘 생각하고 연결하셔야 합니다. 물론 아무 핀에다 연결해도 상관은 없습니다만 아두이노에 연결 했을 때 해당 핀이 어떤 핀인지에 대한 정의를 머리속에서 내리셔야 합니다.

다음 회로도에서 설명하겠지만 Bluetooth Rx핀은 아두이노에서 사용 할 Tx핀에 연결해야 하고 Bluetooth Tx핀은 아두이노에서 사용 할 Rx핀에 연결해야 합니다. 이 부분만 주의하시면 됩니다. 즉, 아두이노가 보내는 데이터 핀은 아두이노 자체니깐 Tx핀이겠죠. 그게 Bluetooth가 읽으니간 Rx에 연결하는 것이고 Bluetooth가 보내는 데이터는 Bluetooth의 Tx핀에서 나와서 아두이노가 그 데이터를 읽기 위해서는 아두이노의 Rx핀에서 읽어들이겠죠. 이렇게 생각하시면 될 듯 싶네요.

2. 회로도 구성


  • 준비물 : - Bluetoooth HC-06 1개, 아두이노우노
  • 내용 : 0,1핀을 제외한 아무핀에다 연결하시오.

0,1 은 아두이노 자체 시리얼통신 핀입니다. 이곳에 Bluetooth를 연결해도 되긴 하는데 처음에 따로 연결해 주세요.


Bluetooth Tx 핀은 아두이노의 Rx 2번핀에 연결하고 Bluetooth Rx 은 아두이노의 Tx 3번핀에 연결합니다. 왜 이렇게 복잡하게 말하냐면 나중에 SoftwareSerial 라이브러리를 통해서 통신을 할 때 통신 함수의 인자 변수명을 일치시키기 위해서 입니다.

SoftwareSerial::SoftwareSerial(uint8_t rxPin, uint8_t txPin, bool inverse_logic)
  • SoftwareSerial(rxPin, txPin) : 시리얼통신 핀을 세팅합니다.

여기서 인자값은 bluetooth의 Tx=rxPin, Rx=txPin으로 넘겨지기 때문입니다.

const int rxPin = 2;
const int txPin = 3;

SoftwareSerial mySerial(rxPin, txPin);
  • Bluetooth Rx -> Arduino txPin
  • Bluetooth TX -> Arduino rxPin

대충 변수명은 SoftwareSerial 생성자 함수의 인자 명하고 일치 시켜 유사한 이름을 써주세요.

간혹 Bluetooth 이름으로 짓는 경우가 많은데 그러면 혼동이 생길 수 있습니다.

int BT_TX = 2;
int BT_RX = 3;

이럴때

SoftwareSerial mySerial(BT_TX, BT_RX);

이렇게 이름을 짓는 경우가 많습니다. Bluetooth 관점으로 핀이름을 만들다 보니 이렇게 되죠. 엄밀히 말하면 생성자 함수의 인자 네임음 (rx, tx)입니다. 그런데 들어가는 값이 블루투스의 (tx, rx)다고 해서 이렇게 이름 만들면 혼동할 수 있습니다. 될 수 있으면 함수 인자의 네임을 일치시켜 주세요.

3. 코딩


  • 내용 : 스마트폰 연결 Bluetooth를 연결하여 간단히 스마트폰에서 입력한 값을 아두이노에서 읽고 아두이노 IDE 시리얼모니터로 읽은 값을 출력을 해보자.

함수

  • Serial.begin(9600) : 시리얼통신 시작
  • Serial.write(출력값) : 시리얼모니터로 출력

SoftwareSerial 통신

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

SoftwareSerial 라이브러리에서 제공되는 기본 예제로 테스트




해당 소스에서 2,3핀을 시리얼통신 핀으로 사용 했습니다. 그리고 전송속도는 9600으로 둘 다 해버렸네요.

  • 전송 속도 : 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 31250, 38400, 57600, 115200
    다른 속도로 하셔도 됩니다.

[기본 소스] : 이 코딩은 위 아두이노공식홈페이지에 있는 기본 예제를 기반으로 약간만 수정한 소스입니다. 제가 만든게 아니라 위 아두이노 공식홈페이지에서 제공되는 오픈 예제 소스입니다.

#include <SoftwareSerial.h>

const int rxPin = 2;
const int txPin = 3;

SoftwareSerial mySerial(rxPin, txPin); // RX, TX

void setup()  
{
  Serial.begin(9600);
  while (!Serial) {
    ; 
  }
  Serial.println("Bluetooth Start!");
  mySerial.begin(9600);  
}

void loop() 
{
  if (mySerial.available()) Serial.write(mySerial.read()); 
  if (Serial.available()) mySerial.write(Serial.read()); 
}

딱히, 어려운 건 없습니다. 링크 예제를 안하더라도 대부분 Bluetooth 샘풀 테스트를 하면 위 소스와 비슷한 방식으로 다들 실험을 합니다.

(1) SoftwareSerial 헤더파일 연결
(2) SoftwareSerial 객체변수 선언
(3) 객체변수.begin(전송속도)
(4) if(객체변수.available()) { byte ch = 객체변수.read(); }
(5) Serial.write(ch);

누가 실험을 하든 이게 젤 처음 다들 하는 Bluetooth 실험 소스입니다. 그냥 자연스럽게 이렇게 코딩하게 됩니다.

여기서 loop()문은 통신을 수행하는 문장입니다.

예전에 시리얼 통신을 할 때 시리얼모니터에서 데이터를 입력하면
if (Serial.available()) {
byte ch = Serial.read();
}

이렇게 읽었습니다. 시리얼 모티터로 출력 한다면
Serial.write(출력값); 
이렇게 사용했습니다. 블루투스 통신에서도 이와 같습니다. 

if (mySerial.available()){
    Serial.write(mySerial.read()); 
}

블루투스를 통해 들어온 데이터를 시리얼모니터로 출력하는 문장입니다. 이것과 반대로 시리얼모니터로 입력한 데이터를 블루투스로 반대로 보내는 동작은 다음과 같습니다.

  if (Serial.available()) {
        mySerial.write(Serial.read()); 
    }

이전에 시리얼 통신을 해왔던 방식 그대로 표현 하시면 됩니다. 단지 블루투스를 SoftwareSerial 라이브러리를 이용하기 때문에 따로 SoftwareSerial 의 객체 변수를 선언해주고 그 객체명으로 시리얼통신함수를 사용하시면 됩니다.

간단히 정리하자면 평소 Serial 통신에서 시리얼모니터에 아두이노의 결과물을 출력했던 방식으로 동일하게 Bluetooth도 동일한 방식으로 푠현한다고 생각하시면 됩니다.

4. Bluetooth 통신을 하기 위한 세팅


1) 스마트폰에서 Bluetooth 등록


위 그림 처럼 HC-06의 이름을 가진 Blutooth가 잡힙니다. 등록하기 위해서는 비번을 쳐야 하는데 초기 비번은 "0000", "1234" 둘 중 하나로 초기 비번으로 설정 되어 있습니다. 둘 중 하나니깐 두 개 다 입력해서 잡히면 그걸로 등록하시면 됩니다. 참고로 AT 명령어로 비번을 변경할 수 있습니다.

2) 스마트 폰 Bluetooth 통신 앱 설치

전 느낌 오는 걸로 구글스토어에서 블루투스 컨트롤을 쳐서 아래 그림의 어플을 설치했네요. 결과만 보기 위해서 대충 선택했네요. 다른 것들도 많으니간 편한 걸로 설치 하시면 됩니다.


혹시, 직접 만드시고자 하시는 분들은 웹인벤터에서 앱을 만들 수 있습니다. 스크래치 방식으로 블록으로 배치하여 직접 만들 수 있는데 구글 검색 키워드로 "bluetooth 앱인벤터"로 치시면 동영상하고 블로그 게시물이 엄청 많습니다. 유튜브에서 검색하셔도 됩니다. 동영상을 보고 따라 만드시면 나중에 자신이 원하는 스타일로 개조 할 수 있습니다.

3)실행



Bluetooth를 잡아놓은 상태어서 HC-06으로 제가 현재 쓰는 Bluetooth 모듈 명이 검색 되어 있네요. 초기 이름울 AT명령으로 Bluetooth 이름을 변경 할 수 있습니다. 그냥 초기 이름을 그대로 사용합니다. 클릭을 하면 옆 그림처럼 선택 목록이 있는데 젤 위의 Controller mode를 누르면 다음과 같이 조종기가 나옵니다.



오른쪽 상단에 톱니모양을 누르면 환경 설정을 할 수 있습니다.


간단히 방향버턴을 1,2,3,4 로 세팅했네요.

이제 아두이노 IDE를 열고 결과를 테스트 하면 됩니다.

5. 결과



스마트폰에서 방향 스위치를 누르면 아두이노에 연결된 Bluetooth가 방향 스위치 값을 읽고 그 값을 아두이노에서 PC에 연결된 아두이노 IDE 시리얼모니터로 결과가 출력된다.

아래 실험 영상 녹화는 데스크탑 PC에서 녹화한 영상입니다. PC에서 스마트폰을 원격접속하여 스마트폰을 PC에서 볼 수 있게 하였고, 아두이노는 라즈베리파이에 연결되어 작동하고 라즈베리파이에 설치된 아두이노 IDE 의 시리얼모니터로 Bluetooth에서 들어온 데이터를 출력하도록 해놓았습니다. 참고로 라즈베리파이도 데스크탑 PC에서 원격 접속을 하였습니다. 결론은 녹화를 하기 위해서 PC가 스마트폰과 라즈베리파이를 동시에 원격접속하여 PC 모니터에 띄워서 둘을 동시에 조정하여 그 결과를 녹화한 영상물입니다.


위 그림처럼 스마트폰과 라즈베리파이를 원격 접속하여 창을 띄운 화면입니다. 참 번거롭게 녹화을 했네요. 스마트폰으로 찍자니 그러면 Bluetooth 어플 조정을 못하니깐 어쩔 수 없이 원격 접속하여 PC로 녹화를 하게 되었습니다.


아 'Start' 단어에 오타가 다시 녹화 하기 귀찮아서 그냥 올립니다. 참고로 영상에서 처음 실행이 되면 "AT"라는 단어를 쳐보세요 그리고 보내면 "OK"라는 단어로 리턴된 값이 시리얼모니터로 출력됩니다. 이 말은 Bluetooth가 정상적으로 동작한다는 의미가 되겠습니다.

6. AT 명령어



구글 검색하시면 레퍼런스가 많습니다. 제가 봤던 사이트 레퍼런스인데 명령어들을 잘 살펴보시면 됩니다.

Bluetooth 설정된 값을 변경할 수 있는 명령어입니다. 현재 Bluetooth 버전, 이름, 비번, 전속속도, Master와 Slave 설정 등을 할 수 있습니다. 참고로 버전이 낮을 경우 Master와 Slave은 고정되어 있는데 구매하실 때 자신이 쓰는 모델이 고정인지 겸용인지를 꼭 확인하세요.

  • AT : "OK" 메세지가 나오면 Bluetooth 정상
  • AT+VERSION : 현재 Bluetooth 사양 정보
  • AT+NAMEaaa 이름(aaa)
  • AT+PIN1234 비번(1234)
  • AT+BAUE1 전송속도(1200) 1~9,A,B,C(레퍼런스참조)

이름과 비번만 바꿔주시고 사용하시면 돼요. 추가적으로 레퍼런스를 읽어보시기 바랍니다.

마무리


if (mySerial.available()) Serial.write(mySerial.read()); 

이 한줄의 명령을 수행하기 위해서 엄청 글을 썼네요. 이 한줄이 오늘 동작하는 명령의 전부입니다. 이걸 하기 위해서 세팅하는 과정이 꽤 길었네요.

지금까지 Serial 통신으로 시리얼모니터로 결과를 출력했던 방식으로 Serial 대신에 여러분들이 작명한 이름으로 변경만 하면 통신을 할 수 있게 됩니다.

마지막으로 방향 버턴을 1,2,3,4 로 값을 세팅했잖아요. 그 값을 기준으로 Servo 머턴을 제어를 코딩해 보세요. 어제 스위치 버턴으로 Servo Motor를 제어 했는데 그 소스랑 오늘 통신 소스를 합쳐서 코딩해보시면 아마도 쉽게 코딩을 할 수 있을 거라 생각됩니다.

이걸 또 내일 포스팅하기 그러니깐요 이 부분은 여러분들이 Servo Motor를 제어하는 하는 걸로 마무리 할까 합니다.

나중에 다른 주제에 오늘 사용한 Bluetooth를 활용하는 것을 포스트 하겠습니다.


댓글()