Go Down

Topic: A simple sketch to send sensor data and sms via hologram without Arduino library (Read 1 time) previous topic - next topic

zbelding

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

Code: [Select]
// 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.

Code: [Select]
/*
  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: <<decdata>>
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.

zbelding

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.

zbelding

#2
Jan 13, 2020, 01:37 am Last Edit: Jan 13, 2020, 10:50 pm by zbelding Reason: Put in delay(5000) after both AT+CFUN=15 resets.
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.

Code: [Select]
/*
  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;
}

zbelding

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


Code: [Select]
/*
  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;
}

zbelding

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.

zbelding

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.


zbelding

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.


Code: [Select]
/*
  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;
}

zbelding

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.


zbelding

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.

zbelding

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.

Code: [Select]
/*
  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;
}

Go Up