A simple sketch to send sensor data and sms via hologram without Arduino library

After weeks of varying success with the Arduino MKRNB libary and the Sparkfun SARA R4 library, I decided to bite the bullet and start from scratch.

In order to handle the SARA R4 power_on and reset pins properly, these changes need to made to the variant.cpp file located in AppData\Local\Arduino15\packages\arduino\hardware\samd\1.8.4\variants\mkrnb1500

 // power off the module
  pinMode(SARA_PWR_ON, INPUT);
  //digitalWrite(SARA_PWR_ON, LOW);

  // put SARA modem in reset on start to conserve power if it's not used
  pinMode(SARA_RESETN, INPUT);
  //digitalWrite(SARA_RESETN, LOW);

The real meat and potatoes of the sketch is the sendCommand(command, timeout) function that works with most AT commands and returns with true on success. The timeout is important and can vary widely depending on command. See AT manual. Some commands can take up to 3 mins to respond.

/*
  This sketch works with the MKR NB 1500 without the need for the MKRNB library.

  It sends message to the hologram cloud every 10 mins.
  This message contains some data that can be sent to thingspeak
  using a hologram route.

  It also sends a sms via the hologram embedded api when a switch is closed.

*/


#include <Arduino_MKRTHERM.h>

char response[100];
char response2[100];
char responseJunk[100];
char smsMessage[] = "Smoke detected";

const char apn[] = "hologram";

const char hologramDeviceKey[] = "########";

const char phoneNumber[] = "15559999999";//11 digit phone #

const char hologramUrl[] = "cloudsocket.hologram.io";
const unsigned int hologramPort = 9999;

int smokeState = 0;

unsigned long lastConnectionTime = 0;


void setup() {
  Serial.begin(115200);
  delay(5000);
  SerialSARA.begin(115200);

  THERM.begin();

  pinMode(1, INPUT_PULLUP);

  modemPowerOn();

  if (modemAttach()) {
    hologram(false);
  }

}

void loop() {

  if (digitalRead(1) == LOW) {
    delay(250);
    if (digitalRead(1) == LOW) {
      if (!hologram(true)) {
        if (modemAttach()) {
          hologram(true);
        }

      }
    }
  }

  if (millis() - lastConnectionTime > 600000) {
    if (!hologram(false)) {
      if (modemAttach()) {
        hologram(false);
      }

    }
  }
}

void modemPowerOn() {
  //Logic high on Arduino pin = logic low on SARA R4
  digitalWrite(SARA_PWR_ON, HIGH);
  pinMode(SARA_PWR_ON, OUTPUT);
  delay(500);
  pinMode(SARA_PWR_ON, INPUT);
  Serial.println("Modem on");
}

void modemPowerOff() {
  //Logic high on Arduino pin = logic low on SARA R4
  digitalWrite(SARA_PWR_ON, HIGH);
  pinMode(SARA_PWR_ON, OUTPUT);
  delay(3000);
  pinMode(SARA_PWR_ON, INPUT);
  Serial.println("Modem off");
}

void modemReset() {
  //Logic high on Arduino pin = logic low on SARA R4
  Serial.println("Modem resetting...");
  digitalWrite(SARA_RESETN, HIGH);
  pinMode(SARA_RESETN, OUTPUT);
  delay(12000);
  pinMode(SARA_RESETN, INPUT);
  Serial.println("Modem reset");
}

bool modemAlive() {
  int counter = 0;

  while (counter < 20) {
    counter++;
    sendCommand("ATE0", 1000);//Turn off Echo
    sendCommand("AT+IPR=115200", 1000);//Set baud rate

    if (sendCommand("ATV0", 1000)) {
      Serial.println("Set to non-verbose result codes");
      return true;
    }
  }
  return false;
}

bool sendCommand(char command[50], unsigned long timeout) {
  int responseLength = 0;
  int responseLength2 = 0;

  SerialSARA.setTimeout(100);
  SerialSARA.readBytes(responseJunk, 99);

  memset(response, 0, sizeof(response));
  memset(response2, 0, sizeof(response2));
  memset(responseJunk, 0, sizeof(responseJunk));

  SerialSARA.setTimeout(timeout);
  Serial.println(command);
  SerialSARA.println(command);

  responseLength = SerialSARA.readBytesUntil('\r', response, 99);

  if (responseLength == 1) {
    Serial.println(response);
    if (response[0] == '0') {
      return true;
    }
    else {
      return false;
    }
  }

  if (responseLength != 0) {
    Serial.println(response);
  }

  SerialSARA.setTimeout(100);
  SerialSARA.readBytesUntil('\n', responseJunk, 99);

  responseLength2 = SerialSARA.readBytesUntil('\r', response2, 99);

  if (responseLength2 == 1) {
    Serial.println(response2);
    if (response2[0] == '0' || response2[0] == '@' ) {
      return true;
    }
    else {
      return false;
    }
  }

  if (responseLength2 != 0) {
    Serial.println(response2);
  }

  return false;
}

