MKR GSM freeze - TCP error handling

Hello All

I have a MKR GSM + CAN shield setup which reads out CAN data and sends a data string through the MKR GSM board over mobile network via TCP.

During initial bench prototyping, using SocketTest on a PC acting as the “TCP listener”, all seemed to work well under server error conditions. i.e.: when connection to the TCP listener was lost either due to:

  • Loss of GSM signal
  • TCP listener client was properly closed
  • TCP listener was abruptly disconnected without closing properly

In above cases, the sketch would continue to run, with “client.write” commands executing but not waiting for acknowledgement.

When I moved the TCP listener service to a Windows run service on a rented server however, in case the service is stopped and the sketch is still running, all “client.xxx” commands seem to freeze the sketch. After a while the board has a few reboot attempts, which makes the CAN shield send error frames on the bus.

I want to write some error handling around this to account for the back end server/service becoming unavailable out of my control. However I don’t seem to have the handles available, as all client.xxx commands have the same behaviour, i.e. resetting (watchdog?) the MKR GSM board when the TCP listener service is stopped after initial successful connection:

client.connected
client.available
client.read
client.write

Any thoughts/leads/suggestions? I can’t share the full code, but below are the parts related to interacting with the GSM network and TCP listener service:

Parts of the connection code:

//Define GSM Parameters
#define PINNUMBER      ""
#define GPRS_APN       "****"   // GPRS APN
#define GPRS_LOGIN     "****"                  // GPRS login
#define GPRS_PASSWORD  "****"                  // GPRS password

GSMClient client;
GPRS gprs;
//GSM gsmAccess(false);                                             // Comment FOR TESTING ONLY
GSM gsmAccess(true);                                                // FOR DEBUGGING ONLY
GSMModem modem;

const char server[] = "****";        // sever address
const int port = ****;                       // server port

volatile boolean GSMNotConnected = true;      // GSM Connection Status
volatile boolean ServerNotConnected = true;   // Server Connection Status
String IMEI = "";                             // IMEI ID
byte IMEIctr = 0;                             // IMEI counter to init modem
volatile boolean ModemReady = false;                   // Status boolean for modem readiness

Modem check during setup:

  // get modem IMEI
  modem.begin();
  IMEI = modem.getIMEI().substring(11);

  while ((IMEI == NULL) && (IMEIctr <= 5)) {
    IMEIctr++;
    modem.begin();
    IMEI = modem.getIMEI().substring(11);
  }

  if (IMEI != NULL) {
    ModemReady = true;
  }
  else {
    ModemReady = false;
  }

  // Start GSM and Server connection
  if (ModemReady) {
    GSMConnect();
  }

Call to DataSend function from within main loop:

  // Transmit data to server
  if (!GSMNotConnected && !ServerNotConnected) {
    DataSend();
  }

DataSend function:

void DataSend() {

  ServerData = ****

  client.print(ServerData);

GSM connection function, called if modem is alive during startup:

void GSMConnect() {

  Serial.println("GSMConnect started.");
  ConnectingTimeStart = millis();

  //Init GSM connection
  while (GSMNotConnected && (ConnectingTime <= ConnectingTimeDuration)) {                    // connection time out included to retain in-car LED warning funcitonality in case connection fails

    Serial.println("Inititalising GSM connection");                                          // TESTING ONLY

    // Check GSM ready
    if (gsmAccess.begin(PINNUMBER) == GSM_READY) {
      delay(2000);

      // Initialise GPRS
      if (gprs.attachGPRS(GPRS_APN, GPRS_LOGIN, GPRS_PASSWORD) == GPRS_READY) {
        GSMNotConnected = false;
        Serial.println("Connected to GSM network");                                         // TESTING ONLY
      }
    }
    else {
      Serial.println("GSM Not Connected");
    }

    ConnectingTime = millis() - ConnectingTimeStart;
    Serial.print("Connection Attempt Time: ");                                        // TESTING ONLY
    Serial.println(ConnectingTime);                                                   // TESTING ONLY
  }

  // start client connection
  if (!GSMNotConnected) {
    client.stop();
    delay(500);
    if (client.connect(server, port)) {
      ServerNotConnected = false;
      Serial.println("Connected to Server");                                         // TESTING ONLY
    }
    else {
      ServerNotConnected = true;
      Serial.println("SERVER CONNECTION FAILED");                                    // TESTING ONLY
    }
  }
}

Disclaimer - I know nothing about the MKR GSM board and have never used cellular for sending data.

However if your goal is just to get data from one place to another then you also have the option of UDP, and yes I'm well aware of its limitations, so it may not be a long term solution but it may allow you to characterise the behaviour of the cellular network which may shed some light on the TCP behaviour you are seeing.

Using UDP had crossed my mind when initially setting this up, as I’m not too fussed about loss of messages. What does arrive I do want to have control over packet order though.

That being said, until I expand the use case, I’m just sending a 25 character string at a time (<< UDP max packet size). Revisiting UDP, even if just to troubleshoot is a good shout, thanks.