현재 상태 : PC와의 UPD 통신, PWM, Motor Controller 용 GPIO, Encoder Reading 완료.

추가할 내용 : Timer Interrupt, I2C for GY-BNO055, PID 

#include <WiFi.h>
#include <WiFiUdp.h>
#include <Wire.h>
#include "Parse.h"
#include <Adafruit_Sensor.h>
#include <Adafruit_BNO055.h>


// ESP32's I2C Pin No.
#define _ESP32_HAL_I2C_H_

#ifdef _ESP32_HAL_I2C_H_
#define SDA_PIN 32
#define SCL_PIN 12
#endif

#define BUFFER_SIZE 100

#define THIS_MACHINE "002"  // Set this bot's no. : 2
const int ThisMachine = 2;

//IP address to send UDP data to:
const char * udpAddress = "192.168.1.100"; // 저의 PC의 고정주소
const int udpPort = 6767;    // 제가 로봇용으로 주로 사용하는 포트번호

const char serverip[]="192.168.1.100";
char packetBuffer[255];

//The udp library class
WiFiUDP udp;

//Are we currently connected?
boolean connected = false;

float ax, ay, az, aSqrt, gx, gy, gz, mDirection, mx, my, mz;

const char * host = "ESP32-02";
const char * networkName = "****";  // 집이나 사무실 와이파이 아이디
const char * networkPswd = "****";  // 와이파이 연결 비밀번호
char botresp[50] = "001-CCCC:4,000,000,000,000";  // default init size should be size of string + 1

String ToServerStr;

char ReplyBuffer[] = "acknowledged"; // a string to send back
unsigned long preMillis = 0;
unsigned long curMillis = 0;

///////////  BNO-055
TwoWire I2CBNO = TwoWire(0);
Adafruit_BNO055 bno = Adafruit_BNO055(55, 0x29, &I2CBNO);
double xPos = 0, yPos = 0, headingVel = 0;
uint16_t BNO055_SAMPLERATE_DELAY_MS = 10; //how often to read data from the board

void ExecuteLogicalCommand() {
   // WHON : Wheel StandBy : Params ( on / off )
   if (((char)p2b_command[0] == 'W') and ((char)p2b_command[1] == 'H')) { 
      if (debugSerial) {
         s = "<*WHON=";  s += p2b_paramcount; s += ">";     
         Serial.println(s);
      }
      digitalWrite(StandBy, param1);
      reset_encoder_count();
   }; 

   // FWDM : Forward Move 양 바퀴 전진, 첫 파라메터는 좌측 바퀴 속도, 둘째는 우측 바퀴 속도.
   if (((char)p2b_command[0] == 'F') and ((char)p2b_command[1] == 'W')) { 
      if (debugSerial) {
         s = "<*FWDM=";  s += p2b_paramcount; s += ">";     
         Serial.println(s);
      }
      reset_encoder_count();
      // Left Wheel (A)
      digitalWrite(Ain1, 1);
      digitalWrite(Ain2, 0);
      ledcWrite(WheelChannelLeft, param1);
      // Right Wheel (B)
      digitalWrite(Bin1, 0);
      digitalWrite(Bin2, 1);
      ledcWrite(WheelChannelRight, param2);   
   }; 

      // BWDM : Backward Move 양 바퀴 후진, 첫 파라메터는 좌측 바퀴 속도, 둘째는 우측 바퀴 속도.
   if (((char)p2b_command[0] == 'B') and ((char)p2b_command[1] == 'W')) { 
      if (debugSerial) {
         s = "<*BWDM=";  s += p2b_paramcount; s += ">";     
         Serial.println(s);
      }
      reset_encoder_count();
      // Left Wheel (A)
      digitalWrite(Ain1, 0);
      digitalWrite(Ain2, 1);
      ledcWrite(WheelChannelLeft, param1);
      // Right Wheel (B) // Currently one signal not working on B (33, 34)
      digitalWrite(Bin1, 1);
      digitalWrite(Bin2, 0);
      ledcWrite(WheelChannelRight, param2);
   };    

}  

