Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 4 of 4

Thread: Using IntervalTimer with multiple timers

  1. #1
    Junior Member
    Join Date
    Apr 2019
    Posts
    2

    Using IntervalTimer with multiple timers

    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();
    }

  2. #2
    Senior Member
    Join Date
    May 2017
    Posts
    202
    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

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    19,929
    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.

  4. #4
    Junior Member
    Join Date
    Apr 2019
    Posts
    2
    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();
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •