BLYNK
HOME       📲 GETTING STARTED       📗 DOCS       ❓HELP CENTER       👉 SKETCH BUILDER

TTGO T-Call ( ESP32 + SIM800l ) combine BlynkClient.ino & HttpClient.ino

Dear Sirs,
first of all I apologize for my ignorance but I am a totally newbie on the GPS/GPRS system. Well, I am trying to combine the functionality of BlynkClient.ino & HttpClient.ino both are standard examples of TinyGSM library. You can find them at: https://github.com/vshymanskyy/TinyGSM/tree/master/examples

My needs is to have Blynk running using GSM only network ( GPRS ) and to have the functionality to access a file that is on my web and to print the content of this file ( it will have / contains only a serial number ).

The big question is how can get this file while the Blynk is running ???

Thanks and Best Regards,
Mike Kranidis

Dear Mike,

Please have a look and test this code I merged and modified from the files you mentioned )(BlynkClient.ino & HttpClient.ino).

As I don’t have any GSM shield to test, I use the Ethernet W5100 shield to test the same code, just change from

//#define USE_ETHERNET_W5X00    false
#define USE_ETHERNET_W5X00    true

to

#define USE_ETHERNET_W5X00    false
//#define USE_ETHERNET_W5X00    true

to use GSM shield.

The full code is hereafter or at

/****************************************************************************************************************************
 * Blynk_HTTPClient_Ethernet_GSM.ino
 * For Mega boards using either W5X00 Ethernet shield or TinyGSM shield
 * 
 * Rewritten to merge HTTPClient.ino and BlynkClient.ino examples in
 * https://github.com/vshymanskyy/TinyGSM/tree/master/examples
 *  
 *  Built by Khoi Hoang https://github.com/khoih-prog
 * Licensed under MIT license
 * 
 * Original Blynk Library author:
 * @file       BlynkSimpleEsp8266.h
 * @author     Volodymyr Shymanskyy
 * @license    This project is released under the MIT License (MIT)
 * @copyright  Copyright (c) 2015 Volodymyr Shymanskyy
 * @date       Jan 2015
 * @brief
 * 
 * For this example, you need to install Blynk library:
 *   https://github.com/blynkkk/blynk-library/releases/latest
 *
 * TinyGSM Getting Started guide:
 *   https://tiny.cc/tinygsm-readme
 *
 *
 * Blynk is a platform with iOS and Android apps to control
 * Arduino, Raspberry Pi and the likes over the Internet.
 * You can easily build graphic interfaces for all your
 * projects by simply dragging and dropping widgets.
 *
 * Blynk supports many development boards with WiFi, Ethernet,
 * GSM, Bluetooth, BLE, USB/Serial connection methods.
 * See more in Blynk library examples and community forum.
 *
 *                http://www.blynk.io/
 *
 * Change GPRS apm, user, pass, and Blynk auth token to run :)
 *
 *****************************************************************************************************************************/

#if defined(ESP8266) || defined(ESP32)
#error This code is designed to run on Arduino AVR (Mega, Mega1280, Mega2560, Mega ADK) platform, not ESP8266 nor ESP32! Please check your Tools->Board setting.
#endif

#define BLYNK_PRINT Serial    // Comment this out to disable prints and save space

// Default heartbeat interval for GSM is 60
// If you want override this value, uncomment and set this option:
//#define BLYNK_HEARTBEAT 30

// Set serial for debug console (to the Serial Monitor, default speed 115200)
#define SerialMon Serial

//#define USE_BLYNK_WM      false
#define USE_BLYNK_WM      true

#if !USE_BLYNK_WM
  //#define USE_LOCAL_SERVER      true
  #define USE_LOCAL_SERVER      false
  
  #if USE_LOCAL_SERVER
    #define blynk_auth     "****"
    #define blynk_server   "****"
  #else
    #define blynk_auth     "****"
    #define blynk_server   "blynk-cloud.com"
  #endif
  
  #define BLYNK_HARDWARE_PORT       8080
#endif

//#define USE_ETHERNET_W5X00    false
#define USE_ETHERNET_W5X00    true

#if USE_ETHERNET_W5X00
  #include <SPI.h>
  #include <Ethernet.h>

  #if USE_BLYNK_WM
    // Start location in EEPROM to store config data. Default 0
    // Config data Size currently is 128 bytes)
    #define EEPROM_START     256
    
    #include <EthernetWebServer.h>
    #include <BlynkSimpleEthernet_WM.h>
  #else
    #include <BlynkSimpleEthernet.h>
  #endif

  // You can specify your board mac adress
  byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
  
  // Use this for static IP
  IPAddress ip      (192, 168, 2, 99);
  IPAddress dns     (192, 168, 2, 1);
  IPAddress gateway (192, 168, 2, 1);
  IPAddress subnet   (255, 255, 255, 0);
  
  // Ethernet shield and SDcard pins
  #define W5100_CS  10
  #define SDCARD_CS 4
  
  EthernetClient client;

#else
  // Select your modem:
  #define TINY_GSM_MODEM_SIM800
  // #define TINY_GSM_MODEM_SIM808
  // #define TINY_GSM_MODEM_SIM900
  // #define TINY_GSM_MODEM_UBLOX
  // #define TINY_GSM_MODEM_BG96
  // #define TINY_GSM_MODEM_A6
  // #define TINY_GSM_MODEM_A7
  // #define TINY_GSM_MODEM_M590
  // #define TINY_GSM_MODEM_ESP8266
  // #define TINY_GSM_MODEM_XBEE

  // Increase RX buffer if needed
  //#define TINY_GSM_RX_BUFFER 512

  #include <TinyGsmClient.h>
  //#include <BlynkSimpleSIM800.h>
  #include <BlynkSimpleTinyGSM.h>

  // Hardware Serial on Mega, Leonardo, Micro
  #define SerialAT Serial1

  // Your GPRS credentials
  // Leave empty, if missing user or pass
  #define apn     "YourAPN"
  #define user   "****"
  #define pass    "****"

  #define blynk_gsm_auth     "****"

  // Uncomment this if you want to see all AT commands
  //#define DUMP_AT_COMMANDS

  #ifdef DUMP_AT_COMMANDS
    #include <StreamDebugger.h>
    StreamDebugger debugger(SerialAT, SerialMon);
    TinyGsm modem(debugger);
  #else
    TinyGsm modem(SerialAT);
  #endif  

  TinyGsmClient client(modem);
