Software Serial Problem

Getting, to me, strange error using software serial:

libraries\SoftwareSerial\SoftwareSerial.cpp.o (symbol from plugin): In function SoftwareSerial::read()': (.text+0x0): multiple definition of __vector_9'
sketch\RC_Mega_Test_S23V4.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
libraries\SoftwareSerial\SoftwareSerial.cpp.o (symbol from plugin): In function SoftwareSerial::read()': (.text+0x0): multiple definition of __vector_11'
sketch\RC_Mega_Test_S23V4.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
exit status 1
Error compiling for board Arduino Mega or Mega 2560.


Code has:

#include <SoftwareSerial.h>
#define rxPin 11
#define txPin  12

// Set up a new SoftwareSerial object
SoftwareSerial myserial =  SoftwareSerial(rxPin, txPin)
// Define pin modes for TX and RX
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
  myserial.begin(9600);

Apparently, there is a conflict with:

//***********************
// INTERRUPT FUNCTIONS 
// 
// As the encoders pulse they cause interrupts which are serviced here
// the functions decide whether the cnt variables should inc or dec.
//
// at the bottom of this page you will find the vector called by the timer interrupt
// this vector, as you can, see calls the function Odometry(left_cnt,right_cnt)
// nothing for you to do here.

// External Interrupt 4 service routine => PIN2 (Right encoder ch A)
ISR(INT4_vect){
  if ((PINK & 0x80) != 0) {     // PK7 Pin A15
    if ((PINE & 0x10) != 0)     // PE4 Pin 2
      right_cnt--;
    else
      right_cnt++;
    } else {
      if ((PINE & 0x10) == 0)   // PE4 Pin 2  
        right_cnt--;
      else
        right_cnt++;
       }
}

// External Interrupt 5 service routine => PIN3 (Left encoder ChA)
ISR(INT5_vect){
  if ((PINB & 0x10) != 0) {
  if ((PINE & 0x20) != 0)
    left_cnt++;
  else
    left_cnt--;
  } else {
    if ((PINE & 0x20) == 0)
      left_cnt++;
    else
      left_cnt--;
    }
}

// Pin change 0-7 interrupt service routine => PIN10 (Left encoder ChB)
ISR(PCINT0_vect){
  if ((PINE & 0x20) != 0) {   // PE5 Pin-3
  if ((PINB & 0x10) != 0) {   // PB4 Pin-10
    left_cnt--;
    } else
    left_cnt++;
    } else {
      if ((PINB & 0x10) == 0) {
        left_cnt--;
      } else
        left_cnt++;
      }
}

// Pin change 16-23 interrupt service routine => PIN-ADC15  (Right encoder ChB)
ISR(PCINT2_vect){
  if ((PINE & 0x10) != 0) {
  if ((PINK & 0x80) != 0)
    right_cnt++;
  else
    right_cnt--;
  } else {
    if ((PINK & 0x80) == 0)
      right_cnt++;
    else
      right_cnt--;
    }
}
 
// Timer 1 overflow interrupt service routine
ISR(TIMER1_OVF_vect){
  sei();                    // enable interrupts  
  Odometry(left_cnt, right_cnt);
} 

Thanks,
John-

Missing a semi-colon at the end there.

Sorry, it's in the code but missed typing it in the post.

Thanks,
John-

Is there some reason you are not using the hardware serial ports?

Ah, true that.

Still, it's not proper constructor
this is --
SoftwareSerial mySerial(10, 11); // RX, TX

yes, they are already used
Serial1 is for a compass
Serial2 is for a GPS
Serial3 sends commands to a motor controller

Also, there are two I2C units:
A servo controller and an IMU.

Thanks,
John-

Software serial is a convenient, useful piece of hell, reserved for embedded coders.

The limitations are well known, but everyone tries to squeeze the last drop out of a useful tool, but it’s not a UART !

1 Like

I'm trying to use it to send ASCII characters to a data logger at 9600 baud,
hoping that wouldn't strain it.
There's also interrupts on pins 2, 3, 10 and A15 for quad encoders.

John-

It's --
SoftwareSerial myserial (rxPin, txPin);

I followed the example on the Arduino Software Serial example.
SoftwareSerial mySerial(10,11);

gives the same error.

