Is this too much to put in an interrupt routine?

I wanted to read a GPS module using mbed 6 using a serial interrupt. I decided to sort the data into the different NEMA sentences inside of the interrupt sub routine which works well on my Nucleo 767ZI but was wondering if I am going to run into problems with a slower board of when I add more sensors to the system.

https://os.mbed.com/users/ericleal/code/GPS/
https://os.mbed.com/users/ericleal/code/GPS_sample_os6//file/932beb312ff9/main.cpp/

Hello Eric,

Thank you for publishing your nice GPS library!

was wondering if I am going to run into problems with a slower board of when I add more sensors to the system.

In Mbed OS 6 by default, there are four threads running after boot:

  • the ISR/scheduler thread,
  • the idle thread,
  • the timer thread and
  • the main application thread.

RTOS, including Mbed OS, needs a mechanism for counting the time and scheduling tasks. A timer that generates periodic interrupts, called system tick timer, usually does this. Under Mbed OS, we call this mechanism the RTOS ticker. The RTOS ticker runs in the timer thread . Keeping the MCU to spin in one thread without enabling the other threads can disrupt the RTOS ticker and consequently all the other RTOS functions depending on it.

The CMSIS_RTOS tutorial recommends the following solution:

With an RTOS application it is best to design the interrupt service code as a
thread within the RTOS and assign it a high priority. The first line of code in the
interrupt thread should make it wait for a signal flag. When an interrupt occurs,
the ISR simply sets the signal flag and terminates. This schedules the interrupt
thread which services the interrupt and then goes back to waiting for the next
signal flag to be set.

If you apply such design to your GPS library then you shouldn’t worry.
That could be done for example as follows.

GPS.h:

#include "mbed.h"

...

class GPS : public UnbufferedSerial {
    public :
        ...

    private :
        ...
        Thread      interruptThread;
        EventFlags  interruptFlags;

        void interruptHandler();
        void interruptTask();
};

GPS.cpp:

#include "GPS.h"

#define SERIAL_RX_INTERRUPT_FLAG   (1UL << 0)

GPS::GPS(PinName serialTX, PinName serialRX) :
    UnbufferedSerial(serialTX, serialRX),
    interruptThread(osPriorityAboveNormal)
{
    interruptThread.start(callback(this, &GPS::interruptTask));
    attach(callback(this, &GPS::interruptHandler));
    rxTimer.start();
}

void GPS::interruptHandler()
{
    interruptFlags.set(SERIAL_RX_INTERRUPT_FLAG);
}

void GPS::interruptTask()
{
    while(true) {
        interruptFlags.wait_all(SERIAL_RX_INTERRUPT_FLAG);
        readData();
    }
}
...

No modification is needed to the main.cpp.

Thank you so much. I will add those changes to my library and test it out.

I think the concern I was having with have a large number of operations in the interrupt is that if the next byte came in as it was doing those operations. If I put a printf in a serial interrupt I always get an error.

My current method of checking if the information transmission is complete is to periodically check the rxTimer to see if I haven’t received data in a defined amount of time. Is that how the interruptFlags.wait_all works?

Unfortunately, not. Since there is now one more thread (the interrupt task) running in parallel with the main one some sort of synchronisation is needed.

One way how to synchronize threads is to use event flags. Because there exist 19 various GPS messages you can set up 19 event flags. For example:

#define GPBOD       (1UL << 1)
#define GPBWC       (1UL << 2)
#define GPGGA       (1UL << 3)
#define GPGLL       (1UL << 4)
#define GPGSA       (1UL << 5)
#define GPGSV       (1UL << 6)
#define GPHDT       (1UL << 7)
#define GPR00       (1UL << 8)
#define GPRMA       (1UL << 9)
#define GPRMB       (1UL << 10)
#define GPRMC       (1UL << 11)
#define GPRTE       (1UL << 12)
#define GPTRF       (1UL << 13)
#define GPSTN       (1UL << 14)
#define GPVBW       (1UL << 15)
#define GPVTG       (1UL << 16)
#define GPWPL       (1UL << 17)
#define GPXTE       (1UL << 18)
#define GPZDA       (1UL << 19)

This will allow the interrupt thread (task) to signal, by setting the corresponding event flag, to the main thread that a certain GPS message has been received.
The main thread can wait in the while(true) loop for the selected event/message. For that purpose you can design a suitable GPS function. For example as below:

