Small project: Reliable and fault-tolerant modem start sequence

Hi community!

The startup and connection sequence for the modem in the examples and from what I have found in the net are not quite satisfactory. What i am looking for is a sequence that tests sim and pin, then tries to connect and eventually gets into the loop despite problems. I think this could be interesting for many .

I make a first suggestion here and hope for improvements from you.

Notes:
WDTZero lib as Watchdog,
MKRNB lib with
NBClient net;
GPRS gprs;
NB nbAccess(true); //(true)
NBScanner scannerNetworks;
NB_SMS sms;
current behavior: Sim and pintest work in my modest tests, sometimes the modem hangs in the

if ((nbAccess.begin(PINNUMBER) == NB_READY) && (gprs.attachGPRS() == GPRS_READY))

statement, then the watchdog resets the board after one minute, sometimes it connects at the second try, sometimes it goes smooth, sometimes i need a cold beer. May someone has a contribution…

Often the modem hangs in limbo for no reason:

14:15:10.038 → Modem, SIM und PIN OK!AT+CFUN=1

14:15:10.038 → OK
14:15:10.075 → AT+CPWROFF

14:15:10.075 → OK
14:15:10.254 → AT

14:15:10.254 → OK
14:15:10.364 → AT+CMEE=0

14:15:10.364 → OK
14:15:10.542 → AT+CFUN=0

14:15:12.259 → OK
And then nothing happens.

Here’s my code:

 MyWatchDoggy.attachShutdown(myshutdown);
  MyWatchDoggy.setup(WDT_SOFTCYCLE1M);  // Software Watchdog set 1 min
  MODEM.begin();
  delay(500);
  String response;
  // SIM present?
  MODEM.sendf("AT+CCID?");
  MODEM.waitForResponse(10000, &response);
  Serial.print("simtest response: ");
  Serial.println(response);
  if (response.startsWith("+CCID: ")) {
    simtest = true;
    Serial.print("SIM Present!");
  }
  else {
    simtest = false;
    Serial.print("SIM NOT present");
  }
  MODEM.send("AT+CPIN?");
  MODEM.waitForResponse(10000, &response);
  Serial.print("pintest response: ");
  Serial.println(response);
  if (response.endsWith("READY")) {
    pintest = true;
    Serial.print("PIN Test Passed");
  } else if (response.endsWith("SIM PIN")) {
    Serial.print("Wrong PIN");
    Serial.print("PIN Test NOT passed");

  } else if (response.endsWith("SIM PUK")) {
    Serial.print("PUK wrong");
    Serial.print("SIM needs PUK");
  }
  if ((simtest == false) || (pintest == false))
  {
    Serial.println("Modem or SIM not ready");
    modemconnected = false;
    delay(2000);
  }
  if ((simtest == true) && (pintest == true))
  {
    Serial.print("Modem, SIM and PIN OK!");
    MyWatchDoggy.clear();
    int gsm_conn_counter = 0;
    // MODEM reset
    //  MODEM.send("AT+CFUN=16");
    //  MODEM.waitForResponse(10000);
    //MODEM.send("AT+CFUN=1");
    // MODEM.waitForResponse(10000);
    do {
      if ((nbAccess.begin(PINNUMBER) == NB_READY) && (gprs.attachGPRS() == GPRS_READY))
        //  (gprs.attachGPRS(GPRS_APN, GPRS_LOGIN, GPRS_PASSWORD) == GPRS_READY)
      {
        modemconnected = true;
       Serial.print("Mobile Uplink Ready!");
        delay(1000);
        break;

      }
      else
      {
      Serial.print("Mobile Uplink Not Ready!");
        delay(2000);
      
     Serial.print("Trying to uplink again..");
        Serial.print("gsm_conn_counter");
        gsm_conn_counter ++;
        Serial.print(gsm_conn_counter);
        if (gsm_conn_counter > 3)
        {
         Serial.print("Start without Network..");
          delay(2000);
        }
      }
    }
    while (gsm_conn_counter <= 2);
  }

void loop(){}
void myshutdown()
{
 Serial.print("Restart now...");
  // Proper Modem Shutdown ?
  MODEM.sendf("AT+CFUN=16");
  MODEM.waitForResponse(10000);
}

UPDATE: My code is not stable.
MODEM.sendf can work but not reliable befor the CREG process, thats why it stucks as described.
The “do while” part seems to work.

So, new version :
do {
if ((nbAccess.begin(PINNUMBER) == NB_READY) && (gprs.attachGPRS() == GPRS_READY))
// (gprs.attachGPRS(GPRS_APN, GPRS_LOGIN, GPRS_PASSWORD) == GPRS_READY)
{
modemconnected = true;
Serial.print(“Mobile Uplink Ready!”);
delay(1000);
break;

      }
      else
      {
      Serial.print("Mobile Uplink Not Ready!");
        delay(2000);
      
     Serial.print("Trying to uplink again..");
        Serial.print("gsm_conn_counter");
        gsm_conn_counter ++;
        Serial.print(gsm_conn_counter);
        if (gsm_conn_counter > 3)
        {
         Serial.print("Start without Network..");
          delay(2000);
        }
      }
    }
    while (gsm_conn_counter <= 2);
  }

Hi,

sounds very interesting what you have achieved so far. Is it possible to share the whole sketch with the start sequence?

