Device goes offline after several hours of service and does not reconnect

Hello,
We have built a remote switch using the Arduino Uno and Botletics SIM7000A modem. The device turns on/off a remote switch via LTE-M using Blynk 2.0 App. The device/software works great for several hours but then the device goes offline and never reconnects. We must manually reboot the device to get it to work again.

Initially, we thought that this was a poor signal issue so we added an external high-gain antenna. The signal quality is always excellent with this antenna.

The code is below, is there a way to add code that would, upon detecting the device being offline, would simply reboot the device or restart the connection? We tried to play with the BlinkTimer function but as soon as we declare it, the code breaks - the device will goes into endless loop where it tries to connect go Blynk sever.

Thank you very much for your help.


/*
 * This project is for a remote AC outlet switch that is made using 
 * an Arduino UNO-like microcontroller, and the Blynk IoT library.
 */

// these Blynk definitions ALWAYS have to be first
#define BLYNK_FIRMWARE_VERSION "XX"
#define BLYNK_TEMPLATE_ID "XXX"
#define BLYNK_DEVICE_NAME "Remote Control Switch"
#define BLYNK_AUTH_TOKEN "XXX"
#define BLYNK_PRINT Serial
#define BLYNK_NO_FANCY_LOGO

/* 
 * IMPORTANT: Configuration of Blynk Heartbeat Interval
 */
#define BLYNK_HEARTBEAT 300 

// define the SIM type
#define TINY_GSM_MODEM_SIM7000

// libraries
#include <SPI.h>
#include <BlynkSimpleTinyGSM.h>



#define SerialMon Serial

// pin definitions
#define TX 10 // Microcontroller RX
#define RX 11 // Microcontroller TX
#define RELAY_PIN1 12 // Relay Control Pin
// just one relay pin needed for now
// if you want to use the other one, just uncomment all the relevant lines
//#define RELAY_PIN2 XX

// initialize software serial
#include <SoftwareSerial.h>
SoftwareSerial SerialAT(TX, RX);


// configure GPRS settings (user and pass may be optional depending on your SIM card)
const char apn[] = "iot.1nce.net";
const char user[] = "";
const char pass[] = "";
// configure Blynk authentication token
const char auth[] = "XXX";
//BlynkTimer timer; // This is breaking the code!
//int timerID;

// initialize modem
TinyGsm modem(SerialAT);

// define global variables
bool startup = true;
float deviceUptime = 0;
bool relayStatus = false;
float relayStartTime = 0;



/*
 * called whenever a widget writes to virtual pin V1
 * it will switch the relay on and off, turn on the status LED, and push a notification
 * lastly it will call the updateDeviceTime() function which will update the uptime
 */
BLYNK_WRITE(V1) {
  // retrieve value from virtual pin V1
  uint8_t pinValue = param.asInt();
  if (pinValue != 0) {
    // turn on relay
    digitalWrite(RELAY_PIN1, LOW);
    //digitalWrite(RELAY_PIN2, LOW);
    digitalWrite(LED_BUILTIN, HIGH);
    SerialMon.println("RELAY ON");
    // turn on Blynk LED
    Blynk.virtualWrite(V4, 255);
    // push notification that the relay is ON
    Blynk.logEvent("relay", "The relay is ON");
    // update device uptime
    updateDeviceTime();
    // start relay uptime
    relayStatus = true;
    relayStartTime = deviceUptime;
  }
  else {
    // turn off relay
    digitalWrite(RELAY_PIN1, HIGH);
    //digitalWrite(RELAY_PIN2, HIGH);
    digitalWrite(LED_BUILTIN, LOW);
    SerialMon.println("RELAY OFF");
    // turn off Blynk LED
    Blynk.virtualWrite(V4, 0);
    // push notification that the relay is OFF
    Blynk.logEvent("relay", "The relay is OFF");
    // update device uptime
    updateDeviceTime();
    // stop relay uptime
    relayStatus = false;
  }
}


/*
 * called whenever a widget reads virtual pin V7
 * first it will update the device uptime then use it to calculate the relay uptime
 * after which it will then write it to virtual pin V6
 * if relay is not on, it will instead return a value of 0
 */
BLYNK_WRITE(V7) {
  uint8_t pinValue = param.asInt();
  if (pinValue != 0) {
    if (relayStatus) {
      updateDeviceTime();
      Blynk.virtualWrite(V6, deviceUptime - relayStartTime);
      SerialMon.print("Relay Uptime: ");
      SerialMon.println(deviceUptime - relayStartTime);
    }
  }
}


/*
 * called whenever a widget reads virtual pin V8 and returns signal quality to virtual pin V9
 */
BLYNK_WRITE(V8) {
  uint8_t pinValue = param.asInt();
  if (pinValue != 0) {
    String signalCond = "";
    String signalColor = "";
    uint8_t signalQuality = 0;
    signalQuality = modem.getSignalQuality();
    if (signalQuality == 99) {
      signalCond = "UNKNOWN";
      // grey color
      signalColor = "#A0A0A0";
    }
    // RSSI less than -93 dBm
    else if (signalQuality < 10) {
      signalCond = "MARGINAL";
      // red color
      signalColor = "#FF0000";
    }
    // RSSI greater than -95 dBm and less than -83 dBm
    else if (signalQuality > 9 && signalQuality < 15) {
      signalCond = "OK";
      // orange color
      signalColor = "#FF7B00";
    }
    // RSSI greater than -85 dBm and less than -73 dBm
    else if (signalQuality > 14 && signalQuality < 20) {
      signalCond = "GOOD";
      // light green color
      signalColor = "#6EFF00";
    }
    // RSSI greater than -75 dBm
    else if (signalQuality > 19) {
      signalCond = "EXCELLENT";
      // brighter darker green color
      signalColor = "#04D700";
    }
    Blynk.virtualWrite(V9, signalCond);
    Blynk.virtualWrite(V10, 255);
    Blynk.setProperty(V10, "color", signalColor);
    SerialMon.print("Signal Quality: ");
    SerialMon.print(signalQuality);
    SerialMon.print(" - ");
    SerialMon.println(signalCond);
  }
}


/*
 * updates the device uptime in hours, and
 * then pushes the uptime to virtual pin V5
 */
void updateDeviceTime() {
  deviceUptime = (((millis() / 1000) / 60) / 60);
  Blynk.virtualWrite(V5, deviceUptime);
}



/*
 * setup and initializes device on startup (or reboot) and will only run once
 */
void setup() {
  //timerID = timer.setInterval(5000L, sensorDataSend); //timer will run every sec
  // configure relay and ensure it is off
  pinMode(RELAY_PIN1, OUTPUT);
  digitalWrite(RELAY_PIN1, HIGH);
  // pinMode(RELAY_PIN2, OUTPUT);
  // digitalWrite(RELAY_PIN2, HIGH);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  
  // set console baud rate
  SerialMon.begin(9600);
  delay(1000);

  // set GSM module baud rate
  SerialAT.begin(9600);
  delay(6000);

  // initialize modem
  SerialMon.println("Initializing modem...");
  modem.restart();
  modem.waitForNetwork(300000L); // 30 second timeout

  // get modem info
  String modemInfo = modem.getModemInfo();
  SerialMon.print("Modem Info: ");
  SerialMon.println(modemInfo);

  // connect to Blynk
  Blynk.begin(auth, modem, apn, user, pass);

  // verify heartbeat is configured correctly
  SerialMon.print("Heartbeat (sec): ");
  SerialMon.println(BLYNK_HEARTBEAT);
}


/*
 * main function that loops during device operation
 * On startup, the device will sync the current value of the virtual pin V1 and V8 
 * which will call the write functions for those respective pins
 * this ensures that if the relay was ON when the device loses power, it will resume on reboot
 * it also makes the device check signal quality on startup
 */
void loop() {
 //timer.run();        // run timer every second
 if (startup) {
    // check relay status
    Blynk.syncVirtual(V1);
    // checks signal quality
    Blynk.syncVirtual(V8);
    startup = false;
    // sends push notification that device has rebooted
    Blynk.logEvent("reboot","The device has rebooted");
  }
  
  Blynk.run();
}

Removing this from your void loop, and instead using the BLYNK_CONNECTED() callback function for this purpose would be a good start.

I’d move this further down your void setup (after you have connected to GPRS and Blynk, and of course ensure that the sensorDataSend function is included in your code.

Once you have that working then you can use the timer to call a function to check your GPRS and Blynk connection and take the appropriate action.

Pete.

Thank you Pete,
We tried to use the timer function but this line is breaking the code - the device will never connect and just goes into endless loop trying to connect to Blynk server when we uncomment this line:

Also, we want to track when the device is rebooted which, per my understanding is different from BLYNK_CONNECTED() function. If the device is disconnected, for instance, the switch can still be ON or OFF. When the device reboots, without this function, the relay would be automatically turned OFF.
The purpose of that function is to ensure that if the state of the switch (ON/OFF) is restored if power is interrupted to the device (e.g. power outage).

Did you take my advice?

In that case, put the code in void setup, which executes once on reboot, but it will not send a message to Blynk inless that portion of the code goes in BLYNK_CONNECTED()
Either way, Blynk will not tolerate this code in the void loop without disconnections.

Pete.

This should be before all the code. It needs to be global

look at this information on timers. It is excelent

Timer FAQ

Hello Pete,

Yes, I have taken your advice to move the part of the code to void setup and put the blynk.run inside the blynk_connected() function and to shift code around where the timerID part of code would be down below after GPRS and Blynk is connected. I also re-read the FAQ suggested by Gyromike and ensured that BlynkTimer timer is declared on top.

The code works and I am not seeing any disconnects (after 24h of continuous time) yet.

However, I was never able to get the timer to work. As soon as the line of code BlynkTimer timer; is uncommented, the device never goes past this line of code:

Blynk.begin(auth, modem, apn, user, pass);

and I never enters the void main loop. I am not sure whether this has something unique to Botletics shield or what but that is where I am stuck.

I don’t understand this comment.

Posting your sketch would be helpful.

Maybe you should re-install the Blynk library, and the SimpleTimer library.
It would also be useful if you changed your IDE settings to show verbose compiler messages, and check the libraries used summary towards the end of the compilation message to see exactly which libraries are being used. It’s possible that you have something installed that is messing-up the BlynkTimer functionality.

Pete.

Here is how the Blynk.run() is now inside the Blynk.connected:

void loop() {
//timer.run(); // run timer every second
if(Blynk.connected()==true){
if (startup) {
// sends push notification that device has rebooted
Serial.println(“The device has rebooted”);
Blynk.logEvent(“reboot”,“The device has rebooted”);
startup = false;
}
Blynk.run();
}
else if(Blynk.connected()==false){
Serial.println(“Arduino will reboot after 3 seconds”);
delay(3000);
resetFunc(); //call reset
}
}

My understanding is that I did not need SimpleTimer library as the Blynk library already has this “built-in” so I never included it in the code. Was this wrong?

I ran the compiler verbose and here are the libraries used:

  • Using library SPI at version 1.0
  • Using library Blynk at version 1.0.1
  • Using library TinyGSM at version 0.11.3
  • Using library SoftwareSerial at version 1.0

You must read this
https://docs.blynk.io/en/legacy-platform/legacy-articles/keep-your-void-loop-clean

You appear to have confused the BLYNK_CONNECTED() callback function with the Blynk.connected() logical test.

Also, as I’m sure you can tell when you look at the code you’ve posted, it’s not displaying correctly in the forum.
That’s because you’ve use the blockquote facility when posting the code, rather than placing triple backticks on a separate line above and below your code.
Triple backticks look like this:
```
please use them when you post code in future.

I never suggested that you should include it in your code, and indeed you should not.
However, if it is installed on your computer then you need to check that it is installed correctly - hence my suggestion to re-install it - as the library files could be being used by the compiler and causing an issue if they are corrupted or badly installed.

Pete.

Hello,

Thank you everyone for your help. We have improved the code based on the suggestions; the code runs great now and there are no disconnects for several days in a row. We do think that fundamentally the disconnects were caused by poor signal quality; even though the CSQ was coming as “excellent” we think that there were periods of time (e.g. during rainy weather) where somehow the signal was degraded.

We installed a high-gain antenna and reoriented the device so it is facing unobstructed area and it seems to have resolved the issue. Alas, we never could get the timer function (nor the temp function which I think uses the timer) to work. We suspect that there is a conflict with the libraries that we use but we are novice Arduino users and I am not a programmer so this is above my pay grade :wink: