A working example of lower power and http requests

After a fairly significant amount of troubleshooting and hacking I've finally found a solution that's a step in the right direction.

My end goal is probably similar to a lot of folks here; have a MKR1500 deep sleep , wakeup, read a sensor, send it over cellular, and return back to a deep sleep. My current period is 15 minutes.

This has been somewhat of a journey, I don't want to elaborate on the problems I have had, but I'll write them down for posterity in case other users who are running MKR1500s in the field want a few thoughts from someone who has three of them running for about a year now.

  • mqtt has memory leaks that I can't explain.
  • https worked fine until the certs expired
  • http is by far the most reliable protocol that I have found.

With all that being said, I have never been able to get the modem to successfully sleep in the field and I was always subject to about a 10.2 mA draw in LowPower.deepSleep().

This post provides a working solution to get the MKR1500NB + SARA-R410M down to 2.7 mA, wake up, send an http request, and return to sleep at 2.7mA. (FYI, without the modem I can get the current draw down to 0.6 mA)

In addition to providing a means to sleep the modem this code also shows a way to modify the bq24195 to no longer blink the orange LED when no battery is detected. (Why is that not a feature already? See github.) I use an SLA battery and a voltage regulator to power the MKR1500 via VIN.

Continuing on, this is no where near production code but I wanted to post it to give back to the community and hopefully have a conversation on how to correctly bring the modem back from sleep - because using the PWR_ON pulse as specified in Table 4.2.9 does not seem to work once AT+CPSMS=1 and AT+UPSV=4 are set. eg

digitalWrite(SARA_PWR_ON, HIGH);
delay(150); // 150-12000 ms Per table 4.2.9
digitalWrite(SARA_PWR_ON, LOW);

That reminds me, without AT+CPSMS=1 the modem pulses and does not remain at 2.7 mA for the duration of the deepSleep.

I feel like this code is more complicated than it needs to be - but every time I try to simplify it, it breaks. I'd like to take the time to get this example working well so if you have any contributions you can make, please speak up.

Some observations

  • Not every loop is the same. Sometimes the LTEConnect() function connects on the first try, sometimes it gets hung up on nbAccess.begin().

This is the complete working example that I am currently running on a Project FI sim card. Signal strength is good, power is stable.

#include <MKRNB.h>
#include <ArduinoLowPower.h>
#include <Wire.h>
#include <ArduinoJson.h>

// BQ24195 Addresses
#define BQ24195_ADDRESS 0x6B
#define CHARGE_TIMER_CONTROL_REGISTER 0x05

// GSM and NB objects
NB nbAccess(false);
NBClient client; // Used for the TCP socket connection
GPRS gprs;
NBModem modem;

// Serial AT command buffer
String response;

void LTEConnect();
void ModemConfig();
void fix_bq24195();

void setup()
{
  Serial.begin(9600);
  delay(1000);
  Serial.println("************************");
  Serial.println("****** Init Start ******");
  Serial.println("************************");
  delay(5000);

  Serial.println("Removing bq24195 led blink to save power.");

  fix_bq24195();

  Serial.println("bq24195 fix complete.");

  Serial.println("Turning the modem on...");

  MODEM.begin();

  delay(4000);

  Serial.println("Setting Power Save Mode: 1.");

  Serial.println("Sending AT+CPSMS=1,,,\"10110100\",\"00001000\"");

  MODEM.send("AT+CPSMS=1,,,\"10110100\",\"00001000\"");

  delay(4000);

  Serial.println("Setting Power Saving Control (UART): 4.");

  Serial.println("Sending AT+UPSV=4");
  MODEM.send("AT+UPSV=4");

  delay(4000);

  Serial.println("And resetting the modem with AT+CFUN=15...");

  MODEM.send("AT+CFUN=15");

  Serial.println("Waiting 12s...");

  delay(12000);

  /*
  // Not needed?
  Serial.println("Toggle a wake up");
  digitalWrite(SARA_PWR_ON, HIGH);
  delay(150); // 150 ms Per table 4.2.9
  digitalWrite(SARA_PWR_ON, LOW);
  */

  Serial.println("************************");
  Serial.println("****** Init Done *******");
  Serial.println("************************");
}

