아두이노와 레고 RC카 (2)

드디어 완성된 모습.

모터고정이 불안정해서 다소 탁탁거리긴 하지만 뭐 작동엔 이상이 없다.

준비물

—-아래는 옵션—-

  • 16*2 LCD
  • LCD 인터페이스 변환 어댑터, I2C로 전환 (원래 LCD는 포트낭비가 많은데 이걸 이용하면 4포트 밖에 안쓴다.)

간단히 모터쉴드와 블루투스 모듈만 있으면 되겠다. 모터쉴드는 꼭 저 모델아니더라도 모터 2개를 운용할 수 있으면 된다.

그리고 블루투스를 연결할 스마트폰 앱이 필요한데
안드로이드에는 BlueStick Control 이라는 잘 만들어진 앱이 있어서 이걸 사용했다.

다음은 소스.

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <SoftwareSerial.h>

LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x20 for a 16 chars and 2 line display
SoftwareSerial btSerial(11,12); // RX, TX of bluetooth

const int moter1_ctrl = 2;
const int moter1 = 3;
const int moter2_ctrl = 4;
const int moter2 = 5;

const int kCodeUp = 56;
const int kCodeDown = 50;
const int kCodeLeft = 52;
const int kCodeRight = 54;
const int kCodeGrab = 67;
const int kCodeRelease = 68;
const int kRotateL = 69;
const int kRotateR = 70;
const int kCodeBtnUp = 48;

int op_m1_run = LOW;
int op_m2_run = LOW;
int op_m1_direct = HIGH;
int op_m2_direct = HIGH;

int last_cmd = 0;

void setup()
{
  Serial.begin(9600);
  btSerial.begin(9600);
  lcd.init();
  lcd.backlight();
  lcd.print("Remote Car");

  pinMode(moter1, OUTPUT);
  pinMode(moter1_ctrl, OUTPUT);
  pinMode(moter2, OUTPUT);
  pinMode(moter2_ctrl, OUTPUT);
  digitalWrite(moter1_ctrl, HIGH);
  digitalWrite(moter2_ctrl, HIGH);
}

String formatMotorStatus(String name, int run, int direct) {
  String str;
  str = name + ":";
  str += run == HIGH ? " ON " : " OFF";
  str += direct == HIGH ? " FWD" : " BWD";
  return str;
}

void runCommand(int cmd) {
  switch (cmd) {
  case kCodeUp:
    op_m1_run = HIGH;
    op_m1_direct = HIGH;
    break;
  case kCodeDown:
    op_m1_run = HIGH;
    op_m1_direct = LOW;
    break;
  case kCodeLeft:
    op_m2_run = HIGH;
    op_m2_direct = HIGH;
    break;
  case kCodeRight:
    op_m2_run = HIGH;
    op_m2_direct = LOW;
    break;
  case kCodeGrab:
    break;
  case kCodeRelease:
    break;
  case kRotateL:
    break;
  case kRotateR:
    break;
  case kCodeBtnUp:
    op_m1_run = LOW;
    op_m2_run = LOW;
    break;
  }

  digitalWrite(moter1, op_m1_run);
  digitalWrite(moter2, op_m2_run);
  digitalWrite(moter1_ctrl, op_m1_direct);
  digitalWrite(moter2_ctrl, op_m2_direct);
}

void display() {
  lcd.clear();
  lcd.print(formatMotorStatus("M1", op_m1_run, op_m1_direct));
  lcd.setCursor(0, 1);
  lcd.print(formatMotorStatus("M2", op_m2_run, op_m2_direct));
}

void commandLoop(const String& line) {
  int start = 0;
  int end = 2;
  while (end <= line.length()) {
    // Split into each command
    char buf[4] = {0,};
    line.substring(start, end).toCharArray(buf, 4);
    int cmd = atoi(buf);

    // Avoid command duplicated
    if (cmd != last_cmd) {
      Serial.print(cmd);
      runCommand(cmd);
      display();
    }

    last_cmd = cmd;
    start += 2;
    end += 2;
  }
}

void loop() { 
  String line;
  if (!btSerial.available()) {
    return;
  }

  line += btSerial.read();
  commandLoop(line);

  delay(50);
}

아래는 LCD 사용로직을 제거한 코드

#include <SoftwareSerial.h>

SoftwareSerial btSerial(11,12); // RX, TX of bluetooth

const int moter1_ctrl = 2;
const int moter1 = 3;
const int moter2_ctrl = 4;
const int moter2 = 5;

const int kCodeUp = 56;
const int kCodeDown = 50;
const int kCodeLeft = 52;
const int kCodeRight = 54;
const int kCodeGrab = 67;
const int kCodeRelease = 68;
const int kRotateL = 69;
const int kRotateR = 70;
const int kCodeBtnUp = 48;

int op_m1_run = LOW;
int op_m2_run = LOW;
int op_m1_direct = HIGH;
int op_m2_direct = HIGH;

int last_cmd = 0;

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

  pinMode(moter1, OUTPUT);
  pinMode(moter1_ctrl, OUTPUT);
  pinMode(moter2, OUTPUT);
  pinMode(moter2_ctrl, OUTPUT);
  digitalWrite(moter1_ctrl, HIGH);
  digitalWrite(moter2_ctrl, HIGH);
}

void runCommand(int cmd) {
  switch (cmd) {
  case kCodeUp:
    op_m1_run = HIGH;
    op_m1_direct = HIGH;
    break;
  case kCodeDown:
    op_m1_run = HIGH;
    op_m1_direct = LOW;
    break;
  case kCodeLeft:
    op_m2_run = HIGH;
    op_m2_direct = HIGH;
    break;
  case kCodeRight:
    op_m2_run = HIGH;
    op_m2_direct = LOW;
    break;
  case kCodeGrab:
    break;
  case kCodeRelease:
    break;
  case kRotateL:
    break;
  case kRotateR:
    break;
  case kCodeBtnUp:
    op_m1_run = LOW;
    op_m2_run = LOW;
    break;
  }

  digitalWrite(moter1, op_m1_run);
  digitalWrite(moter2, op_m2_run);
  digitalWrite(moter1_ctrl, op_m1_direct);
  digitalWrite(moter2_ctrl, op_m2_direct);
}

void commandLoop(const String& line) {
  int start = 0;
  int end = 2;
  while (end <= line.length()) {
    // Split into each command
    char buf[4] = {0,};
    line.substring(start, end).toCharArray(buf, 4);
    int cmd = atoi(buf);

    // Avoid command duplicated
    if (cmd != last_cmd) {
      Serial.print(cmd);
      runCommand(cmd);
    }

    last_cmd = cmd;
    start += 2;
    end += 2;
  }
}

void loop() { 
  String line;
  if (!btSerial.available()) {
    return;
  }

  line += btSerial.read();
  commandLoop(line);

  delay(50);
}

자 일단 처음 목표했던건 만들었고
다음은… 여기다 캠을 달아볼까?? ㅋㅋ

// 수정: 2015.05.30
두번째의 LCD가 제거된 코드에 블루투스 초기화 로직이 빠져있었다..ㄷㄷㄷ
첫번째 코드에는 있는 내용인데 LCD관련 코드 제거하면서 빠졌나보다.
그덕에 괜한 사람들이 삽질을…;

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중

This site uses Akismet to reduce spam. Learn how your comment data is processed.