The interrupt come for pins 2, 3, 10, A15 seems to be causing the problem.
How do I get around that?

Thanks,
John-

¯_(ツ)_/¯

Does this from the documentation relate to your problem:
Not all pins on the Mega and Mega 2560 boards support change interrupts, so only the following can be used for RX: 10, 11, 12, 13, 14, 15, 50, 51, 52, 53, A8 (62), A9 (63), A10 (64), A11 (65), A12 (66), A13 (67), A14 (68), A15 (69). Not all pins on the Leonardo and Micro boards support change interrupts, so only the following can be used for RX: 8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI).

SoftwareSerial uses the pin change interrupts.

Looking at the datasheet, that is PCINT0_vect and PCINT2_vect.

The only PCINT that is not used in your presented code is PCINT1; unfortunately the associated pins are not broken out on a Mega (except for TXD3 and RXD3) so without soldering directly to the processor there is nothing you can do with SoftwareSerial.

I know. Apparently the interrupt code for the encoders is at fault.
The code author tells me it takes control of some interrupts at the assembler level.
Looks like I'll need to ditch it and use a different method for the encoders.

John-

Hi,

No, I'm not using servo.  Worse, here is the code for processing the quad encoders.

//***********************
// INTERRUPT FUNCTIONS 
// 
// As the encoders pulse they cause interrupts which are serviced here
// the functions decide whether the cnt variables should inc or dec.
//
// at the bottom of this page you will find the vector called by the timer interrupt
// this vector, as you can, see calls the function Odometry(left_cnt,right_cnt)
// nothing for you to do here.


// External Interrupt 4 service routine => PIN2 (Right encoder ch A)
ISR(INT4_vect){
  if ((PINK & 0x80) != 0) {     // PK7 Pin A15
    if ((PINE & 0x10) != 0)     // PE4 Pin 2
      right_cnt--;
    else
      right_cnt++;
    } else {
      if ((PINE & 0x10) == 0)   // PE4 Pin 2  
        right_cnt--;
      else
        right_cnt++;
       }
}

// External Interrupt 5 service routine => PIN3 (Left encoder ChA)
ISR(INT5_vect){
  if ((PINB & 0x10) != 0) {
  if ((PINE & 0x20) != 0)
    left_cnt++;
  else
    left_cnt--;
  } else {
    if ((PINE & 0x20) == 0)
      left_cnt++;
    else
      left_cnt--;
    }
}

// Pin change 0-7 interrupt service routine => PIN10 (Left encoder ChB)
ISR(PCINT0_vect){
  if ((PINE & 0x20) != 0) {   // PE5 Pin-3
  if ((PINB & 0x10) != 0) {   // PB4 Pin-10
    left_cnt--;
    } else
    left_cnt++;
    } else {
      if ((PINB & 0x10) == 0) {
        left_cnt--;
      } else
        left_cnt++;
      }
}

// Pin change 16-23 interrupt service routine => PIN-ADC15  (Right encoder ChB)
ISR(PCINT2_vect){
  if ((PINE & 0x10) != 0) {
  if ((PINK & 0x80) != 0)
    right_cnt++;
  else
    right_cnt--;
  } else {
    if ((PINK & 0x80) == 0)
      right_cnt++;
    else
      right_cnt--;
    }
}
 
// Timer 1 overflow interrupt service routine
ISR(TIMER1_OVF_vect){
  sei();                    // enable interrupts  
//  Odometry(left_cnt, right_cnt);
} 

John-

Well nuts. Does it matter what allowable pins are use for software serial?

John-

O.K., given the above interrupts on pins 2, 3, 10 and A15, what interrupt pins are on different ports please?

Thanks,
John-

It's even worse; SoftwareSerial claims all PCINT vectors. So one will have to hack the library to remove the non-used ISRs from the library.

You're currently using pins 11 nd 12 for SoftwareSerial; that is pins PCINT5 and PCINT6 so it uses PCINT0_vect. So don't use any pins that are associated with PCINT0_vect. Based on your code snippet, I think it's pin 10.

