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.