… and has you testet the module with an MQTT broker for example over a long period of time? I’m working / testing these boards since month (!) … and i’m not able to have a stable version (longer than 2-2,5 days) running with a mosquitto broker and secure connection.

I’m using not the latest firmware … a bit to complicated to add the 5.12. version … i hope i will get it running stable sometimes :wink: The support from ublox and also arduino is very bad and is not clear, when the MKR NB boards get sold with the newest firmware version 5.12. … i have ordered two MKR NB boards from mouser a few weeks ago … still the firmware version 5.06.

Hi there (At least on reply :sunglasses:

I can't share the whole sketch, it's over 4000 lines so far..

What i found out meanwhile:

  • Never use the modem. method to communicate direct with the modem, this leads to internal conflicts (probably memory leak) after a while. Instead:
    In the setup: SerialSARA.begin(115200);
    Then (Example with soft reset): SerialSARA.println("AT+CFUN=15");
  • Install the latest firmware, it's a little voodoo but i made it with 3 MKR1500
  • I use GitHub - 256dpi/arduino-mqtt: MQTT library for Arduino as mqtt lib
  • Mr. zbelding, a hardcore coder is not using the mkrnb lib. This could be a reasonable decision.
    His collected experiences can be found here:
    Sketch to http post to thingspeak and what I have learned so far
  • The main problem is losing the socket to the mosquitto broker. The modem answer only with ERROR while trying to establish a new socket. In this case only a PIN reset for the modem and a cpu reset helps. I would appreciate finding a more elegant method....
    The problem is that the modem is a sensitive diva and whatever you use to communicate with the modem (MKRNB or homemade like zbelding), at the end you are shouting AT commands..
  • I stay with the mkrnb lib and have the whole thing with mosquitto now reasonably stable.

I made the following change in the mkrnb lib because my MQTT messages are larger than 256 and i feel better to send this message in one push through the socket then in two..based on superstition...

My start sequence is:


int gsm_conn_counter = 0;
  do {

    if ((nbAccess.begin(PINNUMBER) == NB_READY) &&
        (gprs.attachGPRS() == GPRS_READY)) {
      modemconnected = true;
     print("Mobile Uplink Ready!");
      delay(1000);
      break;
    }

    else
    {
     print("Mobile Uplink Not Ready!");
      delay(2000);
   print("Trying to uplink again..");
      delay(2000);
      Serial.print("gsm_conn_counter");
      gsm_conn_counter ++;
      Serial.print(gsm_conn_counter);
    }
  }
  while (gsm_conn_counter == 2);

  if (gsm_conn_counter == 2)
  {
   print("Start without Network..");
    delay(2000);
  }

My MQTT Watchdog is:

 static unsigned long delayStarttime;
  unsigned long currentTime = millis();


  if  (modemconnected == true)
  {
    if (client.connected() == 0) {
      mqtt_con_counter++;
      if (mqtt_con_counter > 20) // Reboot after 20 attemps
      {
   
        SerialSARA.println("AT+CFUN=15");
        delay(1000);
        // NEVER EVER use RESET_N
        Serial.println("Modem resetting...");
        digitalWrite(SARA_RESETN, HIGH);
        pinMode(SARA_RESETN, OUTPUT);
        delay(12000);
        pinMode(SARA_RESETN, INPUT);
        Serial.println("Modem reset");
        delay(3000);
        digitalWrite(SARA_PWR_ON, HIGH);
        pinMode(SARA_PWR_ON, OUTPUT);
        delay(3000);
        pinMode(SARA_PWR_ON, INPUT);
        Serial.println("Modem off");

        // Board reset
        NVIC_SystemReset();
      }

      //
      if (mqtt_con_counter > 5) //After 5 attemps modem soft reset
      {
        net.stop();
        net.flush();
        delay(200);
        Serial.println("AT+CFUN=15");
        SerialSARA.println("AT+CFUN=15");
        delay(1000);
      }
      client.begin("mqqt server", net);
      client.setOptions(180, true, 1200);

      Serial.println("Neuer MQTT Broker Verbindunsversuch, Nr:");
      Serial.print(mqtt_con_counter);
      client.connect("client name", "group", "password");

      delay(1000);
      if (client.connected() == 1) { // Falls verbunden , Zähler auf null setzen
        mqtt_con_counter = 0;
      }
      //  client.setOptions(120, true, 2000);
      client.subscribe("/mqtt channel name");
    }
  }

// Send a mqtt msg all minutes
  if (currentTime - delayStarttime >= 60000UL) //dev 1min = 60000 5 min =300000 15 min 90000

  {
    delayStarttime = currentTime;

    if ((Cloud == 1) && (modemconnected == true))
    {
      if (client.connected() == 1)
      {
        mqtt_con_counter = 0;
        mqtt_msg = "Message to publish";
        client.publish("/channel", mqtt_msg);

      }
      delay(20);
      //client.loop();   I DO NO LOOP BECAUSE I ONLY SEND DATA
      Serial.print("Last MQTTerror: ");
      Serial.println(client.lastError());
      Serial.print("Last MQTT Return code: ");
      Serial.println(client.returnCode());
    }
  }
}

Ergo: the wooden hammer method....but working for weeks now. Sometimes there is a reset i don't know exactly why. It must be the network.

Nice to have:

  • More elegant way to reset the communication with the MQTT broker
  • Check SIM routine to pass if there is no SIM inserted
  • I've never tested what happens when i lose and regain mobile signal. A proper routine to reconnect would be nice if neccesary.