Using IntervalTimer with multiple timers

Status
Not open for further replies.

guidout

New member
Hi all,
newbie here. I'm making a data log for my bike.
For now I'm dealing with GPS via serial with a Teensy 3.2.
I'm using 2 IntervalTimers, one to read the GPS string and one to get the GPS status.
Below is my code. It's a total work in progress but it's functional.
As you can see the GPS read runs every 0.1 seconds while the timer to get the GPS status runs every 5 seconds.
The problem I'm having is that every time the getGPSstatus timer gets triggered the readGPS timer gets interrupted. I tried to set different priorities but nothing changes.
Basically, every 5 seconds I get a partial GPS NMEA string since (I assume) readGPS get's interrupted.
Do you have any suggestion on what I'm doing wrong?

Thank you in advance,
Guido

Code:
#include <Wire.h>

// Communications
#define GPSserial Serial1
#define I2C_0 Wire
#define I2C_1 Wire1

// Timer objects
IntervalTimer readGPStimer;
IntervalTimer getGPSstatusTimer;
IntervalTimer readVoltSensors;
IntervalTimer logDataToSD;

// Settings
#define ReadVinFromI2Cs_Freq 200
#define ReadGPS_Freq 50
#define DataLog_Freq 100

// Internal Variables
const int chipSelect = 10;
int currTime;
char GPSlineNOW[200];
char GPSlineNEW[200];
byte incomingByte;
char GPSchar;
char GPSstatus[4];

void setup() {
  Serial.begin(9600);
  GPSserial.begin(115200);
  I2C_0.begin();
  I2C_1.begin();
  
  readGPStimer.begin(readGPS, 100000);
  readGPStimer.priority(0);
  getGPSstatusTimer.begin(getGPSstatus, 5000000);
  getGPSstatusTimer.priority(1);
}

void loop() {
  Serial.println( String(GPSlineNOW) );
  Serial.print("GPS Status: ");
  Serial.println(String(GPSstatus));
  delay(100);
  

}

void readGPS() {
  int count = 0;
  while (GPSserial.available()>0) {
    if (count==0){
      memset(GPSlineNOW, 0, sizeof(GPSlineNOW));
      }
    GPSchar = GPSserial.read();
    GPSlineNOW[count] = GPSchar;
    count++;
  }
}

void getGPSstatus() {
  char * strtokIndx; // this is used by strtok() as an index

  for (int n=1; n<=9; n++){
    // code source http://forum.arduino.cc/index.php?topic=288234.0
    if (n==1){
      strtokIndx = strtok(GPSlineNOW,",");
    }
    else if (n!=9){
      strtokIndx = strtok(NULL, ",");
    }
    else {
      strtokIndx = strtok(NULL, ",");
      strcpy(GPSstatus, strtokIndx);
    }
  }
}

void setupVinOnI2C_0() {
  // set calibration for all sensors
  I2C_0.beginTransmission(0x40);
  I2C_0.write(0);
  I2C_0.endTransmission();
}

void setupVinOnI2C_1() {
  // set calibration for all sensors
  I2C_1.beginTransmission(0x40);
  I2C_1.write(0);
  I2C_1.endTransmission();
}
 
I suggest you remove the setting of priority on the interrupts. Interrupts of the same priority do not interrupt each other. That is probably not your problem.

Some general observations about your code that may lead you to find out what is going on...
You do not test for buffer overflow issues. Your main buffer for the gps is 200 bytes long. A quick calculation shows that in 100 ms, you could receive over 1000 characters into that buffer. (( 115200 baud / 10 bits per char ) * .1 sec )

Your GPSstatus string is 4 bytes long, yet you blindly copy a string into that buffer that is of unknown ( to me at least ) length.

With data arriving at 115200 baud and leaving at 9600 baud, your program may be filling some system buffers and then blocking on output to the serial monitor. I don't know which NMEA strings you have enabled. You should count up all the characters that arrive in 0.1 seconds and see if you can transmit them at 9600 baud without running out of time. ( each character will be 10 bits with start and stop bits included ).

Perhaps you could use a GPS library? https://github.com/adafruit/Adafruit_GPS
 
Interrupts are a painful way to do this sort of programming. That's why the IntervalTimer page has a warning in red text "Advanced programming is required to properly use IntervalTimer, because your function runs as an interrupt."

