극한의 아두이노 DIY생활 - NFC RC카6

in kr-dev 커뮤니티2 years ago

안녕하세요! Jimae입니다.

NFC를 어느정도 정리가 끝났고 이제 동력원인대 이것은 스태핑 모터를 2개를 사용했습니다.

모터스팩은 자세히 생각은 안나는대 저번 RC카를 정리하면서 썻던 모터드라이버와 스태핑 모터 2개를 추가했다고 보시면 됩니다.

그래서 이것도 마찬가지로 일정한 거리를 움직이게 구현을 했기때문에 별도로 적지를 않겠습니다.

자 그러면 프로그램이 컨트롤 부분과 RC카 2가지 부분이기에 우선 RC카 프로그램을 공개하자면

#include <AddicoreRFID.h>
#include <SPI.h>
#include <SoftwareSerial.h>
#include <MsTimer2.h>

#define START 1       
#define STOP 0

#define MAX_LEN 16

#define MOTOR_ENABLE    4

#define MOTOR_M1    A5
#define MOTOR_M0    A4 
#define MOTOR_DIR_R   8    
#define MOTOR_CLK_R   7
#define MOTOR_DIR_L   6    
#define MOTOR_CLK_L   5

#define ONESTEPANGLE 0.1125
#define ONEDEGREE 0.0454 // 2 * 파이* r * (1/ 360) (cm)


#define STRAIGHTDISTANCE 15 // 단위 (cm)
#define ROTATIONDISTANCE 8.4 // 단위 (cm)
// 흰색 RC_CAR 회전 8.5cm
// 검정색 RC_CAR 회전 8.4cm
// 빨간색 RC_CAR 회전 8.5cm
// 녹색 RC_CAR 회전 8.3cm

#define STRAIGHTANGLE ((unsigned int)(STRAIGHTDISTANCE / ONEDEGREE))        // 움직여야할 거리 / 1도당 움직일 거리 (직진,후진)
#define ROTATIONANGLE ((unsigned int)(ROTATIONDISTANCE / ONEDEGREE))        // 움직여야할 거리 / 1도당 움직일 거리 (회전)

#define LEDRED A0
#define LEDYELLOW A1
#define LEDGREEN A2
#define LEDBLUE A3

#define SPECIALCODE 0xAA

enum{GO = 0x41,ARRIVE,COMPLETE,CAHNGE,ADVANCE,BACK,RIGHT,LEFT};   // 명령코드들
enum{COMMANDREAD = 0,COMMANDANALYSIS,CHANGENFCREAD,CHANGELED,MOTORDRIVING,MOTORNFCREAD,COMPLETEANSWER};       // 프로그램 동작순서
  
AddicoreRFID RcRFID; // create AddicoreRFID object to control the RFID module
SoftwareSerial BTSerial(2, 3);   //bluetooth module Tx:Digital 2 Rx:Digital 3

const unsigned char NFCID[30][2] = {{0x6F, 0xDA}, // 1 
                                    {0x22, 0xCE}, // 2
                                    {0x3E, 0xC9}, // 3
                                    {0xFB, 0xD8}, // 4 
                                    {0x17, 0xCB}, // 5  
                                    {0x56, 0xCC}, // 6
                                    {0x85, 0xCD}, // 7
                                    {0xC5, 0xCB}, // 8 
                                    {0x1D, 0xD1}, // 9
                                    {0x6E, 0xCE}, // 10 
                                    {0x11, 0xD2}, // 11
                                    {0x0C, 0xD8}, // 12
                                    {0xF5, 0xD3}, // 13
                                    {0xCC, 0xCD}, // 14
                                    {0x87, 0xCB}, // 15
                                    {0x29, 0xC8}, // 16
                                    {0x36, 0xCC}, // 17
                                    {0x1E, 0xD3}, // 18
                                    {0x92, 0xD6}, // 19
                                    {0xD6, 0xDE}, // 20
                                    {0x1D, 0xE2}, // 21
                                    {0xA6, 0xC9}, // 22
                                    {0x91, 0xDC}, // 23
                                    {0xE0, 0xDD}, // 24
                                    {0xB2, 0xCB}, // 25
                                    {0xC4, 0xD7}, // 26
                                    {0x43, 0xCF}, // 27
                                    {0xC8, 0xD7}, // 28
                                    {0xA1, 0xCF}, // 29
                                    {0xCB, 0xD8}}; // 30
                                    
                                         