bool GPS::waitFor(uint32_t flag, uint32_t millisec)
{
    return eventFlags.wait_all(flag, millisec);
}

Such function can wait in the main thread forever while letting the interrupt task to run in parrallel without blocking it. You can also set a maximum wait time in case you’d like to perform more tasks in the main thread.

The main thread and the interrupt task share some variables (i.e. resources). To prevent data from being scrambled, access to those variables has to be protected by a mutex (mutual exclusion). For example:
GPS.cpp

void GPS::updateCheck()
{
    ...
    if (updates & GPGGA) {
        mutex.lock();
        sscanf
        (
            gpgga,
            ",%f,%f,%c,%f,%c,%d,%d,%f,%f,%c,%f",
            &time,
            &latitude,
            &ns,
            &longitude,
            &ew,
            &lock,
            &sats,
            &hdop,
            &alt,
            &unit,
            &geoid
        );
        int_time = static_cast<int>(time);
        mutex.unlock();

        //printf("GPGGA ");
        eventFlags.set(GPGGA);
    }
    ...
}

float GPS::getLat()
{
    float   result;

    mutex.lock();
    result = latitude;
    mutex.unlock();
    return result;
}

This is necessary because it could happen that the GPS::updateCheck() function is called (writes data to the latitude variable) in the interruptTask at the moment when the main task is reading the latitude variable by executing the GPS::getLat() function.

For the sake of completeness, find below the code which compiled fine for the LPC1768 target. However, it has not been tested on real hardware:
GPS.h

#include "mbed.h"
#ifndef GPS_H
#define GPS_H

#define GPBOD   (1UL << 1)
#define GPBWC   (1UL << 2)
#define GPGGA   (1UL << 3)
#define GPGLL   (1UL << 4)
#define GPGSA   (1UL << 5)
#define GPGSV   (1UL << 6)
#define GPHDT   (1UL << 7)
#define GPR00   (1UL << 8)
#define GPRMA   (1UL << 9)
#define GPRMB   (1UL << 10)
#define GPRMC   (1UL << 11)
#define GPRTE   (1UL << 12)
#define GPTRF   (1UL << 13)
#define GPSTN   (1UL << 14)
#define GPVBW   (1UL << 15)
#define GPVTG   (1UL << 16)
#define GPWPL   (1UL << 17)
#define GPXTE   (1UL << 18)
#define GPZDA   (1UL << 19)
//
#define RX_MAXTIME  2000    // Maximum time gap in milliseconds between two GPS messages
#define PACKET_SIZE 80

class GPS :
    public UnbufferedSerial
{
public:
    GPS(PinName serialTX, PinName serialRX, int baud = 9600);
    bool    waitFor(uint32_t flag, uint32_t millisec = osWaitForever);
    int     getTime();
    float   getLat();
    float   getLong();
    int     getSats();
    float   getHDOP();
    float   getSpeed();
private:
    int         gpsRxLen;
    int         updates;    //the 19 message types to be masked
    bool        uart_error = false;
    int         msg_type;
    char        gpbod[PACKET_SIZE];
    char        gpbwc[PACKET_SIZE];
    char        gpgga[PACKET_SIZE];
    char        gpgll[PACKET_SIZE];
    char        gpgsa[PACKET_SIZE];
    char        gpgsv[PACKET_SIZE];
    char        gphdt[PACKET_SIZE];
    char        gpr00[PACKET_SIZE];
    char        gprma[PACKET_SIZE];
    char        gprmb[PACKET_SIZE];
    char        gprmc[PACKET_SIZE];
    char        gprte[PACKET_SIZE];
    char        gptrf[PACKET_SIZE];
    char        gpstn[PACKET_SIZE];
    char        gpvbw[PACKET_SIZE];
    char        gpvtg[PACKET_SIZE];
    char        gpwpl[PACKET_SIZE];
    char        gpxte[PACKET_SIZE];
    char        gpzda[PACKET_SIZE];

    float       time, latitude, longitude, hdop, vdop, pdop, alt, geoid;
    float       track, mag, speedN, speedM;
    char        mode, T, M, N, K;
    char        ns, ew, unit;
    int         lock, sats, checksum;
    int         int_time;

    Thread      interruptThread;
    EventFlags  interruptFlags;
    EventFlags  eventFlags;
    Mutex       mutex;

    void        interruptHandler();
    void        interruptTask();
    void        readData();
    void        updateCheck();
};
#endif

GPS.cpp

#include "GPS.h"