void setupControlPins(){ 
   freq = 5000;
   resolution = 8;
   StartingSpeed = 0; // 0 - 255 (8 bit)   

   ledcSetup(WheelChannelLeft,  freq, resolution);
   ledcSetup(WheelChannelRight, freq, resolution);

   ledcAttachPin(WheelLeft,  WheelChannelLeft);
   ledcAttachPin(WheelRight, WheelChannelRight);

   ledcWrite(WheelRight, StartingSpeed);
   pinMode(Ain2, OUTPUT);
   pinMode(Ain1, OUTPUT);
   pinMode(StandBy, OUTPUT);
   pinMode(Bin1, OUTPUT);
   pinMode(Bin2, OUTPUT);
   ledcWrite(WheelLeft,  StartingSpeed);

   // Two encoder pins for wheels
   pinMode(Encoder2_A_Left, INPUT);
   pinMode(Encoder2_B_Right, INPUT);
}

void reset_encoder_count() {  // Reset before start moving
   count_AL = 0;
   count_BR = 0;
   last_count_AL = 0;
   last_count_BR = 0;
}

void ISR_Left_Encoder() {
   count_AL++;
}

void ISR_Right_Encoder() {
   count_BR++;
}

void setup() {
  
   setupControlPins(); // IO 핀들 세팅할 것.

   // 모든 시리얼은 드론에는 사용 못하고 다른 ESP32 Development Board에서 개발/디버깅 중에만 사용 가능합니다.
   Serial.begin(115200);  // 시리얼을 안쓰더라도 반드시 이렇게 놔 둘 것.

   //Connect to the WiFi network  네트웍에 연결
   connectToWiFi(networkName, networkPswd);

   // Wait for connection
   while (WiFi.status() != WL_CONNECTED) {
     delay(500);
     Serial.print(".");
   }   

   // 모든 시리얼은 무선 작동시에는 사용 못하고 디버깅 중에만 사용 가능합니다.

/*   Wire.begin(SDA_PIN, SCL_PIN); // ESP32는 반드시 이렇게.
   //Serial.println("I2C enabled! ");

   I2CBNO.begin(SDA_PIN, SCL_PIN);
   if (!bno.begin()){
      Serial.print("No BNO055 detected");
      while (1);
   } */

   delay(1000);

   r = 0;

   // Encoder Setting
   pinMode(Encoder2_A_Left, INPUT_PULLUP);
   attachInterrupt(Encoder2_A_Left, ISR_Left_Encoder, FALLING);
   pinMode(Encoder2_B_Right, INPUT_PULLUP);
   attachInterrupt(Encoder2_B_Right, ISR_Right_Encoder, FALLING);
}

void uPrint(){   // print whatever in "udpStr" with station # and ending ">"
   s = "#";  s += THIS_MACHINE;  s += ":"; s += udpStr; s += ">";
   udp.beginPacket(udpAddress,udpPort);
   udp.print(s);
   udp.endPacket();
}