const int chipSelectPin = 10;

unsigned char nowId[2] = {0,};            // NFC 에서 직접 읽은 데이터를 저장
unsigned char commandBugffer[3] = {0.};   // 컨트롤러에서 들어온 데이터를 저장
unsigned char commandBufferAddress = 0;   // 컨트롤러에서 들어온 데이터 주소를 저장
unsigned char answerData[3] = {0.};       // 다시 컨트롤러로 보낼 데이터를 저장

unsigned char programStep = COMMANDREAD;   // 프로그램 전체적인 순서를 관장 

unsigned char rcCarID = 1;    // ID값을 저장 기본적으로는 1번
unsigned char nfcNumber = 0;  // NFC에서 들어온 데이터를 가공한 번호를 저장

unsigned char timeFlag = STOP;  // 타이머 플래그
unsigned int delayCount = 0;   // 1초단위 타이머를 카운트

void setup() { 
                 
  Serial.begin(9600);                        // RFID reader SOUT pin connected to Serial RX pin at 9600bps 
  BTSerial.begin(9600);  
 
  // start the SPI library:
  SPI.begin();      // RFID 기능을 쓰기위해 초기화
  
  pinMode(chipSelectPin,OUTPUT);              // Set digital pin 10 as OUTPUT to connect it to the RFID /ENABLE pin 
  digitalWrite(chipSelectPin, LOW);         // Activate the RFID reader

  RcRFID.AddicoreRFID_Init();  // RFID 기능 초기화
  MotorInit();    // 모터 초기화

  LEDInit();      //LED 초기화

  MsTimer2::set(1000, TimeCount); // TimeCount함수를 1초마다 호출한다
  MsTimer2::start();
}

void loop()
{   
  
 //RFIDRead();
 //Serial.println(RFIDIdCompare()); //RFID 테스트
 
/*    
 unsigned char num = 0;   //LED 테스트
  
 for(num = 1; num < 5; num++) 
 {
  LEDSelect(num);
  delay(500);
 }
 */ 

  /*
  MotorDirection(ADVANCE);          // 모터 움직임 테스트 전진,후진,왼쪽,오른쪽 순으로
  MotorAction(STRAIGHTANGLE,500);
  delay(5000); 
  MotorDirection(BACK);
  MotorAction(STRAIGHTANGLE,500);
  delay(5000); 
  
  
  MotorDirection(LEFT);
  MotorAction(ROTATIONANGLE,500);
  delay(5000); 
  
  MotorDirection(RIGHT);
  MotorAction(ROTATIONANGLE,500);
  delay(5000); 
  */

  RCCarProgram();   // 실제 프로그램
}

