DFRobot - Motor Shield for Arduino #3 JoyStick

2018. 10. 5. 14:55

Arduino/Shield

2A Motor Shield for Arduino Twin with JoyStick

DFRobot사의 2x2A DC Motor Shield for Arduino Twin 쉴드에 대한 세 번째 글입니다. 이번 글에서는 아날로그 방식의 조이스틱을 이용하여 모터를 제어하는 방법을 다루겠습니다.

테스트에 이용할 모터는 이전 글에서도 사용했던 DVD 드라이브를 분해하여 나온 스텝 모터이고, 프로그램 소스도 이전 글에서 작성한 것을 그대로 사용할 예정입니다.

관련글

DFRobot - Motor shield for Arduino #1 DC Motor

여기서 사용하는 모터 쉴드에 대한 소개와 기본적인 사용 방법을 설명하고 있습니다.

DFRobot - Motor shield for Arduino #2 Stepper Motor

동일한 모터 쉴드로 스텝 모터를 다루는 글이며, 이번 글에서 사용할 소스 코드에 대해 설명하고 있습니다.

DFRobot - Motor shield for Arduino #4 Bluetooth 원격 제어

이전에 사용한 DC모터를, 블루투스를 통하여 스마트폰으로 제어하는 간단한 예제를 소개합니다.

테스트를 위해 LED 세그먼트를 설치합니다.

조이스틱의 입력 값을 체크하기 위한 LED 세그먼트를 먼저 설치하겠습니다. 단지 테스트를 위한 용도이므로, 생략하고 컴퓨터 화면에서 시리얼 모니터를 활용해도 됩니다. 나중에 예제 결과를 동영상으로 만들 때 쉽게 참고하기 위함입니다.

사용할 제품은 RobotDyn에서 나온 것으로, TM1637 드라이버를 사용하였습니다. 알리 익스프레스에서 비교적 저렴한 가격에 구입할 수 있으며, 제품 마무리도 괜찮은 편입니다. 시간 표현을 위해 가운데 콜론이 존재하는 대신, 숫자 아래 도트는 표시할 수 없습니다.

관련글

아두이노로 7 segment LED 제어하기 #1

LED 모듈에 대한 소개와 라이브러리 설치 등 관련 정보가 필요하면 링크된 포스트를 참고하세요!

모듈을 사용하기 위해선 총 4개의 핀을 연결해야 합니다. 전원 관련 핀을 제외하고 딱 2개의 제어 핀만 필요하기 때문에 사용하기 쉽습니다. 제어 핀은 CLK, DIO 두 개이고, 아두이노의 디지털 포트 아무 곳에 연결한 후, 프로그램 소스상에서 해당 핀만 선언해 주면 됩니다. 라이브러리 설치에 대한 설명은 생략하겠습니다. 관련 글이나 맨 아래의 동영상을 참고하세요!

아두이노와의 연결은 위와 같습니다. 5V 입력이 필요하므로 아두이노에서 공급 받고, 라이브러리 예제 기본 값대로 D2, D3 포트에 연결하였습니다.

LED 모듈을 설치한 모습입니다. 아두이노 위에 모터 쉴드를 쌓았지만, 쉴드에도 그대로 입출력 포트가 준비되어 있기 때문에, 바로 연결할 수 있습니다.

TM1637 Library - TM1637Test
#include <Arduino.h>
#include <TM1637Display.h>
//
// Module connection pins (Digital Pins)
#define CLK 2
#define DIO 3
//
// The amount of time (in milliseconds) between tests
#define TEST_DELAY   2000
//
const uint8_t SEG_DONE[] = {
	SEG_B | SEG_C | SEG_D | SEG_E | SEG_G,           // d
	SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F,   // O
	SEG_C | SEG_E | SEG_G,                           // n
	SEG_A | SEG_D | SEG_E | SEG_F | SEG_G            // E
	};