void loop()
{
  Serial.println("************************");
  Serial.println("****** Loop Start ******");
  Serial.println("************************");

  Serial.println("Waking up the modem from deep sleep...");

  /*

  digitalWrite(SARA_PWR_ON, HIGH);
  delay(150); // 200 ms Per table 4.2.9
  digitalWrite(SARA_PWR_ON, LOW);

  Serial.println("Waiting 12s..."); // Not sure if this is needed...
  delay(12000);

  */
  // Why do we need to send AT+CFUN=15 as well?
  // Per the manual a 0.15s PWR_ON toggle should have been sufficient.

  Serial.println("And resetting the modem");

  MODEM.send("AT+CFUN=15");

  Serial.println("Waiting 12s..."); // needs to be >= 10s per manual
  delay(12000);

  Serial.println("Modem should be awake...");

  LTEConnect();

  // Check the modem config for curiosity
  Serial.println("Checking Model Config after reset");

  ModemConfig();

  Serial.println("HTTP Get:");

  // if you get a connection, report back via serial:

  const char server[] = "httpbin.org"; // API server (without http://)
  const int port = 80;                 // HTTP port

  if (client.connect(server, port))
  {

    Serial.println("connected");

    // Make a HTTP request:
    client.print("GET ");
    client.print("/get");
    client.println(" HTTP/1.1");
    client.print("Host: ");
    client.println(server);
    client.println("Connection: close");
    client.println();

    // Wait for response (5 second timeout)
    unsigned long timeout = millis();
    while (client.available() == 0)
    {
      if (millis() - timeout > 5000)
      {
        client.stop();
        Serial.println("http timeout.");
        break;
      }
    }

    // Skip headers
    char endOfHeaders[] = "\r\n\r\n";
    if (!client.find(endOfHeaders))
    {
      client.stop();
      Serial.println("No Headers.");
    }

    // Parse JSON
    JsonDocument doc;

    DeserializationError error = deserializeJson(doc, client);
    client.stop();

    serializeJson(doc, Serial);
  }
  else
  {

    // if you didn't get a connection to the server:

    Serial.println("connection failed");
  }

  Serial.println("");

  Serial.println("Sleeping for 15 minutes");
  LowPower.deepSleep(15 * 60 * 1000);
  delay(1000);
  Serial.println("And awake");
}

/******************************************
 * Support Functions
 *******************************************/

void LTEConnect()
{
  // This seems unnecessarily complicated...

  // Connecting to the network
  Serial.println("Starting LTE connection...");

  int LTE_connect_tries = 0;

  while (LTE_connect_tries <= 10)
  {

    if (nbAccess.begin() == NB_READY) // It can get stuck here...
    {
      Serial.println("Connected to the LTE network");
      if (gprs.attachGPRS() == GPRS_READY)
      {
        Serial.println("GPRS connected"); // GPRS is still used for the connection method
        break;
      }
      else
      {
        Serial.println("GPRS connection failed");
      }
    }
    else
    {
      Serial.println("LTE connection failed"); // This happens on occasion
    }
    delay(500);
    LTE_connect_tries++;
  }
}

void ModemConfig()
// Handy function to print power saving config.
{
  Serial.println("Checking modem config");

  Serial.println("Sending AT+CPSMS?");

  MODEM.send("AT+CPSMS?");
  if (MODEM.waitForResponse(60000, &response) == 1)
  {
    Serial.println(response);
  }

  Serial.println("Sending AT+UPSV?");

  MODEM.send("AT+UPSV?");
  if (MODEM.waitForResponse(60000, &response) == 1)
  {

    Serial.println(response);
  }
}

void fix_bq24195()
{
  // This appears to work.
  Wire.begin();

  Serial.println("Disabling BQ24195 watchdog...");

  // Read current register value
  Wire.beginTransmission(BQ24195_ADDRESS);
  Wire.write(CHARGE_TIMER_CONTROL_REGISTER);
  Wire.endTransmission(false);
  Wire.requestFrom(BQ24195_ADDRESS, 1);
  byte regValue = Wire.read();

  Serial.print("Current register value: 0x");
  Serial.println(regValue, HEX);

  // Apply mask to disable watchdog (0b11000110)
  byte newValue = regValue & 0b11000110;

  // Write new value back to register
  Wire.beginTransmission(BQ24195_ADDRESS);
  Wire.write(CHARGE_TIMER_CONTROL_REGISTER);
  Wire.write(newValue);
  Wire.endTransmission();

  Serial.print("New register value: 0x");
  Serial.println(newValue, HEX);
}

And here is the program output

