"SoftwareSerial.h" doesn't work with an interrupt subroutine

Hello everyone

Since a view days I have a problem I really can't solve. I wanted to creat an interrupt subroutine, which is only executed if the the signal on pin 8 is changin. But I regonized that it doesn't work with the libary "SoftwareSerial.h", which I need for my project (because of the GPS-Module). Does anyone know why it doesn't work, or has even a solution to my problem ?

Thanks already, and sorry because of my english, It's not my mother toungh

#include "SoftwareSerial.h"
#include <Servo.h>
byte ch_pitch;   // Needed for Reciver, servo communication
int receiver_pitch; // Needed for storing value of reciver signal-lenght
unsigned long timer_1;  // Needed for Reciver, Servo communication

Servo pitch;
 

void setup(){

  pitch.attach(8);
   //Arduino (Atmega) pins default to inputs, so they don't need to be explicitly declared as inputs
  PCICR |= (1 << PCIE0);    // set PCIE0 to enable PCMSK0 scan
  PCMSK0 |= (1 << PCINT0);  // set PCINT0 (digital input 8) to trigger an interrupt on state change
  PCMSK0 |= (1 << PCINT1);  // set PCINT1 (digital input 9)to trigger an interrupt on state change
  PCMSK0 |= (1 << PCINT2);  // set PCINT2 (digital input 10)to trigger an interrupt on state change
  PCMSK0 |= (1 << PCINT3);  // set PCINT3 (digital input 11)to trigger an interrupt on state change
}

void loop(){
}

ISR(PCINT0_vect){ 
  //Channel 1=========================================
  if(ch_pitch == 0 && PINB & B00000001 ){         //Input 8 changed from 0 to 1
    ch_pitch= 1;                                 //Remember current input state
    timer_1 = micros();                                 //Set timer_1 to micros()
    pitch.write(receiver_pitch);
  }
  else if(ch_pitch == 1 && !(PINB & B00000001)){  //Input 8 changed from 1 to 0
    ch_pitch = 0;                                 //Remember current input state
    receiver_pitch = micros() - timer_1;      //Channel 1 is micros() - timer_1
    
  }
}

test.ino (1.45 KB)

See this post for getting GPS on a software serial port to work with a servo.

See this post to resolve the Pin Change Interrupt conflict that you usually get with software serial ports.

HEllO COMMUNITY

I have a big problem since the last few days, I would like to use my GPS in my code, which uses SoftwareSerial.h, and at the same time I work with ISR(PCINT0_vect) to read data of a RC_Receiver which I hoocked up at my aruino to read if a switch is on or off.

Does anyone know how I could work with the GPS, and still use ISR(PCINT0_vect) ?

Thank you already :slight_smile:

//
----------------------------PROJECT SOMMER 2017 TIMON BINDER------------------------


//Added libaris TIMON---------------------------------------------------------------------------------------------------------------------------------------------
#include <Wire.h>
#include <Servo.h>  // Needed for Servo controlling and Motor controlling
#include <SD.h>     //Load SD library
#include <SoftwareSerial.h> 
#include "TinyGPS++.h"
#define RXPin 4
#define TXPin 3
#define GPSBaud 4800
#define ConsoleBaud 115200

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

// The TinyGPS++ object
TinyGPSPlus gps;
unsigned long lastUpdateTime = 0;

//DEFINE GLOBAL VARIABLES TIMON----------------------------------------------------------------------------------------------------------------------------------

byte  ch_switch;
int  receiver_switch;
unsigned long timer_5;



//I call this routine every time input 12 changed state
ISR(PCINT0_vect){

  //Channel 1
  if(ch_switch == 0 && PINB & B00000001 ){         //Input 12 changed from 0 to 1
    ch_switch = 1;                                 //Remember current input state
    timer_5 = micros();                                 //Set timer_5 to micros()
    //switch.write(receiver_switch);
  }
  else if(ch_switch == 1 && !(PINB & B00000001)){  //Input 12 changed from 1 to 0
    ch_switch = 0;                                 //Remember current input state
    receiver_switch = micros() - timer_5;      //Channel 5 is micros() - timer_5
  }
  
}



void setup(){

Serial.begin(38400); // start serial connection to print data on screen

{PCICR |= (1 << PCIE0);    // set PCIE0 to enable PCMSK0 scan
PCMSK0 |= (1 << PCINT4);  // set PCINT4 (digital input 12)to trigger an interrupt on state change
}

  ss.begin(GPSBaud);

  Serial.println("GPS Example 3");
  Serial.println();
}