//
TM1637Display display(CLK, DIO);
//
void setup()
{
}
//
void loop()
{
  int k;
  uint8_t data[] = { 0xff, 0xff, 0xff, 0xff };
  display.setBrightness(0x0f);
  //
  // All segments on
  display.setSegments(data);
  delay(TEST_DELAY);
  //
  // Selectively set different digits
  data[0] = 0b01001001;
  data[1] = display.encodeDigit(1);
  data[2] = display.encodeDigit(2);
  data[3] = display.encodeDigit(3);
  //
  for(k = 3; k >= 0; k--) {
	display.setSegments(data, 1, k);
	delay(TEST_DELAY);
	}
  //
  display.setSegments(data+2, 2, 2);
  delay(TEST_DELAY);
  //
  display.setSegments(data+2, 2, 1);
  delay(TEST_DELAY);
  //
  display.setSegments(data+1, 3, 1);
  delay(TEST_DELAY);
  //
  // Show decimal numbers with/without leading zeros
  bool lz = false;
  for (uint8_t z = 0; z < 2; z++) {
	for(k = 0; k < 10000; k += k*4 + 7) {
		display.showNumberDec(k, lz);
		delay(TEST_DELAY);
	}
	lz = true;
  }
  // Show decimal number whose length is smaller than 4
  for(k = 0; k < 4; k++)
	data[k] = 0;
  display.setSegments(data);
	// Run through all the dots
	for(k=0; k <= 4; k++) {
		display.showNumberDecEx(0, (0x80 >> k), true);
		delay(TEST_DELAY);
	}
  display.showNumberDec(153, false, 3, 1);
  delay(TEST_DELAY);
  display.showNumberDec(22, false, 2, 2);
  delay(TEST_DELAY);
  display.showNumberDec(0, true, 1, 3);
  delay(TEST_DELAY);
  display.showNumberDec(0, true, 1, 2);
  delay(TEST_DELAY);
  display.showNumberDec(0, true, 1, 1);
  delay(TEST_DELAY);
  display.showNumberDec(0, true, 1, 0);
  delay(TEST_DELAY);
  // Brightness Test
  for(k = 0; k < 4; k++)
	data[k] = 0xff;
  for(k = 0; k < 7; k++) {
    display.setBrightness(k);
    display.setSegments(data);
    delay(TEST_DELAY);
  }
  // On/Off test
  for(k = 0; k < 4; k++) {
    display.setBrightness(7, false);  // Turn off
    display.setSegments(data);
    delay(TEST_DELAY);
    display.setBrightness(7, true); // Turn on
    display.setSegments(data);
    delay(TEST_DELAY);
  }
  // Done!
  display.setSegments(SEG_DONE);
  while(1);
}

라이브러리에서 제공하는 기본 예제로 연결 테스트를 수행하고 정상 작동함을 확인했습니다. 소스 코드는 위 링크를 펼쳐서 확인 가능하고, 테스트 결과 영상은 이 글 마지막 동영상에 있습니다.

아날로그 조이스틱은 가변저항의 일종입니다.

아날로그 방식의 조이스틱은 X축, Y축이 각각 하나의 가변저항과 같습니다. 가변저항을 아날로그 포트에 연결하여 0 ~ 1023 범위의 값(아두이노 우노)을 입력 받아 처리하는 것처럼, 조이스틱도 동일한 방식으로 처리합니다. 따라서, 가변저항을 연결한 아날로그 포트에 바로 조이스틱을 연결하여 코드 수정 없이 그대로 적용할 수 있습니다. 먼저 가변저항에 대해 알아보겠습니다.

가변 저항은 총 세 개의 핀이 있습니다. 바깥쪽 두 개의 핀으로 전원을 공급하고, 가운데 아날로그 핀을 통하여 아두이노에 데이터를 보냅니다.

아두이노 우노에는 총 6개의 아날로그 포트가 있고, 이 중 아무 곳에나 연결한 후 소스상에서 코드로 접근합니다.

DFRobot사에서 판매하는 가변저항 모듈입니다. 간편한 테스트를 위해 이 제품으로 테스트를 진행하겠습니다. 모터 쉴드와 같은 회사 제품이라 연결이 아주 간단합니다.