17:56:43.975 > ************************
17:56:43.976 > ****** Init Start ******
17:56:43.976 > ************************
17:56:48.973 > Removing bq24195 led blink to save power.
17:56:48.973 > Disabling BQ24195 watchdog...
17:56:48.973 > Current register value: 0x82
17:56:48.974 > New register value: 0x82
17:56:48.974 > bq24195 fix complete.
17:56:48.974 > Turning the modem on...
17:56:58.520 > Setting Power Save Mode: 1.
17:56:58.520 > Sending AT+CPSMS=1,,,"10110100","00001000"
17:57:02.520 > Setting Power Saving Control (UART): 4.
17:57:02.520 > Sending AT+UPSV=4
17:57:06.519 > And resetting the modem with AT+CFUN=15...
17:57:06.520 > Waiting 12s...
17:57:18.514 > ************************
17:57:18.514 > ****** Init Done *******
17:57:18.514 > ************************
17:57:18.514 > ************************
17:57:18.514 > ****** Loop Start ******
17:57:18.514 > ************************
17:57:18.514 > Waking up the modem from deep sleep...
17:57:18.514 > And resetting the modem
17:57:18.516 > Waiting 12s...
17:57:30.509 > Modem should be awake...
17:57:30.509 > Starting LTE connection...
17:57:34.921 > Connected to the LTE network
17:57:36.444 > GPRS connected
17:57:36.444 > Checking Model Config after reset
17:57:36.444 > Checking modem config
17:57:36.444 > Sending AT+CPSMS?
17:57:36.474 > +CPSMS:1,,,"10110100","00001000"
17:57:36.474 > Sending AT+UPSV?
17:57:36.500 > +UPSV: 4
17:57:36.500 > HTTP Get:
17:57:37.026 > connected
17:57:37.772 > {"args":{},"headers":{"Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-680ed281-391c5d9c330f979f66e101ac"},"origin":"172.56.51.242","url":"http://httpbin.org/get"}
17:57:37.775 > Sleeping for 15 minutes
18:12:39.774 > And awake
18:12:39.774 > ************************
18:12:39.774 > ****** Loop Start ******
18:12:39.774 > ************************
18:12:39.774 > Waking up the modem from deep sleep...
18:12:39.774 > And resetting the modem
18:12:39.775 > Waiting 12s...
18:12:51.769 > Modem should be awake...
18:12:51.770 > Starting LTE connection...
18:13:43.591 > LTE connection failed
18:14:41.457 > Connected to the LTE network
18:14:42.978 > GPRS connected
18:14:42.978 > Checking Model Config after reset
18:14:42.978 > Checking modem config
18:14:42.978 > Sending AT+CPSMS?
18:14:43.008 > +CPSMS:1,,,"10110100","00001000"
18:14:43.008 > Sending AT+UPSV?
18:14:43.036 > +UPSV: 4
18:14:43.036 > HTTP Get:
18:14:43.661 > connected
18:14:44.523 > {"args":{},"headers":{"Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-680ed684-0c9e81a63be44e751cbf498c"},"origin":"172.56.15.113","url":"http://httpbin.org/get"}
18:14:44.526 > Sleeping for 15 minutes
18:29:45.753 > And awake
18:29:45.753 > ************************
18:29:45.753 > ****** Loop Start ******
18:29:45.753 > ************************
18:29:45.753 > Waking up the modem from deep sleep...
18:29:45.753 > And resetting the modem
18:29:45.754 > Waiting 12s...
18:29:57.749 > Modem should be awake...
18:29:57.749 > Starting LTE connection...
18:30:49.576 > LTE connection failed
18:31:38.773 > Connected to the LTE network
18:31:40.295 > GPRS connected
18:31:40.295 > Checking Model Config after reset
18:31:40.295 > Checking modem config
18:31:40.295 > Sending AT+CPSMS?
18:31:40.323 > +CPSMS:1,,,"10110100","00001000"
18:31:40.323 > Sending AT+UPSV?
18:31:40.350 > +UPSV: 4
18:31:40.350 > HTTP Get:
18:31:40.876 > connected
18:31:41.549 > {"args":{},"headers":{"Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-680eda7d-5e3cc2f9797d06d3356cdfd5"},"origin":"172.56.200.12","url":"http://httpbin.org/get"}
18:31:41.552 > Sleeping for 15 minutes
18:46:42.733 > And awake
18:46:42.733 > ************************
18:46:42.733 > ****** Loop Start ******
18:46:42.733 > ************************
18:46:42.733 > Waking up the modem from deep sleep...
18:46:42.733 > And resetting the modem
18:46:42.734 > Waiting 12s...
18:46:54.728 > Modem should be awake...
18:46:54.729 > Starting LTE connection...
18:47:46.552 > LTE connection failed
18:48:36.353 > Connected to the LTE network
18:48:37.876 > GPRS connected
18:48:37.876 > Checking Model Config after reset
18:48:37.876 > Checking modem config
18:48:37.876 > Sending AT+CPSMS?
18:48:37.903 > +CPSMS:1,,,"10110100","00001000"
18:48:37.903 > Sending AT+UPSV?
18:48:37.930 > +UPSV: 4
18:48:37.930 > HTTP Get:
18:48:38.456 > connected
18:48:39.302 > {"args":{},"headers":{"Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-680ede77-4cb7351d2720a4fe1e50d564"},"origin":"172.56.203.80","url":"http://httpbin.org/get"}
18:48:39.305 > Sleeping for 15 minutes

