Asynchronous GSM connection on Arduino GSM MKR 1400

Hey! I have an arduino that gathers accelerometer data, and sends it up to the cloud. As I want to be constantly taking readings, it would be preferable to have all GSM connection commands run asynchronously, as to not block any data taking.

Is this possible on the Arduino GSM MKR 1400? My code, at the moment, seems to "block" at the GSM parts. Is there a method to ensure the connection is made, and data sent over GSM, async? Is blocking behaviour a limitation of the device?

I've attached my code below, though it may not be necessary. Its heavily based on one of the Arduino Cloud Provider examples.

#include <ArduinoBearSSL.h>
#include <ArduinoECCX08.h>
#include <utility/ECCX08SelfSignedCert.h>
#include <ArduinoMqttClient.h>
#include <MKRGSM.h>

#include "SparkFunLIS3DH.h"
#include "Wire.h"
#include "SPI.h"

#include "arduino_secrets.h"

/////// Enter your sensitive data in arduino_secrets.h
const char pinnumber[]     = SECRET_PINNUMBER;
const char gprs_apn[]      = SECRET_GPRS_APN;
const char gprs_login[]    = SECRET_GPRS_LOGIN;
const char gprs_password[] = SECRET_GPRS_PASSWORD;
const char broker[]        = SECRET_BROKER;
String     deviceId        = SECRET_DEVICE_ID;

GSM gsmAccess;
GPRS gprs;

GSMClient     gsmClient;            // Used for the TCP socket connection
BearSSLClient sslClient(gsmClient); // Used for SSL/TLS connection, integrates with ECC508
MqttClient    mqttClient(sslClient);

LIS3DH myIMU; //Default constructor is I2C, addr 0x19.

unsigned long lastMillis = 0;

void setup() {
  Serial.begin(9600);
  while (!Serial);

  if (!ECCX08.begin()) {
    Serial.println("No ECCX08 present!");
    while (1);
  }

  // reconstruct the self signed cert
  ECCX08SelfSignedCert.beginReconstruction(0, 8);
  ECCX08SelfSignedCert.setCommonName(ECCX08.serialNumber());
  ECCX08SelfSignedCert.endReconstruction();

  // Set a callback to get the current time
  // used to validate the servers certificate
  ArduinoBearSSL.onGetTime(getTime);

  // Set the ECCX08 slot to use for the private key
  // and the accompanying public certificate for it
  sslClient.setEccSlot(0, ECCX08SelfSignedCert.bytes(), ECCX08SelfSignedCert.length());

  // Set the client id used for MQTT as the device id
  mqttClient.setId(deviceId);

  // Set the username to "<broker>/<device id>/api-version=2018-06-30" and empty password
  String username;

  username += broker;
  username += "/";
  username += deviceId;
  username += "/api-version=2018-06-30";

  mqttClient.setUsernamePassword(username, "");

  myIMU.begin();
}

void loop() {
  Serial.println("Beginning this read loop");
  // gonna grab x,y,z from accelo
  // drop it in array
  int minute_array[960][3];

  int i =0;
  while (i < 960) {
    //Get all parameters
    float X = myIMU.readFloatAccelX();
    int x = int(X*100000); // make an int to save space?
    float Y = myIMU.readFloatAccelY();
    int y = int(Y*100000);
    float Z = myIMU.readFloatAccelZ();
    int z = int(Z*100000);

    int acc[] = {x, y, z};
    for(int j=0; j<3; j++) {
        minute_array[i][j] = acc[j];
    }
    
    i++;
    delay(250);
  }
  Serial.println("Ending this read loop");
  Serial.println("Beginning loop send");
  azureTime(minute_array);
  Serial.println("Ending loop's send");
}

void azureTime(int message[][3]) {
  if (gsmAccess.status() != GSM_READY || gprs.status() != GPRS_READY) {
    connectGSM();
  }

  if (!mqttClient.connected()) {
    // MQTT client is disconnected, connect
    connectMQTT();
  }

  // poll for new MQTT messages and send keep alives
  mqttClient.poll();
  publishMessage(message); 
}

unsigned long getTime() {
  // get the current time from the cellular module
  return gsmAccess.getTime();
}

void connectGSM() {
  Serial.println("Attempting to connect to the cellular network");

  while ((gsmAccess.begin(pinnumber) != GSM_READY) ||
         (gprs.attachGPRS(gprs_apn, gprs_login, gprs_password) != GPRS_READY)) {
    // failed, retry
    Serial.print(".");
    delay(1000);
  }

  Serial.println("You're connected to the cellular network");
  Serial.println();
}

void connectMQTT() {
  Serial.print("Attempting to MQTT broker: ");
  Serial.print(broker);
  Serial.println(" ");

  while (!mqttClient.connect(broker, 8883)) {
    // failed, retry
    Serial.print(".");
    Serial.print(gsmAccess.getTime());
    Serial.println(mqttClient.connectError());
    delay(5000);
    if (gsmAccess.status() != GSM_READY || gprs.status() != GPRS_READY) {
      connectGSM();
    }
  }
  Serial.println();

  Serial.println("You're connected to the MQTT broker");
  Serial.println();

  // subscribe to a topic
  mqttClient.subscribe("devices/" + deviceId + "/messages/devicebound/#");
}

void publishMessage(int message[][3]) {
  Serial.println("Publishing message");

  // max buffer size on a message is 256 bytes
  // need to send results in chunks

  // send message, the Print interface can be used to set the message contents
  // this sets up message 1
  mqttClient.beginMessage("devices/" + deviceId + "/messages/events/");
  
  for(int n=0; n<960; n++) {

    // send every 3 messages together
    if (n>0 && n%3==0){
      mqttClient.print(" / ");
      mqttClient.print(getTime());
      mqttClient.endMessage();
      mqttClient.beginMessage("devices/" + deviceId + "/messages/events/");
    }
    for(int m=0; m<3; m++) {
        mqttClient.print(message[n][m]);
        mqttClient.print(",");
    //    Serial.println(message[n][m]);
    }
  }

  // this finalises the last message of the batch
  mqttClient.print(" / ");
  mqttClient.print(getTime());
  mqttClient.endMessage();
}

when I work with GSM kind of stuffs for serious projects, I'd build my own functions that issue AT command directly than depend on them libraries

especially those functions that run in the loop()

when you peep at the functions into MKRGSM lib, you will find bunch of while()s looping around, waiting for some GMS/GPRS status/flag to be set true before breaking out of it, thus causing blocking issues

I can see lots of irritating delay()s and for()s in your custom code

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.