이 모터 쉴드에는 위와 같이 아날로그 연결을 위한 포트가 따로 준비되어 있습니다. 아날로그 핀 따로, 전원  따로 연결할 필요 없이, 하나의 커넥터로 연결할 수 있으며, 같은 회사에서 나오는 센서들은 여기에 맞게 구성되어 케이블과 함께 판매되고 있습니다. DFRobot사는 이 방식을 Gravity라고 부릅니다. Seeed Studio사의 Grove 브랜드와 비슷하네요.

해당 가변저항 모듈의 패키지입니다. 3핀짜리 전용 케이블과 모듈만 들어 있습니다.

전용 케이블을 이용해 쉴드에 연결한 모습입니다. 연결이 훨씬 간단해졌고, 또 아두이노에서 따로 전원을 연결할 필요가 없기 때문에 포트 관리에도 도움이 됩니다. 아날로그 연결을 하지 않더라도 전원만 이용할 수도 있구요.

가변 저항 및 LED 세그먼트를 이용한 예제
#include <TM1637Display.h>

먼저, LED 세그먼트를 위한 라이브러리를 포함시켜야 합니다. 위와 같은 내용을 직접 입력해줘도 되지만, sketch 메뉴의 Include Library 항목을 이용하면 헤더 파일 이름을 몰라도 자동으로 추가할 수 있습니다.

#include <TM1637Display.h>
//
const int CLK = 2;// LED segments CLK
const int DIO = 3;// LED segments DIO
const int pot = 0;// potentiometer, A0
// display instance
TM1637Display dsp(CLK, DIO);

필요한 상수들을 선언합니다. 생략하고 그냥 숫자로된 핀 번호(2, 3, 0)를 사용해도 되지만, 위와 같이 선언한 후 사용하는 것이 프로그램 코딩을 더 쉽게 해줍니다. 그 다음, 선언한 상수들을 이용해 LED 모듈의 인스턴스를 하나 생성합니다. CLK는 디지털 2번, DIO는 3번에 연결하게 됩니다.

#include <TM1637Display.h>
//
const int CLK = 2;// LED segments CLK
const int DIO = 3;// LED segments DIO
const int pot = 0;// potentiometer, A0
// display instance
TM1637Display dsp(CLK, DIO);
//
void setup() {
  // set brightness, 0 ~ 7, before display
  dsp.setBrightness(7);
}

LED 모듈에 데이터를 출력하기 전에 먼저 밝기 세팅을 해야 합니다. 0부터 7까지 숫자로 지정하며, 7이 가장 밝은 값입니다. 프로그램 내에서 변할 일이 없기 때문에, setup() 함수에서 한 번만 실행하였습니다.

#include <TM1637Display.h>
//
const int CLK = 2;// LED segments CLK
const int DIO = 3;// LED segments DIO
const int pot = 0;// potentiometer, A0
//
TM1637Display dsp(CLK, DIO); // display instance
//
void setup() {
  // set brightness, 0 ~ 7, before display
  dsp.setBrightness(7);
}
//
void loop() {
  int val = analogRead(pot); // read A0
  dsp.showNumberDec(val); // display decimal number
}

마지막으로 loop() 함수를 작성하였습니다. 아날로그 0번(pot)에서 값을 읽어서 LED 모듈에 출력합니다. showNumberDec() 함수는 십진 숫자를 출력하는 함수이며, 다른 변환없이 가변 저항에서 읽은 값을 그대로 LED 모듈에 출력합니다.

결과 화면은 아래 동영상으로 확인할 수 있습니다. 아두이노 우노의 경우 가변 저항으로부터 입력 받은 신호를 0에서 1023까지의 정수값으로 변환하여 제공합니다.

조이스틱 연결하기

위에서 언급했듯이, 아날로그 방식의 조이스틱은 가변 저항과 작동 방식이 동일합니다. 가변 저항 대신 조이스틱을 그대로 연결하면 되고, 소스도 수정할 필요가 없습니다. 단지, 조이스틱은 스틱을 움직이지 않은 중간 상태가 존재할 뿐입니다.

가변 저항과 마찬가지로 입력 값은 0 ~ 1023의 범위를 가집니다. 정지 상태일 때 중간 값인 512를 반환하고, 스틱이 움직이는 방향에 따라 반환 값의 범위가 달라 구분이 가능합니다. 움직이는 방향 뿐만아니라, 이 값을 이용하면 자동차의 속도 제어처럼 움직임의 가중치도 적용할 수 있겠습니다.

이 글에서 사용할 조이스틱입니다. 쉽게 살 수 있는 저가형 제품으로 입력 값에 약간의 오차가 있었습니다. 그림상 표시대로 X축, Y축에 해당하며, 이 글에서는 모터가 하나라서 X축만 연결하여 테스트하였습니다.

아두이노와의 연결은 간단합니다. 전원은 아두이노 보드 따라 가고, X축 Y축 외에 디지털 스위치가 더 있습니다. 스틱을 누르면 작동되는 것이고, 필요할 경우 아두이노 디지털 포트에 연결합니다. X축만 연결할 것이기 때문에 1~3번 핀만 연결하였습니다.

조이스틱의 1~3번까지 차례대로 연결하면 되기 때문에, 3핀 커넥터로 연결하였고, 반대쪽은 모터 쉴드의 아날로그 포트에 맞게 역시 3핀 커넥터로 연결하였습니다.

가변 저항 모듈을 빼고 그대로 조이스틱을 연결한 후, 소스 수정 없이 그대로 실행하였고, 결과는 위와 같습니다. 역시 마지막 동영상에서 확인할 수 있고, 센터는 524, 최대값은 1022로 약간의 오차가 있습니다. 프로젝트에 적용할 때는 오류 보정이 필요하겠지만, 여기서는 그냥 사용하겠습니다.

조이스틱으로 모터 움직이기(코딩)

프로그램 소스는 이전 글에서 작성한 것을  기본으로 하고, 먼저 스텝 모터 구동 부분을 각각 forward(), backward() 함수로 변환하여 필요할 때마다 불러 쓸 수 있도록 변경합니다.

// constants
const int E1pin = 10;
const int E2pin = 11;
const int M1pin = 12;
const int M2pin = 13;
//
void setup() {
  // control signal out to shield
  pinMode(E1pin, OUTPUT);
  pinMode(E2pin, OUTPUT);
  pinMode(M1pin, OUTPUT);
  pinMode(M2pin, OUTPUT);
}
//
void loop() {
  for (int i = 0; i < 20; i++) {
    forward();
  }
  for (int i = 0; i < 20; i++) {
    backward();
  }
}
//
void forward() {
    // rotation order : M1+, M2+, M1-, M2-
    digitalWrite(E2pin, LOW); // to stop rotation previously
    digitalWrite(E1pin, HIGH); // M1 enable
    digitalWrite(M1pin, LOW); // positive at M1+
    delay(10); // Do not missing, needs a time
    //
    digitalWrite(E1pin, LOW); // M1 stop
    digitalWrite(E2pin, HIGH); // M2 enable
    digitalWrite(M2pin, LOW); // positive at M2+
    delay(10);
    //
    digitalWrite(E2pin, LOW); // M2 stop
    digitalWrite(E1pin, HIGH); // M1 enable
    digitalWrite(M1pin, HIGH); // positive at M1-
    delay(10);
	//
    digitalWrite(E1pin, LOW); // M1 stop
    digitalWrite(E2pin, HIGH); // M2 enable
    digitalWrite(M2pin, HIGH); // positive at M2-
    delay(10);
}
//
void backward() {
    // rotation order : M1+, M2-, M1-, M2+
    digitalWrite(E2pin, LOW); // to stop rotation previously
    digitalWrite(E1pin, HIGH); // M1 enable
    digitalWrite(M1pin, LOW); // positive at M1+
    delay(10); // Do not missing, needs a time
	//
    digitalWrite(E1pin, LOW); // M1 stop
    digitalWrite(E2pin, HIGH); // M2 enable
    digitalWrite(M2pin, HIGH); // positive at M2-
    delay(10);
	//
    digitalWrite(E2pin, LOW); // M2 stop
    digitalWrite(E1pin, HIGH); // M1 enable
    digitalWrite(M1pin, HIGH); // positive at M1-
    delay(10);
	//
    digitalWrite(E1pin, LOW); // M1 stop
    digitalWrite(E2pin, HIGH); // M2 enable
    digitalWrite(M2pin, LOW); // positive at M2+
    delay(10);
}

