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.

Update:

After long struggles with the modem here are my latest tips:

  • Connect only in the loop and not already in setup, so the loop starts even without sim and mobile connection. So no hassle in setup but simply in the loop:
if (modemconnected == false) {
    if ((nbAccess.begin(PINNUMBER) == NB_READY) &&
        (gprs.attachGPRS() == GPRS_READY)) {
      modemconnected = true;
    }
  }
  • Modems can be flaky. I have long struggled with an error that did not occur on another MKRNB with the same modem firmware. So if you are on the verge of despair, you should try to change the hardware.
  • If the SIM card is changed, the MKRNB must be briefly disconnected from the power. USB briefly unplug! Otherwise the modem will not connect properly.
  • If the option nbaccess(true) is set then the lib changes some parameters.
    To avoid endless waiting i have changed in nb.ccp Line 62
    _timeout(15000) // set dev timeout
  • In NBClient i changed at line 288
 command.reserve(19 + (size > 512 ? 512 : size) * 2);

  while (size) {
    size_t chunkSize = size;

    if (chunkSize > 512) {//dev512
      chunkSize = 512;
    }

Because the modem on the MKR 1500 can handle more data to put into a socket in one rush then the MKR1400. This avoids problems with larger mqtt packages in my case.

The MKRNB lib is more or less a copy of MKRGSM which deserved a little more attention from the developers...
Ergo: This is a much more elegant aproach for a proper start up sequence.

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