bool modemAttach() {

  char command[100];
  int counter = 0;
  int socket = 0;


  //Check if the modem is responding. If not, restart modem.
  if (!modemAlive()) {
    modemReset();
    delay(5000);
    modemPowerOn();
    modemAlive();
  }

  //Make sure all sockets are closed. Modem will
  //respond with CME Error if they already are closed.
  while (socket < 7) {
    sprintf(command, "AT+USOCL=%i", socket);
    sendCommand(command, 1000);
    socket++;
  }

  if (sendCommand("AT+UMNOPROF=0", 1000)) {
    Serial.println("Profile set");
    sprintf(command, "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
    if (sendCommand(command, 1000)) {
      Serial.println("APN set");
      if (sendCommand("AT+CEREG=0", 1000)) {
        Serial.println("Registration URC disabled");
        while (counter < 120) {
          counter++;
          if (modemSignal() > 4) {
            if (sendCommand("AT+CEREG?", 1000)) {
              if (response[10] == '1' || response[10] == '5') {
                Serial.println("Registered");
                if (sendCommand("AT+CGATT=1", 180000)) {
                  Serial.println("Attached");
                  return true;
                }
              }
            }
          }
          delay(1000);
        }
      }
    }
  }
  Serial.println("Attach failed");
  return false;
}


bool hologram(bool sms) {

  lastConnectionTime = millis();
  int socket = -1;
  float refTemp = (THERM.readTemperature() * 9 / 5) + 32;
  char hologramMessage[100];
  char socketCommand[100];
  int hologramMessageSize = 0;
  bool result = false;

  memset(socketCommand, 0, sizeof(socketCommand));
  if (sms) {
    sprintf(hologramMessage, "S%s+%s %s\n\n", hologramDeviceKey, phoneNumber, smsMessage);
  }

  else {
    sprintf(hologramMessage, "{\"k\":\"%s\",\"d\":\"field1=%f&field2=%i\",\"t\":\"thingspeak\"}", hologramDeviceKey, refTemp, smokeState);
  }
  hologramMessageSize = strlen(hologramMessage);


  if (modemSignal() > 4) {
    if (sendCommand("AT+USOCR=6", 1000)) {
      socket = response[8] - '0';
      sprintf(socketCommand, "AT+USOCO=%i,\"%s\",%i", socket, hologramUrl, hologramPort);
      if (sendCommand(socketCommand, 120000)) {
        Serial.println("Socket connected");
        memset(socketCommand, 0, sizeof(socketCommand));
        sprintf(socketCommand, "AT+USOWR=%i,%i", socket, hologramMessageSize);
        if (sendCommand(socketCommand, 120000)) {
          Serial.println("Write data:");
          if (sendCommand(hologramMessage, 120000)) {
            Serial.println("Data sent");
            result = true;
          }
        }
      }
    }
  }


  if (socket >= 0) {
    memset(socketCommand, 0, sizeof(socketCommand));
    sprintf(socketCommand, "AT+USOCL=%i", socket);
    if (sendCommand(socketCommand, 120000)) {
      Serial.println("Socket closed");
    }
  }

  return result;
}

int modemSignal() {
  int rsrq = 0;
  if (sendCommand("AT+CESQ", 1000)) {
    int rsrq1 = response[21] - '0';
    if (response[22] != ',') {
      int rsrq2 = response[22] - '0';
      rsrq = (rsrq1 * 10) + rsrq2;
      if (response[23] != ',') {
        rsrq = 0;
      }
    }
    else {
      rsrq = rsrq1;
    }
    Serial.print("Signal rsrq: ");
    Serial.println(rsrq);
  }
  return rsrq;
}

Data sent to hologram can be sent to thingspeak with a route that has the following details:

Topic: thingspeak
Advanced Webhook Builder
URL: https://api.thingspeak.com/update
Payload: <>
Headers:

  • THINGSPEAKAPIKEY your write api key
  • Connection close
  • Content-Type application/x-www-form-urlencoded

I welcome input as C++ is very new to me.

I edited the above sketch with the following:

  • Fixed issues with the modemSignal() function
  • Edited the modemAttach() function to check signal first. Also nested if statements.

Below is my latest version. The network profile can now be selected. Before, I was unable to select a profile except 0 or "undefined". The reason I was unable to use other profiles was because when you select any profile besides 0, power save mode(PSM) is turned on by default. When the modem enters PSM mode, the AT terminal will be completely unresponsive. If you don't realize the modem is in PSM, you might think the modem is hung. The only way to bring the modem out of PSM is to pulse the power_on pin. I have enable PSM in this version and every time the modem needs to send data, it is brought out of PSM.

/*
  This sketch works with the MKR NB 1500 without the need for the MKRNB library.

  It sends message to the hologram cloud every 10 mins.
  This message contains some data that can be sent to thingspeak
  using a hologram route.

  It also sends a sms via the hologram embedded api when a switch is closed.

*/


#include <Arduino_MKRTHERM.h>

char response[100];
char response2[100];
char responseJunk[100];
char smsMessage[] = "Smoke detected";

const char apn[] = "hologram";

int networkProfile = 2;//Profile ID, see AT Manual. 2 = ATT.

const char hologramDeviceKey[] = "########";

const char phoneNumber[] = "15559999999";//11 digit phone #

const char hologramUrl[] = "cloudsocket.hologram.io";
const unsigned int hologramPort = 9999;

int smokeState = 0;

unsigned long lastConnectionTime = 0;


void setup() {
  Serial.begin(115200);
  delay(5000);
  SerialSARA.begin(115200);

  THERM.begin();

  pinMode(1, INPUT_PULLUP);

  if (modemAttach()) {
    hologram(false);
  }

}

void loop() {

  if (digitalRead(1) == LOW) {
    delay(250);
    if (digitalRead(1) == LOW) {
      if (!hologram(true)) {
        if (modemAttach()) {
          hologram(true);
        }

      }
    }
  }

  if (millis() - lastConnectionTime > 600000) {
    lastConnectionTime = millis();
    if (!hologram(false)) {
      if (modemAttach()) {
        hologram(false);
      }

    }
  }
}

void modemPowerOn() {
  //Logic high on Arduino pin = logic low on SARA R4
  digitalWrite(SARA_PWR_ON, HIGH);
  pinMode(SARA_PWR_ON, OUTPUT);
  delay(500);
  pinMode(SARA_PWR_ON, INPUT);
  Serial.println("Modem on");
}

void modemPowerOff() {
  //Logic high on Arduino pin = logic low on SARA R4
  digitalWrite(SARA_PWR_ON, HIGH);
  pinMode(SARA_PWR_ON, OUTPUT);
  delay(3000);
  pinMode(SARA_PWR_ON, INPUT);
  Serial.println("Modem off");
}

void modemReset() {
  //Logic high on Arduino pin = logic low on SARA R4
  Serial.println("Modem resetting...");
  digitalWrite(SARA_RESETN, HIGH);
  pinMode(SARA_RESETN, OUTPUT);
  delay(12000);
  pinMode(SARA_RESETN, INPUT);
  Serial.println("Modem reset");
}

bool modemAlive() {
  int counter = 0;

  while (counter < 20) {
    counter++;
    sendCommand("ATE0", 1000);//Turn off Echo
    sendCommand("AT+IPR=115200", 1000);//Set baud rate

    if (sendCommand("ATV0", 1000)) {
      Serial.println("Set to non-verbose result codes");
      Serial.println("Modem is alive");
      return true;
    }
  }
  return false;
}

bool sendCommand(char command[50], unsigned long timeout) {
  int responseLength = 0;
  int responseLength2 = 0;

  SerialSARA.setTimeout(100);
  SerialSARA.readBytes(responseJunk, 99);

  memset(response, 0, sizeof(response));
  memset(response2, 0, sizeof(response2));
  memset(responseJunk, 0, sizeof(responseJunk));

  SerialSARA.setTimeout(timeout);
  Serial.println(command);
  SerialSARA.println(command);

  responseLength = SerialSARA.readBytesUntil('\r', response, 99);

  if (responseLength == 1) {
    Serial.println(response);
    if (response[0] == '0') {
      return true;
    }
    else {
      return false;
    }
  }

  if (responseLength != 0) {
    Serial.println(response);
  }

  SerialSARA.setTimeout(100);
  SerialSARA.readBytesUntil('\n', responseJunk, 99);

  responseLength2 = SerialSARA.readBytesUntil('\r', response2, 99);

  if (responseLength2 == 1) {
    Serial.println(response2);
    if (response2[0] == '0' || response2[0] == '@' ) {
      return true;
    }
    else {
      return false;
    }
  }

  if (responseLength2 != 0) {
    Serial.println(response2);
  }

  return false;
}

bool modemAttach() {

  char command[100];
  int counter = 0;
  int socket = 0;

  memset(command, 0, sizeof(command));

  modemPowerOn();

  //Check if the modem is responding. If not, restart modem.
  if (!modemAlive()) {
    modemReset();
    delay(5000);
    modemPowerOn();
    modemAlive();
  }

  if (sendCommand("AT+UMNOPROF?", 1000)) {
    if (response[11] - '0' != networkProfile) {
      sprintf(command, "AT+UMNOPROF=%i", networkProfile);
      if (sendCommand("AT+CFUN=0", 180000)) {
        if (sendCommand(command, 1000)) {
          if (sendCommand("AT+CFUN=15", 180000)) {
            delay(5000);
            if (modemAlive()) {
              Serial.println("Profile set");
            }
            else {
              return false;
            }
          }
        }
      }
    }
  }

  //Turn power save mode on with a
  //Requested extended periodic TAU value (T3412) of 20 min
  //and a Requested Active Time value (T3324) of 16 sec.
  if (sendCommand("AT+CPSMS=1,,,\"00000010\",\"00001000\"", 10000)) {
    if (sendCommand("AT+CFUN=15", 180000)) {
      delay(5000);
      if (modemAlive()) {
        Serial.println("PSM set");
      }
      else{
        return false;
      }
    }
  }

  memset(command, 0, sizeof(command));
  sprintf(command, "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
  if (sendCommand(command, 1000)) {
    Serial.println("APN set");
    if (sendCommand("AT+CEREG=0", 1000)) {
      Serial.println("Registration URC disabled");
      while (counter < 120) {
        counter++;
        if (modemSignal() > 4) {
          if (sendCommand("AT+CEREG?", 1000)) {
            if (response[10] == '1' || response[10] == '5') {
              Serial.println("Registered");
              if (sendCommand("AT+CGATT=1", 180000)) {
                Serial.println("Attached");
                return true;
              }
            }
          }
        }
        delay(1000);
      }
    }

  }
  Serial.println("Attach failed");
  return false;
}


bool hologram(bool sms) {

  int socket = -1;
  float refTemp = (THERM.readTemperature() * 9 / 5) + 32;
  char hologramMessage[100];
  char socketCommand[100];
  int hologramMessageSize = 0;
  bool result = false;

  memset(socketCommand, 0, sizeof(socketCommand));
  if (sms) {
    sprintf(hologramMessage, "S%s+%s %s\n\n", hologramDeviceKey, phoneNumber, smsMessage);
  }

  else {
    sprintf(hologramMessage, "{\"k\":\"%s\",\"d\":\"field1=%f&field2=%i\",\"t\":\"thingspeak\"}", hologramDeviceKey, refTemp, smokeState);
  }
  hologramMessageSize = strlen(hologramMessage);




  if (modemWakeUp()) {
    if (sendCommand("AT+USOCR=6", 1000)) {
      socket = response[8] - '0';
      sprintf(socketCommand, "AT+USOCO=%i,\"%s\",%i", socket, hologramUrl, hologramPort);
      if (sendCommand(socketCommand, 120000)) {
        Serial.println("Socket connected");
        memset(socketCommand, 0, sizeof(socketCommand));
        sprintf(socketCommand, "AT+USOWR=%i,%i", socket, hologramMessageSize);
        if (sendCommand(socketCommand, 120000)) {
          Serial.println("Write data:");
          if (sendCommand(hologramMessage, 120000)) {
            Serial.println("Data sent");
            result = true;
          }
        }
      }
    }
  }


  if (socket >= 0) {
    memset(socketCommand, 0, sizeof(socketCommand));
    sprintf(socketCommand, "AT+USOCL=%i", socket);
    if (sendCommand(socketCommand, 120000)) {
      Serial.println("Socket closed");
    }
  }

  return result;
}

int modemSignal() {
  int rsrq = 0;
  if (sendCommand("AT+CESQ", 1000)) {
    int rsrq1 = response[21] - '0';
    if (response[22] != ',') {
      int rsrq2 = response[22] - '0';
      rsrq = (rsrq1 * 10) + rsrq2;
      if (response[23] != ',') {
        rsrq = 0;
      }
    }
    else {
      rsrq = rsrq1;
    }
    Serial.print("Signal rsrq: ");
    Serial.println(rsrq);
  }
  return rsrq;
}

bool modemWakeUp() {
  int counter = 0;

  modemPowerOn();

  if (modemAlive()) {
    while (counter < 120) {
      counter++;
      if (modemSignal() > 4) {
        Serial.println("Modem out of PSM");
        return true;
      }
      delay(1000);
    }
  }
  return false;
}
  • Changed the PSM requested extended periodic TAU value (T3412) to 2 hr
  • Added two extra tries to the hologram() function calls in the main loop
  • Changed the hologramUrl to the IP address. Apparently, a DNS lookup at socket connect can use a fair amount of data. We'll see if it makes any difference...
/*
  This sketch works with the MKR NB 1500 without the need for the MKRNB library.

  It sends message to the hologram cloud every 10 mins.
  This message contains some data that can be sent to thingspeak
  using a hologram route.

  It also sends a sms via the hologram embedded api when a switch is closed.

*/


#include <Arduino_MKRTHERM.h>

char response[100];
char response2[100];
char responseJunk[100];
char smsMessage[] = "Smoke detected";

const char apn[] = "hologram";

int networkProfile = 2;//Profile ID, see AT Manual. 2 = ATT.

const char hologramDeviceKey[] = "########";

const char phoneNumber[] = "15559999999";//11 digit phone #

const char hologramUrl[] = "34.233.146.107";//could also use: cloudsocket.hologram.io
const unsigned int hologramPort = 9999;

int smokeState = 0;

unsigned long lastConnectionTime = 0;


void setup() {
  Serial.begin(115200);
  delay(5000);
  SerialSARA.begin(115200);

  THERM.begin();

  pinMode(1, INPUT_PULLUP);

  //Attach modem and send first hologram data message.
  if (modemAttach()) {
    hologram(false);
  }

}

void loop() {

  if (digitalRead(1) == LOW) {
    delay(250);
    if (digitalRead(1) == LOW) {
      if (!hologram(true)) {
        if (!hologram(true)) {
          if (!hologram(true)) {
            if (modemAttach()) {
              hologram(true);
            }
          }
        }
      }
    }
  }

  if (millis() - lastConnectionTime > 600000) {
    lastConnectionTime = millis();
    if (!hologram(false)) {
      if (!hologram(false)) {
        if (!hologram(false)) {
          if (modemAttach()) {
            hologram(false);
          }
        }
      }
    }
  }
}
void modemPowerOn() {
  //Logic high on Arduino pin = logic low on SARA R4
  digitalWrite(SARA_PWR_ON, HIGH);
  pinMode(SARA_PWR_ON, OUTPUT);
  delay(500);
  pinMode(SARA_PWR_ON, INPUT);
  Serial.println("Modem on");
}

void modemPowerOff() {
  //Logic high on Arduino pin = logic low on SARA R4
  digitalWrite(SARA_PWR_ON, HIGH);
  pinMode(SARA_PWR_ON, OUTPUT);
  delay(3000);
  pinMode(SARA_PWR_ON, INPUT);
  Serial.println("Modem off");
}

void modemReset() {
  //Logic high on Arduino pin = logic low on SARA R4
  Serial.println("Modem resetting...");
  digitalWrite(SARA_RESETN, HIGH);
  pinMode(SARA_RESETN, OUTPUT);
  delay(12000);
  pinMode(SARA_RESETN, INPUT);
  Serial.println("Modem reset");
}

bool modemAlive() {
  int counter = 0;

  while (counter < 20) {
    counter++;
    sendCommand("ATE0", 1000);//Turn off Echo
    sendCommand("AT+IPR=115200", 1000);//Set baud rate

    if (sendCommand("ATV0", 1000)) {
      Serial.println("Set to non-verbose result codes");
      Serial.println("Modem is alive");
      return true;
    }
  }
  return false;
}

bool sendCommand(char command[50], unsigned long timeout) {
  int responseLength = 0;
  int responseLength2 = 0;

  SerialSARA.setTimeout(100);
  SerialSARA.readBytes(responseJunk, 99);

  memset(response, 0, sizeof(response));
  memset(response2, 0, sizeof(response2));
  memset(responseJunk, 0, sizeof(responseJunk));

  SerialSARA.setTimeout(timeout);
  Serial.println(command);
  SerialSARA.println(command);

  responseLength = SerialSARA.readBytesUntil('\r', response, 99);

  if (responseLength == 1) {
    Serial.println(response);
    if (response[0] == '0') {
      return true;
    }
    else {
      return false;
    }
  }

  if (responseLength != 0) {
    Serial.println(response);
  }

  SerialSARA.setTimeout(100);
  SerialSARA.readBytesUntil('\n', responseJunk, 99);

  responseLength2 = SerialSARA.readBytesUntil('\r', response2, 99);

  if (responseLength2 == 1) {
    Serial.println(response2);
    if (response2[0] == '0' || response2[0] == '@' ) {
      return true;
    }
    else {
      return false;
    }
  }

  if (responseLength2 != 0) {
    Serial.println(response2);
  }

  return false;
}

bool modemAttach() {

  char command[100];
  int counter = 0;

  memset(command, 0, sizeof(command));

  modemPowerOn();

  //Check if the modem is responding. If not, restart modem.
  if (!modemAlive()) {
    modemReset();
    delay(5000);
    modemPowerOn();
    modemAlive();
  }

  if (sendCommand("AT+UMNOPROF?", 1000)) {
    if (response[11] - '0' != networkProfile) {
      sprintf(command, "AT+UMNOPROF=%i", networkProfile);
      if (sendCommand("AT+CFUN=0", 180000)) {
        if (sendCommand(command, 1000)) {
          if (sendCommand("AT+CFUN=15", 180000)) {
            delay(5000);
            if (modemAlive()) {
              Serial.println("Profile set");
            }
            else {
              Serial.println("Profile setting failed");
              return false;
            }
          }
        }
      }
    }
  }

  //Turn power save mode on with a
  //Requested extended periodic TAU value (T3412) of 2 hr
  //and a Requested Active Time value (T3324) of 16 sec.
  //See: https://weekly-geekly.github.io/articles/435722/index.html
  if (sendCommand("AT+CPSMS=1,,,\"00100010\",\"00001000\"", 10000)) {
    if (sendCommand("AT+CFUN=15", 180000)) {
      delay(5000);
      if (modemAlive()) {
        Serial.println("PSM set");
      }
      else {
        Serial.println("PSM setting failed");
        return false;
      }
    }
  }

  memset(command, 0, sizeof(command));
  sprintf(command, "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
  if (sendCommand(command, 1000)) {
    Serial.println("APN set");
    if (sendCommand("AT+CEREG=0", 1000)) {
      Serial.println("Registration URC disabled");
      while (counter < 120) {
        counter++;
        if (modemSignal() > 4) {
          if (sendCommand("AT+CEREG?", 1000)) {
            if (response[10] == '1' || response[10] == '5') {
              Serial.println("Registered");
              if (sendCommand("AT+CGATT=1", 180000)) {
                Serial.println("Attached");
                return true;
              }
            }
          }
        }
        delay(1000);
      }
    }

  }
  Serial.println("Attach failed");
  return false;
}


bool hologram(bool sms) {

  int socket = -1;
  float refTemp = (THERM.readTemperature() * 9 / 5) + 32;
  char hologramMessage[100];
  char socketCommand[100];
  int hologramMessageSize = 0;
  bool result = false;

  memset(socketCommand, 0, sizeof(socketCommand));
  if (sms) {
    sprintf(hologramMessage, "S%s+%s %s\n\n", hologramDeviceKey, phoneNumber, smsMessage);
  }

  else {
    sprintf(hologramMessage, "{\"k\":\"%s\",\"d\":\"field1=%f&field2=%i\",\"t\":\"thingspeak\"}", hologramDeviceKey, refTemp, smokeState);
  }
  hologramMessageSize = strlen(hologramMessage);




  if (modemWakeUp()) {
    if (sendCommand("AT+USOCR=6", 1000)) {
      socket = response[8] - '0';
      sprintf(socketCommand, "AT+USOCO=%i,\"%s\",%i", socket, hologramUrl, hologramPort);
      if (sendCommand(socketCommand, 120000)) {
        Serial.println("Socket connected");
        memset(socketCommand, 0, sizeof(socketCommand));
        sprintf(socketCommand, "AT+USOWR=%i,%i", socket, hologramMessageSize);
        if (sendCommand(socketCommand, 120000)) {
          Serial.println("Write data:");
          if (sendCommand(hologramMessage, 120000)) {
            Serial.println("Data sent");
            result = true;
          }
        }
      }
    }
  }


  if (socket >= 0) {
    memset(socketCommand, 0, sizeof(socketCommand));
    sprintf(socketCommand, "AT+USOCL=%i", socket);
    if (sendCommand(socketCommand, 120000)) {
      Serial.println("Socket closed");
    }
  }

  return result;
}

int modemSignal() {
  int rsrq = 0;
  if (sendCommand("AT+CESQ", 1000)) {
    int rsrq1 = response[21] - '0';
    if (response[22] != ',') {
      int rsrq2 = response[22] - '0';
      rsrq = (rsrq1 * 10) + rsrq2;
      if (response[23] != ',') {
        rsrq = 0;
      }
    }
    else {
      rsrq = rsrq1;
    }
    Serial.print("Signal rsrq: ");
    Serial.println(rsrq);
  }
  return rsrq;
}

bool modemWakeUp() {
  int counter = 0;

  modemPowerOn();

  if (modemAlive()) {
    while (counter < 120) {
      counter++;
      if (modemSignal() > 4) {
        Serial.println("Modem out of PSM");
        return true;
      }
      delay(1000);
    }
  }
  return false;
}

Removing the DNS lookup seems to have cut the data usage by about half.

With updates every 10 mins, I am using about 3KB per hour. This should be about 2.2MB per month. This should be about $2 per month from hologram.

I have been running this for a couple days and it seems reliable so far. I sometimes have two or so data points over a 12 hour span that come in late. The 3KB/hr looks pretty consistent.

Made a bunch changes to this latest version...

Network attach:
The SARA R410 automatically registers, attaches, and activates context by itself without any commands. All you have to do is pool the modem until all these things have happened.

Power Save Mode:
For my application(updates every 10 mins) PSM doesn't really make sense. I have disabled it. I am also still confused about the timers and have seen conflicting information about them.

/*
  This sketch works with the MKR NB 1500 without the need for the MKRNB library.

  It sends message to the hologram cloud every 10 mins.
  This message contains some data that can be sent to thingspeak
  using a hologram route.

  It also sends a sms via the hologram embedded api when a switch is closed.

*/


#include "Adafruit_MAX31855.h"

char response[100];
char response2[100];
char responseJunk[100];
char smsMessage[] = "Smoke detected";

const char apn[] = "hologram";

int networkProfile = 2;//Profile ID, see AT Manual. 2 = ATT.

const char hologramDeviceKey[] = "########";

const char phoneNumber[] = "19995555555";//11 digit phone #

const char hologramUrl[] = "34.233.146.107";//could also use: cloudsocket.hologram.io
const unsigned int hologramPort = 9999;

int smokeState = 0;

int rsrq = 0;

unsigned long lastConnectionTime = 0;

Adafruit_MAX31855 thermocouple(0);


void setup() {
  Serial.begin(115200);
  delay(5000);
  SerialSARA.begin(115200);

  pinMode(1, INPUT_PULLUP);


  //Attach modem and send first hologram data message.
  if (modemAttach(true)) {
    hologram(false);
  }

}

void loop() {

  if (digitalRead(1) == LOW) {
    delay(250);
    if (digitalRead(1) == LOW) {
      if (!hologram(true)) {
        if (modemAttach(true)) {
          hologram(true);
        }
      }
    }
  }

  if (millis() - lastConnectionTime > 600000) {
    lastConnectionTime = millis();
    if (!hologram(false)) {
      if (modemAttach(true)) {
        hologram(false);
      }
    }
  }
}
void modemPowerOn() {
  //Logic high on Arduino pin = logic low on SARA R4
  digitalWrite(SARA_PWR_ON, HIGH);
  pinMode(SARA_PWR_ON, OUTPUT);
  delay(500);
  pinMode(SARA_PWR_ON, INPUT);
  Serial.println("Modem on");
}

void modemPowerOff() {
  //Logic high on Arduino pin = logic low on SARA R4
  digitalWrite(SARA_PWR_ON, HIGH);
  pinMode(SARA_PWR_ON, OUTPUT);
  delay(3000);
  pinMode(SARA_PWR_ON, INPUT);
  Serial.println("Modem off");
}

void modemReset() {
  //Logic high on Arduino pin = logic low on SARA R4
  Serial.println("Modem resetting...");
  digitalWrite(SARA_RESETN, HIGH);
  pinMode(SARA_RESETN, OUTPUT);
  delay(12000);
  pinMode(SARA_RESETN, INPUT);
  Serial.println("Modem reset");
}

bool modemAlive() {
  int counter = 0;

  while (counter < 60) {
    counter++;
    sendCommand("ATE0", 1000);//Turn off Echo
    sendCommand("AT+IPR=115200", 1000);//Set baud rate

    if (sendCommand("ATV0", 1000)) {
      Serial.println("Set to non-verbose result codes");
      Serial.println("Modem is alive");
      return true;
    }
  }
  return false;
}

bool sendCommand(char command[50], unsigned long timeout) {
  int responseLength = 0;
  int responseLength2 = 0;

  SerialSARA.setTimeout(100);
  SerialSARA.readBytes(responseJunk, 99);

  memset(response, 0, sizeof(response));
  memset(response2, 0, sizeof(response2));
  memset(responseJunk, 0, sizeof(responseJunk));

  SerialSARA.setTimeout(timeout);
  Serial.println(command);
  SerialSARA.println(command);

  responseLength = SerialSARA.readBytesUntil('\r', response, 99);

  if (responseLength == 1) {
    Serial.println(response);
    if (response[0] == '0') {
      return true;
    }
    else {
      return false;
    }
  }

  if (responseLength != 0) {
    Serial.println(response);
  }

  SerialSARA.setTimeout(100);
  SerialSARA.readBytesUntil('\n', responseJunk, 99);

  responseLength2 = SerialSARA.readBytesUntil('\r', response2, 99);

  if (responseLength2 == 1) {
    Serial.println(response2);
    if (response2[0] == '0' || response2[0] == '@' ) {
      return true;
    }
    else {
      return false;
    }
  }

  if (responseLength2 != 0) {
    Serial.println(response2);
  }

  return false;
}

bool modemAttach(bool firstAttach) {

  char command[100];
  int counter = 0;

  modemPowerOn();
  if (firstAttach) {
    delay(5000);
  }

  //Check if the modem is responding. If not, reset modem.
  if (!modemAlive()) {
    modemReset();
    delay(5000);
    modemPowerOn();
    delay(5000);
    modemAlive();
  }

  if (firstAttach) {
    if (sendCommand("AT+UMNOPROF?", 1000)) {
      if (response[11] - '0' != networkProfile) {
        memset(command, 0, sizeof(command));
        sprintf(command, "AT+UMNOPROF=%i", networkProfile);
        if (sendCommand("AT+CFUN=0", 180000)) {
          if (sendCommand(command, 1000)) {
            Serial.println("Profile set, Rebooting...");
            if (sendCommand("AT+CFUN=15", 180000)) {
              delay(5000);
              modemAlive();
            }
          }
        }
      }
    }
  }

  if (firstAttach) {
    if (sendCommand("AT+CPSMS=0", 10000)) {
      Serial.println("PSM off");
      memset(command, 0, sizeof(command));
      sprintf(command, "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
      if (sendCommand(command, 1000)) {
        Serial.println("APN set, Rebooting...");
        if (sendCommand("AT+CFUN=15", 180000)) {
          delay(5000);
          modemAlive();
        }
      }
    }
  }

  if (firstAttach) {
    if (sendCommand("AT+USOCLCFG=0", 1000)) {
      Serial.println("TCP socket \"graceful dormant close\" disabled");
    }
  }


  if (sendCommand("AT+CEREG=0", 1000)) {
    Serial.println("Registration URC disabled");
    while (counter < 120) {
      counter++;
      if (sendCommand("AT+CEREG?", 1000)) {
        if (response[10] == '1' || response[10] == '5') {
          Serial.println("Registered");
          if (sendCommand("AT+CGATT?", 180000)) {
            if (response[8] == '1') {
              Serial.println("Attached");
              if (sendCommand("AT+CGACT?", 150000)) {
                if (response[10] == '1') {
                  Serial.println("Context Activated");
                  return true;
                }
              }
            }
          }
        }
      }
      delay(1000);
    }
  }

  Serial.println("Attach failed");
  return false;
}


bool hologram(bool sms) {

  int socket = -1;
  float refTemp = thermocouple.readFahrenheit();
  char hologramMessage[100];
  char socketCommand[100];
  int hologramMessageSize = 0;
  bool result = false;


  if (modemAttach(false)) {
    modemSignal();
    
    if (sms) {
      sprintf(hologramMessage, "S%s+%s %s\n", hologramDeviceKey, phoneNumber, smsMessage);
    }
    else {
      sprintf(hologramMessage, "{\"k\":\"%s\",\"d\":\"field1=%f&field2=%i&field3=%i\",\"t\":\"thingspeak\"}", hologramDeviceKey, refTemp, smokeState, rsrq);
    }
    hologramMessageSize = strlen(hologramMessage);

    if (sendCommand("AT+USOCR=6", 1000)) {
      socket = response[8] - '0';
      memset(socketCommand, 0, sizeof(socketCommand));
      sprintf(socketCommand, "AT+USOCO=%i,\"%s\",%i", socket, hologramUrl, hologramPort);
      if (sendCommand(socketCommand, 150000)) {
        Serial.println("Socket connected");
        memset(socketCommand, 0, sizeof(socketCommand));
        sprintf(socketCommand, "AT+USOWR=%i,%i", socket, hologramMessageSize);
        if (sendCommand(socketCommand, 120000)) {
          Serial.println("Write data:");
          if (sendCommand(hologramMessage, 120000)) {
            Serial.println("Data sent");
            result = true;
          }
        }
      }
    }
  }

  if (socket >= 0) {
    memset(socketCommand, 0, sizeof(socketCommand));
    sprintf(socketCommand, "AT+USOCL=%i,1", socket);
    if (sendCommand(socketCommand, 120000)) {
      Serial.println("Socket closed");
    }
  }

  return result;
}

int modemSignal() {
  rsrq = 0;
  if (sendCommand("AT+CESQ", 1000)) {
    int rsrq1 = response[21] - '0';
    if (response[22] != ',') {
      int rsrq2 = response[22] - '0';
      rsrq = (rsrq1 * 10) + rsrq2;
      if (response[23] != ',') {
        rsrq = 0;
      }
    }
    else {
      rsrq = rsrq1;
    }
    Serial.print("Signal rsrq: ");
    Serial.println(rsrq);
  }
  return rsrq;
}

More changes:

Signal:
I added another field to my thingspeak channel that shows the modem signal at the time the data was sent. This helps with troubleshooting.

Socket Close(USOCL) behavior:
I changed the "Asynchronous connect flag" to 1. This way the modem does not lock the AT terminal while the TCP socket is being closed. This brings me back to the main loop more quickly.

Thanks zbelding for the work, really useful!

JocPelletier:
Thanks zbelding for the work, really useful!

You're welcome. Let me know if you can use any of it, or if you have any suggestions.

I have found that being able to send any AT command gives me a lot more control over the modem.

The sendCommand() initialized the command char array with length [50]. This was too short. Not sure why it was working?... Changed it to command[100].

Removed most of the memset()'s that were not needed.

Simplified the modemAttach() function.

/*
  This sketch works with the MKR NB 1500 without the need for the MKRNB library.

  It sends message to the hologram cloud every 10 mins.
  This message contains some data that can be sent to thingspeak
  using a hologram route.

  It also sends a sms via the hologram embedded api when a switch is closed.

*/


#include "Adafruit_MAX31855.h"

char response[100];
char response2[100];
char responseJunk[100];
char smsMessage[] = "Smoke detected";

const char apn[] = "hologram";

int networkProfile = 2;//Profile ID, see AT Manual. 2 = ATT.

const char hologramDeviceKey[] = "########";

const char phoneNumber[] = "19995555555";//11 digit phone #

const char hologramUrl[] = "34.233.146.107";//could also use: cloudsocket.hologram.io
const unsigned int hologramPort = 9999;

int smokeState = 0;

int rsrq = 0;

unsigned long lastConnectionTime = 0;

Adafruit_MAX31855 thermocouple(0);


void setup() {
  Serial.begin(115200);
  delay(5000);
  SerialSARA.begin(115200);

  pinMode(1, INPUT_PULLUP);


  //Attach modem and send first hologram data message.
  if (modemAttach(true)) {
    hologram(false);
  }

}

void loop() {

  if (digitalRead(1) == LOW) {
    delay(250);
    if (digitalRead(1) == LOW) {
      if (!hologram(true)) {
        if (modemAttach(true)) {
          hologram(true);
        }
      }
    }
  }

  if (millis() - lastConnectionTime > 600000) {
    lastConnectionTime = millis();
    if (!hologram(false)) {
      hologram(false);
    }
  }
}
void modemPowerOn() {
  //Logic high on Arduino pin = logic low on SARA R4
  digitalWrite(SARA_PWR_ON, HIGH);
  pinMode(SARA_PWR_ON, OUTPUT);
  delay(500);
  pinMode(SARA_PWR_ON, INPUT);
  Serial.println("Modem on");
}

void modemPowerOff() {
  //Logic high on Arduino pin = logic low on SARA R4
  digitalWrite(SARA_PWR_ON, HIGH);
  pinMode(SARA_PWR_ON, OUTPUT);
  delay(3000);
  pinMode(SARA_PWR_ON, INPUT);
  Serial.println("Modem off");
}

void modemReset() {
  //Logic high on Arduino pin = logic low on SARA R4
  Serial.println("Modem resetting...");
  digitalWrite(SARA_RESETN, HIGH);
  pinMode(SARA_RESETN, OUTPUT);
  delay(12000);
  pinMode(SARA_RESETN, INPUT);
  Serial.println("Modem reset");
}

bool modemAlive() {
  int counter = 0;

  while (counter < 20) {
    counter++;
    sendCommand("ATE0", 1000);//Turn off Echo
    sendCommand("AT+IPR=115200", 1000);//Set baud rate

    if (sendCommand("ATV0", 1000)) {
      Serial.println("Set to non-verbose result codes");
      Serial.println("Modem is alive");
      return true;
    }
  }
  return false;
}

bool sendCommand(char command[100], unsigned long timeout) {
  int responseLength = 0;
  int responseLength2 = 0;

  SerialSARA.setTimeout(100);
  SerialSARA.readBytes(responseJunk, 99);

  memset(response, 0, sizeof(response));
  memset(response2, 0, sizeof(response2));
  memset(responseJunk, 0, sizeof(responseJunk));

  SerialSARA.setTimeout(timeout);
  Serial.println(command);
  SerialSARA.println(command);

  responseLength = SerialSARA.readBytesUntil('\r', response, 99);

  if (responseLength == 1) {
    Serial.println(response);
    if (response[0] == '0') {
      return true;
    }
    else {
      return false;
    }
  }

  if (responseLength != 0) {
    Serial.println(response);
  }

  SerialSARA.setTimeout(100);
  SerialSARA.readBytesUntil('\n', responseJunk, 99);

  responseLength2 = SerialSARA.readBytesUntil('\r', response2, 99);

  if (responseLength2 == 1) {
    Serial.println(response2);
    if (response2[0] == '0' || response2[0] == '@' ) {
      return true;
    }
    else {
      return false;
    }
  }

  if (responseLength2 != 0) {
    Serial.println(response2);
  }

  return false;
}

bool modemAttach(bool firstAttach) {

  char command[100];
  int counter = 0;

  modemPowerOn();
  if (firstAttach) {
    delay(5000);
  }

  //Check if the modem is responding. If not, reset modem.
  if (!modemAlive()) {
    modemReset();
    delay(5000);
    modemPowerOn();
    delay(5000);
    modemAlive();
  }

  if (firstAttach) {
    if (sendCommand("AT+UMNOPROF?", 1000)) {
      if (response[11] - '0' != networkProfile) {
        sprintf(command, "AT+UMNOPROF=%i", networkProfile);
        if (sendCommand("AT+CFUN=0", 180000)) {
          if (sendCommand(command, 1000)) {
            Serial.println("Profile set, Rebooting...");
            if (sendCommand("AT+CFUN=15", 180000)) {
              delay(5000);
              modemAlive();
            }
          }
        }
      }
    }
    
    sprintf(command, "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
    if (sendCommand(command, 1000)) {
      Serial.println("APN set, Rebooting...");
      if (sendCommand("AT+CFUN=15", 180000)) {
        delay(5000);
        modemAlive();
      }
    }
    if (sendCommand("AT+CPSMS=0", 10000)) {
      Serial.println("PSM off");
    }
    if (sendCommand("AT+USOCLCFG=0", 1000)) {
      Serial.println("TCP socket \"graceful dormant close\" disabled");
    }
  }

  if (sendCommand("AT+CEREG=0", 1000)) {
    Serial.println("Registration URC disabled");
    while (counter < 120) {
      counter++;
      if (sendCommand("AT+CEREG?", 1000)) {
        if (response[10] == '1' || response[10] == '5') {
          Serial.println("Registered");
          if (sendCommand("AT+CGATT?", 180000)) {
            if (response[8] == '1') {
              Serial.println("Attached");
              if (sendCommand("AT+CGACT?", 150000)) {
                if (response[10] == '1') {
                  Serial.println("Context Activated");
                  return true;
                }
              }
            }
          }
        }
      }
      delay(1000);
    }
  }

  Serial.println("Attach failed");
  return false;
}


bool hologram(bool sms) {

  int socket = -1;
  float refTemp = thermocouple.readFahrenheit();
  char hologramMessage[100];
  char socketCommand[100];
  int hologramMessageSize = 0;
  bool result = false;


  if (modemAttach(false)) {
    modemSignal();

    if (sms) {
      sprintf(hologramMessage, "S%s+%s %s\n", hologramDeviceKey, phoneNumber, smsMessage);
    }
    else {
      sprintf(hologramMessage, "{\"k\":\"%s\",\"d\":\"field1=%f&field2=%i&field3=%i\",\"t\":\"thingspeak\"}", hologramDeviceKey, refTemp, smokeState, rsrq);
    }
    hologramMessageSize = strlen(hologramMessage);

    if (sendCommand("AT+USOCR=6", 1000)) {
      socket = response[8] - '0';
      sprintf(socketCommand, "AT+USOCO=%i,\"%s\",%i", socket, hologramUrl, hologramPort);
      if (sendCommand(socketCommand, 150000)) {
        Serial.println("Socket connected");
        sprintf(socketCommand, "AT+USOWR=%i,%i", socket, hologramMessageSize);
        if (sendCommand(socketCommand, 120000)) {
          Serial.println("Write data:");
          if (sendCommand(hologramMessage, 120000)) {
            Serial.println("Data sent");
            result = true;
          }
        }
      }
    }
  }

  if (socket >= 0) {
    sprintf(socketCommand, "AT+USOCL=%i,1", socket);
    if (sendCommand(socketCommand, 120000)) {
      Serial.println("Socket closed");
    }
  }

  return result;
}

int modemSignal() {
  rsrq = 0;
  if (sendCommand("AT+CESQ", 1000)) {
    int rsrq1 = response[21] - '0';
    if (response[22] != ',') {
      int rsrq2 = response[22] - '0';
      rsrq = (rsrq1 * 10) + rsrq2;
      if (response[23] != ',') {
        rsrq = 0;
      }
    }
    else {
      rsrq = rsrq1;
    }
    Serial.print("Signal rsrq: ");
    Serial.println(rsrq);
  }
  return rsrq;
}
/*
  This sketch works with the MKR NB 1500 without the need for the MKRNB library.

  It sends message to the hologram cloud every 10 mins.
  This message contains some data that can be sent to thingspeak
  using a hologram route.

  It also sends a sms via the hologram embedded api when a switch is closed.

*/


#include "Adafruit_MAX31855.h"

char response[100];
char response2[100];
char responseJunk[100];

const char apn[] = "hologram";

int networkProfile = 2;//Profile ID, see AT Manual. 2 = ATT.

const char hologramDeviceKey[] = "########";

const char phoneNumber[] = "15559999999";//11 digit phone #

const char hologramUrl[] = "34.233.146.107";//could also use: cloudsocket.hologram.io
const unsigned int hologramPort = 9999;

int smokeState = 0;
int carbonState = 0;
int resetCount = 0;

int rsrq = 0;

unsigned long lastConnectionTime = 0;
unsigned long lastAlarmTime = 0;
unsigned long lastAlarmTime2 = 0;


Adafruit_MAX31855 thermocouple(0);


void setup() {
  Serial.begin(115200);
  delay(5000);
  SerialSARA.begin(115200);

  pinMode(1, INPUT);
  pinMode(2, INPUT);


  //Attach modem and send first hologram data message & sms.
  if (modemAttach(true)) {
    hologram(false, NULL);
    hologram(true, "Arduino reset");
  }

}

void loop() {

  if (smokeState != 1) {
    if (digitalRead(1) == LOW) {
      delay(100);
      if (digitalRead(1) == LOW) {
        smokeState = 1;
      }
    }
  }

  if ( smokeState == 1) {
    if (millis() - lastAlarmTime > 300000) {
      if (hologram(true, "Smoke detected!")) {
        lastAlarmTime = millis();
        smokeState = 0;
      }
    }
  }

  if (carbonState != 1) {
    if (digitalRead(2) == LOW) {
      delay(100);
      if (digitalRead(2) == LOW) {
        carbonState = 1;
      }
    }
  }

  if ( carbonState == 1) {
    if (millis() - lastAlarmTime2 > 300000) {
      if (hologram(true, "Carbon monoxide detected!")) {
        lastAlarmTime2 = millis();
        carbonState = 0;
      }
    }
  }

  if (millis() - lastConnectionTime > 600000) {
    lastConnectionTime = millis();
    if (!hologram(false, NULL)) {
      hologram(false, NULL);
    }
  }
}
void modemPowerOn() {
  //Logic high on Arduino pin = logic low on SARA R4
  digitalWrite(SARA_PWR_ON, HIGH);
  pinMode(SARA_PWR_ON, OUTPUT);
  delay(500);
  pinMode(SARA_PWR_ON, INPUT);
  Serial.println("Modem on");
}

void modemPowerOff() {
  //Logic high on Arduino pin = logic low on SARA R4
  digitalWrite(SARA_PWR_ON, HIGH);
  pinMode(SARA_PWR_ON, OUTPUT);
  delay(3000);
  pinMode(SARA_PWR_ON, INPUT);
  Serial.println("Modem off");
}

void modemReset() {
  //Logic high on Arduino pin = logic low on SARA R4
  Serial.println("Modem resetting...");
  digitalWrite(SARA_RESETN, HIGH);
  pinMode(SARA_RESETN, OUTPUT);
  delay(12000);
  pinMode(SARA_RESETN, INPUT);
  resetCount++;
  Serial.println("Modem reset");
}

bool modemAlive() {
  int counter = 0;

  while (counter < 20) {
    counter++;
    sendCommand("ATE0", 1000);//Turn off Echo
    sendCommand("AT+IPR=115200", 1000);//Set baud rate

    if (sendCommand("ATV0", 1000)) {
      Serial.println("Set to non-verbose result codes");
      Serial.println("Modem is alive");
      return true;
    }
  }
  return false;
}

bool sendCommand(char command[150], unsigned long timeout) {
  int responseLength = 0;
  int responseLength2 = 0;

  SerialSARA.setTimeout(100);
  SerialSARA.readBytes(responseJunk, 99);

  memset(response, 0, sizeof(response));
  memset(response2, 0, sizeof(response2));
  memset(responseJunk, 0, sizeof(responseJunk));

  SerialSARA.setTimeout(timeout);
  Serial.println(command);
  SerialSARA.println(command);

  responseLength = SerialSARA.readBytesUntil('\r', response, 99);

  if (responseLength == 1) {
    Serial.println(response);
    if (response[0] == '0') {
      return true;
    }
    else {
      return false;
    }
  }

  if (responseLength != 0) {
    Serial.println(response);
  }

  SerialSARA.setTimeout(100);
  SerialSARA.readBytesUntil('\n', responseJunk, 99);

  responseLength2 = SerialSARA.readBytesUntil('\r', response2, 99);

  if (responseLength2 == 1) {
    Serial.println(response2);
    if (response2[0] == '0' || response2[0] == '@' ) {
      return true;
    }
    else {
      return false;
    }
  }

  if (responseLength2 != 0) {
    Serial.println(response2);
  }

  return false;
}

bool modemAttach(bool firstAttach) {

  char command[100];
  int counter = 0;

  modemPowerOn();
  if (firstAttach) {
    delay(5000);
  }

  //Check if the modem is responding. If not, reset modem.
  if (!modemAlive()) {
    modemReset();
    delay(5000);
    modemPowerOn();
    delay(5000);
    modemAlive();
  }

  if (firstAttach) {
    if (sendCommand("AT+UMNOPROF?", 1000)) {
      if (response[11] - '0' != networkProfile) {
        sprintf(command, "AT+UMNOPROF=%i", networkProfile);
        if (sendCommand("AT+CFUN=0", 180000)) {
          if (sendCommand(command, 1000)) {
            Serial.println("Profile set");
            if (sendCommand("at+crsm=214,28539,0,0,12,\"FFFFFFFFFFFFFFFFFFFFFFFF\"", 10000)) {
              Serial.println("FPLMN cleared, Rebooting...");
              if (sendCommand("AT+CFUN=15", 180000)) {
                delay(5000);
                modemAlive();
              }
            }
          }
        }
      }
    }
    sprintf(command, "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
    if (sendCommand(command, 1000)) {
      Serial.println("APN set, Rebooting...");
      if (sendCommand("AT+CFUN=15", 180000)) {
        delay(5000);
        modemAlive();
      }
    }
    if (sendCommand("AT+CPSMS=0", 10000)) {
      Serial.println("PSM off");
    }
    if (sendCommand("AT+USOCLCFG=0", 1000)) {
      Serial.println("TCP socket \"graceful dormant close\" disabled");
    }
  }

  if (sendCommand("AT+CEREG=0", 1000)) {
    Serial.println("Registration URC disabled");
    while (counter < 120) {
      counter++;
      if (sendCommand("AT+CEREG?", 1000)) {
        if (response[10] == '1' || response[10] == '5') {
          Serial.println("Registered");
          if (sendCommand("AT+CGATT?", 180000)) {
            if (response[8] == '1') {
              Serial.println("Attached");
              if (sendCommand("AT+CGACT?", 150000)) {
                if (response[10] == '1') {
                  Serial.println("Context Activated");
                  return true;
                }
              }
            }
          }
        }
      }
      delay(1000);
    }
  }

  Serial.println("Attach failed");
  return false;
}


bool hologram(bool sms, char smsMessage[100]) {

  int socket = -1;
  char hologramMessage[150];
  char socketCommand[100];
  int hologramMessageSize = 0;
  bool result = false;


  if (modemAttach(false)) {
    modemSignal();

    if (sms) {
      sprintf(hologramMessage, "S%s+%s %s\n", hologramDeviceKey, phoneNumber, smsMessage);
    }
    else {
      float thermocoupleTemp = (thermocouple.readCelsius() * 9 / 5) + 32;
      float refTemp = (thermocouple.readInternal() * 9 / 5) + 32;

      sprintf(hologramMessage, "{\"k\":\"%s\",\"d\":\"field1=%f&field2=%i&field3=%i&field4=%i&field5=%i&field6=%f\",\"t\":\"thingspeak\"}", hologramDeviceKey, thermocoupleTemp, smokeState, rsrq, carbonState, resetCount, refTemp);
    }
    hologramMessageSize = strlen(hologramMessage);

    if (sendCommand("AT+USOCR=6", 1000)) {
      socket = response[8] - '0';
      sprintf(socketCommand, "AT+USOCO=%i,\"%s\",%i", socket, hologramUrl, hologramPort);
      if (sendCommand(socketCommand, 150000)) {
        Serial.println("Socket connected");
        sprintf(socketCommand, "AT+USOWR=%i,%i", socket, hologramMessageSize);
        if (sendCommand(socketCommand, 120000)) {
          Serial.println("Write data:");
          if (sendCommand(hologramMessage, 120000)) {
            Serial.println("Data sent");
            result = true;
          }
        }
      }
    }
  }

  if (socket >= 0) {
    sprintf(socketCommand, "AT+USOCL=%i,1", socket);
    if (sendCommand(socketCommand, 120000)) {
      Serial.println("Socket closed");
    }
  }

  return result;
}

int modemSignal() {
  rsrq = 0;
  if (sendCommand("AT+CESQ", 1000)) {
    int rsrq1 = response[21] - '0';
    if (response[22] != ',') {
      int rsrq2 = response[22] - '0';
      rsrq = (rsrq1 * 10) + rsrq2;
      if (response[23] != ',') {
        rsrq = 0;
      }
    }
    else {
      rsrq = rsrq1;
    }
    Serial.print("Signal rsrq: ");
    Serial.println(rsrq);
  }
  return rsrq;
}

Changes:

Changed the main loop to only send a alarm sms every five mins if in alarm state.

Changed the hologram() function so that the alarm message can passed directly to it.

Added another couple fields to the thingspeak channel. One of which is a reset count. This way I can keep track of how many times the Modem was unresponsive and had to be reset. When the signal is poor, it seems that once and a while the modem can become unresponsive, needing a reset.

I have been running this for about a week, and it seems pretty reliable so far.

My program updates a thingspeak channel every 10 minutes. Seemingly randomly, the modem will become unresponsive via AT terminal. When this happens, the program does a hard reset of the modem and then sends the data. As you can see from the graph on the lower right, this happened 5 times in a row last night.

Any ideas why?

The modem is in a fixed location. Please note: the "signal" chart shows 0 signal during this reset period, but that is typical after a reset because it takes the modem a little while to calculate rsrq after a hard reset.

The mystery continues... Happened again last night with the same number of resets, but not at the same time. In this chart I have both nightly resets shown with one reset during the day almost exactly in the middle... 8.5 hrs on either side. Coincidence?

No more resets in the last 36 hrs, so it must be tied to signal somehow.

Thanks for sharing your code and findings. It will be very beneficial to me. I posted earlier in another thread asking if this was available before finding it here. In my testing I've found that a send on rare occasions takes longer than 120000 millis. The test program was sending a MQTT message every 2 seconds and had one case where the response to a send exceeded 120000. This was using the MKRNB library, but don't think the library had anything to do with the longer than expected response.

icole:
Thanks for sharing your code and findings. It will be very beneficial to me. I posted earlier in another thread asking if this was available before finding it here. In my testing I've found that a send on rare occasions takes longer than 120000 millis. The test program was sending a MQTT message every 2 seconds and had one case where the response to a send exceeded 120000. This was using the MKRNB library, but don't think the library had anything to do with the longer than expected response.

I am working on a complete re-write of this program. It will be non-blocking(main loop will be able to do other tasks while waiting for modem response). I also plan on utilizing the built-in MQTT AT Commands of the modem. It will also use clickatell API to send sms alerts. This will make the sketch non-specific to a hologram sim. I will start a new thread soon.

zbelding:
I am working on a complete re-write of this program. It will be non-blocking(main loop will be able to do other tasks while waiting for modem response). I also plan on utilizing the built-in MQTT AT Commands of the modem. It will also use clickatell API to send sms alerts. This will make the sketch non-specific to a hologram sim. I will start a new thread soon.

I have finished a working version of the above(minus the clickatell API). It uses the built-in MQTT AT commands of the modem. I will start a new thread tonight and post the code.

Hello, and thanks for sharing all your findings :slight_smile:

 // power off the module
  pinMode(SARA_PWR_ON, INPUT);
  //digitalWrite(SARA_PWR_ON, LOW);

  // put SARA modem in reset on start to conserve power if it's not used
  pinMode(SARA_RESETN, INPUT);
  //digitalWrite(SARA_RESETN, LOW);

Do you have a reason for why these pins needs to set as input?
My application (custom PCB based on NB 1500) is very dependent on power consumption, and I can confirm that setting the PWR_ON to LOW, then the board is still dead.

Thanks again.

  • Richard