Thanks for reading, hope some folks have some improvements to this code which I'll be happy to run and test.

Here is an example at 20:24:21.612 of the code hanging at

if (nbAccess.begin() == NB_READY) // It can get stuck here...

This hung for over an hour before I pulled the plug.

.
.
.
18:12:39.774 > ************************
18:12:39.774 > ****** Loop Start ******
18:12:39.774 > ************************
18:12:39.774 > Waking up the modem from deep sleep...
18:12:39.774 > And resetting the modem
18:12:39.775 > Waiting 12s...
18:12:51.769 > Modem should be awake...
18:12:51.770 > Starting LTE connection...
18:13:43.591 > LTE connection failed
18:14:41.457 > Connected to the LTE network
18:14:42.978 > GPRS connected
18:14:42.978 > Checking Model Config after reset
18:14:42.978 > Checking modem config
18:14:42.978 > Sending AT+CPSMS?
18:14:43.008 > +CPSMS:1,,,"10110100","00001000"
18:14:43.008 > Sending AT+UPSV?
18:14:43.036 > +UPSV: 4
18:14:43.036 > HTTP Get:
18:14:43.661 > connected
18:14:44.523 > {"args":{},"headers":{"Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-680ed684-0c9e81a63be44e751cbf498c"},"origin":"172.56.15.113","url":"http://httpbin.org/get"}
18:14:44.526 > Sleeping for 15 minutes
18:29:45.753 > And awake
18:29:45.753 > ************************
18:29:45.753 > ****** Loop Start ******
18:29:45.753 > ************************
18:29:45.753 > Waking up the modem from deep sleep...
18:29:45.753 > And resetting the modem
18:29:45.754 > Waiting 12s...
18:29:57.749 > Modem should be awake...
18:29:57.749 > Starting LTE connection...
18:30:49.576 > LTE connection failed
18:31:38.773 > Connected to the LTE network
18:31:40.295 > GPRS connected
18:31:40.295 > Checking Model Config after reset
18:31:40.295 > Checking modem config
18:31:40.295 > Sending AT+CPSMS?
18:31:40.323 > +CPSMS:1,,,"10110100","00001000"
18:31:40.323 > Sending AT+UPSV?
18:31:40.350 > +UPSV: 4
18:31:40.350 > HTTP Get:
18:31:40.876 > connected
18:31:41.549 > {"args":{},"headers":{"Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-680eda7d-5e3cc2f9797d06d3356cdfd5"},"origin":"172.56.200.12","url":"http://httpbin.org/get"}
18:31:41.552 > Sleeping for 15 minutes
18:46:42.733 > And awake
18:46:42.733 > ************************
18:46:42.733 > ****** Loop Start ******
18:46:42.733 > ************************
18:46:42.733 > Waking up the modem from deep sleep...
18:46:42.733 > And resetting the modem
18:46:42.734 > Waiting 12s...
18:46:54.728 > Modem should be awake...
18:46:54.729 > Starting LTE connection...
18:47:46.552 > LTE connection failed
18:48:36.353 > Connected to the LTE network
18:48:37.876 > GPRS connected
18:48:37.876 > Checking Model Config after reset
18:48:37.876 > Checking modem config
18:48:37.876 > Sending AT+CPSMS?
18:48:37.903 > +CPSMS:1,,,"10110100","00001000"
18:48:37.903 > Sending AT+UPSV?
18:48:37.930 > +UPSV: 4
18:48:37.930 > HTTP Get:
18:48:38.456 > connected
18:48:39.302 > {"args":{},"headers":{"Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-680ede77-4cb7351d2720a4fe1e50d564"},"origin":"172.56.203.80","url":"http://httpbin.org/get"}
18:48:39.305 > Sleeping for 15 minutes
19:03:40.712 > And awake
19:03:40.713 > ************************
19:03:40.713 > ****** Loop Start ******
19:03:40.713 > ************************
19:03:40.713 > Waking up the modem from deep sleep...
19:03:40.713 > And resetting the modem
19:03:40.714 > Waiting 12s...
19:03:52.710 > Modem should be awake...
19:03:52.710 > Starting LTE connection...
19:04:43.019 > Connected to the LTE network
19:04:44.542 > GPRS connected
19:04:44.542 > Checking Model Config after reset
19:04:44.542 > Checking modem config
19:04:44.542 > Sending AT+CPSMS?
19:04:44.571 > +CPSMS:1,,,"10110100","00001000"
19:04:44.571 > Sending AT+UPSV?
19:04:44.598 > +UPSV: 4
19:04:44.598 > HTTP Get:
19:04:45.123 > connected
19:04:46.214 > {"args":{},"headers":{"Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-680ee23d-2193a46160bed46b7603679e"},"origin":"172.56.168.243","url":"http://httpbin.org/get"}
19:04:46.217 > Sleeping for 15 minutes
19:19:47.693 > 
19:19:47.694 > ************************
19:19:47.694 > ****** Loop Start ******
19:19:47.694 > ************************
19:19:47.694 > Waking up the modem from deep sleep...
19:19:47.694 > And resetting the modem
19:19:47.695 > Waiting 12s...
19:19:59.689 > Modem should be awake...
19:19:59.689 > Starting LTE connection...
19:20:49.393 > Connected to the LTE network
19:20:50.915 > GPRS connected
19:20:50.916 > Checking Model Config after reset
19:20:50.916 > Checking modem config
19:20:50.916 > Sending AT+CPSMS?
19:20:50.944 > +CPSMS:1,,,"10110100","00001000"
19:20:50.944 > Sending AT+UPSV?
19:20:50.971 > +UPSV: 4
19:20:50.971 > HTTP Get:
19:20:51.497 > connected
19:20:52.442 > {"args":{},"headers":{"Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-680ee603-35de27bb54205610740220fd"},"origin":"172.56.169.185","url":"http://httpbin.org/get"}
19:20:52.444 > Sleeping for 15 minutes
19:35:53.675 > And awake
19:35:53.675 > ************************
19:35:53.675 > ****** Loop Start ******
19:35:53.675 > ************************
19:35:53.675 > Waking up the modem from deep sleep...
19:35:53.675 > And resetting the modem
19:35:53.676 > Waiting 12s...
19:36:05.670 > Modem should be awake...
19:36:05.670 > Starting LTE connection...
19:36:55.572 > Connected to the LTE network
19:36:57.095 > GPRS connected
19:36:57.095 > Checking Model Config after reset
19:36:57.095 > Checking modem config
19:36:57.095 > Sending AT+CPSMS?
19:36:57.124 > +CPSMS:1,,,"10110100","00001000"
19:36:57.124 > Sending AT+UPSV?
19:36:57.151 > +UPSV: 4
19:36:57.151 > HTTP Get:
19:36:57.676 > connected
19:36:58.421 > {"args":{},"headers":{"Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-680ee9ca-70205c9344285fef4719bf88"},"origin":"172.56.51.57","url":"http://httpbin.org/get"}
19:36:58.423 > Sleeping for 15 minutes
19:51:59.656 > 
19:51:59.657 > ************************
19:51:59.657 > ****** Loop Start ******
19:51:59.657 > ************************
19:51:59.657 > Waking up the modem from deep sleep...
19:51:59.657 > And resetting the modem
19:51:59.658 > Waiting 12s...
19:52:11.653 > Modem should be awake...
19:52:11.653 > Starting LTE connection...
19:53:00.749 > Connected to the LTE network
19:53:02.273 > GPRS connected
19:53:02.273 > Checking Model Config after reset
19:53:02.273 > Checking modem config
19:53:02.273 > Sending AT+CPSMS?
19:53:02.303 > +CPSMS:1,,,"10110100","00001000"
19:53:02.303 > Sending AT+UPSV?
19:53:02.329 > +UPSV: 4
19:53:02.329 > HTTP Get:
19:53:02.855 > connected
19:53:03.620 > {"args":{},"headers":{"Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-680eed8f-03fa8d5a33594ecb77690ff5"},"origin":"172.56.51.72","url":"http://httpbin.org/get"}
19:53:03.623 > Sleeping for 15 minutes
20:08:04.638 > And awake
20:08:04.638 > ************************
20:08:04.638 > ****** Loop Start ******
20:08:04.638 > ************************
20:08:04.638 > Waking up the modem from deep sleep...
20:08:04.638 > And resetting the modem
20:08:04.639 > Waiting 12s...
20:08:16.633 > Modem should be awake...
20:08:16.633 > Starting LTE connection...
20:09:05.728 > Connected to the LTE network
20:09:07.250 > GPRS connected
20:09:07.250 > Checking Model Config after reset
20:09:07.250 > Checking modem config
20:09:07.250 > Sending AT+CPSMS?
20:09:07.279 > +CPSMS:1,,,"10110100","00001000"
20:09:07.279 > Sending AT+UPSV?
20:09:07.305 > +UPSV: 4
20:09:07.306 > HTTP Get:
20:09:07.831 > connected
20:09:08.596 > {"args":{},"headers":{"Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-680ef154-36b2f6eb4f4722c771f2d17c"},"origin":"172.56.171.41","url":"http://httpbin.org/get"}
20:09:08.599 > Sleeping for 15 minutes
20:24:09.617 > And awake
20:24:09.618 > ************************
20:24:09.618 > ****** Loop Start ******
20:24:09.618 > ************************
20:24:09.618 > Waking up the modem from deep sleep...
20:24:09.618 > And resetting the modem
20:24:09.618 > Waiting 12s...
20:24:21.611 > Modem should be awake...
20:24:21.612 > Starting LTE connection...
Disconnected (device reports readiness to read but returned no data (device disconnected or multiple access on port?))
Reconnecting to /dev/ttyACM0 ....--- Logging an output to ./logs/device-monitor-250427-214530.log
         Connected!
