MKR1500 Sleep after set amount of time, not working

Hello,

I'm trying to make my MKR1500 with a GPS6MV2 GPS module, connect to the mobile network when powered on and send positional data. Then when the power to the USB is turned off the board continues to send positional data for a given amount of time before eventually going to sleep, until the USB receives power again.

However, i can't get the sleep functionality to work how i need.
Can someone please help ?

What i know so far.

The GPS module works.
The MQTT connection to the mobile network and dash board works.
The wakeup interrupt detects when the board is plugged in.

Currently the board will, Setup correctly and send data whilst the USB is plugged in. It then sends data as expected for 1 min before sleeping. Unfortunately when i plug the USB back in the board wakes up and seems to continue in the main loop() before going to setup(), here it either freezes or when i then unplug the USB it goes straight to sleep and seems to now skip the 1 min sending data section.

I have tried printing the variables which control this functionality and they seem to be correct. I can't understand why the boards functionality falls apart after it's been sent to sleep and woken back up.

Here is the Code that i've been using.

#include "main.h"
#include <MKRNB.h>
#include <ArduinoMqttClient.h>
#include <Arduino_MKRGPS.h>
#include <TimeLib.h>
#include <TinyGPSPlus.h>
#include "wiring_private.h"
#include "ArduinoLowPower.h"


// Please enter your sensitive data in the Secret tab or arduino_secrets.h
// PIN Number
const char PINNUMBER[] = SECRET_PINNUMBER;

// #define serialGPS Serial1
Uart serialGPS (&sercom3, 1, 0, SERCOM_RX_PAD_1, UART_TX_PAD_0); // Create the new UART instance assigning it to pin 1 and 0

// initialize the library instance
NBClient client;
MqttClient mqttClient(client);
GPRS gprs;
NB nbAccess;
NBModem modem;
TinyGPSPlus gps;

// Publishing Frequency Variables 
unsigned long interval = 1 * 60 * 1000; // n Minutes
unsigned long previousMillis = 0;

// Sending Data Length Variables 
unsigned long sendingDataCounter;
unsigned long dataSendingDuration = 2;

float latitude;
float longitude;

volatile bool hasBeenWoken = false;
const int powerDetectionPin = 5;

// I want the board to wake up when reciving power form the USB port. 
// Once awake i want it to send it's positional data to the dashboard. 
// when the power is turned off, i want it to continue to send data, 
// for a given amount of time (3 hours), before eventually going to sleep. 

void charging() {
  hasBeenWoken = true;
}

void connectToNetwork(){
    // connection state
    boolean connected = false;

    Serial.print("Modem Initialization ...");
    
    // After starting the modem with NB.begin()
    // attach to the GPRS network with the APN, login and password
    while (!connected)
    {
        if ((nbAccess.begin(PINNUMBER) == NB_READY) &&
            (gprs.attachGPRS() == GPRS_READY))
        {
            connected = true;
        }
        else
        {
            Serial.println("Not connected");
            delay(1000);
        }
    }
    //prints the modems IMEI number cut and paste into Adafruit cloud
    Serial.println(modem.getIMEI());
    mqttClient.setId(modem.getIMEI());
    mqttClient.setUsernamePassword(AIO_USERNAME, AIO_KEY);

    Serial.print("MQTT Initialization ...");
    if (!mqttClient.connect(AIO_SERVER, AIO_SERVERPORT))
    {
        Serial.print("MQTT connection failed! Error code = ");
        Serial.println(mqttClient.connectError());
        while (1)
            ;
    }    
}

void getGpsPostion(){
  // - - - GPS Section - - - 
  while (serialGPS.available()) {
    if(gps.encode(serialGPS.read()))
    {
      if (gps.location.isValid())
      {
          latitude = gps.location.lat();
          longitude = gps.location.lng();
      }
    }
  }
}

void setup()
{
  for (int i = 0; i < 20; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN, LOW);
    delay(100);
  }

  // initialize serial communications.
  Serial.begin(9600);
  serialGPS.begin(9600);
  Serial.println("In Setup...");

  // GPS Input and Output Pins.
  pinPeripheral(1, PIO_SERCOM); //Assign RX function to pin 1
  pinPeripheral(0, PIO_SERCOM); //Assign TX function to pin 0

  pinMode(LED_BUILTIN, OUTPUT);
  // setting Pin for detecting 5v USB power spike.
  pinMode(powerDetectionPin, INPUT_PULLUP);
  
  LowPower.attachInterruptWakeup(powerDetectionPin, charging, RISING);
  connectToNetwork();
  delay(3000);
}