#endif

#include <ArduinoHttpClient.h>

// Server details. Currently hardcoded.
#define http_server     "vsh.pp.ua"
#define resource        "/TinyGSM/logo.txt"

#define http_port       80

HttpClient http(client, http_server, http_port);

void setup()
{
  // Set console baud rate
  SerialMon.begin(115200);
  delay(10);


#if USE_ETHERNET_W5X00
  // Deselect the SD card
  pinMode(SDCARD_CS, OUTPUT);
  digitalWrite(SDCARD_CS, HIGH);
  
#else
  // Set GSM module baud rate
  SerialAT.begin(115200);
  delay(3000);

  // Restart takes quite some time
  // To skip it, call init() instead of restart()
  SerialMon.println("Initializing modem...");
  modem.restart();

  String modemInfo = modem.getModemInfo();
  SerialMon.print("Modem: ");
  SerialMon.println(modemInfo);

  // Unlock your SIM card with a PIN
  //modem.simUnlock("1234");
#endif

  #if USE_ETHERNET_W5X00
    #if USE_BLYNK_WM
      Blynk.begin();
    #else
      #if USE_LOCAL_SERVER
        Blynk.begin(blynk_auth, blynk_server, BLYNK_HARDWARE_PORT);
      #else
        Blynk.begin(blynk_auth);
      #endif
    #endif
  #else
    Blynk.begin(blynk_gsm_auth, modem, apn, user, pass);
  #endif

  if (Blynk.connected())
    SerialMon.println("Blynk connected");  
}

void HTTPClientHandle(void)
{
  #if !USE_ETHERNET_W5X00
  
    SerialMon.print(F("Waiting for network..."));
    if (!modem.waitForNetwork()) 
    {
      SerialMon.println(" fail");
      return;
    }
    
    SerialMon.println(" OK");  
    
    SerialMon.print(F("Connecting to "));
    SerialMon.print(apn);
    if (!modem.gprsConnect(apn, user, pass)) 
    {
      SerialMon.println(" fail");
      return;
    }
    SerialMon.println(" OK");
  #endif

  SerialMon.print(F("Performing HTTP GET request... "));
  int err = http.get(resource);
  if (err != 0) 
  {
    SerialMon.println(F("failed to connect"));
    return;
  }

  int status = http.responseStatusCode();
  SerialMon.println(status);
  if (!status) 
  {
    return;
  }

  while (http.headerAvailable()) 
  {
    String headerName = http.readHeaderName();
    String headerValue = http.readHeaderValue();
    //SerialMon.println(headerName + " : " + headerValue);
  }

  int length = http.contentLength();
  if (length >= 0) 
  {
    SerialMon.print(F("Content length is: "));
    SerialMon.println(length);
  }
  
  if (http.isResponseChunked()) 
  {
    SerialMon.println(F("The response is chunked"));
  }

  String body = http.responseBody();
  SerialMon.println(F("Response:"));
  SerialMon.println(body);

  SerialMon.print(F("Body length is: "));
  SerialMon.println(body.length());

  // Shutdown

  http.stop();
  SerialMon.println(F("Server disconnected"));

  #if !USE_ETHERNET_W5X00
    modem.gprsDisconnect();
    SerialMon.println(F("GPRS disconnected"));
  #endif
}

void check_status()
{
  static unsigned long checkstatus_timeout = 0;

#define STATUS_CHECK_INTERVAL     60000L

  // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change.
  if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0))
  {
    HTTPClientHandle();
    checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL;
  }
}

void loop()
{
  Blynk.run();
  check_status();
}


and this is the terminal output when running with W5100 Ethernet shield

[9] Getting IP...
[10] MAC: FE-D9-84-AB-FB-B8
EthernetClass: begin
EthernetClass: W5100 init OK
EthernetClass: beginWithDHCP, ret = 1
DHCP server found
[5697] IP:192.168.2.38
[5698] 
    ___  __          __
   / _ )/ /_ _____  / /__
  / _  / / // / _ \/  '_/
 /____/_/\_, /_//_/_/\_\
        /___/ v0.6.1 on Arduino Mega

[6003] Ready (ping: 18ms).
Blynk connected
Performing HTTP GET request... 200
Content length is: 121
Response:

  _____            _____  _____  _____
    |  | |\ | \_/ |  ___ |_____ |  |  |
    |  | | \|  |  |_____| _____||  |  |


Body length is: 121
Server disconnected
Performing HTTP GET request... 200
Content length is: 121
Response:

  _____            _____  _____  _____
    |  | |\ | \_/ |  ___ |_____ |  |  |
    |  | | \|  |  |_____| _____||  |  |

This is the code written for ESP32, using GSM shield for login into Blynk Cloud Server and doing HTTP GET.
As I don’t have GSM SIM800I shield to test, I just compile and leave it for you to test.

  • Note: The GSM shield will use ESP32’s Serial2 port (for ESP32, pin RXD2 = 16, pin TXD2 = 17)