void RCCarProgram(void)
{
  unsigned char num = 0;
  switch(programStep)
  {
    case COMMANDREAD:
                     if (BTSerial.available())    // 컨트롤러에서 들어오는 데이터가 있을때
                     {
                        commandBugffer[commandBufferAddress++] = BTSerial.read();   // 데이터를 읽고

                        //Serial.println(commandBugffer[commandBufferAddress++],HEX);

                        if(commandBufferAddress >= 3)   // 3개 이상의 데이터가 들어왔을때
                        {
                          commandBufferAddress = 0; 
                          
                          programStep = COMMANDANALYSIS;    // 데이터를 분석하는 구역으로 변경
                          
                          break;
                        }
                     }
                     break;
                     
    case COMMANDANALYSIS:     // 데이터 분석
                         if(commandBugffer[0] == 0xAA && commandBugffer[1] == 0xAA && commandBugffer[2] == CAHNGE)  // 특수코드 LED 및 ID 변환하는 명령어가 들어올때
                         {
                          programStep = CHANGENFCREAD; // 변환하는 구역으로 변경
                          break;
                         }
                         else if(commandBugffer[0] == rcCarID)    // 그게아니라 모터움직이는 명령어가 들어올때
                         {
                          programStep = MOTORDRIVING;   // 모터 움직이는 구역으로 변경
                          break;
                         }
                         programStep = COMMANDREAD;   // 그게아니면 다시 읽기
                         break;
    case CHANGENFCREAD:  // ID,LED 변경하기전 NFC값 읽기
                       if(RFIDRead() == MI_OK)   // 우선 NFC 값을 읽는다
                       {
                        nfcNumber = RFIDIdCompare();  // 읽은 NFC값을 위에 아이디 배열과 비교해서 가공하여 번호로 저장
                        programStep = CHANGELED;    // LED 변경 및 보낼데이터 정하는 구역으로 변경 
                       }
                     
                       break;
    case CHANGELED:     // LED 변경 및 보낼데이터 정하기
                   rcCarID = LEDDataChange(nfcNumber);  //NFC 값 14 = 1,15 = 2,16 = 3,17 = 4 의 값으로 나와서 저장

                   LEDSelect(rcCarID);  // LED 변경

                   answerData[0] = rcCarID;   // 보낼데이터 ID값 저장
                   answerData[1] = 0xAA;      // 특수코드
                   answerData[2] = COMPLETE;  // 확인명령어 

                   programStep = COMPLETEANSWER;  // 컨트롤러로 데이터 보내는 구역으로 전환
                  
                   break;
    case MOTORDRIVING:
                      MotorDirection(commandBugffer[2]);     // 명령어에 3번째 데이터 가야할 방향으로 모터 셋팅
                      
                      if(commandBugffer[2] == ADVANCE || commandBugffer[2] == BACK)  MotorAction(STRAIGHTANGLE,500);    // 전진, 후진 모터 움직임
                      else  MotorAction(ROTATIONANGLE,500);   // 회전 모터 움직임

                      timeFlag = START;                   //타이머 시작
                      programStep = MOTORNFCREAD;         //모터 움직이고난후  NFC데이터 확인하는 구역으로 변경
                      break;

    case MOTORNFCREAD:
                      if(RFIDRead() == MI_OK)       //NFC 값을 읽은후
                      {
                        nfcNumber = RFIDIdCompare();  // 비교하고

                        answerData[0] = rcCarID;      // 현재 RCcar의 ID 저장
                        answerData[1] = nfcNumber;    // 현재 NFC 위치
                        answerData[2] = ARRIVE;       // 도착한 명령어
                        
                        programStep = COMPLETEANSWER;   // 컨트롤러로 데이터 보내는 구역으로 전환
                      }
                      else
                      {
                        if(delayCount >= 2)  // 2초 대기  NFC값 안읽힐 경우 무시하고 움직이기 위한 루틴
                        {

                          answerData[0] = rcCarID;           // 현재 RCcar의 ID 저장
                          answerData[1] = commandBugffer[1]; // 아까 명령어로 읽어들여온 가야할 NFC 위치
                          answerData[2] = ARRIVE;            // 도착한 명령어
                          
                          timeFlag = STOP;   // 타이머 스탑
                          delayCount = 0; 
                          programStep = COMPLETEANSWER;  // 컨트롤러로 데이터 보내는 구역으로 전환
                        }
                      }
                      
                      break;

    case COMPLETEANSWER:      //블루투스 쪽 응답 
                        for(num = 0; num < 3; num++)
                        {
                          BTSerial.write(answerData[num]);    // 데이터 3개 보낸다

                          answerData[num] = 0;        // 보낸 데이터 초기화

                          commandBugffer[num] = 0;    // 들어온 데이터 초기화
                        }

                        programStep = COMMANDREAD;    // 다시 처음으로 명령어를 읽기위해 돌아감
                        
                        break;
    
    default:
           break;
  }
}

unsigned char RFIDRead(void)    // RFID값을 읽기위한 함수
{
  unsigned char status;
  unsigned char str[MAX_LEN];

  //Find tags, return tag type
  RcRFID.AddicoreRFID_Request(PICC_REQIDL, str); 

  //Anti-collision, return tag serial number 4 bytes
  status = RcRFID.AddicoreRFID_Anticoll(str);
  if (status == MI_OK)
  {
          nowId[0] = str[2];
          nowId[1] = str[3];
/*
           Serial.print(nowId[0],HEX);       // 고장난 NFC 스티커 여기서 주석풀어서 읽으시면 나옵니다.
           Serial.print(" ");
           Serial.println(nowId[1],HEX);
 */             
            // Should really check all pairs, but for now we'll just use the first
            if(str[0] == 156)                      //You can change this to the first byte of your tag by finding the card's ID through the Serial Monitor
            {
                Serial.print("Hello Craig!\n");
            } else if(str[0] == 244) {             //You can change this to the first byte of your tag by finding the card's ID through the Serial Monitor
                Serial.print("Hello Erin!\n");
            }
  }

  delay(1000);   // NFC 읽는 1초딜레이 너무느리면 바꾸세요 단위는 ms 
    
  RcRFID.AddicoreRFID_Halt();      //Command tag into hibernation

  return status;
}

unsigned char RFIDIdCompare(void)   //RFID 값을 1~30번 형태로 변환하기 위한 함수
{
  unsigned char locationNumber = 0;
  for(unsigned char num = 0; num < 30; num++)
  {
    if(NFCID[num][0] == nowId[0] && NFCID[num][1] == nowId[1]) locationNumber = num + 1;
  }

  return locationNumber;
}

void MotorInit(void)    // 모터 초기화 함수
{
  pinMode(MOTOR_CLK_L, OUTPUT);
  pinMode(MOTOR_DIR_L, OUTPUT);
  pinMode(MOTOR_CLK_R, OUTPUT);
  pinMode(MOTOR_DIR_R, OUTPUT);
  pinMode(MOTOR_M0, OUTPUT);
  pinMode(MOTOR_M1, OUTPUT);
  pinMode(MOTOR_ENABLE, OUTPUT);  

  digitalWrite(MOTOR_M1, LOW);            // Motor M2 as LOW
  digitalWrite(MOTOR_M0, LOW);            // Motor M1 as LOW  sixteenth mode
}

void MotorAction(unsigned int motorAngle,unsigned int motorSpeed)   // 모터 제어 함수  PWM 대신 딜레이를 사용하여 모터제어
{
  double stepCalculation = 0;

  stepCalculation = motorAngle / ONESTEPANGLE;    // 움직여야할 모터각도를 스텝으로 계산

  motorSpeed += 400; 
  
  digitalWrite(MOTOR_ENABLE, HIGH);       // Motor Enable as HIGH
  
  for(unsigned int num = 0; num < (unsigned int)stepCalculation; num++)
  {
      if(num < 400) motorSpeed--;
      else if((unsigned int)(stepCalculation - num) < 600) motorSpeed++;  //모터 속도 제어
      
      digitalWrite(MOTOR_CLK_L, LOW);     // Motor CLK_L as LOW    
      digitalWrite(MOTOR_CLK_R, LOW);     // Motor CLK_R as LOW          
      delayMicroseconds(motorSpeed);                          // wait for a second
      digitalWrite(MOTOR_CLK_L, HIGH);    // Motor CLK_L as HIGH     
      digitalWrite(MOTOR_CLK_R, HIGH);    // Motor CLK_R as HIGH 
      delayMicroseconds(motorSpeed);   
  }

  digitalWrite(MOTOR_ENABLE, LOW);       // Motor Enable as HIGH
}

void MotorDirection(unsigned char moDirection)    // 가야할 모터 방향 제어
{
  switch(moDirection)
  {
    case ADVANCE:
                 digitalWrite(MOTOR_DIR_R, LOW);         // Motor DIR_R as CW (LOW)
                 digitalWrite(MOTOR_DIR_L, HIGH);         // Motor DIR_L as CW (LOW)
                 break;
    case BACK:
                 digitalWrite(MOTOR_DIR_R, HIGH);         // Motor DIR_R as CW (LOW)
                 digitalWrite(MOTOR_DIR_L, LOW);         // Motor DIR_L as CW (LOW)
                 break;
    case RIGHT:                 
                 digitalWrite(MOTOR_DIR_R, HIGH);         // Motor DIR_R as CW (LOW)
                 digitalWrite(MOTOR_DIR_L, HIGH);         // Motor DIR_L as CW (LOW)
                 break;
    case LEFT:
                 digitalWrite(MOTOR_DIR_R, LOW);         // Motor DIR_R as CW (LOW)
                 digitalWrite(MOTOR_DIR_L, LOW);         // Motor DIR_L as CW (LOW)
                 break;

    default:
                 break;
  }
}

void LEDInit(void)    // LED 초기화
{
   pinMode(LEDRED,OUTPUT);    
   pinMode(LEDYELLOW,OUTPUT);  
   pinMode(LEDGREEN,OUTPUT);  
   pinMode(LEDBLUE,OUTPUT);  
                
   digitalWrite(LEDRED, LOW);        
   digitalWrite(LEDYELLOW, LOW);    
   digitalWrite(LEDGREEN, LOW);    
   digitalWrite(LEDBLUE, LOW);    
}

unsigned char LEDDataChange(unsigned char nfcNumber)    // LED 및 ID 데이터를 변경 14 = 1, 15 = 2, 16 = 3, 17 = 4
{
  unsigned char dataBuffer = 0;
  if(nfcNumber == 14) dataBuffer = 1;
  else if(nfcNumber == 15) dataBuffer = 2;
  else if(nfcNumber == 16) dataBuffer = 3;
  else if(nfcNumber == 17) dataBuffer = 4;
  else dataBuffer = 0;

  return dataBuffer;
}

void LEDSelect(unsigned char ledNumber)   // LED 변경
{
  switch(ledNumber)
  {
    case 1: 
           digitalWrite(LEDRED, LOW);        
           digitalWrite(LEDYELLOW, LOW);    
           digitalWrite(LEDGREEN, LOW);    
           digitalWrite(LEDBLUE, HIGH);    
           break;
    case 2:           
           digitalWrite(LEDRED, LOW);        
           digitalWrite(LEDYELLOW, LOW);    
           digitalWrite(LEDGREEN, HIGH);    
           digitalWrite(LEDBLUE, LOW);       
           break;
    case 3:
           digitalWrite(LEDRED, LOW);        
           digitalWrite(LEDYELLOW, HIGH);    
           digitalWrite(LEDGREEN, LOW);    
           digitalWrite(LEDBLUE, LOW);   
           break;
    case 4:
           digitalWrite(LEDRED, HIGH);        
           digitalWrite(LEDYELLOW, LOW);    
           digitalWrite(LEDGREEN, LOW);    
           digitalWrite(LEDBLUE, LOW);    
           break;
    default:
           digitalWrite(LEDRED, LOW);        
           digitalWrite(LEDYELLOW, LOW);    
           digitalWrite(LEDGREEN, LOW); 
           digitalWrite(LEDBLUE, LOW);    
           break;      
  }
}

void TimeCount(void)      // 1초마다 걸리는 타이머 인터럽트 함수
{
  if(timeFlag != START) return;   // 플래그를 사용해서 인터럽트 함수 제어
  
  delayCount++;
}

이렇게 되고요 RC카가 총 4개이고 이것이 각각움직이지만 하나의 프로그램으로써 동작을 해야했기에 프로그램을 이런식으로 구성했습니다.

설명은 다음에 하도록 하겠습니다.

다들 좋은하루 되세요.

극한의 아두이노 DIY생활 - NFC RC카1
극한의 아두이노 DIY생활 - NFC RC카2
극한의 아두이노 DIY생활 - NFC RC카3
극한의 아두이노 DIY생활 - NFC RC카4
극한의 아두이노 DIY생활 - NFC RC카5

Sort:  
 2 years ago 

[광고] STEEM 개발자 커뮤니티에 참여 하시면, 다양한 혜택을 받을 수 있습니다.

Coin Marketplace

STEEM 0.20
TRX 0.12
JST 0.029
BTC 60744.52
ETH 3381.73
USDT 1.00
SBD 2.57