Muhammad Nazeer
Muhammad Nazeer

Reputation: 1

ESP32 board Reboot error with Dual Core Tasks

I'm using DOIT ESP32 Devkit V1 board (don't have much experience with it). I want to make use of Dual Core feature, where for core0 I want to have the code of a basic Bluetooth RC car, and for core1 I want to have the code for a self Stabilizing platform (2 servos, using MPU6050 sensor). Serial Monitor Output

My "Tools" setting in Arduino IDE is as follows.

Tools Menu

My code:

TaskHandle_t Task1;
TaskHandle_t Task2;

#include "BluetoothSerial.h" //Header File for Serial Bluetooth
BluetoothSerial ESP_BT; //Object for Bluetooth
#include <Wire.h>

#include "I2Cdev.h"
#include <ESP32Servo.h>
#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif

MPU6050 mpu;

#define OUTPUT_READABLE_YAWPITCHROLL 
#define INTERRUPT_PIN 2  // use pin 2 on Arduino Uno & most boards
bool blinkState = false;
 
// MPU control/status vars
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
 
// orientation/motion vars
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
 
// packet structure for InvenSense teapot demo
uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };
 
volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
    mpuInterrupt = true;
}
 
int  servoPin2  =  25 , servoPin3  =  26 ;
Servo servo2, servo3;

String incoming;// varible store received data
int M1A = 27; // motor pins
int M1B = 12;
int En1 = 14;

int M2A = 32;
int M2B = 33;
int En2 = 13;

void setup() {
  Serial.begin(115200); 
  ESP_BT.begin("Stair Climber"); //Name of your Bluetooth Signal
  pinMode(M1A, OUTPUT); // configure outputs
  pinMode(M1B, OUTPUT);
  pinMode(En1, OUTPUT);
  
  pinMode(M2A, OUTPUT);
  pinMode(M2B, OUTPUT);
  pinMode(En2, OUTPUT);
  
  digitalWrite(En1, HIGH);
  digitalWrite(En2, HIGH);
  
  servo2.attach (servoPin2);
  servo3.attach (servoPin3);

  servo2.write ( 0 ); // Init the servo2 angle to 0
  servo3.write ( 0 ); // Init the servo2 angle to 0
  
  Wire.begin(21, 22, 100000); // sda, scl, clock speed
  Wire.beginTransmission(0x68);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU−6050)
  Wire.endTransmission(true);
 
  // initialize device
  mpu.initialize();
  pinMode(INTERRUPT_PIN, INPUT);
  
  // verify connection
  Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
 
  // load and configure the DMP
  Serial.println(F("Initializing DMP..."));
  devStatus = mpu.dmpInitialize();

  // supply your own gyro offsets here, scaled for min sensitivity
  mpu.setXGyroOffset(-1885);
  mpu.setYGyroOffset(-39);
  mpu.setZGyroOffset(-20);

  mpu.setXAccelOffset(-3048); // 1688 factory default for my test chip
  mpu.setYAccelOffset(-807); 
  mpu.setZAccelOffset(796); 
 
  // make sure it worked (returns 0 if so)
  if (devStatus == 0) {
        // turn on the DMP, now that it's ready
        Serial.println(F("Enabling DMP..."));
        mpu.setDMPEnabled(true);
 
        // enable Arduino interrupt detection
        Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
        attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
        mpuIntStatus = mpu.getIntStatus();
 
        // set our DMP Ready flag so the main loop() function knows it's okay to use it
        Serial.println(F("DMP ready! Waiting for first interrupt..."));
        dmpReady = true;
 
        // get expected DMP packet size for later comparison
        packetSize = mpu.dmpGetFIFOPacketSize();
  } else {
        // ERROR!
        // 1 = initial memory load failed
        // 2 = DMP configuration updates failed
        // (if it's going to break, usually the code will be 1)
        Serial.print(F("DMP Initialization failed (code "));
        Serial.print(devStatus);
        Serial.println(F(")"));
  }
 

  //create a task that will be executed in the Task1code() function, with priority 1 and executed on core 0
  xTaskCreatePinnedToCore(
                    Task1code,   /* Task function. */
                    "RC_Car",     /* name of task. */
                    10000,       /* Stack size of task */
                    NULL,        /* parameter of the task */
                    1,           /* priority of the task */
                    &Task1,      /* Task handle to keep track of created task */
                    0);          /* pin task to core 0 */                  

  //create a task that will be executed in the Task2code() function, with priority 1 and executed on core 1
  xTaskCreatePinnedToCore(
                    Task2code,   /* Task function. */
                    "Gimbal",     /* name of task. */
                    10000,       /* Stack size of task */
                    NULL,        /* parameter of the task */
                    1,           /* priority of the task */
                    &Task2,      /* Task handle to keep track of created task */
                    1);          /* pin task to core 1 */
}

//Task1code: Bluetooth RC Car
void Task1code( void * pvParameters ){
  Serial.print("Task1 running on core ");
  Serial.println(xPortGetCoreID());
  Serial.println("Bluetooth Device is Ready to Pair");

  for(;;){
      if (ESP_BT.available()) //Check if we receive anything from Bluetooth
      {
      incoming = char(ESP_BT.read()); //Read what we received
      }
      Serial.println(incoming);
      if (incoming == "F") { // forward
      Serial.println("Forward");      
      digitalWrite(M1A, HIGH);
      digitalWrite(M1B, LOW);
      digitalWrite(M2A, HIGH);
      digitalWrite(M2B, LOW);
      }
      else if (incoming == "B") { // reverse
      Serial.println("Reverse");
      digitalWrite(M1B, HIGH);
      digitalWrite(M1A, LOW);
      digitalWrite(M2B, HIGH);
      digitalWrite(M2A, LOW);
      }
      else if (incoming == "R") { //turn right
      Serial.println("Right");
      digitalWrite(M1A, HIGH);
      digitalWrite(M1B, LOW);
      digitalWrite(M2B, LOW);
      digitalWrite(M2A, LOW);
      }
      else if (incoming == "L") { // turn left
      Serial.println("Left");
      digitalWrite(M1B, LOW);
      digitalWrite(M1A, LOW);
      digitalWrite(M2A, HIGH);
      digitalWrite(M2B, LOW);
      }
      
      else if (incoming == "S") { // turn stop
      Serial.println("Stop");
      digitalWrite(M1B, LOW);
      digitalWrite(M1A, LOW);
      digitalWrite(M2A, LOW);
      digitalWrite(M2B, LOW);
      }
      
      else {
      digitalWrite(M1B, LOW);
      digitalWrite(M1A, LOW);
      digitalWrite(M2A, LOW);
      digitalWrite(M2B, LOW);
      } 
  }
}

//Task2code: Self Balancing
void Task2code( void * pvParameters ){
  Serial.println("\t\tTask2 running on core ");
  Serial.print(xPortGetCoreID());

  for(;;){
 // if programming failed, don't try to do anything
    if (!dmpReady) return;
 
    // reset interrupt flag and get INT_STATUS byte
    mpuInterrupt = false;
    mpuIntStatus = mpu.getIntStatus();
 
    // get current FIFO count
    fifoCount = mpu.getFIFOCount();
 
    // check for overflow (this should never happen unless our code is too inefficient)
    if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
        // reset so we can continue cleanly
        mpu.resetFIFO();
        Serial.println(F("FIFO overflow!"));
 
    // otherwise, check for DMP data ready interrupt (this should happen frequently)
    } else if (mpuIntStatus & 0x02) {
        // wait for correct available data length, should be a VERY short wait
        while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
 
        // read a packet from FIFO
        mpu.getFIFOBytes(fifoBuffer, packetSize);
 
        // track FIFO count here in case there is > 1 packet available
        // (this lets us immediately read more without waiting for an interrupt)
        fifoCount -= packetSize;
 
        #ifdef OUTPUT_READABLE_YAWPITCHROLL
            // display Euler angles in degrees
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
            Serial.print("ypr\t");
            Serial.print(ypr[0] * 180/M_PI);
            Serial.print("\t");
            Serial.print(ypr[1] * 180/M_PI);
            Serial.print("\t");
            Serial.println(ypr[2] * 180/M_PI);
        #endif
 
        #ifdef  OUTPUT_READABLE_YAWPITCHROLL
 
            // display Euler angles in degrees
 
            mpu.dmpGetQuaternion (& q, fifoBuffer);
            mpu.dmpGetGravity (& gravity, & q);
            mpu.dmpGetYawPitchRoll (ypr, & q, & gravity);
 
            Serial.print ( "ypr \ t" );
            Serial.print (ypr [ 0 ]  *  180 / M_PI);
 
            Serial.print ( "\ t" );
            Serial.print (ypr [ 1 ]  *  180 / M_PI);            
            servo2.write (map (ypr [ 1 ]  *  180 / M_PI,  - 90 ,  90 ,  0 ,  180 )); // Control servo2
 
            Serial.print ( "\ t" );
            Serial.println (ypr [ 2 ]  *  180 / M_PI);
            servo3.write (map (ypr [ 2 ]  *  180 / M_PI,  90 ,  -90 ,  0 ,  180 )); // Control servo3       
 
        #endif
    }
  }
}

void loop() {
}

.

Individually, both codes works fine with the board. But when I try to combine both into 2 tasks pinned to separate cores, I get the error in Serial Monitor over and over as a loop.

I tried commenting out one task and testing the other, didn't work.

I tried adding a delay to each task and also the main loop, no success. Tried putting my core1 code inside the loop() function instead of a pinned task, still no success.

Sorry if I broke any rules, I haven't posted a question here before. TIA.

Upvotes: 0

Views: 735

Answers (1)

romkey
romkey

Reputation: 7089

Your interrupt handler is currently written like this:

void dmpDataReady() {
    mpuInterrupt = true;
}

It needs to be defined with the attribute IRAM_ATTR. The ESP32 fetches instructions from flash memory on demand, caches them in an area of RAM called IRAM (Instruction RAM) and executes them there. It can't do this while handling an interrupt, so interrupt handlers (and any code they might call) must be guaranteed to already be in IRAM or else the ESP32 will crash. The IRAM_ATTR attribute tells the linker to make sure the function is kept in IRAM.

There's only a small amount of IRAM available, so it's important to only keep functions there that absolutely must be there.

To fix that problem, you'd change the code to:

void IRAM_ATTR dmpDataReady() {
    mpuInterrupt = true;
}

Upvotes: 1

Related Questions