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 onnbAccess.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.