Using more than 1 interrupt compounds all these tough problems!

I highly recommend you switch to using elapsedMillis. It may seem more complex, involving more code to be checking the variable from your loop() function, but in the end using elapsedMillis or other non-interrupt ways is so much easier.
 
Thanks, Paul.
Sadly I followed your advise and started using elapsedMillis.
This is how my code looks like so far.
As I said, I'm only a few hours into this project...don't judge :)

Code:
#include <Wire.h>
#include <SD.h>
#include <SPI.h>

// Communications
#define GPSserial Serial1
#define I2C_0 Wire
#define I2C_1 Wire1

// elapsedMillis objects
elapsedMillis readGPSMillis;
elapsedMillis getGPSstatusMillis;
elapsedMillis readVinMillis;

// Settings
#define debug 1
float ReadVinFromI2Cs_Freq=100; //Hz
float ReadGPS_Freq=20; //Hz
float RefreshGPSstatus_Freq=1; //Hz
float DataLog_Freq=100; //Hz

// Internal Variables
File LogFile;
const int chipSelect = 10;
int currTime;
char GPSlineNew[200];
char GPSline[200];
byte incomingByte;
//char GPSchar;
char GPSstatus[4];
int16_t Vin0;

void setup() {
  Serial.begin(38400);
  GPSserial.begin(115200);
  I2C_0.begin();
  I2C_1.begin();
  
  Serial.print("Initializing SD card...");
  if (!SD.begin(chipSelect)) {
    Serial.println("initialization failed!");
    return;}
  Serial.println("initialization done.");
  LogFile = SD.open("testLog.txt", FILE_WRITE);
}

void loop() {
  // Log GPS
  if (readGPSMillis >= 1/ReadGPS_Freq*1000){
    readGPS();
    readGPSMillis = 0;
  }
  // Get GPS status
  if (getGPSstatusMillis >= 1/RefreshGPSstatus_Freq*1000){
    getGPSstatus();
    getGPSstatusMillis = 0;
    if (debug){
      Serial.print("GPS Status: ");
      Serial.println(String(GPSstatus));
    }
  }
  // Read voltage sensors
  if (1 && readVinMillis >= (1/ReadVinFromI2Cs_Freq)*1000){
    readVinFromI2Cs();
    readVinMillis = 0;
    if (debug){
      Serial.print("Time:");
      Serial.print(millis());
      Serial.print("; Vin: ");
      Serial.println(Vin0);
    }
  }
  
  
  //LogFile = SD.open("testLog.txt", FILE_WRITE);
  //LogFile.println(String(GPSlineNOW));
  //LogFile.close();
  

}

void readGPS() {
  char GPSchar;
  int len;
  char LineStop = '\n';
  
  while (GPSserial.available()>0) {
    GPSchar = GPSserial.read();
    if (GPSchar==LineStop){
      strcpy(GPSline,GPSlineNew);
      memset(GPSlineNew, 0, sizeof(GPSlineNew));
      if (debug){
        Serial.println( String(GPSline) );
      }
    }
    else{
      len = strlen(GPSlineNew);
      GPSlineNew[len] = GPSchar;
    }
  }
}

void getGPSstatus() {
  char * strtokIndx; // this is used by strtok() as an index

  for (int n=1; n<=9; n++){
    // code source http://forum.arduino.cc/index.php?topic=288234.0
    if (n==1){
      strtokIndx = strtok(GPSline,",");
    }
    else if (n!=9){
      strtokIndx = strtok(NULL, ",");
    }
    else {
      strtokIndx = strtok(NULL, ",");
      strcpy(GPSstatus, strtokIndx);
    }
  }
}

void readVinFromI2Cs() {
  I2C_0.beginTransmission(0x40);
  I2C_0.requestFrom(0x40, 2 );
  Vin0 = ((I2C_0.read() << 8) | I2C_0.read());
  I2C_0.endTransmission();
}

void setupVinOnI2C_0() {
  // set calibration for all sensors
  I2C_0.beginTransmission(0x40);
  I2C_0.write(0x02);
  I2C_0.endTransmission();
}

void setupVinOnI2C_1() {
  // set calibration for all sensors
  I2C_1.beginTransmission(0x40);
  I2C_1.write(0x02);
  I2C_1.endTransmission();
}
 
Status
Not open for further replies.
Back
Top