void loop() {
   r++;

   if ((r % 3000) == 0){ 
      if(connected){
         //Send a packet
         udpStr = "#L/R: "; udpStr += count_AL; udpStr += ", ";  udpStr += count_BR; ;
         uPrint(); // it prints whatever in udpString      
      }
   }

   // Expected message format : <002-MECH:4,10,10,70,70>
   // "<"  beginning mark, 002 mechine no., MECH command, 4 params, + 4 values ,">" ending mark 
   //  3 digit machine no., 4 char command, # of params, params seperated by ","
   int packetSize = udp.parsePacket();
   if (packetSize) {
        
      int len = udp.read(packetBuffer, 255); // here, len = packetSize
      if (len > 0) {
         packetBuffer[len] = 0;
         if (len < 51) {
            for (i=1;i<51;i++)
               botresp[i+3] = packetBuffer[i];            
         }
      };  
      i = 0;

      while ((packetSize) >= i){
         inChar = packetBuffer[i];  // get the new byte:
         inputString[i] = inChar;
         i++;
      
         // if the incoming character is a newline, set a flag
         if ((inChar == '#') or (inChar == '<')) {
            p2b_status = P2B_STAT_CMD;
            ClearP2BCommand();
            j = i-1;         
            // ===== Debug Purpose =====
            Serial.println("시작점  #, <");
            
         } else if (inChar == '>') {  // so the main loop can do something about it:

            // First, get the machine # first, who is this command to
            p2b_machno  = 0;
            if (inputString[j+1] != '0') {
               p2b_machno = (int(inputString[j+1])-48) * 100;
            }
            if (inputString[j+2] != '0') {
               p2b_machno = p2b_machno + (int(inputString[j+2])-48) * 10;
            }
            if (inputString[j+3] != '0') {
               p2b_machno = p2b_machno + (int(inputString[j+3])-48);
            }
 
            // If this command is for ME (This machine)
            if (ThisMachine == p2b_machno){
           
               p2b_mach[0] = inputString[j+1];
               p2b_mach[1] = inputString[j+2];
               p2b_mach[2] = inputString[j+3];
               
               p2b_command[0] = inputString[j+5];
               p2b_command[1] = inputString[j+6];
               p2b_command[2] = inputString[j+7];
               p2b_command[3] = inputString[j+8];

               p2b_paramcount = int(inputString[j+10])-48; // take out 0

               Serial.print("Machine : ");
               Serial.println(p2b_machno); 
               Serial.print("Command :");
               Serial.println(p2b_command); 
               Serial.print("Parameter Count : ");
               Serial.println(p2b_paramcount);              

               j = j + 12;  // now j is pointing param starting position 
               if (p2b_paramcount > 0) {
                  tempVal = 0; 
                  nPos = 0;
                  k = 1;
                  for (n=j;n<=packetSize;n++){
                     // end of param 
                     if ((inputString[n] == ',') or (inputString[n] == '>')){  
                        if (k == 1){
                           param1 = tempVal;
                           Serial.print("Param 1 : ");
                           Serial.println(param1);
                        }
                        if (k == 2){
                           param2 = tempVal;
                           Serial.print("Param 2 : ");
                           Serial.println(param2);
                        }
                        if (k == 3){
                           param3 = tempVal;
                           Serial.print("Param 3 : ");
                           Serial.println(param3);
                        }
                        if (k == 4){
                           param4 = tempVal;
                           Serial.print("Param 4 : ");
                           Serial.println(param4);
                        }
                        tempVal = 0; 
                        k++;  // Param No.
                        nPos = 0;
                     } else {
                        if (nPos == 1) {
                           tempVal = (byte(inputString[n]) - 48); 
                        } else {
                           tempVal = tempVal * 10 + (byte(inputString[n]) - 48); 
                        }
                     }
                  }
               }
            //   Serial.println("");  
            
               ExecuteLogicalCommand();
            } // end of if command is for this Machine
         
            i = 100;  // refresh counter
            stringComplete = true;
            p2b_status = P2B_STAT_NONE;
            
         } else {
            lp = i-1;  // set last position on each entry;
         }
      }
   }   //  while ((packetSize) >= i){
 
   if (stringComplete) {
      ClearParamsAndInputstring(); 
   };      

   if (r == 5000) { 
//      if (contGetAll) {  GetAll();     }
//      if (contAccel)  {  Get3DAccel(); } 
//      if (contGyro)   {  Get3DGyro();  } 
//      if (contMag)    {  Get3DMag();   }  
//      if (contHeight) {  GetHeight();  }        
   }
   
   // Reset r
   if (r > 1000000) { r = 0; }

}

void connectToWiFi(const char * ssid, const char * pwd){
  Serial.println("Connecting to WiFi network: " + String(ssid));

  // delete old config
  WiFi.disconnect(true);
  //register event handler
  WiFi.onEvent(WiFiEvent);
  
  //Initiate connection
  WiFi.begin(ssid, pwd);

  connected = true;

  Serial.println("Waiting for WIFI connection...");
}

//wifi event handler
void WiFiEvent(WiFiEvent_t event){
    switch(event) {
      case SYSTEM_EVENT_STA_GOT_IP:
          //When connected set 
          Serial.print("WiFi connected! IP address: ");
          Serial.println(WiFi.localIP());  
          //initializes the UDP state
          //This initializes the transfer buffer
          udp.begin(WiFi.localIP(),udpPort);
          connected = true;
          break;
      case SYSTEM_EVENT_STA_DISCONNECTED:
          Serial.println("WiFi lost connection");
          connected = false;
          break;
      default: break;
    }
}

 

Parse.h 화일

int P2B_NEWLINE  = 11; // < -- Beginning of Command to Remote Control that controls R/C Car
int P2B_COM_TYPE = 12; // 0..9 -- First 2 letters following "<"
                       // Total Command Types : 10 x 10 = 100
int P2B_DATA     = 12; // Data section
int P2B_ENDING   = 13; // > -- End of Message Signal