Move the conflicting encoder to one of the analog pins A8..A14 (based on your code, you're already using A15).

Modify your PCINT2_vect to handle the additional pin as well.

Next hack the SoftwareSerial library and remove PCINT2_vect from the cpp file. The best way is probably to add a copy of the library in your sketch directory and modify that.

I don't think that this relates to timers; the error indicates PCINT vectors. Your advise on AltSoftSerial seems sound (I checked NeoSWSerial but that also conflicts).

Attached a zip with a code that compiles based on previous advise. It contains an ino file as well as a local copy of SoftwareSerial. You have to copy the two SoftwareSerial files to your project directory.

Changes in local copy of SoftwareSerial.cpp

#include "SoftwareSerial.h"     // <<<=== sterretje modified

The above change tells the compiler to use the local copy of SoftwareSerial.h when compiling the SoftwareSerial.cpp.

// <<<=== sterretje modified
/*
#if defined(PCINT1_vect)
ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect));
#endif

#if defined(PCINT2_vect)
ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect));
#endif

#if defined(PCINT3_vect)
ISR(PCINT3_vect, ISR_ALIASOF(PCINT0_vect));
#endif
*/
// <<<=== sterretje modified end

This change removes all PCINT vectors that are not needed.

The sketch, only to demonstrate how to approach with the 'offending' encoder removed from PCINT0_vect.

#include "SoftwareSerial.h"  // <<<=== sterretje modified
#define rxPin 11
#define txPin 12

uint32_t left_cnt;
uint32_t right_cnt;

SoftwareSerial myserial(rxPin, txPin);

void setup()
{
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
  myserial.begin(9600);
}

void loop()
{
  // put your main code here, to run repeatedly:
}



//***********************
// INTERRUPT FUNCTIONS
//
// As the encoders pulse they cause interrupts which are serviced here
// the functions decide whether the cnt variables should inc or dec.
//
// at the bottom of this page you will find the vector called by the timer interrupt
// this vector, as you can, see calls the function Odometry(left_cnt,right_cnt)
// nothing for you to do here.

// External Interrupt 4 service routine => PIN2 (Right encoder ch A)
ISR(INT4_vect)
{
  if ((PINK & 0x80) != 0)
  {                          // PK7 Pin A15
    if ((PINE & 0x10) != 0)  // PE4 Pin 2
      right_cnt--;
    else
      right_cnt++;
  }
  else
  {
    if ((PINE & 0x10) == 0)  // PE4 Pin 2
      right_cnt--;
    else
      right_cnt++;
  }
}

// External Interrupt 5 service routine => PIN3 (Left encoder ChA)
ISR(INT5_vect)
{
  if ((PINB & 0x10) != 0)
  {
    if ((PINE & 0x20) != 0)
      left_cnt++;
    else
      left_cnt--;
  }
  else
  {
    if ((PINE & 0x20) == 0)
      left_cnt++;
    else
      left_cnt--;
  }
}

// <<<=== sterretje modified
// You'll have to handle this in PCINT1_vect
// Pin change 0-7 interrupt service routine => PIN10 (Left encoder ChB)
/*
ISR(PCINT0_vect)
{
  if ((PINE & 0x20) != 0)
  {  // PE5 Pin-3
    if ((PINB & 0x10) != 0)
    {  // PB4 Pin-10
      left_cnt--;
    }
    else
      left_cnt++;
  }
  else
  {
    if ((PINB & 0x10) == 0)
    {
      left_cnt--;
    }
    else
      left_cnt++;
  }
}
*/
// Pin change 16-23 interrupt service routine => PIN-ADC15  (Right encoder ChB)
ISR(PCINT2_vect)
{
  if ((PINE & 0x10) != 0)
  {
    if ((PINK & 0x80) != 0)
      right_cnt++;
    else
      right_cnt--;
  }
  else
  {
    if ((PINK & 0x80) == 0)
      right_cnt++;
    else
      right_cnt--;
  }
}

// Timer 1 overflow interrupt service routine
ISR(TIMER1_OVF_vect)
{
  sei();  // enable interrupts
  //Odometry(left_cnt, right_cnt);
}

1145787.0.1.zip (7.6 KB)

PS
If you encounter problems, please post your full sketch as well as a proper schematic (photo of hand-drawn is fine); we need to ne able to identify every component as well as all wiring (including power and GND).

edit
I see I referred to PCINT1_vect; that should be PCINT2_vect (both in the ino in the zip and in the post). Apologies.

1 Like