void loop(){

  while (ss.available() > 0)
    gps.encode(ss.read());

  // Every 1 seconds, do an update.
  if (millis() - lastUpdateTime >= 1000)
  {
    lastUpdateTime = millis();
    Serial.println();

    Serial.print("  LAT ");
    Serial.println(gps.location.lat());
     Serial.print("  LONG ");
    Serial.println(gps.location.lng());


  }

  if(receiver_switch >= 1500){
    
    Serial.print("Switch is on");
  }

  if(receiver_switch < 1500){
    Serial.print("Switch is on");
  }

  delay(250);
}

Use different pins for software serial ?

SoftwareSerial is very inefficient, because it disables interrupts for long periods of time. This can interfere with other libraries (like Servo) or prevent receiving data on Serial. It cannot transmit and receive at the same time. It cannot be used with Pin Change Interrupt ISRs or libraries (e.g., EnableInterrupt).

You could avoid all this if you hooked the GPS to Serial (RX pin 0) instead of a software serial pin. The GPS will not interfere with your print statements, but you will have to disconnect the GPS device from pin 0 every time you want to upload a new sketch over USB. Read more here.

If you could use pins 8 & 9, AltSoftSerial is the next best choice.

If you can't switch pins, NeoSWSerial would work if you could set the GPS baud rate to 9600, 19200 or 38400.

And you shouldn't hook a switch to a Pin Change Interrupt, unless it is debounced by the hardware (Schmitt trigger or RC smoothing). You can just poll the pin in loop. Even better, try the Bounce2 library.

I would also suggest trying the NeoGPS library. It's smaller, faster, more accurate and more reliable than all other libraries. There are lots of hints and tips in the Installation and Troubleshooting pages. Here is a version of your sketch that uses NeoGPS, simply polls the switch (no PCI), and expects the GPS to be connected to Arduino RX pin 0:

//----------------------PROJECT SOMMER 2017 TIMON BINDER------------------------


//Added libaris TIMON -------------------------------------------------------------
#include <Wire.h>
#include <Servo.h>  // Needed for Servo controlling and Motor controlling
#include <SD.h>     //Load SD library
#include <NMEAGPS.h>

#define GPSBaud 4800  // Are you sure?  Most GPS devices run at 9600
#define ConsoleBaud 38400

// The serial connection to the GPS device
#define gpsPort Serial   // pin 0 to GPS TX

//    ...or...
//#include <AltSoftSerial.h>
//AltSoftSerial gpsPort; // pin 8 to GPS TX

NMEAGPS gps; // The NeoGPS parser object
gps_fix fix; // A structure of all the parsed GPS fields


//DEFINE GLOBAL VARIABLES TIMON ----------------------------------------

byte  ch_switch;
int  receiver_switch;
unsigned long timer_5;


void checkSwitch()
{
  byte input12 = digitalRead( 12 );

  //Channel 1
  if(ch_switch == 0 && input12 ){          //Input 12 changed from 0 to 1
    ch_switch = 1;                         //Remember current input state
    timer_5 = micros();                    //Set timer_5 to micros()
    receiver_switch = 0;                   // reset
  }
  else if(ch_switch == 1 && !input12){     //Input 12 changed from 1 to 0
    ch_switch = 0;                         //Remember current input state
    receiver_switch = micros() - timer_5;  //Channel 5 is micros() - timer_5
    if(receiver_switch >= 1500){
      Serial.print("Switch is on");
    }
  }
  
}


void setup(){
  pinMode( 12, INPUT_PULLUP );

  Serial.begin(ConsoleBaud); // start serial connection to print data on screen

  //gpsPort.begin(GPSBaud);   <-- uncomment if you use AltSoftSerial for GPS on pin 8

  Serial.println( F("GPS Example 3") );
  Serial.println();
}

void loop(){

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

    Serial.println();

    Serial.print("  LAT ");
    Serial.println( fix.latitude(), 6 );
    Serial.print("  LONG ");
    Serial.println( fix.longitude(), 6 );
  }

  checkSwitch();
}

This avoids the ISR vector conflict and is much more reliable. And it doesn't use delay, which can cause you to lose GPS data. You can see how to use AltSoftSerial instead, if you can connect the GPS TX pin to Arduino pin 8 (assuming you are using an UNO).

The original sketch used 12958 bytes of program space and 1341 bytes of RAM.
The NeoGPS version uses 12484 bytes of program space and 1154 bytes of RAM.

If you'd like to try it, NeoGPS is available from the Arduino IDE Library Manager, under the menu Sketch -> Include Library -> Manage Libraries.

Threads merged.