21:45:33.585 > Removing bq24195 led blink to save power.
21:45:33.585 > Disabling BQ24195 watchdog...
21:45:33.585 > Current register value: 0x8A
21:45:33.585 > New register value: 0x82
21:45:33.586 > bq24195 fix complete.
21:45:33.586 > Turning the modem on...
21:45:43.130 > Setting Power Save Mode: 1.
21:45:43.131 > Sending AT+CPSMS=1,,,"10110100","00001000"
21:45:47.131 > Setting Power Saving Control (UART): 4.
21:45:47.132 > Sending AT+UPSV=4
21:45:51.130 > And resetting the modem with AT+CFUN=15...
21:45:51.131 > Waiting 12s...
21:46:03.124 > ************************
21:46:03.124 > ****** Init Done *******
21:46:03.124 > ************************
21:46:03.124 > ************************
21:46:03.124 > ****** Loop Start ******
21:46:03.124 > ************************
21:46:03.124 > Waking up the modem from deep sleep...
21:46:03.124 > And resetting the modem
21:46:03.125 > Waiting 12s...
21:46:15.118 > Modem should be awake...
21:46:15.118 > Starting LTE connection...
21:46:23.866 > Connected to the LTE network
21:46:25.388 > GPRS connected
21:46:25.388 > Checking Model Config after reset
21:46:25.388 > Checking modem config
21:46:25.388 > Sending AT+CPSMS?
21:46:25.417 > +CPSMS:1,,,"10110100","00001000"
21:46:25.417 > Sending AT+UPSV?
21:46:25.443 > +UPSV: 4
21:46:25.443 > HTTP Get:
21:46:26.068 > connected
21:46:26.900 > {"args":{},"headers":{"Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-680f0822-7564f2dd3a59498a108953d8"},"origin":"172.56.201.216","url":"http://httpbin.org/get"}
21:46:26.903 > Sleeping for 15 minutes

