conflicting libraries error for "SoftwareSerial.h" and "PinChangeInt.h"

Hello Forum,
I have a problem with conflicting libraries. Anyone who knows how I can overcome this.

I have two interrupts; one to wake the ATMEGA328 and the other to retrieve data from a sensor register. I want to use pin 9 for interrupt. I get below error message when I run the code.

Here is the code I am using, libraries attached.

#include "PinChangeInt.h"


#include <SPI.h>
#include "AS3935n.h"
#include <avr/sleep.h>
#include<SoftwareSerial.h>
#include "<PinChangeInt.h>"

SoftwareSerial mySerial(8, 7); //RX, TX

String inputString = "";
bool stringComplete = false;
char time1[30];

unsigned long beit; //Stores bytes from AS3935 registers
unsigned long strokeEnergy;     //Stores stroke energy from AS3935 registers

#define ARDUINOPIN 9 // Maps to ATmega328 pin15, connected to IRQ Thunder click

void printAS3935Registers();

// Function that provides SPI transfer and is passed to
// AS3935 to be used from within library.
byte SPItransfer(byte sendByte);

// Iterrupt handler for AS3935 irqs
// and flag variable that indicates interrupt has been triggered
volatile int AS3935IrqTriggered;
void AS3935Irq()// Used when 
                 // 1. Controller wakes up from sleep
                  //2. When there is lightning strike and there is lightning strike. 
{
  //FIRST THING AFTER WAKING UP. 
  sleep_disable();         // first thing after waking from sleep: // disable sleep...
  detachInterrupt(ARDUINOPIN);      // wakeUpNow code will not be executed. WAKE UP NOW will not be executed during normal running time
  
  
  AS3935IrqTriggered = true;
}

// First parameter - SPI transfer function, second - Arduino pin used for CS
// and finally third argument - Arduino pin used for IRQ
// Library internally polls this pin when doing calibration, so being an interrupt pin
// is not a requirement

AS3935 AS3935(SPItransfer,SS,ARDUINOPIN);  // Here, ARDUINOPIN is not yet associated with an IRQ, 

int count = 0; 
char str[30];

void setup()
{ 
  
 
  Serial.begin(115200);  
  // first begin, then set parameters
  SPI.begin();
  // NB! chip uses SPI MODE1
  SPI.setDataMode(SPI_MODE1);
  // NB! max SPI clock speed that chip supports is 2MHz,
  // but never use 500kHz, because that will cause interference
  // to lightning detection circuit
  SPI.setClockDivider(SPI_CLOCK_DIV16);
  // and chip is MSB first
  SPI.setBitOrder(MSBFIRST);
  // reset all internal register values to defaults
  AS3935.reset();
  // and run calibration
  // if lightning detector can not tune tank circuit to required tolerance,
  // calibration function will return false
  if(!AS3935.calibrate())
    Serial.println("Tuning out of range, check your wiring, your sensor and make sure physics laws have not changed!");


  AS3935.setIndoors();
  disableDisturbers()
  AS3935.enableDisturbers();
  //AS3935.disableDisturbers();

  AS3935.setNoiseFloor(0011);
  printAS3935Registers();
  AS3935IrqTriggered = 0;

  
  attachPinChangeInterrupt(ARDUINOPIN, AS3935Irq, RISING);
  //attachInterrupt(ARDUINOPIN, AS3935Irq, RISING);

  beit=256ul; //Forces beit to be unsigned long integer

  // set the data rate for the SoftwareSerial port
  mySerial.begin(9600);
  //mySerial.println("Hello, world?");
  
  // reserve 200 bytes for the inputString:
  inputString.reserve(200);
}