/********************************************************************************************************************
 * ESP32_GSM_BlynkHTTPClient.ino
 * For ESP32 boards using TinyGSM shield
 * 
 * Rewritten to merge HTTPClient.ino and BlynkClient.ino examples in
 * https://github.com/vshymanskyy/TinyGSM/tree/master/examples
 *  
 * Built by Khoi Hoang https://github.com/khoih-prog
 * Licensed under MIT license
 * 
 * Original Blynk Library author:
 * @file       BlynkSimpleEsp8266.h
 * @author     Volodymyr Shymanskyy
 * @license    This project is released under the MIT License (MIT)
 * @copyright  Copyright (c) 2015 Volodymyr Shymanskyy
 * @date       Jan 2015
 * @brief
 * 
 * For this example, you need to install Blynk library:
 *   https://github.com/blynkkk/blynk-library/releases/latest
 *
 * TinyGSM Getting Started guide:
 *   https://tiny.cc/tinygsm-readme
 *
 *
 * Blynk is a platform with iOS and Android apps to control
 * Arduino, Raspberry Pi and the likes over the Internet.
 * You can easily build graphic interfaces for all your
 * projects by simply dragging and dropping widgets.
 *
 * Blynk supports many development boards with WiFi, Ethernet,
 * GSM, Bluetooth, BLE, USB/Serial connection methods.
 * See more in Blynk library examples and community forum.
 *
 *                http://www.blynk.io/
 *
 * Change GPRS apm, user, pass, and Blynk auth token to run :)
 * 
 * See https://github.com/G6EJD/ESP32-Using-Hardware-Serial-Ports/blob/master/ESP32_Using_Serial2.ino
 * 
 * There are three serial ports on the ESP32 known as U0UXD, U1UXD and U2UXD.
 * 
 * U0UXD is used to communicate with the ESP32 for programming and during reset/boot. Serial
 * U1UXD is unused and can be used for your projects. Some boards use this port for SPI Flash access though. Serial 1
 * U2UXD is unused and can be used for your projects. Serial2 (pin 16 and 17)
 *
 ********************************************************************************************************************/

#if !defined(ESP32)
#error This code is designed to run on ESP32 platform, not ESP8266 nor Arduino! Please check your Tools->Board setting.
#endif

#define BLYNK_PRINT Serial    // Comment this out to disable prints and save space

// Server details. Currently hardcoded.  You'll need to use a widget to dynamically input it
#define http_server     "vsh.pp.ua"
#define resource        "/TinyGSM/logo.txt"

#define http_port       80

// Default heartbeat interval for GSM is 60
// If you want override this value, uncomment and set this option:
//#define BLYNK_HEARTBEAT 30

// Set serial for debug console (to the Serial Monitor, default speed 115200)
#define SerialMon Serial

// Select your modem:
#define TINY_GSM_MODEM_SIM800
// #define TINY_GSM_MODEM_SIM808
// #define TINY_GSM_MODEM_SIM900
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
// #define TINY_GSM_MODEM_A7
// #define TINY_GSM_MODEM_M590
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_XBEE

// Increase RX buffer if needed
//#define TINY_GSM_RX_BUFFER 512

#include <TinyGsmClient.h>
#include <BlynkSimpleTinyGSM.h>
#include <ArduinoHttpClient.h>

#define RXD2      16
#define TXD2      17
// Use ESP32 Serial2 for GSM
#define SerialAT  Serial2

// Your GPRS credentials
// Leave empty, if missing user or pass
#define apn               "YourAPN"
#define user              "****"
#define pass              "****"

#define blynk_gsm_auth    "****"

// Uncomment this if you want to see all AT commands
//#define DUMP_AT_COMMANDS

#ifdef DUMP_AT_COMMANDS
  #include <StreamDebugger.h>
  StreamDebugger debugger(SerialAT, SerialMon);
  TinyGsm modem(debugger);
#else
  TinyGsm modem(SerialAT);
#endif  

TinyGsmClient client(modem);
HttpClient http(client, http_server, http_port);

void setup()
{
  // Set console baud rate
  SerialMon.begin(115200);
  //pinMode(LED_BUILTIN, OUTPUT);
  Serial.println("\nStarting ESP32_BlynkHTTPClient_GSM");

  // Set GSM module baud rate
  // Note the format for setting a serial port is as follows: Serial2.begin(baud-rate, protocol, RX pin, TX pin);
  //SerialAT.begin(115200, SERIAL_8N1, RXD2, TXD2);
  SerialAT.begin(115200);
  delay(3000);

  // Restart takes quite some time
  // To skip it, call init() instead of restart()
  SerialMon.println("Initializing modem...");
  modem.restart();

  String modemInfo = modem.getModemInfo();
  SerialMon.print("Modem: ");
  SerialMon.println(modemInfo);

  // Unlock your SIM card with a PIN
  //modem.simUnlock("1234");

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

  if (Blynk.connected())
    SerialMon.println("Blynk connected");  
}

void HTTPClientHandle(void)
{
  #if !USE_BUILTIN_WIFI
  
    SerialMon.print(F("Waiting for network..."));
    if (!modem.waitForNetwork()) 
    {
      SerialMon.println(" fail");
      return;
    }
    
    SerialMon.println(" OK");  
    
    SerialMon.print(F("Connecting to "));
    SerialMon.print(apn);
    if (!modem.gprsConnect(apn, user, pass)) 
    {
      SerialMon.println(" fail");
      return;
    }
    SerialMon.println(" OK");
  #endif

  SerialMon.print(F("Performing HTTP GET request... "));
  int err = http.get(resource);
  if (err != 0) 
  {
    SerialMon.println(F("failed to connect"));
    return;
  }

  int status = http.responseStatusCode();
  SerialMon.println(status);
  if (!status) 
  {
    return;
  }

  while (http.headerAvailable()) 
  {
    String headerName = http.readHeaderName();
    String headerValue = http.readHeaderValue();
    //SerialMon.println(headerName + " : " + headerValue);
  }

  int length = http.contentLength();
  if (length >= 0) 
  {
    SerialMon.print(F("Content length is: "));
    SerialMon.println(length);
  }
  
  if (http.isResponseChunked()) 
  {
    SerialMon.println(F("The response is chunked"));
  }

  String body = http.responseBody();
  SerialMon.println(F("Response:"));
  SerialMon.println(body);

  SerialMon.print(F("Body length is: "));
  SerialMon.println(body.length());

  // Shutdown

  http.stop();
  SerialMon.println(F("Server disconnected"));

  #if !USE_BUILTIN_WIFI
    modem.gprsDisconnect();
    SerialMon.println(F("GPRS disconnected"));
  #endif
}

void check_status()
{
  static unsigned long checkstatus_timeout = 0;

#define STATUS_CHECK_INTERVAL     60000L

  // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change.
  if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0))
  {
    HTTPClientHandle();
    checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL;
  }
}

void loop()
{
  Blynk.run();
  check_status();
}

Dear friend,
I will check the sketch and I will let you know.
Many thanks for your help, I appreciate it.

Thanks and Best Regards,
Mike Kranidis

Dear @khoih,
I modified your sketch in order to get tested using the real hardware ( TTGO T-Call ) that I have. Unfortunatelly, the bellow sketch can only be running as Blynk ONLY or as HTTP Client ONLY not both…
Please see it and let me know your thoughts about. Probably there is something fishy in the internal operation of the Blynk… I hope Mr. @vshymanskyy Can give his valuable help.
Thanks for your help anyway.

/// Change this from 1 to 0 to test the functionality between Blynk only and BLynk with HTTPClientHandle that does not work ... ///
#define BLYNK_ONLY 1

// TTGO T-Call pin definitions
#define MODEM_RST            5
#define MODEM_PWKEY          4
#define MODEM_POWER_ON       23
#define MODEM_TX             27
#define MODEM_RX             26
#define I2C_SDA              21
#define I2C_SCL              22

#define BLYNK_PRINT Serial    
#define BLYNK_HEARTBEAT 60
// Select your modem:
#define TINY_GSM_MODEM_SIM800
// Increase RX buffer if needed
#define TINY_GSM_RX_BUFFER 1024

#include <TinyGsmClient.h>
#include <BlynkSimpleTinyGSM.h>
#include <Wire.h>
#include <ArduinoHttpClient.h>

#define IP5306_ADDR          0x75
#define IP5306_REG_SYS_CTL0  0x00

bool setPowerBoostKeepOn(int en)
{
  Wire.beginTransmission(IP5306_ADDR);
  Wire.write(IP5306_REG_SYS_CTL0);
  if (en) {
    Wire.write(0x37); // Set bit1: 1 enable 0 disable boost keep on
  } else {
    Wire.write(0x35); // 0x37 is default reg value
  }
  return Wire.endTransmission() == 0;
}

// Increase RX buffer if needed
#define TINY_GSM_RX_BUFFER 1024
/// PUT YOUR BLYNK AUTH HERE ///
const char auth[] = "PUT-YOUR-BLYNK-AUTH-HERE";

// Uncomment this if you want to see all AT commands
//#define DUMP_AT_COMMANDS

// Set serial for debug console (to the Serial Monitor, default speed 115200)
#define SerialMon Serial

// Use Hardware Serial on Mega, Leonardo, Micro
#define SerialAT Serial1
// or Software Serial on Uno, Nano
//#include <SoftwareSerial.h>
//SoftwareSerial SerialAT(2, 3); // RX, TX

#define STATUS_CHECK_INTERVAL     60000L
unsigned long checkstatus_timeout = 0;
bool connectionStatus=false;

// Your GPRS credentials
// Leave empty, if missing user or pass
/// PUT YOUR GSM / GPRS RELATED ///
const char apn[]  = "PUT-YOUR-APN-HERE";
const char gprsUser[] = "";
const char gprsPass[] = "";

// Server details
const char server[] = "vsh.pp.ua";
const char resource[] = "/TinyGSM/logo.txt";
const int  port = 80;

int ReCnctFlag;  // Reconnection Flag
int ReCnctCount = 0;  // Reconnection counter

WidgetLED led1(V1);
BlynkTimer timer;

// V1 LED Widget is blinking
void blinkLedWidget()
{
  if (led1.getValue()) {
    led1.off();
    /// SerialMon.println("LED on V1: off");
  } else {
    led1.on();
    /// SerialMon.println("LED on V1: on");
  }
}

#ifdef DUMP_AT_COMMANDS
  #include <StreamDebugger.h>
  StreamDebugger debugger(SerialAT, SerialMon);
  TinyGsm modem(debugger);
#else
  TinyGsm modem(SerialAT);
#endif

TinyGsmClient client(modem);
HttpClient http(client, server, port);

void setup() {
  // Set console baud rate
  SerialMon.begin(115200);
  delay(10);

  Wire.begin(I2C_SDA, I2C_SCL);
  bool   isOk = setPowerBoostKeepOn(1);
  SerialMon.println(String("IP5306 KeepOn ") + (isOk ? "OK" : "FAIL"));

  // Set GSM module baud rate and UART pins
  SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX);
  delay(3000);
  
// Set-up modem reset, enable, power pins
  pinMode(MODEM_PWKEY, OUTPUT);
  pinMode(MODEM_RST, OUTPUT);
  pinMode(MODEM_POWER_ON, OUTPUT);

  digitalWrite(MODEM_PWKEY, LOW);
  digitalWrite(MODEM_RST, HIGH);
  digitalWrite(MODEM_POWER_ON, HIGH);
  // Set GSM module baud rate
  ///SerialAT.begin(115200);
  SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX);
  delay(3000);

  // Restart takes quite some time
  // To skip it, call init() instead of restart()
  SerialMon.println(F("Initializing modem..."));
  modem.restart();

  String modemInfo = modem.getModemInfo();
  SerialMon.print(F("Modem: "));
  SerialMon.println(modemInfo);

  // Unlock your SIM card with a PIN
  //modem.simUnlock("1234");

  SerialMon.print("Waiting for network...");
  if (!modem.waitForNetwork(240000L)) {
    SerialMon.println(" fail");
    delay(10000);
    return;
  }
  SerialMon.println(" OK");

  if (modem.isNetworkConnected()) {
    SerialMon.println("Network connected");
  }

  SerialMon.print(F("Connecting to APN: "));
  SerialMon.print(apn);
  if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
    SerialMon.println(" fail");
    delay(10000);
    return;
  }
  SerialMon.println(" OK");

  if(BLYNK_ONLY) {
    Blynk.begin(auth, modem, apn, gprsUser, gprsPass);
  }
  timer.setInterval(1000L, blinkLedWidget);
}

void HTTPClientHandle(void){
  if(!connectionStatus){
     SerialMon.print(F("One time connection. Waiting for network..."));
  if (!modem.waitForNetwork()) {
    SerialMon.println(" fail");
    connectionStatus=false;
    delay(3000);
    return;
  }
  SerialMon.println(" OK");

  SerialMon.print(F("Connecting to "));
  SerialMon.print(apn);
  if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
    SerialMon.println(" fail");
    connectionStatus=false;
    delay(3000);
    return;
  }
  SerialMon.println(" OK");
  connectionStatus=true;
}

  SerialMon.print(F("Performing HTTP GET request... "));
  int err = http.get(resource);
  if (err != 0) {
    SerialMon.println(F("failed to connect"));
    delay(5000);
    return;
  }

  int status = http.responseStatusCode();
  SerialMon.println(status);
  if (!status) {
    delay(5000);
    return;
  }
/* /// this is for informational reasons only not needed ///
  while (http.headerAvailable()) {
    String headerName = http.readHeaderName();
    String headerValue = http.readHeaderValue();
   //SerialMon.println(headerName + " : " + headerValue);
  }
*/
  int length = http.contentLength();
  if (length >= 0) {
    SerialMon.print(F("Content length is: "));
    SerialMon.println(length);
  }
  if (http.isResponseChunked()) {
    SerialMon.println(F("The response is chunked"));
  }

  String body = http.responseBody();
  SerialMon.println(F("Response:"));
  SerialMon.println(body);

  SerialMon.print(F("Body length is: "));
  SerialMon.println(body.length());

  // Shutdown

  http.stop();
  SerialMon.println(F("Server disconnected"));

  ///modem.gprsDisconnect(); /// leave GPRS connected ///
  ///SerialMon.println(F("GPRS disconnected"));
  SerialMon.println(F("GPRS is NOT disconnected"));
}

void check_status()
{
  ///static unsigned long checkstatus_timeout = 0;
  // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change.
  if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0))
  {
    if(!BLYNK_ONLY) {
    HTTPClientHandle();
    }
    checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL;
    ///SerialMon.printf("Now millis are:%d \nThe new checkstatus_timeout is: %d\n",millis(),checkstatus_timeout);
  }
}

void loop() {
  check_status();
  if(BLYNK_ONLY) {
  timer.run();
  if (Blynk.connected()) {  // If connected run as normal
    Blynk.run();
    } else if (ReCnctFlag == 0) {  // If NOT connected and not already trying to reconnect, set timer to try to reconnect in 30 seconds
      ReCnctFlag = 1;  // Set reconnection Flag
      SerialMon.printf("\nStarting reconnection timer in 30 seconds...\n");
      timer.setTimeout(30000L, []() {  // Lambda Reconnection Timer Function
      ReCnctFlag = 0;  // Reset reconnection Flag
      ReCnctCount++;  // Increment reconnection Counter
      SerialMon.printf("\nAttempting reconnection # %d\n",ReCnctCount);
      Blynk.connect();  // Try to reconnect to the server
      SerialMon.printf("\n re-initiate gprs connection... \n");
      modem.gprsConnect(apn, gprsUser, gprsPass);
      /// TinyGSM does not have this... ///Blynk.config(auth);
      SerialMon.printf("\n re-initiate Blynk connection... \n");
      Blynk.begin(auth, modem, apn, gprsUser, gprsPass);
      Blynk.connect();  /// Try to reconnect to the server for 30 seconds /// http://docs.blynk.cc/#blynk-firmware-connection-management
      });  // END Timer Function
    }
  }
}

Dear @mikekgr

Could you remove all the delay() and make loop() simple as in


void HTTPClientHandle(void) {
  if (!connectionStatus) {
    SerialMon.print(F("One time connection. Waiting for network..."));
    if (!modem.waitForNetwork()) {
      SerialMon.println(" fail");
      connectionStatus = false;
      //delay(3000);
      return;
    }
    SerialMon.println(" OK");

    SerialMon.print(F("Connecting to "));
    SerialMon.print(apn);
    if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
      SerialMon.println(" fail");
      connectionStatus = false;
      //delay(3000);
      return;
    }
    SerialMon.println(" OK");
    connectionStatus = true;
  }

  SerialMon.print(F("Performing HTTP GET request... "));
  int err = http.get(resource);
  if (err != 0) {
    SerialMon.println(F("failed to connect"));
    //delay(5000);
    return;
  }

  int status = http.responseStatusCode();
  SerialMon.println(status);
  if (!status) {
    //delay(5000);
    return;
  }
  /* /// this is for informational reasons only not needed ///
    while (http.headerAvailable()) {
      String headerName = http.readHeaderName();
      String headerValue = http.readHeaderValue();
     //SerialMon.println(headerName + " : " + headerValue);
    }
  */
  int length = http.contentLength();
  if (length >= 0) {
    SerialMon.print(F("Content length is: "));
    SerialMon.println(length);
  }
  if (http.isResponseChunked()) {
    SerialMon.println(F("The response is chunked"));
  }

  String body = http.responseBody();
  SerialMon.println(F("Response:"));
  SerialMon.println(body);

  SerialMon.print(F("Body length is: "));
  SerialMon.println(body.length());

  // Shutdown

  http.stop();
  SerialMon.println(F("Server disconnected"));

  ///modem.gprsDisconnect(); /// leave GPRS connected ///
  ///SerialMon.println(F("GPRS disconnected"));
  SerialMon.println(F("GPRS is NOT disconnected"));
}

void check_status()
{
  ///static unsigned long checkstatus_timeout = 0;
  // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change.
  if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0))
  {
    //if (!BLYNK_ONLY) {
      HTTPClientHandle();
    //}
    checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL;
    ///SerialMon.printf("Now millis are:%d \nThe new checkstatus_timeout is: %d\n",millis(),checkstatus_timeout);
  }
}

void loop() {
  check_status();
  Blynk.run();
  timer.run();
}

Then retest and post the error and terminal output as we have no way to know what’s wrong.
Regards,

Dear @khoih,
I will able to make the changes and to test them later and I will report back to you. I am not optimistic about the result though…

Thanks and Best Regards,
Mike Kranidis

Dear @khoih,
I change the sketch exactly as you wrote. In the serial monitor I got the following results:

Case A with #define BLYNK_ONLY 1 { The Blynk only, is working OK }

22:55:32.249 -> IP5306 KeepOn OK
22:55:38.252 -> Initializing modem...
22:55:43.396 -> Modem: SIM800 R14.18
22:55:43.396 -> Waiting for network... OK
22:55:52.536 -> Network connected
22:55:52.536 -> Connecting to APN: myq OK
22:56:00.129 -> [27883] 
22:56:00.129 ->     ___  __          __
22:56:00.129 ->    / _ )/ /_ _____  / /__
22:56:00.129 ->   / _  / / // / _ \/  '_/
22:56:00.129 ->  /____/_/\_, /_//_/_/\_\
22:56:00.129 ->         /___/ v0.6.1 on ESP32
22:56:00.129 -> 
22:56:00.129 -> [27885] Modem init...
22:56:00.223 -> [28008] Connecting to network...
22:56:00.223 -> [28018] Network: TIM
22:56:00.223 -> [28018] Connecting to myq ...
22:56:04.757 -> [32537] Connected to GPRS
22:56:04.757 -> [32548] Connecting to blynk-cloud.com:80
22:56:06.022 -> [33796] Ready (ping: 342ms).
The App LED is flashing normally...

Case B with #define BLYNK_ONLY 0 { The HttpClient only, is working OK }
23:18:09.498 -> IP5306 KeepOn OK
23:18:15.508 -> Initializing modem...
23:18:20.617 -> Modem: SIM800 R14.18
23:18:20.617 -> Waiting for network... OK
23:18:28.014 -> Network connected
23:18:28.014 -> Connecting to APN: myq OK
23:18:35.661 -> One time connection. Waiting for network... OK
23:18:35.661 -> Connecting to myq OK
23:18:39.584 -> Performing HTTP GET request... 200
23:18:43.006 -> Content length is: 121
23:18:43.006 -> Response:
23:18:43.006 -> 
23:18:43.006 ->   _____            _____  _____  _____
23:18:43.006 ->     |  | |\ | \_/ |  ___ |_____ |  |  |
23:18:43.006 ->     |  | | \|  |  |_____| _____||  |  |
23:18:43.006 -> 
23:18:43.006 -> 
23:18:43.006 -> Body length is: 121
23:18:43.006 -> Server disconnected
23:18:43.006 -> GPRS is NOT disconnected
23:19:43.007 -> Performing HTTP GET request... 200
23:19:46.007 -> Content length is: 121
23:19:46.007 -> Response:
23:19:46.007 -> 
23:19:46.007 ->   _____            _____  _____  _____
23:19:46.007 ->     |  | |\ | \_/ |  ___ |_____ |  |  |
23:19:46.007 ->     |  | | \|  |  |_____| _____||  |  |
23:19:46.007 -> 
23:19:46.007 -> 
23:19:46.007 -> Body length is: 121
23:19:46.007 -> Server disconnected
23:19:46.007 -> GPRS is NOT disconnected
... everything repeated normally

Case C all together ( Blynk + HttpClient ) NOT WORKING 

23:25:07.626 -> IP5306 KeepOn OK
23:25:13.614 -> Initializing modem...
23:25:18.770 -> Modem: SIM800 R14.18
23:25:18.770 -> Waiting for network... OK
23:25:26.146 -> Network connected
23:25:26.146 -> Connecting to APN: myq OK
23:25:33.666 -> [26062] 
23:25:33.666 ->     ___  __          __
23:25:33.666 ->    / _ )/ /_ _____  / /__
23:25:33.666 ->   / _  / / // / _ \/  '_/
23:25:33.666 ->  /____/_/\_, /_//_/_/\_\
23:25:33.666 ->         /___/ v0.6.1 on ESP32
23:25:33.666 -> 
23:25:33.666 -> [26064] Modem init...
23:25:33.760 -> [26186] Connecting to network...
23:25:33.806 -> [26196] Network: TIM
23:25:33.806 -> [26196] Connecting to myq ...
23:25:39.648 -> [32062] Connected to GPRS
23:25:39.648 -> [32073] Connecting to blynk-cloud.com:80
23:25:41.007 -> [33401] Ready (ping: 347ms).
23:25:41.101 -> One time connection. Waiting for network... OK
23:25:41.101 -> Connecting to myq OK
23:25:44.851 -> Performing HTTP GET request... -3
it is stalled in this message, nothing more

Dear @mikekgr,

It’s really difficult to make a blind debugging. So with the posted info, I suggest you try this mods:

  1. Don’t try to reconnect GSM unless it’s disconnected. Put this in setup() to set

connectionStatus = true;

to avoid reconnection in loop()

void setup() 
{
  ....
 SerialMon.print("Waiting for network...");
  if (!modem.waitForNetwork(240000L)) {
    SerialMon.println(" fail");
    delay(10000);
    return;
  }
  
  SerialMon.println(" OK");

  if (modem.isNetworkConnected()) {
    SerialMon.println("Network connected");
  }

  SerialMon.print(F("Connecting to APN: "));
  SerialMon.print(apn);
  if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
    SerialMon.println(" fail");
    delay(10000);
    return;
  }
  // Add this
  connectionStatus = true;
  
  SerialMon.println(" OK");

  if (BLYNK_ONLY) {
    Blynk.begin(auth, modem, apn, gprsUser, gprsPass);
  }
  timer.setInterval(1000L, blinkLedWidget);
}

  1. From the stalling, the return code is HTTP_ERROR_TIMED_OUT (-3), which means we spent too long time waiting for a reply (timeout is default 30s). But we still don’t know what it’s doing while stalling, unless we put more debugging msgs in the library code.

I suggest to use direct client access code to send HTTP GET request, in which we have more control by adding debug / longer timeout later in the debugging process

void HTTPClientHandle(void)
{
  if (!connectionStatus)
  {
    SerialMon.print(F("One time connection. Waiting for network..."));
    if (!modem.waitForNetwork())
    {
      SerialMon.println(F(" fail"));
      connectionStatus = false;
      //delay(3000);
      return;
    }
    SerialMon.println(F(" OK"));

    SerialMon.print(F("Connecting to "));
    SerialMon.print(apn);
    if (!modem.gprsConnect(apn, gprsUser, gprsPass))
    {
      SerialMon.println(F(" fail"));
      connectionStatus = false;
      //delay(3000);
      return;
    }
    SerialMon.println(F(" OK"));
    connectionStatus = true;
  }

   // Test client directly, see Arduino_TinyGSM.ino
   // Just added
   SerialMon.print(F("Connecting to "));
   SerialMon.print(server);
   if (!client.connect(server, port)) 
   {
     SerialMon.println(F(" fail"));
     //delay(10000);
     return;
   }
   SerialMon.println(" OK");
  
   // Make a HTTP GET request:
   SerialMon.println("Performing HTTP GET request...");
   client.print(String("GET ") + resource + " HTTP/1.1\r\n");
   client.print(String("Host: ") + server + "\r\n");
   client.print("Connection: close\r\n\r\n");
   client.println();
  
   unsigned long timeout = millis();
   // We can increase the timeout  to test, current 10s, and have more control here
   while (client.connected() && millis() - timeout < 10000L) 
   {
     // Print available data
     while (client.available()) 
     {
       char c = client.read();
       SerialMon.print(c);
       timeout = millis();
     }
   }
   SerialMon.println();
  
   // Shutdown
  
  client.stop();
      
  SerialMon.println(F("Server disconnected"));

  ///modem.gprsDisconnect(); /// leave GPRS connected ///
  ///SerialMon.println(F("GPRS disconnected"));
  SerialMon.println(F("GPRS is NOT disconnected"));
}

  1. Later, if you’d like to use the original HTTP code, you can increase the timeout to test if HTTP_ERROR_TIMED_OUT error can be avoid by using function

setHttpResponseTimeout(uint32_t timeout);

(timeout in ms, currently 30000L = 30s)

  1. I also recognized your GSM network speed is too slow, and certainly will have certain role in HTTP_ERROR_TIMED_OUT

23:25:41.007 -> [33401] Ready (ping: 347ms).

Here, they are shutting down the 2G network on which the SIM800L is relying on. I don’t know if there is enough time to buy and test that hardware. I guess in your place as well.

  1. Will you think to update to new generation such as 4G?

Would you still be interested to try some more and post results?

Dear @khoih,
thanks for your help and appreciate your efforts to help me.
I will try your suggestion and I will report back the results to you later on. Regarding the speed of my GPS/GPRS connection and the ping value, I think that is ok for GPRS and without the Bkynk, the file getting is performed just fine as you can see from my testing above. Also, when I use just Blynk, even complicated, all are running just fine…

P.S. I am glad to send you a free TTGO T-Call device to have for debugging testing and development. If you interested in please send me in PM your full address to buy and send you one, of course as a gift.

Thanks and Best Regards,
Mike Kranidis

1 Like

Dear @mikekgr

Thanks a lot for your offer, but I’ll not take it.

Don’t worry, I’m very financially healthy. I just ordered several SIM800L shields from Amazon and will order TTGO T-Call (ESP-32 + SIM800L) from AliExpress and Amazon (if found) just to satisfy my curiosity.

I’m always open to learn new things (sadly still zillions and zillions of them) or something I haven’t got chance to do. Whenever dealing with and solving problems, isn’t it exciting and worth to spend time and effort? I believe the harder the situation is, the more we’ll learn in the process.

From this experience, I’m currently writing / modifying / creating (done 80-90%) a new so-called BlynkGSM_Manager for ESP32/ESP8266 and MegaWiFiR3. Just hope to help in this situation.

The APN, GPRS_User, GPRS_PW, SIM PIN, Blynk Server / Port / token will be stored in SPIFFS / EEPROM to avoid being hardcoded. This is very similar to those previous Blynk_WM library using WiFi Config Portal (you can access by Cellphone / Computer / Tablet)

  1. If config data already stored in SPIFFS/ EEPROM and valid, init GSM modem, then connect to Blynk. If not connected to Blynk, go to 2)
  2. If no config data stored in SPIFFS/EEPROM or invalid, or to start config portal
    • init wifi,
    • start config portal,
    • let user to input GSM/Blynk credentials,
    • store in SPIFFS/EEPROM (for other boards such as Mega, just use EEPROM),
    • finally restart. Repeat step 1.
  3. The Blynk.begin() will not be blocking
  4. The Blynk.run() will take care of reconnecting, restarting, reconfig, etc. and will not be blocking to allow other critical tasks working normally (similar features to the Blynk_WM library).

If you have more ideas, please add.

Regards,
KH

1 Like

OK, I Understood.
Regarding the TTGO T-Call you can ordered from here, there is warehouse in the USA so you can get them realy fast:

Let me think about your new library and I will let you know if I think something good.

Best Regards.
Mike Kranidis

1 Like

Thanks. Just place order. Hope will get it within a week.
I hope I still can use the LTE/4G SIM, and the 2G/2+G network here in Canada still not shutdown yet.
They have the plan to shutdown it for many years, just recently extend and close sometime in 2020.

Don’t forget to ask/save also the GPRS_Pin ( the GSM SIM card’s pin if there is set }
B.R.

1 Like

OK, will add.
Is that to use to unlock SIM function if necessary?

simUnlock(const char *pin)

Sorry I’m new here.

Dear @khoih
I made the changes proposed in your message, I tested timeout 10000L & 20000L but no satisfactory result. See the serial log:

09:08:02.166 -> IP5306 KeepOn OK
09:08:08.171 -> Initializing modem...
09:08:13.330 -> Modem: SIM800 R14.18
09:08:13.330 -> Waiting for network... OK
09:08:21.206 -> Network connected
09:08:21.206 -> Connecting to APN: myq OK
09:08:29.663 -> [27517] 
09:08:29.663 ->     ___  __          __
09:08:29.663 ->    / _ )/ /_ _____  / /__
09:08:29.663 ->   / _  / / // / _ \/  '_/
09:08:29.663 ->  /____/_/\_, /_//_/_/\_\
09:08:29.663 ->         /___/ v0.6.1 on ESP32
09:08:29.663 -> 
09:08:29.663 -> [27519] Modem init...
09:08:29.808 -> [27641] Connecting to network...
09:08:29.808 -> [27651] Network: TIM
09:08:29.808 -> [27652] Connecting to myq ...
09:08:36.892 -> [34759] Connected to GPRS
09:08:36.927 -> [34770] Connecting to blynk-cloud.com:80
09:08:38.304 -> [36175] Ready (ping: 424ms).
09:08:38.398 -> Connecting to vsh.pp.ua OK
09:08:39.192 -> Performing HTTP GET request...
09:08:59.219 -> 
09:08:59.253 -> Server disconnected
09:08:59.253 -> GPRS is NOT disconnected

After the above last serial log { 09:08:59.253 -> GPRS is NOT disconnected } there is nothing more and from the beginning there is not Blynk operation ( LED flashing at Blynk App ).
It seems that something is not “compatible” when the BlynkSimpleTinyGSM.h and/or TinyGsmClient.h is used …

Thanks and Best Regards,
Mike Kranidis

From the test result, I guess the culprit is Blynk going to endless loop after trashing the HTTPdata.

This symptom is very similar to other boards (Mega, UNO, etc) using WiFi shields with AT commands.

They experience Packet too big, etc. issue, and sometimes go to random endless loop.

This is hard-to-catch and slippery issue, and I were in the middle of it some time ago. I might go back to look at it whenever I get the TTGO board.

In the mean time, I’ll try to finish the library for you to have a dry test.

1 Like

I think you have to do something like that:

  // Unlock your SIM card with a PIN
  modem.simUnlock("1234");

see also:

1 Like

Dear @mikekgr,

I think we can have 2 simple solutions like this

  1. Bypass Blynk for HTTP
  • create 2 instances of TinyGsm,
  • modem for Blynk and
  • direct_modem fo HTTP
    so that we can bypass Blynk and send HTTP-related commands directly, not via hungry BlynkProtocol.h because we don’t need Blynk for that HTTP purpose.

If not working, we might have to modify TinyGSM library a little bit to simultaneously support 2 instances.

The code is as follows

....
#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
// Creating Second instance direct_modem
TinyGsm direct_modem(SerialAT);
#endif

TinyGsmClient client(modem);

// Using Second instance direct_modem
TinyGsmClient direct_client(direct_modem);
HttpClient http(direct_client, server, port);

....

void HTTPClientHandle(void)
{
  if (!connectionStatus)
  {
    SerialMon.print(F("One time connection. Waiting for network..."));
    if (!modem.waitForNetwork())
    {
      SerialMon.println(F(" fail"));
      connectionStatus = false;
      //delay(3000);
      return;
    }
    SerialMon.println(F(" OK"));

    SerialMon.print(F("Connecting to "));
    SerialMon.print(apn);
    if (!modem.gprsConnect(apn, gprsUser, gprsPass))
    {
      SerialMon.println(F(" fail"));
      connectionStatus = false;
      //delay(3000);
      return;
    }
    SerialMon.println(F(" OK"));
    connectionStatus = true;
  }
  #if 1     
    // Test client directly, using direct_modem, bypassing Blynk, see Arduino_TinyGSM.ino
   // Using Second instance direct_modem

    SerialMon.print(F("Connecting to "));
    SerialMon.print(server);
    if (!direct_client.connect(server, port)) 
    {
      SerialMon.println(F(" fail"));
      //delay(10000);
      return;
    }
    SerialMon.println(" OK");
  
      // Make a HTTP GET request:
    SerialMon.println("Performing HTTP GET request...");
    direct_client.print(String("GET ") + resource + " HTTP/1.1\r\n");
    direct_client.print(String("Host: ") + server + "\r\n");
    direct_client.print("Connection: close\r\n\r\n");
    direct_client.println();
  
    unsigned long timeout = millis();
    while (direct_client.connected() && millis() - timeout < 10000L) 
    {
      // Print available data
      while (direct_client.available()) 
      {
        char c = direct_client.read();
        SerialMon.print(c);
        timeout = millis();
      }
    }
    SerialMon.println();
  
    // Shutdown
  
    direct_client.stop();
    
  #else
   // Using Second instance direct_modem

    SerialMon.print(F("Performing HTTP GET request... "));
    int err = http.get(resource);
    if (err != 0)
    {
      SerialMon.println(F("failed to connect"));
      //delay(5000);
      return;
    }
  
    int status = http.responseStatusCode();
    SerialMon.println(status);
    if (!status)
    {
      //delay(5000);
      return;
    }
    /* /// this is for informational reasons only not needed ///
      while (http.headerAvailable()) {
        String headerName = http.readHeaderName();
        String headerValue = http.readHeaderValue();
       //SerialMon.println(headerName + " : " + headerValue);
      }
    */
    int length = http.contentLength();
    if (length >= 0)
    {
      SerialMon.print(F("Content length is: "));
      SerialMon.println(length);
    }
    if (http.isResponseChunked())
    {
      SerialMon.println(F("The response is chunked"));
    }
  
    String body = http.responseBody();
    SerialMon.println(F("Response:"));
    SerialMon.println(body);
  
    SerialMon.print(F("Body length is: "));
    SerialMon.println(body.length());
  
    // Shutdown
  
    http.stop();
  #endif
  
  SerialMon.println(F("Server disconnected"));

  ///modem.gprsDisconnect(); /// leave GPRS connected ///
  ///SerialMon.println(F("GPRS disconnected"));
  SerialMon.println(F("GPRS is NOT disconnected"));
}

void check_status()
{
  ///static unsigned long checkstatus_timeout = 0;
  // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change.
  if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0))
  {
    HTTPClientHandle();
    checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL;
    ///SerialMon.printf("Now millis are:%d \nThe new checkstatus_timeout is: %d\n",millis(),checkstatus_timeout);
  }
}

void loop() {
  check_status();
  Blynk.run();
  timer.run();
}

  1. Use Blynk Webhook for HTTP

Another way to try is to use Blynk WebHook (with just one instance of modem)

Dear @khoih,
you are a non stop fantastic supporter and I would like to thank you one more time!

When at home, I will test the first proposed solution and I will let you know.
The second one, the WebHook is a working way, I have test it but I prefer general solutions not tightly bounded to Blynk…

Best Regards,
Mike Kranidis

1 Like