Over a course of 12 hours only 2 failed connections occurred:

cat device-monitor-250427-214530.log | grep fail
02:04:07.432 > LTE connection failed
06:06:40.151 > LTE connection failed

Unhappy with the convoluted approach above, specifically the means to "wake the modem up" after deep sleep I tried a few more ideas that were inspired by yet another reading of the SARA R4 documentation. I had some hypotheses

  • Does the power on sequence of 150ms potentially confuse the modem if it is already on and functioning?
  • After waking the modem up with the 150ms pulse, I probably perhaps need to get the USART awake as well due to the AT+UPSV=4 setting.

So in the code below you'll find some changes to the LTEConenct() function, and the removal of AT+CFUN=15 at the loop start.

This seems like a step in the right direction.

Again, this is a complete stand alone example.

PS. http://httpbin.org is down this morning.

#include <MKRNB.h>
#include <ArduinoLowPower.h>
#include <Wire.h>
#include <ArduinoJson.h>

// BQ24195 Addresses
#define BQ24195_ADDRESS 0x6B
#define CHARGE_TIMER_CONTROL_REGISTER 0x05

// GSM and NB objects
NB nbAccess(false);
NBClient client; // Used for the TCP socket connection
GPRS gprs;
NBModem modem;

// Serial AT command buffer
String response;