이전 소스를 함수를 사용한 방식으로 변경한 소스입니다. 함수에 인수도 없고 반환 값도 없기 때문에 간단히 처리할 수 있었습니다. 이제 여기에 LED 모듈과 조이스틱을 처리하는 코드를 삽입하겠습니다.

#include <TM1637Display.h>
//
// constants
const int E1pin = 10;
const int E2pin = 11;
const int M1pin = 12;
const int M2pin = 13;
//
const int CLK = 2; // LED segments CLK
const int DIO = 3; // LED segments DIO
//
const int joystick = 0; // joystick, A0
//
TM1637Display dsp(CLK, DIO); // display instance
//
void setup() {
  // control signal out to shield
  pinMode(E1pin, OUTPUT);
  pinMode(E2pin, OUTPUT);
  pinMode(M1pin, OUTPUT);
  pinMode(M2pin, OUTPUT);
  //
  dsp.setBrightness(7); // set display brightness
}
//
void loop() {
  int val = analogRead(joystick); // read A0
  dsp.showNumberDec(val); // display joystick data
  //
  if ( val < 500 ) {
    forward();
  } else if ( val > 550 ) {
    backward();
  }
}
//
void forward() {
    // rotation order : M1+, M2+, M1-, M2-
    digitalWrite(E2pin, LOW); // to stop rotation previously
    digitalWrite(E1pin, HIGH); // M1 enable
    digitalWrite(M1pin, LOW); // positive at M1+
    delay(10); // Do not missing, needs a time
    //
    digitalWrite(E1pin, LOW); // M1 stop
    digitalWrite(E2pin, HIGH); // M2 enable
    digitalWrite(M2pin, LOW); // positive at M2+
    delay(10);
    //
    digitalWrite(E2pin, LOW); // M2 stop
    digitalWrite(E1pin, HIGH); // M1 enable
    digitalWrite(M1pin, HIGH); // positive at M1-
    delay(10);
	//
    digitalWrite(E1pin, LOW); // M1 stop
    digitalWrite(E2pin, HIGH); // M2 enable
    digitalWrite(M2pin, HIGH); // positive at M2-
    delay(10);
}
//
void backward() {
    // rotation order : M1+, M2-, M1-, M2+
    digitalWrite(E2pin, LOW); // to stop rotation previously
    digitalWrite(E1pin, HIGH); // M1 enable
    digitalWrite(M1pin, LOW); // positive at M1+
    delay(10); // Do not missing, needs a time
	//
    digitalWrite(E1pin, LOW); // M1 stop
    digitalWrite(E2pin, HIGH); // M2 enable
    digitalWrite(M2pin, HIGH); // positive at M2-
    delay(10);
	//
    digitalWrite(E2pin, LOW); // M2 stop
    digitalWrite(E1pin, HIGH); // M1 enable
    digitalWrite(M1pin, HIGH); // positive at M1-
    delay(10);
	//
    digitalWrite(E1pin, LOW); // M1 stop
    digitalWrite(E2pin, HIGH); // M2 enable
    digitalWrite(M2pin, LOW); // positive at M2+
    delay(10);
}

최종 프로그램 소스입니다. 조이스틱과 LED 모듈을 위한 코드는 앞에서 작성한 예제에서 그대로 복사해서 붙였고, 함수 호출 부분은 좌측, 정지, 우측으로 동작을 구분하여, 범위 값을 약간 여유를 두어 IF문으로 작성하였습니다.

여기 까지 모터 쉴드와 조이스틱을 이용한 모터 제어 방법에 대해 다루었고, 모든 예제의 작동 영상은 아래 동영상을 통해 확인하실 수 있습니다. LED 모듈과 조이스틱을 위한 코드를 추가하자 모터 구동 속도가 현저하게 느려졌습니다. 이는 이 모터 쉴드가 스텝 모터 제어에는 어울리지 않기 때문이며, 모터와 쉴드에서 엄청난 발열을 경험할 수 있습니다. 다음 글에서는 조이스틱 대신 블루투스를 이용하여 스마트폰으로 간단하게 제어하는 예제를 작성해 보겠습니다.


Comments