int P2B_STAT_NONE = 1; // Do nothing until '<' has received
int P2B_STAT_CMD  = 2; // Accepting Command
int P2B_STAT_DATA = 3; // Accepting Command data
int P2B_STAT_END  = 4; // '>' has receieve, it is the end of   command;

char p2b_mach[4];
char p2b_command[5];
char p2b_data[50];
int  p2b_machno = 0;
int  p2b_paramcount = 0;
int  p2b_status = 0;

int i = 0; // dumb int
int j = 0; // dumb int
int k = 0; // dumb int
int n = 0; // dumb int
int lp =0; // dumb last position of input command string

int r = 0; // Simple Counter to replace delay() in the main loop

String s; // Temporary string for general purpose
String udpStr; 

boolean debugSerial = false;

char    inputString[200];           // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete
char    inChar;
String  tempStr;
char    buffer[12]; // buffer used for itoa  (int to string conversion)
int     inlen = 0;  // Length of inputString
char    RemoteString[50];    
// value from PC Command (integer params 1 .. 4 max) 
int     paramcount = 0; // How many params in input String
int     param1 = -1;  // value from PC Command (Param 1)
int     param2 = -1;  // value from PC Command (Param 2)
int     param3 = -1;  // value from PC Command (Param 3)
int     param4 = -1;  // value from PC Command (Param 4)

int tempVal = 0;
int nPos = 1;

float fparam1 = 0.0; 
float fparam2 = 0.0; 
float fparam3 = 0.0; 
float fparam4 = 0.0; 

boolean SecondParam = false;   // Now gettign second parameter (after ,)

// Wheel PWM Related Definitions
// A = Left, B = Right

int WheelChannelLeft  = 0; // motor A PWM Channel
int WheelChannelRight = 1; // motor B PWM Channel

// NEW PINS 19, 23, 26, 5, 18
const int WheelLeft  = 19; // GREY - motor A on my design sheet
const int Ain2 = 25;       // WHITE
const int Ain1 = 23;       // ORANGE
const int StandBy = 5;     // YELLOW
const int Bin1 = 14;       // PINK (34 didn't work, changed to 14
const int Bin2 = 33;       // BROWN
const int WheelRight = 18; // BLUE - motor B on my design sheet

// Speed Read from Encoder
const int Encoder2_A_Left  = 36;  // Yellow Color 
const int Encoder2_B_Right = 26;  // Yellow Color 
int count_AL = 0;
int count_BR = 0;
int last_count_AL = 0;
int last_count_BR = 0;

int freq = 5000;
int resolution = 8;

int StartingSpeed = 0; // 0 - 255 (8 bit)

int targetHeight = 0;
int msAi = 0;  
int msBi = 0;
int msCi = 0;
int msDi = 0;
int maximumSpeed = 50;

int height = 0;
int targetLR = 0;  // Target Left/Right
int targetFB = 0;  // Target Forward / Backward
int targetTN = 0;  // Target Turn (+ CW, - CCW)

boolean contMag = false;
boolean contAccel = false;
boolean contGyro = false;
boolean contHeight = false;
boolean contGetAll = false;
boolean AutoBalancing = true;
boolean NoOffset = false;

////////////////////////////////////////////////////////////////

void ClearP2BCommand() {
   p2b_mach[0] = 0;  // null
   p2b_mach[1] = 0;  // null
   p2b_mach[2] = 0;  // null
   p2b_mach[3] = 0;  // null   
     
   p2b_command[0] = 0;  // null
   p2b_command[1] = 0;  // null
   p2b_command[2] = 0;  // null   
   p2b_command[3] = 0;  // null
   p2b_command[4] = 0;  // null 
   for (int i=0; i <= 49; i++){
      p2b_data[i] = 0;  // null
   }
   p2b_machno = 0;

   // set as none, 0 could be value
   param1 = -1;  // 
   param2 = -1;  // 
   param3 = -1;  // 
   param4 = -1;  // 

   SecondParam = false;
}

void ClearRemoteString() {
   for (i=0;i<=49;i++) {
      RemoteString[i] = 0;
   };
}