void LTEConnect();
void ModemConfig();
void fix_bq24195();
bool isModemActive();

void setup()
{
  Serial.begin(9600);
  delay(1000);
  Serial.println("************************");
  Serial.println("****** Init Start ******");
  Serial.println("************************");
  delay(5000);

  Serial.println("Removing bq24195 led blink to save power.");

  fix_bq24195();

  Serial.println("bq24195 fix complete.");

  Serial.println("Turning the modem on...");

  MODEM.begin();

  delay(4000);

  Serial.println("Setting Power Save Mode: 1.");

  Serial.println("Sending AT+CPSMS=1,,,\"01011000\",\"00011110\"");

  MODEM.send("AT+CPSMS=1,,,\"01011000\",\"00011110\"");

  delay(4000);

  Serial.println("Setting Power Saving Control (UART): 4.");

  Serial.println("Sending AT+UPSV=4");
  MODEM.send("AT+UPSV=4");

  delay(4000);

  Serial.println("And resetting the modem with AT+CFUN=15...");

  MODEM.send("AT+CFUN=15");

  Serial.println("Waiting 12s...");

  delay(12000);

  Serial.println("Clearing SARA Serial...");
  while (SerialSARA.available()) {
  SerialSARA.read();
}

  Serial.println("************************");
  Serial.println("****** Init Done *******");
  Serial.println("************************");
}

void loop()
{
  Serial.println("************************");
  Serial.println("****** Loop Start ******");
  Serial.println("************************");

  LTEConnect();

  // Check the modem config for curiosity
  Serial.println("Checking Model Config after reset");

  ModemConfig();

  Serial.println("HTTP Get:");

  // if you get a connection, report back via serial:

  const char server[] = "httpbin.org"; // API server (without http://)
  const int port = 80;                 // HTTP port

  if (client.connect(server, port))
  {

    Serial.println("connected");

    // Make a HTTP request:
    client.print("GET ");
    client.print("/get?test=mkr");
    client.println(" HTTP/1.1");
    client.print("Host: ");
    client.println(server);
    client.println("Connection: close");
    client.println();

    // Wait for response (5 second timeout)
    unsigned long timeout = millis();
    while (client.available() == 0)
    {
      if (millis() - timeout > 1500)
      {
        client.stop();
        Serial.println("http timeout.");
        break;
      }
    }

    // Skip headers
    char endOfHeaders[] = "\r\n\r\n";
    if (!client.find(endOfHeaders))
    {
      client.stop();
      Serial.println("No Headers.");
    }

    // Parse JSON
    JsonDocument doc;

    DeserializationError error = deserializeJson(doc, client);
    client.stop();

    serializeJson(doc, Serial);
  }
  else
  {

    // if you didn't get a connection to the server:

    Serial.println("connection failed");
  }

  Serial.println("");

  Serial.println("Sleeping for 15 minutes");
  LowPower.deepSleep(15 * 60 * 1000);
  delay(1000);
  Serial.println("And awake");
}

/******************************************
 * Support Functions
 *******************************************/

void LTEConnect()
{
  // Connecting to the network
  Serial.println("Starting LTE connection...");

  int maxtries=100;
  

  int LTE_connect_tries = 0;

  while (LTE_connect_tries <= maxtries)
  {

    // Before we "wake" the modem up, we can to actually make sure it is sleeping.
    // If it is not sleeping, the toggle may send it to sleep? Granted table 4.2.0
    // suggests that anything shorter than 1500 ms should only power on.

    // Clear any pending data
    if (isModemActive())
    {
      Serial.println("Modem is already awake!");
    }
    else
    {
      Serial.println("Waking up the modem");
    

      digitalWrite(SARA_PWR_ON, HIGH);
      delay(150); // 150-3200 ms Per table 4.2.9
      digitalWrite(SARA_PWR_ON, LOW);

      // Wait for module to initialize
      delay(1000);

    }

    Serial.println("Modem should be awake...");


    if (isModemActive())
    {
      Serial.println("Modem is awake!");
    }
    else
    {
      Serial.println("Modem is NOT awake! ???");
    }

    Serial.println("Begining nbAccess...");

    if (nbAccess.begin() == NB_READY)
    {
      Serial.println("Connected to the LTE network");
      if (gprs.attachGPRS() == GPRS_READY)
      {
        Serial.println("GPRS connected"); // GPRS is still used for the connection method
        break;
      }
      else
      {
        Serial.println("GPRS connection failed");
      }
    }
    else
    {
      Serial.print("LTE connection failed ");
      Serial.print(LTE_connect_tries);
      Serial.print("/");
      Serial.println(maxtries);

      if (LTE_connect_tries % 5 == 0 && LTE_connect_tries != 0)
      {
        Serial.print("Should something more extreme be done here?");
      }
    }
    delay(1000);
    LTE_connect_tries++;
  }

  if (LTE_connect_tries > maxtries)
  {
    Serial.println("LTE connection abort!");
  }
}



void ModemConfig()
// Handy function to print power saving config.
{
  // Serial AT command buffer
  String response;

  Serial.println("Checking modem config");

  Serial.println("Sending AT+CPSMS?");

  MODEM.send("AT+CPSMS?");
  if (MODEM.waitForResponse(60000, &response) == 1)
  {
    Serial.println(response);
  }

  Serial.println("Sending AT+UPSV?");

  MODEM.send("AT+UPSV?");
  if (MODEM.waitForResponse(60000, &response) == 1)
  {
    Serial.println(response);
  }

  Serial.println("Sending ATI9");

  MODEM.send("ATI9");
  if (MODEM.waitForResponse(60000, &response) == 1)
  {
    Serial.println(response);
  }

  Serial.println("Sending AT+CGDCONT?");

  MODEM.send("AT+CGDCONT?");
  if (MODEM.waitForResponse(60000, &response) == 1)
  {
    Serial.println(response);
  }

  Serial.println("Sending AT+CGMR");

  MODEM.send("AT+CGMR");
  if (MODEM.waitForResponse(60000, &response) == 1)
  {
    Serial.println(response);
  }
}

void fix_bq24195()
{
  // This appears to work.
  Wire.begin();

  Serial.println("Disabling BQ24195 watchdog...");

  // Read current register value
  Wire.beginTransmission(BQ24195_ADDRESS);
  Wire.write(CHARGE_TIMER_CONTROL_REGISTER);
  Wire.endTransmission(false);
  Wire.requestFrom(BQ24195_ADDRESS, 1);
  byte regValue = Wire.read();

  Serial.print("Current register value: 0x");
  Serial.println(regValue, HEX);

  // Apply mask to disable watchdog (0b11000110)
  byte newValue = regValue & 0b11000110;

  // Write new value back to register
  Wire.beginTransmission(BQ24195_ADDRESS);
  Wire.write(CHARGE_TIMER_CONTROL_REGISTER);
  Wire.write(newValue);
  Wire.endTransmission();

  Serial.print("New register value: 0x");
  Serial.println(newValue, HEX);
}

bool isModemActive() {
  bool awake = false;

    for (int i = 0; i < 10; i++) {
      SerialSARA.println("AT");
      delay(500);
      if (SerialSARA.find("OK")) {
        awake=true;
        break;
      }
    }

  return awake;
}

Post #4 is the best performing code I have tested yet. Over a 20 hour time frame it only had a single failed attempt to connect which was quick to correct.

13:30:55.667 > ************************
13:30:55.667 > ****** Loop Start ******
13:30:55.667 > ************************
13:30:55.667 > Starting LTE connection...
13:30:57.663 > Modem is already awake!
13:30:57.664 > Modem should be awake...
13:30:58.163 > Modem is awake!
13:30:58.163 > Begining nbAccess...
13:31:40.609 > LTE connection failed 0/100
13:31:42.109 > Modem is already awake!
13:31:42.109 > Modem should be awake...
13:31:42.609 > Modem is awake!
13:31:42.609 > Begining nbAccess...
13:31:55.333 > Connected to the LTE network
13:31:56.857 > GPRS connected
13:31:56.857 > Checking Model Config after reset
13:31:56.857 > Checking modem config
13:31:56.857 > Sending AT+CPSMS?
13:31:56.885 > +CPSMS:1,,,"01011000","00011110"
13:31:56.885 > Sending AT+UPSV?
13:31:56.912 > +UPSV: 4
13:31:56.912 > Sending ATI9
13:31:56.941 > L0.0.00.00.05.12,A.02.21
13:31:56.941 > Sending AT+CGDCONT?
13:31:56.975 > +CGDCONT: 1,"IP","h2g2","48.224.231.72",0,0,0,0,0,0
13:31:56.975 > Sending AT+CGMR
13:31:56.995 > L0.0.00.00.05.12 [Mar 09 2022 17:00:00]
13:31:56.995 > HTTP Get:
13:31:57.426 > connected