// - - - - - MAIN LOOP Section - - - - - - 
void loop()
{
  mqttClient.poll();
  unsigned long currentMillis = millis();
  getGpsPostion();

  // - - - Sending Data Section - - - 
  if(currentMillis - previousMillis >= interval)
  {
    // save the last time a message was sent
    previousMillis = currentMillis;
    Serial.println("Publishing Data");

    // Updating the dashboard Logs
    mqttClient.beginMessage(AIO_FEED_LOGS);
    mqttClient.println(" - Modem IMEI number: " + String(modem.getIMEI()));
    mqttClient.println(" - GPS Coordinates: " + String(latitude) + " latitude, " + String(longitude) + " longitude");
    mqttClient.endMessage();
  }

  // - - - - - Sleep control section - - - - - -
  // Counts miniutes when not plugged in. 
  if(digitalRead(5)){
    sendingDataCounter = 0;
  } else {
    sendingDataCounter = minute();
    hasBeenWoken = false;
  }

  // Check used to recal setup after sleep. 
  if(hasBeenWoken){
    Serial.println("Running Setup Function");
    hasBeenWoken = false;
    setup();
  }

  // Final check before sending board to sleep. 
  if(!digitalRead(5) && (sendingDataCounter >= dataSendingDuration)){
    hasBeenWoken = false;
    sendingDataCounter = 0;
    LowPower.sleep();
  }

  // Makes LED flash to show loop is turning. 
  for (int i = 0; i < 5; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
  }

  Serial.print(".");
  delay(2000);
}


void SERCOM3_Handler()
{
  serialGPS.IrqHandler();
}

Here's a pic of the hardware i'm using.

Because you do the time-keeping wrongly. You simply call minute() which returns the minutes since startup. If that's bigger than 2 you go to sleep. That's not how you described it.

What would you suggest I use instead? I want the board to send data for 3 hours after it's been unplugged, then sleep until it receives power on the USB.

Don't use a library you don't know exactly what it's doing.

Save the time (from millis()) when you lost USB power, check (by comparing to the value of millis()) if the time you want to still send values is over, then go to sleep. About as you do for the MQTT publishing.

Eliminate that part completely, it's not needed anymore.

Ahhh thank you for the suggestion.

I've implemented the unplugTime check and the sleep functionality seems pretty robust now.

However, when i then add the MQTT Client, Modem and GPS code back in, the sleep / wake stability falls apart again.

What i know so far.
The Connection to the MQTT works.
The Board sends data for a desired amount of time before sleeping.
However when i wake the board back up by plugging in the USB the board freezes, showing nothing on the serial monitor AND none of the LED flashing sections activate.

What's causing the board to lock after sleep wake cycle?
Can anyone please help ?

I really need this set up to be as reliable as possible.

Here's my code, apologies for the numerous LED flashing sections, it's the only way i know where the program is in the script when unplugged.

#include "main.h"
#include <MKRNB.h>
#include <ArduinoMqttClient.h>
#include <Arduino_MKRGPS.h>
#include <TimeLib.h>
#include <TinyGPSPlus.h>
#include "wiring_private.h"
#include "ArduinoLowPower.h"


// Please enter your sensitive data in the Secret tab or arduino_secrets.h
// PIN Number
const char PINNUMBER[] = SECRET_PINNUMBER;

// #define serialGPS Serial1
Uart serialGPS (&sercom3, 1, 0, SERCOM_RX_PAD_1, UART_TX_PAD_0); // Create the new UART instance assigning it to pin 1 and 0

// initialize the library instance
NBClient client;
MqttClient mqttClient(client);
GPRS gprs;
NB nbAccess;
NBModem modem;
TinyGPSPlus gps;

// Publishing Frequency Variables 
unsigned long interval = 1 * 60 * 1000; // n Minutes
unsigned long previousMillis = 0;

// Sending Data Length Variables 
unsigned long unpluggedTime;
unsigned long dataSendingDuration = 20 * 60 * 1000; // n Minutes

float latitude;
float longitude;
int sattaliteCount;

const int powerDetectionPin = 5;
volatile bool powerToggle = true;
volatile bool hasBeenWoken = false;
volatile bool wasSentToSleep;

// I want the board to wake up when reciving power form the USB port. 
// Once awake i want it to send it's positional data to the dashboard. 
// when the power is turned off, i want it to continue to send data, 
// for a given amount of time (3 hours), before eventually going to sleep. 

void charging() {
  powerToggle = true;
  hasBeenWoken = true;
}

void connectToNetwork(){
    // connection state
    boolean connected = false;

    Serial.print("Modem Initialization ...");
    
    // After starting the modem with NB.begin()
    // attach to the GPRS network with the APN, login and password
    while (!connected)
    {
      Serial.println("Inside nbAcess while loop..");

      if ((nbAccess.begin(PINNUMBER) == NB_READY) &&
          (gprs.attachGPRS() == GPRS_READY))
      {
          connected = true;
      }
      else
      {
          Serial.println("Not connected");
          delay(1000);
      }
    }
    //prints the modems IMEI number cut and paste into Adafruit cloud
    Serial.println(modem.getIMEI());
    mqttClient.setId(modem.getIMEI());
    mqttClient.setUsernamePassword(AIO_USERNAME, AIO_KEY);

    Serial.print("MQTT Initialization ...");
    if (!mqttClient.connect(AIO_SERVER, AIO_SERVERPORT))
    {
        Serial.print("MQTT connection failed! Error code = ");
        Serial.println(mqttClient.connectError());
        while (1)
            ;
    }    
}

void getGpsPostion(){
  Serial.println("get GPS Funciton has been called.");
}

void setup()
{
  for (int i = 0; i < 20; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN, LOW);
    delay(100);
  }

  // initialize serial communications.
  Serial.begin(9600);
  serialGPS.begin(9600);
  Serial.println("In Setup...");

  // GPS Input and Output Pins.
  pinPeripheral(1, PIO_SERCOM); //Assign RX function to pin 1
  pinPeripheral(0, PIO_SERCOM); //Assign TX function to pin 0

  pinMode(LED_BUILTIN, OUTPUT);
  // setting Pin for detecting 5v USB power spike.
  pinMode(powerDetectionPin, INPUT_PULLUP);
  
  LowPower.attachInterruptWakeup(powerDetectionPin, charging, RISING);
  connectToNetwork();
  delay(3000);
}


// - - - - - MAIN LOOP Section - - - - - - 
void loop()
{
  unsigned long currentMillis = millis();
  unsigned long currentMins = millis();

  // - - - Sleep Contorl section - - -
  // Check used to recal setup after sleep. 
  if(hasBeenWoken && wasSentToSleep){
    Serial.println("Running Setup Function");
    hasBeenWoken = false;
    wasSentToSleep = false;
    setup();
  }

  // Records time when board is unplugged.
  if(!digitalRead(5) && powerToggle){
    powerToggle = false;
    unpluggedTime = currentMins;
  } else if (digitalRead(5)){
    unpluggedTime = currentMillis;
  } 

  // Check data sending duration before sleep. 
  if((currentMins - unpluggedTime >= dataSendingDuration)){
    unpluggedTime = currentMillis;
    wasSentToSleep = true;
    nbAccess.shutdown();
    gprs.detachGPRS();
    LowPower.sleep();
  }

  mqttClient.poll();

  // - - - GPS Section - - - 
  if (serialGPS.available()) {
    Serial.println("Searching for GPS position.");

    for (int i = 0; i < 10; i++) {
      digitalWrite(LED_BUILTIN, HIGH);
      delay(200);
      digitalWrite(LED_BUILTIN, LOW);
      delay(200);
    }
    delay(1000);
    for (int i = 0; i < 10; i++) {
      digitalWrite(LED_BUILTIN, HIGH);
      delay(200);
      digitalWrite(LED_BUILTIN, LOW);
      delay(200);
    }
    if(gps.encode(serialGPS.read()))
    {
      sattaliteCount = gps.satellites.value();
      if (gps.location.isValid())
      {
          latitude = gps.location.lat();
          longitude = gps.location.lng();
      }
    }
    delay(1000);
  }

  float batVoltage = analogRead(ADC_BATTERY) * 3.3 / 1023.0 * 1.275;

  // - - - Sending Data Section - - - 
  if(currentMillis - previousMillis >= interval)
  {
    // save the last time a message was sent
    previousMillis = currentMillis;
    Serial.println("Publishing Data");

    // Updating the dashboard with location. 
    mqttClient.beginMessage(AIO_FEED_LOCATION);
    mqttClient.print("{\"lat\":");
    mqttClient.print(latitude);
    mqttClient.print(",");
    mqttClient.print("\"lon\":");
    mqttClient.print(longitude);
    mqttClient.println("}");
    mqttClient.endMessage();

    // Updating the dashboard with battery voltage. 
    mqttClient.beginMessage(AIO_FEED_VOLTAGE);
    mqttClient.print(batVoltage);
    mqttClient.endMessage();

    // Updating the dashboard Logs
    mqttClient.beginMessage(AIO_FEED_LOGS);
    mqttClient.println(" - Modem IMEI number: " + String(modem.getIMEI()));
    mqttClient.println(" - GPS Satellite Count: " + String(sattaliteCount));
    mqttClient.println(" - GPS Coordinates: " + String(latitude) + " latitude, " + String(longitude) + " longitude");
    mqttClient.endMessage();
  }

  // Makes LED flash to show loop is turning. 
  for (int i = 0; i < 5; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
  }
  Serial.print(".");
  delay(2000);
}


void SERCOM3_Handler()
{
  serialGPS.IrqHandler();
}

Additionally, i've yet to get the GPS module to work reliably, i've had it outside for 20 mins with decent view of the sky and still no blue LED or GPS satellite count.

Thanks in advance.

When the MCU wakes up it immediately polls the MQTT client. That doesn't make sense. If it isn't a problem for your application I would recommend to reset the board after wakeup by calling

NVIC_SystemReset();

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.