void loop()
{
 
 //Serial.println(count);
  
  Wakeloop(); // We check whether ATMEGA328 needs to be asleep or awake

 
  if(AS3935IrqTriggered)
  {
           
    // reset the flag
    AS3935IrqTriggered = 0;
    // first step is to find out what caused interrupt
    // as soon as we read interrupt cause register, irq pin goes low
    int irqSource = AS3935.interruptSource();
    // returned value is bitmap field, bit 0 - noise level too high, bit 2 - disturber detected, and finally bit 3 - lightning!
    if (irqSource & 0b0001)
    {
      
          Serial.println("Noise level too high, try adjusting noise floor");
     
         Serial.println("Entering Sleep Mode ");
     sleepNow();
      //return;
    }

    if (irqSource & 0b1000)  //Original code to select only valid lightning events
    {
      
      // need to find how far that lightning stroke, function returns approximate distance in kilometers,
      //ke where value 1 represents storm in detector's near victinity, and 63 - very distant, out of range stroke
      // everything in between is just distance in kilometers
     int strokeDistance = AS3935.lightningDistanceKm();
      if (strokeDistance == 1)
        Serial.println("Storm overhead, watch out!");
      if (strokeDistance == 63)
        Serial.println("Out of range lightning detected.");
      if (strokeDistance < 63 && strokeDistance > 1)
      {
          Serial.print("Lightning detected ");
          Serial.print(strokeDistance,DEC);
          Serial.println(" kilometers away."); 
      }
    
          byte strokeEnergyB0 = AS3935.registerRead(0x06, 31);
          byte strokeEnergyB1 = AS3935.registerRead(0x05, 255);
        byte strokeEnergyB2 = AS3935.registerRead(0x04, 255);
          unsigned long strokeEnergy = beit*beit*strokeEnergyB0 + beit*strokeEnergyB1 + strokeEnergyB2;
          
          Serial.println(strokeEnergy);
      
  
       itoa(strokeDistance, str, 10); //Turn value into a character array
       Serial.write(strokeDistance);
       itoa(strokeEnergy, str, 10); 
      Serial.write(strokeEnergy);
       //itoa(strokeEnergy, str, 10); 
          /*
          function to send data via SD12 will be here
           Parameters to be sent
          1. Stroke Distance
          2. StrokeEnergy
          3. GPS timestamp
          4. Latitude and Longitude (from GPS)
          
          
          count=0;  


      }
    
  }
  
}

void printAS3935Registers()
{
  int noiseFloor = AS3935.getNoiseFloor();
  int spikeRejection = AS3935.getSpikeRejection();
  int watchdogThreshold = AS3935.getWatchdogThreshold();
  Serial.print("Noise floor is: ");
  Serial.println(noiseFloor,DEC);
  Serial.print("Spike rejection is: ");
  Serial.println(spikeRejection,DEC);
  Serial.print("Watchdog threshold is: ");
  Serial.println(watchdogThreshold,DEC);  
}

// this is implementation of SPI transfer that gets passed to AS3935
// you can (hopefully) wrap any SPI implementation in this
byte SPItransfer(byte sendByte)
{
  return SPI.transfer(sendByte);
}




void sleepNow()         
{

  sleep_enable();
  attachInterrupt(ARDUINOPIN, AS3935Irq, RISING);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   
  sleep_mode();           

  attachPinChangeInterrupt(ARDUINOPIN, AS3935Irq, RISING);
}

void Wakeloop()
{
 
   count++;  
   
   if (count >= 100000){ 
      Serial.println("Timer: Entering Sleep mode");
      delay(100);    
      
      sleepNow();    
      count = 0;      
 
      
  }
}

and here is the error I get

libraries/SoftwareSerial/SoftwareSerial.cpp.o (symbol from plugin): In function `SoftwareSerial::read()':
(.text+0x0): multiple definition of `__vector_3'
sketch/AS3935.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_5'
sketch/AS3935.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_4'
sketch/AS3935.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
exit status 1
Error compiling for board Arduino/Genuino Uno.

AS3935.ino (14 KB)

PinChangeInt.h (21.9 KB)

AS3935n.cpp (4.88 KB)

AS3935n.h (2.47 KB)

Anyone who knows how I can overcome this.

Decide which ONE you want to use.

If I can combine them to one, I would better work with pinChangeInt.h

Pins 8 and 9 use the same pinchange interrupt interrupt so it will never work; you will have to choose another combination. If you use 7 and 8 for SoftwareSerial, you need to use either a pin on D0..D7 or a pin on A0..A5 for your interrupt pin.

Next you need to hack the libraries. If you don't use the pinchangeint library, life is probably reasonably easy. Make a copy of SoftwareSerial.h and SoftwareSerial.cpp in your sketch directory (both can be found in C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial (Windows System). Restart your IDE and open your sketch; it will now have 3 tabs

Change the first line in SoftwareSerial.cpp from

#include <SoftwareSerial.h>

to

#include "SoftwareSerial.h"

Next find the part in SoftwareSerial.cpp that defines the ISRs

#if defined(PCINT0_vect)
ISR(PCINT0_vect)
{
  SoftwareSerial::handle_interrupt();
}
#endif

#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

//
// Constructor
//

And remove everything but the PCINT0_vect.

#if defined(PCINT0_vect)
ISR(PCINT0_vect)
{
  SoftwareSerial::handle_interrupt();
}
#endif

REMOVED THIS

//
// Constructor
//

Below a sketch framework (base on Arduino Playground - PinChangeInterrupt) that will probably work

#include "SoftwareSerial.h"
SoftwareSerial mySerial(8,7); //RX, TX

void setup()
{
  pinMode(A0, INPUT_PULLUP);
  // interrupt on A0
  pciSetup(A0);
  // put your setup code here, to run once:

}

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

}

void pciSetup(byte pin)
{
    *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin));  // enable pin
    PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
    PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
}


// D8 - D13
ISR (PCINT0_vect)
{

}

// A0 - A5
ISR (PCINT1_vect)
{

}

// D0 - D7
ISR (PCINT2_vect)
{

}

You will have to remove the first ISR as that is also used by the modified SoftwareSerial. You will also have to fill in the appropriate ISR.

Note the way that SoftwareSerial is now included using "" instead of <>.

I hope this helps. It compiles but is not tested.

Note:
There might be other ways and there is a better location for the modified SoftwareSerial library, but this will show the idea.

You should be using the new EnableInterrupt library; the PinChangeInt library is deprecated.

And PLEASE name your variables with something meaningful. I can't tell what the pins are connected to. Is "myserial" for a sensor? What does ARDUINOPIN connect to?

First, I would not recommend SoftwareSerial. It is very inefficient, because it disables interrupts for long periods of time. This can interfere with other parts of your sketch or with other libraries. It would be much, much better to use AltSoftSerial on pins 8 & 9. It is very efficient and can transmit and receive at the same time, unlike SoftwareSerial. This will also avoid the ISR conflict.

If you can't use those pins (are you sure?), use my NeoSWSerial on any two pins. It has an option to disable the PCI ISRs so you can use the PinChange library at the same time (uncomment that define).

Your sketch will have to call the NeoSWSerial::rxISR method from your sketch. Pass in PINx, the corresponding register for the RXpin you selected. When pin 7 is the RX pin, pass in PIND:

#include <EnableInterrupt.h>
#define WAKE_PIN ???
#define AS3935_PIN ???

#include <NeoSWSerial.h>
#define RX_PIN 7
#define TX_PIN 8
NeoSWSerial sensor( RX_PIN, TX_PIN ); // Is this connected to a sensor?  What is myserial?!?

void sensorISR()
{
  NeoSWSerial::rxISR( PIND );
}

void setup()
{
  sensor.begin( 9600 );
  enableInterrupt( RX_PIN, sensorISR, CHANGE);

  enableInterrupt( WAKE_PIN, wakeISR, FALLING );

  enableInterrupt(AS3935_PIN, AS3935Irq, RISING);
}

void loop()
{
  if (sensor.available()) { // check for sensor chars
     ...

If you want to try it, NeoSWSerial is available from the IDE Library Manager, under the menu Sketch -> Include Library -> Manage Libraries.

Cheers,
/dev

Hello all,

Thanks for all your input.

-dev: Your solution is the closest to what I want to achieve. ARDUINOPIN connects to IRQ of AS3935, which is another sensor. The idea is to use PIN 9 to trigger both interrupts; wake up, and reading the data from the sensor itself.
RX_PIN and TX_PIN are connected to a GPS sensor.

The sketch below solves the problem of conflicting libraries. Would you explain the meaning of

Your sketch will have to call the NeoSWSerial::rxISR method from your sketch. Pass in PINx, the corresponding register for the RXpin you selected. When pin 7 is the RX pin, pass in PIND

. I am still figuring out how to use it in my code.

#include "NeoSWSerial.h"
#include "Enableinterrupt.h"
#include "AS3935n.h"
#include <avr/sleep.h>

#include <SPI.h>
#define AS3935_PIN 9 // Changed from ARDUINOPIN 9
int count;
//...AS3935 initializations

// and flag variable that indicates interrupt has been triggered
volatile int AS3935IrqTriggered;


void AS3935Irq()
{
  AS3935IrqTriggered = 1;
}


AS3935 AS3935(SPItransfer,SS,AS3935_PIN);  // Here, ARDUINOPIN is not yet associated with an IRQ

void sensorISR()
{
  NeoSWSerial::rxISR( PIND );
}

void setup()
{
  //...AS3935 Setups-Elminated to make the code clear


  
enableInterrupt(AS3935_PIN, AS3935Irq, RISING);

  beit=256ul;     //Forces beit to be unsigned long integer
}

void loop()
{
  sleepTime(); 
  if(AS3935IrqTriggered)
  {
    // reset the flag
    AS3935IrqTriggered = 0;
    count = 0;

    //...capture data from AS3935 and GPS, then send by SPI
    
    
    }
  }
}


byte SPItransfer(byte sendByte)
{
  return SPI.transfer(sendByte);
}

void sleepTime ()
{
  
   count++;  
   
   if (count >= 100000){ // If no data from AS3935 is detected for about 10 minutes, Controller goes to sleep.
      delay(100);     // this delay is needed, the sleep 
                      //function will provoke a Serial error otherwise!!
      goSleep ();     // we go to sleep
      count = 0;      // Reset the counter after waking up
 
      
  }
  
}
void goSleep ()
{
  
  sleep_enable();
  enableInterrupt(AS3935_PIN, AS3935Irq, RISING);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
  sleep_mode();            // here the device is actually put to sleep!!
                             // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
  sleep_disable();
 
}

//GPS function

The sketch solves the problem of conflicting libraries....I note there is a new conflict between #include "EnableInterrupt.h" and #include "NeoSWSerial.h"

This is the code I am using

#include "EnableInterrupt.h"
#include "NeoSWSerial.h"

#define WAKE_PIN 6
#define AS3935_PIN 9


#define RX_PIN 7
#define TX_PIN 8
NeoSWSerial sensor( RX_PIN, TX_PIN ); // Is this connected to a sensor?  What is myserial?!?

void sensorISR()
{
  NeoSWSerial::rxISR( PIND );
}

void setup()
{
  sensor.begin( 9600 );
//  enableInterrupt( RX_PIN, sensorISR, CHANGE);

}

void loop()
{
  if (sensor.available()) { // check for sensor chars
  }
}

and I get the error below:

ketch/code3_a.ino.cpp.o (symbol from plugin): In function `sensor':
(.text+0x0): multiple definition of `__vector_3'
sketch/NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here
sketch/code3_a.ino.cpp.o (symbol from plugin): In function `sensor':
(.text+0x0): multiple definition of `__vector_4'
sketch/NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here
sketch/code3_a.ino.cpp.o (symbol from plugin): In function `sensor':
(.text+0x0): multiple definition of `__vector_5'
sketch/NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
exit status 1
Error compiling for board Arduino/Genuino Uno.

How do I go about solving this?

Thanks...

Image embedded for our convenience:

Technique described here.

RX_PIN and TX_PIN are connected to a GPS sensor.

Great, I can use that to name the NeoSWSerial variable:

#include <EnableInterrupt.h>

#define WAKE_PIN 6
#define AS3935_PIN 9


#include <NeoSWSerial.h>
#define RX_PIN 7
#define TX_PIN 8
NeoSWSerial gpsPort( RX_PIN, TX_PIN ); //   <--  Now THAT'S a name!  :)

// Since it's hooked to a GPS, try NeoGPS for parsing...
#include <NMEAGPS.h>
NMEAGPS gps;
gps_fix fix;

void gpsPortISR()
{
  NeoSWSerial::rxISR( *portInputRegister( digitalPinToPort( RX_PIN ) ) );
     //  This is uglier than passing PIND, but it works for any RX_PIN you choose.
}

void setup()
{
  Serial.begin( 9600 );
  gpsPort.begin( 9600 );
  enableInterrupt( RX_PIN, gpsPortISR, CHANGE );
}

void loop()
{
  if (gps.available( gpsPort )) {
    fix = gps.read();

    // A new fix just came in, this is a good time to do other work
    if (fix.valid.location) {
      Serial.println();
      Serial.print( fix.latitude(), 6 );
      Serial.print( ',' );
      Serial.print( fix.longitude(), 6 );
      Serial.print( ' ' );
    } else {
      Serial.print( '.' );
    }
  }
}

Notice the <> include filenames. You also have to edit this line in NeoSWSerial.h:

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

That will fix the link errors you were getting.

This also uses NeoGPS for parsing the characters coming from gpsPort. If you want to try that sketch, NeoGPS is available from the Arduino IDE Library Manager, under the menu Sketch -> Include Library -> Manage Libraries.

Cheers,
/dev

1 Like

Thanks, this code works