#define GPS_SERIAL_RX_INTERRUPT_FLAG    (1UL << 0)

/**
 * @brief
 * @note
 * @param
 * @retval
 */
GPS::GPS(PinName serialTX, PinName serialRX, int baud /*= 9600*/ ) :
    UnbufferedSerial(serialTX, serialRX, baud),
    interruptThread(osPriorityNormal)
{
    interruptThread.start(callback(this, &GPS::interruptTask));
    attach(callback(this, &GPS::interruptHandler));
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void GPS::interruptHandler()
{
    attach(nullptr);
    interruptFlags.set(GPS_SERIAL_RX_INTERRUPT_FLAG);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void GPS::interruptTask()
{
    while (true) {
        interruptFlags.wait_all(GPS_SERIAL_RX_INTERRUPT_FLAG);
        readData();
    }
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void GPS::readData()
{
    char        gps_byte;
    static int  step;
    static int  array_title;

    read(&gps_byte, 1);
    attach(callback(this, &GPS::interruptHandler));

    // '$' begin-packet symbol
    if (gps_byte == '$') {
        updateCheck();
        step = 0;
        gpsRxLen = 0;
        updates = 0;
    }

    switch (step) {
        case 0:
            step++;
            break;

        case 1:
            if (gps_byte == 'G') {
                step++;
            }
            break;

        case 2:
            if (gps_byte == 'P') {
                step++;
            }
            break;

        case 3:
            msg_type = gps_byte << 16;
            step++;
            break;

        case 4:
            msg_type = msg_type + (gps_byte << 8);
            step++;
            break;

        case 5:
            msg_type = msg_type + gps_byte;
            if (msg_type == 4345668) {
                //BOD
                array_title = 0;
                updates |= GPBOD;
                step++;
            }
            else
            if (msg_type == 4347715) {
                //BWC
                array_title = 1;
                updates |= GPBWC;
                step++;
            }
            else
            if (msg_type == 4671297) {
                //GGA
                array_title = 2;
                updates |= GPGGA;
                step++;
            }
            else
            if (msg_type == 4672588) {
                //GLL
                array_title = 3;
                updates |= GPGLL;
                step++;
            }
            else
            if (msg_type == 4674369) {
                //GSA
                array_title = 4;
                updates |= GPGSA;
                step++;
            }
            else
            if (msg_type == 4674390) {
                //GSV
                array_title = 5;
                updates |= GPGSV;
                step++;
            }
            else
            if (msg_type == 4736084) {
                //HDT
                array_title = 6;
                updates |= GPHDT;
                step++;
            }
            else
            if (msg_type == 5386288) {
                //R00
                array_title = 7;
                updates |= GPR00;
                step++;
            }
            else
            if (msg_type == 5393729) {
                //RMA
                array_title = 8;
                updates |= GPRMA;
                step++;
            }
            else
            if (msg_type == 5393730) {
                //RMB
                array_title = 9;
                updates |= GPRMB;
                step++;
            }
            else
            if (msg_type == 5393731) {
                //RMC
                array_title = 10;
                updates |= GPRMC;
                step++;
            }
            else
            if (msg_type == 5395525) {
                //RTE
                array_title = 11;
                updates |= GPRTE;
                step++;
            }
            else
            if (msg_type == 5526086) {
                //RRF
                array_title = 12;
                updates |= GPTRF;
                step++;
            }
            else
            if (msg_type == 5461070) {
                //STN
                array_title = 13;
                updates |= GPSTN;
                step++;
            }
            else
            if (msg_type == 5653079) {
                //VBW
                array_title = 14;
                updates |= GPVBW;
                step++;
            }
            else
            if (msg_type == 5657671) {
                //VTG
                array_title = 15;
                updates |= GPVTG;
                step++;
            }
            else
            if (msg_type == 5722188) {
                //WPL
                array_title = 16;
                updates |= GPWPL;
                step++;
            }
            else
            if (msg_type == 5788741) {
                //XTE
                array_title = 17;
                updates |= GPXTE;
                step++;
            }
            else
            if (msg_type == 5915713) {
                //ZDA
                array_title = 18;
                updates |= GPZDA;
                step++;
            }
            else {
                uart_error = true;
            }
            break;

        case 6:
            if (gpsRxLen < PACKET_SIZE) {
                switch (array_title) {
                    case 0:
                        gpbod[gpsRxLen++] = gps_byte;
                        break;

                    case 1:
                        gpbwc[gpsRxLen++] = gps_byte;
                        break;

                    case 2:
                        gpgga[gpsRxLen++] = gps_byte;
                        break;

                    case 3:
                        gpgll[gpsRxLen++] = gps_byte;
                        break;

                    case 4:
                        gpgsa[gpsRxLen++] = gps_byte;
                        break;

                    case 5:
                        gpgsv[gpsRxLen++] = gps_byte;
                        break;

                    case 6:
                        gphdt[gpsRxLen++] = gps_byte;
                        break;

                    case 7:
                        gpr00[gpsRxLen++] = gps_byte;
                        break;

                    case 8:
                        gprma[gpsRxLen++] = gps_byte;
                        break;

                    case 9:
                        gprmb[gpsRxLen++] = gps_byte;
                        break;

                    case 10:
                        gprmc[gpsRxLen++] = gps_byte;
                        break;

                    case 11:
                        gprte[gpsRxLen++] = gps_byte;
                        break;

                    case 12:
                        gptrf[gpsRxLen++] = gps_byte;
                        break;

                    case 13:
                        gpstn[gpsRxLen++] = gps_byte;
                        break;

                    case 14:
                        gpvbw[gpsRxLen++] = gps_byte;
                        break;

                    case 15:
                        gpvtg[gpsRxLen++] = gps_byte;
                        break;

                    case 16:
                        gpwpl[gpsRxLen++] = gps_byte;
                        break;

                    case 17:
                        gpxte[gpsRxLen++] = gps_byte;
                        break;

                    case 18:
                        gpzda[gpsRxLen++] = gps_byte;
                        break;
                }
                break;
            }
    }
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void GPS::updateCheck()
{
    if (updates & GPBOD) {
        //printf("GPBOD ");
        eventFlags.set(GPBOD);
    }

    if (updates & GPBWC) {
        //printf("GPBWC ");
        eventFlags.set(GPBWC);
    }

    if (updates & GPGGA) {
        mutex.lock();
        sscanf
        (
            gpgga,
            ",%f,%f,%c,%f,%c,%d,%d,%f,%f,%c,%f",
            &time,
            &latitude,
            &ns,
            &longitude,
            &ew,
            &lock,
            &sats,
            &hdop,
            &alt,
            &unit,
            &geoid
        );
        int_time = static_cast<int>(time);
        mutex.unlock();

        //printf("GPGGA ");
        eventFlags.set(GPGGA);
    }

    if (updates & GPGLL) {
        //printf("GPGLL ");
        eventFlags.set(GPGGA);
    }

    if (updates & GPGSA) {
        //printf("GPGSA ");
        eventFlags.set(GPGGA);
    }

    if (updates & GPGSV) {
        //printf("GPGSV ");
        eventFlags.set(GPGSV);
    }

    if (updates & GPHDT) {
        //printf("GPHDT ");
        eventFlags.set(GPHDT);
    }

    if (updates & GPR00) {
        //printf("GPR00 ");
        eventFlags.set(GPR00);
    }

    if (updates & GPRMA) {
        //printf("GPRMA ");
        eventFlags.set(GPRMA);
    }

    if (updates & GPRMB) {
        //printf("GPRMB ");
        eventFlags.set(GPRMB);
    }

    if (updates & GPRMC) {
        //printf("GPRMC ");
        eventFlags.set(GPRMC);
    }

    if (updates & GPRTE) {
        //printf("GPRTE ");
        eventFlags.set(GPRTE);
    }

    if (updates & GPTRF) {
        //printf("GPRTF ");
        eventFlags.set(GPTRF);
    }

    if (updates & GPSTN) {
        //printf("GPSTN ");
        eventFlags.set(GPSTN);
    }

    if (updates & GPVBW) {
        //printf("GPVBW ");
        eventFlags.set(GPVBW);
    }

    if (updates & GPVTG) {
        //printf("GPVTG ");
        mutex.lock();
        sscanf(gpvtg, ",%f,%c,,%c,%f,%c,%f,%c,%c", &track, &T, &M, &speedN, &N, &speedM, &K, &mode);
        mutex.unlock();
        eventFlags.set(GPVTG);
    }

    if (updates & GPWPL) {
        //printf("GPWPL ");
        eventFlags.set(GPWPL);
    }

    if (updates & GPXTE) {
        //printf("GPXTE ");
        eventFlags.set(GPXTE);
    }

    if (updates & GPZDA) {
        //printf("GPZDA ");
        eventFlags.set(GPZDA);
    }

    uart_error = false;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
bool GPS::waitFor(uint32_t flag, uint32_t millisec)
{
    eventFlags.wait_all(flag, millisec);
    return true;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
int GPS::getTime()
{
    int result;

    mutex.lock();
    result = int_time;
    mutex.unlock();
    return result;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
float GPS::getLat()
{
    float   result;

    mutex.lock();
    result = latitude;
    mutex.unlock();
    return result;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
float GPS::getLong()
{
    float   result;

    mutex.lock();
    result = longitude;
    mutex.unlock();
    return result;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
int GPS::getSats()
{
    float   result;

    mutex.lock();
    result = sats;
    mutex.unlock();
    return result;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
float GPS::getHDOP()
{
    float   result;

    mutex.lock();
    result = hdop;
    mutex.unlock();
    return result;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
float GPS::getSpeed()
{
    float   result;

    mutex.lock();
    result = speedM;
    mutex.unlock();
    return result;
}

main.cpp

/* mbed Microcontroller Library
 * Copyright (c) 2019 ARM Limited
 *

SPDX

-License-Identifier: Apache-2.0
 */
#include "mbed.h"
#include "GPS.h"

GPS gps(PG_14, PG_9);

/**
 * @brief
 * @note
 * @param
 * @retval
 */
int main()
{
    printf("Starting...\r\n");

    // Initialise the digital pin LED1 as an output
    DigitalOut  led(LED1);

    while (true) {
        if (gps.waitFor(GPGGA, RX_MAXTIME)) {
            int time = gps.getTime();
            int latitude = static_cast<int>(gps.getLat() * 100000.0f);
            int longitude = static_cast<int>(gps.getLong() * 100000.0f);
            int speed = static_cast<int>(gps.getSpeed() * 100.0f);
            int hdop = static_cast<int>(gps.getHDOP() * 10.0f);
            int sats = gps.getSats();

            printf("time:%d lat:%d long:%d speed:%d hdop:%d sats:%d\r\n", time, latitude, longitude, speed, hdop, sats);
            led = !led;
        }

        ThisThread::sleep_for(1000ms);
    }
}

I copied this back into mbed studio and changed the pin assignment back to (PG_14, PG_9) for my board and got an ISR Queue overflow error message. If I restart the board with the RX pin disconnected it will print its message with the values of 0 and then show the error as soon as I plug the RX pin back in. The error also occurs when I comment out the printf statement.

++ MbedOS Error Info ++
Error Status: 0x80020126 Code: 294 Module: 2
Error Message: CMSIS-RTOS error: ISR Queue overflow
Location: 0x800C09F
Error Value: 0x2
Current Thread: rtx_idle Id: 0x20006238 Entry: 0x800C171 StackSize: 0x380 StackMem: 0x20006308 SP: 0x2007FEC0
For more info, visit: mbedos-error
– MbedOS Error Info –

= System will be rebooted due to a fatal error =
= Reboot count(=2) reached maximum, system will halt after rebooting =

Sorry, I forgot that the UART rx interrupt register flag is not cleared automatically. So when the program execution returns from the interrupt handler a new UART rx interrupt is triggered immediately (because the flag in the interrupt register is still on) without having received a new byte. This is repeated until the ISR Queue overflows. To prevent that the rx interrupt handler should be detached in the interrupt handler:

void GPS::interruptHandler()
{
    attach(nullptr);  // detach the rx interrupt handler (this function)
    interruptFlags.set(GPS_SERIAL_RX_INTERRUPT_FLAG);
}

To clear the UART rx interrupt flag in the interrupt register we have to read the received byte. After that we can safely re-attach the rx interrupt handler:

void GPS::readData()
{
    char            gps_byte;
    volatile int    step;
    volatile int    array_title;

    read(&gps_byte, 1);
    attach(callback(this, &GPS::interruptHandler));
    ...
}

This does clear the ISR error. However the values printed are all 0 and they are printing once every 10 seconds instead of every 1 second.

I have to MTK3339 GPS chip. The 1 second timing is the default message stream update rate so with the original program I posted the rxTimer expires once per second. Each message starts with a “$” but you get what I am seeing as 3 to 7 of them with no break in between. Where I am sitting I get GPGGA in most streams but not every time but I always get GPVTG.

I am an amateur at C++ so I don’t understand how wairFor works here.

if (gps.waitFor(GPGGA, RX_MAXTIME)) {

is in the main.cpp. The program builds and runs without error but GPGGA and RX_MAXTIME are not defined in the main. I tried hard coding those in

if (gps.waitFor((1UL << 3), 10000LL)) {

but I got the same result.

This does clear the ISR error. However the values printed are all 0 and they are printing once every 10 seconds instead of every 1 second.

  • The void GPS::readData() function doesn’t work correctly because the step and array_title are declared as volatile. This design implies that the values stored in these variables are lost when the program execution leaves the function. To retain the values for next call they shall be declared as static (I wonder how could your original program work?).

  • In the original code the RX_MAXTIME represented a time period in microseconds. In the new one it represents the maximum time period, in milliseconds, to wait for the GPGGA message to be completely received.

The program builds and runs without error but GPGGA and RX_MAXTIME are not defined in the main.

  • They are defined in the GPS.h header file which is included (copied) into the main.cpp by the
    C preprocessor :

main.cpp:

...
#include "GPS.h"
...

Additional changes:

  • I have modified the program so that the GPS::updateCheck() function is now getting called when a begin-packet ($) symbol is received.

  • The GPS class constructor was also extended by an additional parameter which enables to set the UART’s bit rate (the default value is 9600 bits per second).

The modified source codes are available in my earlier post above (I replaced the old codes by the new ones).

I can’t find the original topic I used for reading UART sensors but it was for mbed 2 and the sensorRxLen was set as volatile and I didn’t know why but I always copied it. When I originally made this and tried to sort the data into the different message types I set array_title as an int found that the array_title switch would only go to 0. After adding volatile it worked.

In the original code the rxTimer is reset every time a byte is received. The timer is then checked periodically to see if a byte has not been received in a while. I have tried to change RX_MAXTIME and it seems like the printf occurs at whatever time that is set at. If I am receiving data every second and the RX_MAXTIME is more than one second shouldn’t it never print?

A note about changing the baud rate and other configuration options is that I was at one point changing the baud and data rate which is something I would like to put back into this and found that you have to put a delay in before sending the configuration messages. It also resets every time the GPS is power cycled so you have to set the baud to 9600, tell the GPS chip at 9600 to be a different baud, and then change the mbed device baud to the new rate to continue communication.

The sensorRxLen was set as volatile and I didn’t know why but I always copied it.

  • Because the C++/C compiler doesn’t know anything about interrupt service routines (the UART interrupt is triggered by hardware rather than by software) it concludes, based on the code structure, that the ISR is never called and that’s why in the optimization phase the function is not included into the final binary program. To cope with this problem the variables modified inside the ISR are declared as volatile to indicate that their values can change any time (for a reason unknown for the compiler). This prevents the compiler from optimizing away such variables as well the related function.

If I am receiving data every second and the RX_MAXTIME is more than one second shouldn’t it never print?

The GPS::waitFor(uint32_t flag, uint32_t millisec) function is waiting in the main thread for the flag to be set by the interruptThread (in the interruptTask). If that happens the waitFor returns immediately regardless of the millisec parameter. However, if you for example did not want to wait in the main thread at this point forever in case when no GPGGA messages are arriving you can specify a timeout limit by setting the millisec parameter to an adequate value (which should be usually greater than the normal interval). Actually, there is no need to use the if condition. The code below will work too.

main.cpp

...
    while (true) {
        gps.waitFor(GPGGA, RX_MAXTIME); // wait until GPGGA is set or RX_MAXTIME elapsed
        
        int time = gps.getTime();
        int latitude = static_cast<int>(gps.getLat() * 100000.0f);
        int longitude = static_cast<int>(gps.getLong() * 100000.0f);
        int speed = static_cast<int>(gps.getSpeed() * 100.0f);
        int hdop = static_cast<int>(gps.getHDOP() * 10.0f);
        int sats = gps.getSats();

        printf("time:%d lat:%d long:%d speed:%d hdop:%d sats:%d\r\n", time, latitude, longitude, speed, hdop, sats);
        led = !led;
        ThisThread::sleep_for(1000ms);
    }
  • Have you tested the last version of the modified code? Does it work?

Thank you for the help!

I thought I had made the changes you suggested this morning but I must have missed something. I just copied and pasted the whole thing and this does work now.

I’m glad that it works. Now it should be safe to include printf into any GPS function, the GPS::interruptTask() included, except the GPS::interruptHandler(). And the code should run on any target that is slower than the NUCLEO_F767ZI provided it has enough RAM for the Mbed RTOS.