LilyGO-T-SIM7000G , Blynk Connectivity in Australia and battery use

Hi! I have a LilyGO SIM7000G here in Australia, and it’s working quite well, actually. The only issue is that on the Blynk platform, I notice it has frequent reconnections. Sometimes it can go 6 hours without reconnecting, and other times it reconnects every minute…

So, I have two questions:
-To save battery and use Blynk efficiently (Sleep mode or something), is there a way to do this effectively?
-Regarding the reconnections, is it normal for it to be like this, or is it possibly trying to connect to an incorrect band? It’s connected to LTE in Australia, but I have almost all bands enabled for the country and haven’t selected any particular one.
Thanks!

The most likely cause of your disconnections is poor signal strength. Adding an appropriate external antenna is a good first step.

Yes, using deep sleep will save battery life, but obviously there can be no Blynk control of your device, or any data transfer while it’s asleep.

Pete.

Do you have any example of good sleep practices using blynk and Lilygo 7000g?

TBH, it doesn’t matter whether you’re using a GSM or WiFi device, the deep sleep process is very similar. When the device wakes from deep sleep it executes the sketch as though it booted after a reset.

I how you structure your code for deep sleep depends on what you’re wanting to achieve during the wake period.

Pete.

I’ve tried it and it’s working with the sleep function, thanks! I have another question. I’m sending data to blynk with virtualwrite function, 5 data in total, but sometimes it just sends the first one, sometimes sends everything, how can I solve this?

Blynk.virtualWrite(V0, PH);
    Blynk.virtualWrite(V1, EC);
    Blynk.virtualWrite(V2, TEMP);
    int rssi = modem.getSignalQuality();
    int signalPercentage = 0;
    if (rssi == 99 || rssi < 0 || rssi > 31) rssi = 0; // Not known, not detectable, or invalid value
    signalPercentage = map(rssi, 0, 31, 0, 100);
    Blynk.virtualWrite(V3, signalPercentage);

    int battV = (adc.analogRead(BATT_CH_ADC) * 3300) / 1023.0;
    float battPercentage = map(battV, 1600, 2100, 0, 100);
    Blynk.virtualWrite(V4, battPercentage);

If i use Blynk.beginGroup(), Blynk.endGroup() would it make any difference? thanks

It very much depends on how you’ve structured the rest of your sketch. Generally, deep sleep sketches need to be structured very differently to regular ones, but as I said before, it depends what you wnat to do during the wake periods.

Pete.

But it always had that problem of sometimes sendins only V0, and not the others, even before I write the sleep function.
Here is my code, it’s a bit messy but it’s working, the only problem is that, sometimes it only sends V0 to blynk, so basically what is does is there is a sequence of reading sensors, when that finishes sends the data to blynks and goes to sleep for 15 minutes, and then start over again

#define BLYNK_TEMPLATE_ID "TMPL6HKt-Gbrm"
#define BLYNK_TEMPLATE_NAME "Test"
#define BLYNK_AUTH_TOKEN "nA-QIT6CDdAIN-lAKT1IXZWU04TJN0Ee"
#define TINY_GSM_MODEM_SIM7000

#include <TinyGsmClient.h>
#include <BlynkSimpleTinyGSM.h>
#include <Arduino.h>
#include <Wire.h>
#include <MCP3XXX.h>

MCP3008 adc;

char auth[] = BLYNK_AUTH_TOKEN;
char apn[] = "telstra.internet";
char user[] = "";
char pass[] = "";

#define NUM_SAMPLES 32  // Número de muestras para promediar
#define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 900      /* ESP32 should sleep for 900 seconds (15 minutes) */
RTC_DATA_ATTR int bootCount = 0;

#define RES2 820.0
#define ECREF 200.0
#define GDIFF (30/1.8)
#define VR0  0.223
#define G0  2
#define I  (1.24 / 10000)
#define SerialAT Serial1
#define UART_BAUD   115200
#define PIN_DTR     25
#define PIN_TX      27
#define PIN_RX      26
#define PWR_PIN     4
#define MODEM_RST   5
#define LED_PIN     12   