void ClearParamsAndInputstring(){
   // clear the string:
   for (i=0;i<=49;i++) {
      inputString[i] = 0;
   };
   i = 0;
   j = 0;

   paramcount = 0; // How many params in input String
   param1 = -1; 
   param2 = -1; 
   param3 = -1; 
   param4 = -1; 
   
   fparam1 = 0.0; 
   fparam2 = 0.0; 
   fparam3 = 0.0; 
   fparam4 = 0.0;
      
   stringComplete = false;
}

'ESP32 BOT > My ESP32 Bot' 카테고리의 다른 글

My ESP32 Bot - The beginning.  (0) 2021.11.03

이때 주의할 사항은 PWM과 사용하는 타이머가 겹칠수 있으므로 반드시 실험해서 잘 작동하나 확인해야 함.

 

좋은 설명 : https://youtu.be/LONGI_JcwEQ

샘플 코드. 출처 : techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/

volatile int interruptCounter;
int totalInterruptCounter;
 
hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
 
void IRAM_ATTR onTimer() {
  portENTER_CRITICAL_ISR(&timerMux);
  interruptCounter++;
  portEXIT_CRITICAL_ISR(&timerMux);
 
}
 
void setup() {
 
  Serial.begin(115200);
 
  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, 1000000, true);
  timerAlarmEnable(timer);
 
}
 
void loop() {
 
  if (interruptCounter > 0) {
 
    portENTER_CRITICAL(&timerMux);
    interruptCounter--;
    portEXIT_CRITICAL(&timerMux);
 
    totalInterruptCounter++;
 
    Serial.print("An interrupt as occurred. Total number: ");
    Serial.println(totalInterruptCounter);
 
  }
}

============================================================================

ESP32 하드웨어 타이머 관련 기본 지식 :

  2 그룹의 2개씩의 64비트 하드웨어 타이머 (총 4개, 0 ~ 3)

   16 bit prescaler (2 ~ 2^16 사이의 값)

 

   ESP32는 80 Mhz짜리 수정(Crystal)을 사용하므로 (80,000,000 / prescaler) tics / sec 의 속도임.

   만약 80으로 나눠주면 80,000,000 / 80 = 1,000,000 tics / sec 이 됨.

 

   아두이노 코드에서 타이머를 사용하려면 다음과 같이 포인터를 만들어 주면 됨.

      hw_timer_t * timer = NULL;   // 포인터 설정.

 

   그 다음에는 타이머를 지정할 때 3개의 값을 주는데,

         1: 타이머 ID (0~3),

         2: 나눌 Prescaler 값,

         3: true (Rising Edge), false (Falling Edge) 

      timer = timerBegin(0, 80, true);  // 이건 타이머 0를 사용하고, 틱 80,000,000을 80으로 나누고, Rising Edge 사용.

 

  타이머를 Real Time용으로 만들기 위해서 아두이노 IDE에서는 다음과 같은 portMUX_TYPE 오브젝트를 사용함.

    portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

  

   타이머 함수를 만들어 줄 것. 

 

      (*) 타이머 함수를 만들때 주의사항.

        1. 코드가 아주 빨리 실행되는 일만 시킬 것.  (시리얼 통신같은 무거운 일 시키면 안됨)

        2. 각 코드는 고유의 타이머를 연결시킬 것.

        3. 타이머 함수에서 함수 외부 코드와 공유할 수 있는 변수는 반드시 (volatile)로 지정할 것.

           예 :  volatile  int  t0count;

        4. PWM 코드와 사용하는 타이머가 겹치는 지 반드시 확인할 것.

 

     코드 예 :

      void  IRAM_ATTR  onTimer() {

           portENTER_CRITICAL_ISR(&timerMux);

           t0count++;  // ... critical code executed in the RAM of the ESP32 

           portEXIT_CRITICAL_ISR(&timerMux);

      }

 

   타이머를 활성화(Enable) 시키기 전에 반드시 타이머 함수를 지정하고 연결시킬 것.

      timerAttachInterrupt(timer, &onTimer, true);

 

   그 다음에 알람용 (실제 시간마다 실행될 시간)을 지정함.

      timerAlarmWrite(timer, 1000000, true);    // 이전에 80으로 나눠서 1초에 백만 tic이 돌게 세팅된 상태에 

                                                                       // 백만 tic마다 실행하게 함.  그러므로 이 코드는 1초에 1번 작동함. 

 

   마지막에 타이머를 활성화(Enable) 시킴.

      timerAlarmEnable(timer);

 

 

 

 

+ Recent posts