SoftwareSerial::read()': (.text+0x0): multiple definition of `__vector_3`

Hi,

I am trying to write a quadcopter code - stuck on the following error

In function SoftwareSerial::read()':** **(.text+0x0): multiple definition of __vector_3'

i get this error after including the software serial library

#include<Servo.h>
#include <PID_v1.h> //PID Library by Brett Beauregard ref:https://github.com/br3ttb/Arduino-PID-Library

//Arduino - MPU6050 Library ref : https://github.com/jarzebski/Arduino-MPU6050+
#include <Wire.h>
#include <I2Cdev.h>
#include <helper_3dmath.h>
#include <MPU6050_6Axis_MotionApps20.h>


#include <SoftwareSerial.h> //Software Serial Library for HC-05 Bluetooth Module - this module act as a Slave and sends the data to the master or receive the data from master
//SoftwareSerial BTSerial(A0,A1); // TX, RX

#define DEBUG

//MotorConnections
#define m0 4
#define m1 5
#define m2 6
#define m3 7

// Date : 20Mar - Changing the Motor value from Angle(i.e servo.write) to microSeconds(servo.writeMicroSeconds)
#define motorArmValue 1152 // 60
#define motorMinValue 1200 //65
#define motorMaxValue 1800 //130
#define motorArmDelay 2000 //wait after motors armed

#define throttleMinValue 1200
#define throttleMaxValue 1800

#define pitchPVal 0.5
#define pitchDVal 0
#define pitchIVal 0

#define rollPVal 0
#define rollDVal 0
#define rollIVal 0

#define yawPVal 0
#define yawDVal 0
#define yawIVal 0


//Flight Parameters
#define pitchMin -90
#define pitchMax 90
#define rollMin -90
#define rollMax 90
#define yawMin -180
#define yawMax 180


//#define pidPitchInfluence 20
//#define pidRollInfluence 20
//#define pidYawInfluence 20


//MPU 6050 Offsets
#define mpuGyroXOffset 98
#define mpuGyroYOffset 20
#define mpuGyroZOffset 34

#define mpuAccelXOffset -1034
#define mpuAccelYOffset -571
#define mpuAccelZOffset 884


volatile boolean recvPCInt = false;

Servo motor0;
Servo motor1;
Servo motor2;
Servo motor3;

//-------------------------------------------------------------------
//MPU variables
//Check example in Library for more details
//-------------------------------------------------------------------

MPU6050 mpu;                           // mpu interface object


uint8_t mpuIntStatus;                  // mpu statusbyte
uint8_t devStatus;                     // device status
uint16_t packetSize;                   // estimated packet size
uint16_t fifoCount;                    // fifo buffer size
uint8_t fifoBuffer[64];                // fifo buffer

Quaternion q;                          // quaternion for mpu output
VectorFloat gravity;                   // gravity vector for ypr
float ypr[3] = {0.0f, 0.0f, 0.0f};     // yaw pitch roll values

volatile bool mpuInterrupt = false;    //interrupt flag

double pidPitchIn, pidPitchOut, pidPitchSetPoint = 0; //though in UNO there is no diff between float and double - PID library throwing error if type is float
double pidRollIn, pidRollOut, pidRollSetPoint = 0;
double pidYawIn, pidYawOut, pidYawSetPoint = 0;


PID pitchController(&pidPitchIn, &pidPitchOut, &pidPitchSetPoint, pitchPVal, pitchIVal, pitchDVal, REVERSE); //what is that reverse and forward ?
PID rollController(&pidRollIn, &pidRollOut, &pidRollSetPoint, rollPVal, rollIVal, rollDVal, REVERSE);
PID yawController(&pidYawIn, &pidYawOut, &pidYawSetPoint, yawPVal, yawIVal, yawDVal, DIRECT);


//used to store the pwm duration
volatile unsigned long pwmDuration[4];
volatile unsigned long pwmStart[4];
unsigned long pwmEnd[4];

//pinDeclaration for Rx
const byte rxCh[] = {8, 9, 10, 11};
const byte noOfChannels = sizeof(rxCh);

//portStatus
volatile int prevPortState[] = {0, 0, 0, 0};
volatile int presentPortState[4];
//Interrupt Service Routine will fire when for PinChange in PortB
ISR(PCINT0_vect) {
  recvPCInt = true;
  for (int ch = 0; ch < noOfChannels ; ch++) {
    presentPortState[ch] = digitalRead(rxCh[ch]);
  }//end of for looptr

  for (int c = 0; c < noOfChannels ; c++) {
    if (prevPortState[c] == 0 & presentPortState[c] == 1) {
      //if previous state is 0 and present state is 1 (Raising Edge) then take the time stamp
      pwmStart[c] = micros();
      prevPortState[c] = 1; //update the prevPort State
    } else if (prevPortState[c] == 1 & presentPortState[c] == 0) {
      //if previous state is 1 and present state is 0 (Falling Edge) then calculate the width based on the change
      pwmDuration[c] = micros() - pwmStart[c];
      prevPortState[c] = 0; //update Present PortState
    }
  }//end of for loop
  //portValue = PINB & 0x0f;//we are only intrested in first four bits
}

void updateMotors() {

  int width = pwmDuration[2];

  if (width < 1200 ) {
    motor0.write(motorArmValue);
    motor1.write(motorArmValue);
    motor2.write(motorArmValue);
    motor3.write(motorArmValue);


  }
  else
  {
    int throttle = map(width, throttleMinValue, throttleMaxValue, motorMinValue, motorMaxValue);
     int m0Value = throttle - pidPitchOut; //- pidYawOut;
     int m1Value = throttle - pidRollOut; //+ pidYawOut;
     int m2Value = throttle + pidPitchOut; //- pidYawOut;
     int m3Value = throttle + pidRollOut; //+ pidYawOut;


    motor0.write(m0Value);
    motor1.write(m1Value);
    motor2.write(m2Value);
    motor3.write(m3Value);
  }//end of ifElse
}//end of update motors


void initializeMotors() {
  motor0.attach(m0);
  motor1.attach(m1);
  motor2.attach(m2);
  motor3.attach(m3);
#ifdef DEBUG
  Serial.println("motors initialized");
#endif
}//end of initializeMotorsFunction


void armAllMotors() {
  motor0.write(motorArmValue);
  motor1.write(motorArmValue);
  motor2.write(motorArmValue);
  motor3.write(motorArmValue);
  delay(motorArmDelay);
}//end of armAllMotors function

//ISR for MPU 6050
void dmpDataReady() {
  mpuInterrupt = true;
}//end of dmpDataReady

void initMPU() {
  Wire.begin();
  mpu.initialize();
  devStatus = mpu.dmpInitialize();

  //MPU Offsets
  mpu.setXGyroOffset(mpuGyroXOffset);
  mpu.setYGyroOffset(mpuGyroYOffset);
  mpu.setZGyroOffset(mpuGyroZOffset);
  mpu.setXAccelOffset(mpuAccelXOffset);
  mpu.setYAccelOffset(mpuAccelYOffset);
  mpu.setZAccelOffset(mpuAccelZOffset);

  if (devStatus == 0) {
    mpu.setDMPEnabled(true);
    attachInterrupt(digitalPinToInterrupt(2), dmpDataReady, RISING);
    mpuIntStatus = mpu.getIntStatus();
    packetSize = mpu.dmpGetFIFOPacketSize();
  }//end of If

}//end of initMPU function


void updateAnglesFromMPU() {
  //Check example from MPU6050 Library
  mpuInterrupt = false;
  mpuIntStatus = mpu.getIntStatus();
  fifoCount = mpu.getFIFOCount();

  if ((mpuIntStatus & 0x10) || fifoCount >= 1024) {

    mpu.resetFIFO();

  } else if (mpuIntStatus & 0x02) {

    while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();

    mpu.getFIFOBytes(fifoBuffer, packetSize);

    fifoCount -= packetSize;

    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);

  }

}//end of getAnglesFromMPU

void initPID() {
  rollController.SetOutputLimits(rollMin, rollMax);
  pitchController.SetOutputLimits(pitchMin, pitchMax);
  yawController.SetOutputLimits(yawMin, yawMax);
  rollController.SetMode(AUTOMATIC);
  pitchController.SetMode(AUTOMATIC);
  yawController.SetMode(AUTOMATIC);
}//end of initPID

void computePID() {
  pidYawIn = ypr[0]*180/M_PI; //Converts Radians to degrees ref - https://forum.arduino.cc/index.php?topic=446713.msg3078076#msg3078076
  pidPitchIn = ypr[2]*180/M_PI; //Changed 1 to 2 Due to some problem in Orientation or sensor ? NEED TO FIX IT
  pidRollIn = ypr[1]*180/M_PI;
  
  rollController.Compute();
  pitchController.Compute();
  yawController.Compute();
}//end of computePID


void checkForBTInput(){
  
}//end of checkForBTInput

void setup() {
  cli(); //Clear all interrupts
  PCICR |= 1 << PCIE0; //Enable port B Registers i.e D8-D13
  PCMSK0 |= 1 << PCINT2;// Pin10
  sei(); //enable all interrupts
  pinMode(10, INPUT);
  digitalWrite(10, HIGH); //enable pull up in pin
//#ifdef DEBUG
  Serial.begin(115200);
//#endif
//  BTSerial.begin(38400); // HC-05 Default baudrate
  initializeMotors();
  armAllMotors();
  initMPU();
  initPID();
}//end of setup

void loop() {
  while (!mpuInterrupt && fifoCount < packetSize) {
    //Do nothing while mpu is not working
    //they are saying it is a short delay .. i need a way to avoid this
//    Serial.print("waiting for mpu");
  }//end of while loop

  updateAnglesFromMPU();
  computePID();
  updateMotors();
  checkForBTInput();
}//end of loop

And I read on the other posts there is conflict on Servo Library and Software Serial Library - Is it rectified ? or shall i look in to alternatives ?

It's not Servo.

It's your code. You're using PCINT ISR, but that is already used by SoftwareSerial.

so, I think Mega is Only Option for me then ?

I am using Hardware Serial for now to get hc-05 data. Are there any alternatives ?

There are two alternative libraries. AltSoftserial and NeoSoftwareSerial (or something like that, not sure about the exact names).

But you might run into the same issue. Just be aware that there are limited resources (timers, interrupts, ...) in your microcontroller; and you can not (or not easily) share them, especially when using libraries.

If you just need one additional serial port and want to maintain the same footprint as an Uno, you can use a Leonardo.

Yes, AltSoftSerial would avoid the ISR conflict. It is the most efficient software serial library, but it requires two specific pins: 8 & 9 on an UNO or Nano. It also disables PWM on pin 10, so maybe you don't want to lose a PWM source.

You could use NeoSWSerial to avoid the conflict, because the NeoSWSerial ISRs can be disabled. This can help you avoid linking conflicts with your ISR. Simply uncomment this define in NeoSWSerial.h:

    #define NEOSWSERIAL_EXTERNAL_PCINT // uncomment to use your own PCINT ISRs

Then you have to call the NeoSWSerial function from your ISR to handle its pin changes:

void myDeviceISR()
{
 NeoSWSerial::rxISR( *portInputRegister( digitalPinToPort( RX_PIN ) ) );
 // if you know the exact PIN register, you could do this:
 //    NeoSWSerial::rxISR( PIND );
}

If you are handling other pin changes on that same port, just call rxISR when the change is on the RX_PIN. 38400 is the max speed for this kind of software serial library, so call the rxISR as soon as possible.

There are other reasons to avoid SoftwareSerial:

  • It is very inefficient, because it disables interrupts for long periods of time: 1 complete character time, which is 250us at 38400 buad. This happens when a character is sent or received.

  • It cannot transmit and receive at the same time.

Cheers,
/dev