#define PH_PIN 0
#define EC_PIN 21
#define TEMP_PIN 22

#define PH_CH_ADC 0
#define EC_CH_ADC 1
#define TEMP_CH_ADC 2
#define BATT_CH_ADC 3

#define STABILIZATION_TIME 90000  // Stabilization time in milliseconds
#define SENSOR_OFF_TIME 60000     // Time to turn off sensors before measurements
#define MEASUREMENT_DELAY 60000   // Delay between measurements

TinyGsm modem(SerialAT);

enum SensorState {
    IDLE,
    PREPARE_SENSOR,
    READ_SENSOR,
    DEACTIVATE_SENSORS,
    WAIT_FOR_NEXT_MEASUREMENT
};

SensorState sensorState = IDLE;
uint8_t currentSensorPin = PH_PIN;
int currentChannel = PH_CH_ADC;
unsigned long lastActivationTime = 0;
unsigned long lastMeasurementTime = 0;
unsigned long lastSampleTime = 0;
unsigned long lastConnectionAttempt = 0; 
float PH = 0, TEMP = 0, EC = 0;
int PH_count = 0, TEMP_count = 0, EC_count = 0;
float PH_sum = 0, TEMP_sum = 0, EC_sum = 0;
float voltage = 0;
int sampleCount = 0;
float voltageSum = 0;
bool sensorsRead = false;  // Flag to indicate if all sensors have been read

void setup() {
    Serial.begin(115200);
    adc.begin();
    pinMode(PH_PIN, OUTPUT);
    pinMode(EC_PIN, OUTPUT);
    pinMode(TEMP_PIN, OUTPUT);
    digitalWrite(PH_PIN, LOW);
    digitalWrite(EC_PIN, LOW);
    digitalWrite(TEMP_PIN, LOW);
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, LOW);
    modem_on();

    SerialAT.begin(UART_BAUD, SERIAL_8N1, PIN_RX, PIN_TX);
    if (!modem.restart()) {
        Serial.println("Failed to restart modem, attempting to continue without restarting");
    }
    String name = modem.getModemName();
      Serial.println("Modem Name: " + name);

      modem.disableGPS();
  modem.sendAT("+SGPIO=0,4,1,0");
  if (modem.waitResponse(10000L) != 1) {
    DBG(" SGPIO=0,4,1,0 false ");
  }
  
    Blynk.begin(auth, modem, apn, user, pass);

    ++bootCount;
    Serial.println("Boot number: " + String(bootCount));

    esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
    Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) + " Seconds");

    print_wakeup_reason();
}

void print_wakeup_reason() {
  esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
  switch (wakeup_reason) {
    case ESP_SLEEP_WAKEUP_EXT0:
      Serial.println("Wakeup caused by external signal using RTC_IO");
      break;
    case ESP_SLEEP_WAKEUP_EXT1:
      Serial.println("Wakeup caused by external signal using RTC_CNTL");
      break;
    case ESP_SLEEP_WAKEUP_TIMER:
      Serial.println("Wakeup caused by timer");
      break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD:
      Serial.println("Wakeup caused by touchpad");
      break;
    case ESP_SLEEP_WAKEUP_ULP:
      Serial.println("Wakeup caused by ULP program");
      break;
    default:
      Serial.printf("Wakeup was not caused by deep sleep: %d\n", wakeup_reason);
      break;
  }
}

void deactivateAllSensors() {
    digitalWrite(PH_PIN, LOW);
    digitalWrite(EC_PIN, LOW);
    digitalWrite(TEMP_PIN, LOW);
    sensorState = DEACTIVATE_SENSORS;
    lastActivationTime = millis();
}

void activateSensor(uint8_t pin) {
    digitalWrite(pin, HIGH);
    lastActivationTime = millis();
    sensorState = PREPARE_SENSOR;
}

void deactivateSensor(uint8_t pin) {
    digitalWrite(pin, LOW);
}

void startVoltageReading() {
    sampleCount = 0;
    voltageSum = 0;
    lastSampleTime = millis();
}

bool readVoltage() {
    if (sampleCount < NUM_SAMPLES) {
        if (millis() - lastSampleTime >= 1000) { 
            voltageSum += (adc.analogRead(currentChannel) * 3300) / 1023.0;
            sampleCount++;
            lastSampleTime = millis();
        }
        return false;
    } else {
        voltage = voltageSum / NUM_SAMPLES;
        return true;
    }
}

void sendSensor() {
    if (PH_count > 0) {
        PH = PH_sum / PH_count;
        PH_sum = 0;
        PH_count = 0;
    }
    if (EC_count > 0) {
        EC = EC_sum / EC_count;
        EC_sum = 0;
        EC_count = 0;
    }
    if (TEMP_count > 0) {
        TEMP = TEMP_sum / TEMP_count;
        TEMP_sum = 0;
        TEMP_count = 0;
    }

    Blynk.virtualWrite(V0, PH);
    Blynk.virtualWrite(V1, EC);
    Blynk.virtualWrite(V2, TEMP);
    int rssi = modem.getSignalQuality();
    int signalPercentage = 0;
    if (rssi == 99 || rssi < 0 || rssi > 31) rssi = 0; // Not known, not detectable, or invalid value
    signalPercentage = map(rssi, 0, 31, 0, 100);
    Blynk.virtualWrite(V3, signalPercentage);

    int battV = (adc.analogRead(BATT_CH_ADC) * 3300) / 1023.0;
    float battPercentage = map(battV, 1600, 2100, 0, 100);
    Blynk.virtualWrite(V4, battPercentage);

    Serial.print("Signal %: "); Serial.println(signalPercentage);
    Serial.print(" PH : "); Serial.println(PH);
    Serial.print(" Temp : "); Serial.println(TEMP);
    Serial.print(" EC: "); Serial.println(EC);
}

void shutdown() {
  modem_off();
  delay(1000);
  Serial.flush();
  Serial.println("Going to sleep now");
  esp_deep_sleep_start();
}

void modem_off() {
  Serial.println("Turning modem off");
  modem.poweroff();
}

void modem_on()
{
  // Set-up modem  power pin
  pinMode(PWR_PIN, OUTPUT);
  digitalWrite(PWR_PIN, HIGH);
  delay(10);
  digitalWrite(PWR_PIN, LOW);
  delay(1010); //Ton 1sec
  digitalWrite(PWR_PIN, HIGH);

  //wait_till_ready();
  Serial.println("Waiting till modem ready...");
  delay(10000); //Ton uart 4.5sec but seems to need ~7sec after hard (button) reset
               //On soft-reset serial replies immediately.
}
void loop() {
    Blynk.run();

    if (!Blynk.connected()) {
        Serial.println("Attempting to reconnect to Blynk...");
        if (millis() - lastConnectionAttempt >= 300000) {  // Verifica si ha pasado más de cinco minutos
            modem_reset();
            lastConnectionAttempt = millis();  // Actualiza el tiempo del último intento de conexión
        }
        Blynk.connect();
    } else {
        lastConnectionAttempt = millis();  // Reinicia el contador si la conexión es exitosa
    }

    switch (sensorState) {
        case IDLE:
            deactivateAllSensors();
            break;
        case DEACTIVATE_SENSORS:
            if (millis() - lastActivationTime >= SENSOR_OFF_TIME) {
                activateSensor(currentSensorPin);
            }
            break;
        case PREPARE_SENSOR:
            if (millis() - lastActivationTime >= STABILIZATION_TIME) {
                startVoltageReading();
                sensorState = READ_SENSOR;
            }
            break;
        case READ_SENSOR:
            if (readVoltage()) {
                // Process sensor reading based on the current sensor
                switch (currentSensorPin) {
                    case PH_PIN:
                        PH = calculatePH(voltage);
                        PH_sum += PH;
                        PH_count++;
                        break;
                    case EC_PIN:
                        EC = calculateEC(voltage, TEMP);  // Assume TEMP is known or previously measured
                        EC_sum += EC;
                        EC_count++;
                        break;
                    case TEMP_PIN:
                        TEMP = calculateTEMP(voltage);
                        TEMP_sum += TEMP;
                        TEMP_count++;
                        break;
                }
                deactivateSensor(currentSensorPin);
                lastMeasurementTime = millis();
                sensorState = WAIT_FOR_NEXT_MEASUREMENT;
            }
            break;
        case WAIT_FOR_NEXT_MEASUREMENT:
            if (millis() - lastMeasurementTime >= MEASUREMENT_DELAY) {
                // Cycle to the next sensor
                if (currentSensorPin == PH_PIN) {
                    currentSensorPin = EC_PIN;
                    currentChannel = EC_CH_ADC;
                } else if (currentSensorPin == EC_PIN) {
                    currentSensorPin = TEMP_PIN;
                    currentChannel = TEMP_CH_ADC;
                } else {
                    currentSensorPin = PH_PIN;
                    currentChannel = PH_CH_ADC;
                    sensorsRead = true;  // All sensors have been read
                }
                sensorState = IDLE;
            }
            break;
    }

    // Update Blynk if all sensors have been read
    if (sensorsRead) {
        sendSensor();
        sensorsRead = false;
        delay(1000); // Ensure the data is sent before going to sleep
        shutdown(); // Call shutdown to turn off the modem and put the ESP32 to sleep
    }
}

float calculatePH(float voltage) {
    float slope = (7.00 - 4.0) / ((1500 - 1500) / 3.0 - (2010 - 1500) / 3.0);
    float intercept = 7.00 - slope * ((1500 - 1500) / 3.0);
    return slope * (voltage - 1500) / 3.0 + intercept;
}

float calculateTEMP(float voltage) {
    float Rpt1000 = ((voltage / 1000) / GDIFF + VR0) / I / G0;
    return (Rpt1000 - 1000) / 3.1;
}

float calculateEC(float voltage, float temperature) {
    float ecvalueRaw = 100000 * voltage / RES2 / ECREF * 1.3;
    float tempCompensationFactor = (temperature == 25.0) ? 1.0 : (1.0 + 0.01 * (temperature - 25.0));
    return ecvalueRaw * tempCompensationFactor;
}

void modem_reset() {
  Serial.println("\nModem hardware reset\n");
  pinMode(MODEM_RST, OUTPUT);
  digitalWrite(MODEM_RST, LOW);
  delay(260); // Treset 252ms
  digitalWrite(MODEM_RST, HIGH);
  delay(15000); // Modem takes longer to get ready and reply after this kind of reset vs power on
  SerialAT.begin(UART_BAUD, SERIAL_8N1, PIN_RX, PIN_TX);
  if (!modem.restart()) {
      Serial.println("Failed to restart modem, attempting to continue without restarting");
  }
  String name = modem.getModemName();
  Serial.println("Modem Name: " + name);

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

You should always avoid using Blynk.begin() with a deep sleep system, because it’s a blocking command.
If it can’t establish a connection to the Blynk server then all code execution will halt at that point, and your device will never sleep, causing the battery to be exhausted.

Also, I’m not sure how long you expect the device to be awake, but Blynk.connected() will always report true until the heartbeat period has expired, even if the device is no longer connected, so I would have thought that having this code in your void loop would be a waste of time.

Data not sending to Blynk is caused by not executing Blynk.run() enough times before sleeping. Doing this…

serves no purpose, as delay() simply blocks all code execution for the delay period. You’d be far better throwing a few Blynk.run()s in there.

Pete.