OTA firmware update in ESP32 using A7670C GSM module

Hello community,
I want to do OTA firmware update in ESP32- WROOM-32E using A7670c GSM Module.
I want use cellular network for this.
I used GitHub as a server (API) for get the bin file.
I want to know how to this? anyone can give the idea about it .

use the Update library

can you please give me a link

it is part of the esp32 platform. see the Examples menu in the IDE

hello @Juraj
I have one doubt I want to use cellular network how will hhtpClient work because it is working with Wi-Fi ?

I didn't recommend HttpClient. just the Update library

ok thank you!!!

I have doubt that firstly I want to do the GET request to github.com to get bin file from it
from my private repo …how to do this with gsmA7670c. I don't get the idea about it
I read the AT commands I can't understand how to do this....
I did the ota with WIFI network using httpclient.h and httpupdate.h
how to do this with gsm and save this bin file to spiffs of esp32 .
OTA PROBLEM with esp32
here is the link .... of gsm with WiFi

so you do the download of the update file over GSM on esp32 the same way as any other file on any other MCU. so don't limit your search.
maybe TinyGSM library can help.

#define TINY_GSM_MODEM_SIM7600

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

// Set serial for AT commands (to the module)
// Use Hardware Serial on Mega, Leonardo, Micro
#define SerialAT Serial1

// See all AT commands, if wanted
// #define DUMP_AT_COMMANDS

// Define the serial console for debug prints, if needed
#define TINY_GSM_DEBUG SerialMon

/*
 * Tests enabled
 */
#define TINY_GSM_TEST_GPRS          true
#define TINY_GSM_TEST_TCP           true
// powerdown modem after tests
#define TINY_GSM_POWERDOWN          true

// set GSM PIN, if any
#define GSM_PIN             ""

// Your GPRS credentials, if any
const char apn[] = "jionet"; // Your operator APN
const char gprsUser[] = "";
const char gprsPass[] = "";

// Server details to test TCP/SSL
const char server[] = "api.github.com";
const char resource[] = "/repos/Nazneenashrafi/ota/contents/version.txt";
const int port = 443;

#include <TinyGsmClient.h>
#include <ArduinoHttpClient.h>
#include "SSLClient.h"
#include "utilities.h"
#include "certs.h"

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

// HTTPS Transport
TinyGsmClient base_client(modem, 0);
SSLClient secure_layer(&base_client);
HttpClient client = HttpClient( secure_layer, server, port);

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

    // Set GSM module baud rate
    SerialAT.begin(UART_BAUD, SERIAL_8N1, MODEM_RX, MODEM_TX);

    /*
    The indicator light of the board can be controlled
    */
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, HIGH);

    /*
    MODEM_PWRKEY IO:4 The power-on signal of the modulator must be given to it,
    otherwise the modulator will not reply when the command is sent
    */
    pinMode(MODEM_PWRKEY, OUTPUT);
    digitalWrite(MODEM_PWRKEY, HIGH);
    delay(300); //Need delay
    digitalWrite(MODEM_PWRKEY, LOW);

    /*
    MODEM_FLIGHT IO:25 Modulator flight mode control,
    need to enable modulator, this pin must be set to high
    */
    pinMode(MODEM_FLIGHT, OUTPUT);
    digitalWrite(MODEM_FLIGHT, HIGH);

    //Add CA Certificate
     secure_layer.setCACert(root_ca);
}

void light_sleep(uint32_t sec )
{
    esp_sleep_enable_timer_wakeup(sec * 1000000ULL);
    esp_light_sleep_start();
}

void loop()
{
    bool res ;

    // Restart takes quite some time
    // To skip it, call init() instead of restart()
    DBG("Initializing modem...");
    if (!modem.init()) {
      if (!modem.restart()) {
        DBG("Failed to restart modem, delaying 10s and retrying");
        // restart autobaud in case GSM just rebooted
        return;
      }
    }

#if TINY_GSM_TEST_GPRS
    /*  Preferred mode selection : AT+CNMP
          2 – Automatic
          13 – GSM Only
          14 – WCDMA Only
          38 – LTE Only
          59 – TDS-CDMA Only
          9 – CDMA Only
          10 – EVDO Only
          19 – GSM+WCDMA Only
          22 – CDMA+EVDO Only
          48 – Any but LTE
          60 – GSM+TDSCDMA Only
          63 – GSM+WCDMA+TDSCDMA Only
          67 – CDMA+EVDO+GSM+WCDMA+TDSCDMA Only
          39 – GSM+WCDMA+LTE Only
          51 – GSM+LTE Only
          54 – WCDMA+LTE Only
      */
    String ret;
    do {
        ret = modem.setNetworkMode(54);
        delay(500);
    } while (!ret);

    String name = modem.getModemName();
    DBG("Modem Name:", name);

    String modemInfo = modem.getModemInfo();
    DBG("Modem Info:", modemInfo);

    // Unlock your SIM card with a PIN if needed
    if (GSM_PIN && modem.getSimStatus() != 3) {
        modem.simUnlock(GSM_PIN);
    }

    DBG("Waiting for network...");
    if (!modem.waitForNetwork(600000L)) {
        light_sleep(10);
        return;
    }

    if (modem.isNetworkConnected()) {
        DBG("Network connected");
    }
#endif


#if TINY_GSM_TEST_GPRS
    DBG("Connecting to", apn);
    if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
        light_sleep(10);
        return;
    }

    res = modem.isGprsConnected();
    DBG("GPRS status:", res ? "connected" : "not connected");

    String ccid = modem.getSimCCID();
    DBG("CCID:", ccid);

    String imei = modem.getIMEI();
    DBG("IMEI:", imei);

    String imsi = modem.getIMSI();
    DBG("IMSI:", imsi);

    String cop = modem.getOperator();
    DBG("Operator:", cop);

      /*  Network mode : AT+CNSMOD
        0 – no service
        1 - GSM
        2 - GPRS
        3 - EGPRS (EDGE)
        4 - WCDMA
        5 - HSDPA only(WCDMA)
        6 - HSUPA only(WCDMA)
        7 - HSPA (HSDPA and HSUPA, WCDMA)
        8 - LTE
        9 - TDS-CDMA
        10 - TDS-HSDPA only
        11 - TDS- HSUPA only
        12 - TDS- HSPA (HSDPA and HSUPA)
        13 - CDMA
        14 - EVDO
        15 - HYBRID (CDMA and EVDO)
        16 - 1XLTE(CDMA and LTE)
        23 - eHRPD
        24 - HYBRID(CDMA and eHRPD)
    */
    modem.sendAT(GF("+CNSMOD?"));
    if (modem.waitResponse(GF(GSM_NL "+CNSMOD:")) != 1) { }
    int nmodec = modem.stream.readStringUntil(',').toInt() != 0;
    int nmode = modem.stream.readStringUntil('\n').toInt();
    modem.waitResponse();
    DBG("Network Mode:", nmode);

    IPAddress local = modem.localIP();
    DBG("Local IP:", local);

    int csq = modem.getSignalQuality();
    DBG("Signal quality:", csq);
#endif

#if TINY_GSM_TEST_TCP && defined TINY_GSM_MODEM_HAS_TCP
    if (!modem.isGprsConnected()) {
        DBG("... not connected");
    } else {
        DBG("Connecting to ", server);
        // Make a HTTPS GET request:
        Serial.println("Making GET request securely...");
        // client.println( "application/vnd.github.v3.raw");
        //  client.println( "Bearer ghp_YEAXh4LLE19hdZqCUwZEWsaKn6HUbs0YHHN");
  client.beginRequest();
  client.sendHeader("Accept","application/vnd.github.object+json");
  client.sendHeader("Authorization","Bearer ghpXXXXXXXXXXXXXw81UZ08x");
  client.sendHeader("X-GitHub-Api-Version","2022-11-28");
  client.get(resource);
  client.endRequest();
 
        int status_code = client.responseStatusCode();
        String response = client.responseBody();
        
        Serial.print("Status code: ");
        Serial.println(status_code);
        Serial.print("Response: ");
        Serial.println(response);

        client.stop();        
    }
#endif

#if TINY_GSM_TEST_GPRS
    modem.gprsDisconnect();
    light_sleep(5);
    if (!modem.isGprsConnected()) {
        DBG("GPRS disconnected");
    } else {
        DBG("GPRS disconnect: Failed.");
    }
#endif

#if TINY_GSM_POWERDOWN
    // Try to power-off (modem may decide to restart automatically)
    // To turn off modem completely, please use Reset/Enable pins
    modem.poweroff();
    DBG("Poweroff.");
#endif

    SerialMon.printf("End of tests. Enable deep sleep , Will wake up in %d seconds", TIME_TO_SLEEP);

    //Wait moden power off
    light_sleep(5);

    esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
    delay(200);
    esp_deep_sleep_start();

    while (1);
}

output on serial monitor


[1223] Signal quality: 28

[1236] Connecting to api.github.com

Making GET request securely...

[5330] ### Closed: 0

Status code: 404

Response: {"message":"Not Found","documentation_url":"REST API endpoints for repository contents - GitHub Docs"}

[11685] ### Unhandled: +NETOPEN: 0

OK

[11685] GPRS disconnected

[11688] Poweroff.


I check on curl everything working fine


help me!!!!!!!

anyone can help?

this is a code __________________________________________________________________________________

#include <ArduinoHttpClient.h>
 #define TINY_GSM_MODEM_SIM7600

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


#include <HardwareSerial.h>
HardwareSerial SerialAT(2);  // RX, TX


#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 1024
#endif

#define TINY_GSM_DEBUG SerialMon
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false

// set GSM PIN, if any
#define GSM_PIN ""

// Your GPRS credentials, if any
const char apn[]   = "jionet";
const char gprsUser[] = "";
const char gprsPass[] = "";

#include <TinyGsmClient.h>
#include <CRC32.h>
#include <ArduinoHttpClient.h>
#include "SSLClient.h"

#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS false
#define TINY_GSM_USE_WIFI true
#endif
#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
#endif

const char server[] = "api.github.com";
const char resource[] = "/repos/Nazneenashrafi/ota/contents/sketch_may21b.ino.bin";
const int  port= 443;

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

TinyGsmClient base_client(modem, 0);
SSLClient secure_layer(&base_client);
HttpClient client = HttpClient( secure_layer, server, port);

const char * root_ca = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIEojCCBEigAwIBAgIRAIvcD/9Udy+q0XMnPyM2Kq8wCgYIKoZIzj0EAwIwgY8x\n"
"CzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNV\n"
"BAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDE3MDUGA1UEAxMu\n"
"U2VjdGlnbyBFQ0MgRG9tYWluIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTAe\n"
"Fw0yNDAzMDcwMDAwMDBaFw0yNTAzMDcyMzU5NTlaMBcxFTATBgNVBAMMDCouZ2l0\n"
"aHViLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHADGElEmwEKQTOjCTeZ\n"
"EQ+YFacbykIKQ+I0OI2NQqjTnlj+3zpJ/j8XYiau+kL+Wz5r97U8Q+qZYaDQ2A6I\n"
"bzKjggL6MIIC9jAfBgNVHSMEGDAWgBT2hQo7EYbhBH0Oqgss0u7MZHt7rjAdBgNV\n"
"HQ4EFgQULNWfMkiYavm5W71lUenpddcgsZYwDgYDVR0PAQH/BAQDAgeAMAwGA1Ud\n"
"EwEB/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEkGA1UdIARC\n"
"MEAwNAYLKwYBBAGyMQECAgcwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdv\n"
"LmNvbS9DUFMwCAYGZ4EMAQIBMIGEBggrBgEFBQcBAQR4MHYwTwYIKwYBBQUHMAKG\n"
"Q2h0dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb0VDQ0RvbWFpblZhbGlkYXRp\n"
"b25TZWN1cmVTZXJ2ZXJDQS5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNl\n"
"Y3RpZ28uY29tMIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdQDPEVbu1S58r/OH\n"
"W9lpLpvpGnFnSrAX7KwB0lt3zsw7CAAAAY4WRSJBAAAEAwBGMEQCIEn9RPTj/mWa\n"
"DNJYWLd5adwMh7lQLd0H4U687S0Vgw+IAiAWGZTh1I6KUlgK6RI2mF1Q+xxZvOIg\n"
"8FYfjyZYiosoewB3AKLjCuRF772tm3447Udnd1PXgluElNcrXhssxLlQpEfnAAAB\n"
"jhZFIbkAAAQDAEgwRgIhALcwnByZOMSyk9PPja2cXaA5/xwHG3n9zTUeVnErWwcS\n"
"AiEA5+cybN+U4jFHP/uWSLhtrysqmW+SUJnFxS2HNgjr25QAdgBOdaMnXJoQwzhb\n"
"bNTfP1LrHfDgjhuNacCx+mSxYpo53wAAAY4WRSG4AAAEAwBHMEUCIQD630D/OUY5\n"
"I0wDRYh6rvYhIaFYSHArqdPkP7vtRZa4EgIgOVY+ffcboNA/3MVjV4HbPJcFPntP\n"
"uh2wvdD4mlcFFkEwIwYDVR0RBBwwGoIMKi5naXRodWIuY29tggpnaXRodWIuY29t\n"
"MAoGCCqGSM49BAMCA0gAMEUCIQC4ntULeA2fj3hbPy2Ho1aUiJLU/qZe568a8//X\n"
"5PNAOAIgbK6eh8zyKr7uYc1/1qRMeDrvekcpOGOFnOHd3aOXVqc=\n"
"-----END CERTIFICATE-----\n"
"-----BEGIN CERTIFICATE-----\n" \
"MIIDqDCCAy6gAwIBAgIRAPNkTmtuAFAjfglGvXvh9R0wCgYIKoZIzj0EAwMwgYgx\n"
"CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtKZXJz\n"
"ZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYDVQQD\n"
"EyVVU0VSVHJ1c3QgRUNDIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE4MTEw\n"
"MjAwMDAwMFoXDTMwMTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAkdCMRswGQYDVQQI\n"
"ExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoT\n"
"D1NlY3RpZ28gTGltaXRlZDE3MDUGA1UEAxMuU2VjdGlnbyBFQ0MgRG9tYWluIFZh\n"
"bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEH\n"
"A0IABHkYk8qfbZ5sVwAjBTcLXw9YWsTef1Wj6R7W2SUKiKAgSh16TwUwimNJE4xk\n"
"IQeV/To14UrOkPAY9z2vaKb71EijggFuMIIBajAfBgNVHSMEGDAWgBQ64QmG1M8Z\n"
"wpZ2dEl23OA1xmNjmjAdBgNVHQ4EFgQU9oUKOxGG4QR9DqoLLNLuzGR7e64wDgYD\n"
"VR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYB\n"
"BQUHAwEGCCsGAQUFBwMCMBsGA1UdIAQUMBIwBgYEVR0gADAIBgZngQwBAgEwUAYD\n"
"VR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVz\n"
"dEVDQ0NlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMHYGCCsGAQUFBwEBBGowaDA/\n"
"BggrBgEFBQcwAoYzaHR0cDovL2NydC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdEVD\n"
"Q0FkZFRydXN0Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1\n"
"c3QuY29tMAoGCCqGSM49BAMDA2gAMGUCMEvnx3FcsVwJbZpCYF9z6fDWJtS1UVRs\n"
"cS0chWBNKPFNpvDKdrdKRe+oAkr2jU+ubgIxAODheSr2XhcA7oz9HmedGdMhlrd9\n"
"4ToKFbZl+/OnFFzqnvOhcjHvClECEQcKmc8fmA==\n"
"-----END CERTIFICATE-----\n"
"-----BEGIN CERTIFICATE-----\n" \
"MIID0zCCArugAwIBAgIQVmcdBOpPmUxvEIFHWdJ1lDANBgkqhkiG9w0BAQwFADB7\n"
"MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD\n"
"VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE\n"
"AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTE5MDMxMjAwMDAwMFoXDTI4\n"
"MTIzMTIzNTk1OVowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5\n"
"MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBO\n"
"ZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgRUNDIENlcnRpZmljYXRpb24gQXV0\n"
"aG9yaXR5MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEGqxUWqn5aCPnetUkb1PGWthL\n"
"q8bVttHmc3Gu3ZzWDGH926CJA7gFFOxXzu5dP+Ihs8731Ip54KODfi2X0GHE8Znc\n"
"JZFjq38wo7Rw4sehM5zzvy5cU7Ffs30yf4o043l5o4HyMIHvMB8GA1UdIwQYMBaA\n"
"FKARCiM+lvEH7OKvKe+CpX/QMKS0MB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1\n"
"xmNjmjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zARBgNVHSAECjAI\n"
"MAYGBFUdIAAwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5j\n"
"b20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEEKDAmMCQG\n"
"CCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQEM\n"
"BQADggEBABns652JLCALBIAdGN5CmXKZFjK9Dpx1WywV4ilAbe7/ctvbq5AfjJXy\n"
"ij0IckKJUAfiORVsAYfZFhr1wHUrxeZWEQff2Ji8fJ8ZOd+LygBkc7xGEJuTI42+\n"
"FsMuCIKchjN0djsoTI0DQoWz4rIjQtUfenVqGtF8qmchxDM6OW1TyaLtYiKou+JV\n"
"bJlsQ2uRl9EMC5MCHdK8aXdJ5htN978UeAOwproLtOGFfy/cQjutdAFI3tZs4RmY\n"
"CV4Ks2dH/hzg1cEo70qLRDEmBDeNiXQ2Lu+lIg+DdEmSx/cQwgwp+7e9un/jX9Wf\n\n"
"8qn0dNW44bOwgeThpWOjzOoEeJBuv/c=\n"
"-----END CERTIFICATE-----\n";

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

  SerialMon.println("Wait...");

  // Set GSM module baud rate
  SerialAT.begin(115200,SERIAL_8N1,16,17);
  delay(6000);

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

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

#if TINY_GSM_USE_GPRS
  // Unlock your SIM card with a PIN if needed
  if (GSM_PIN && modem.getSimStatus() != 3) { modem.simUnlock(GSM_PIN); }
#endif
secure_layer.setCACert(root_ca);
}

void printPercent(uint32_t readLength, uint32_t contentLength) {
  // If we know the total length
  if (contentLength != (uint32_t)-1) {
    SerialMon.print("\r ");
    SerialMon.print((100.0 * readLength) / contentLength);
    SerialMon.print('%');
  } else {
    SerialMon.println(readLength);
  }

}

void loop() {
#if TINY_GSM_USE_WIFI
  // Wifi connection parameters must be set before waiting for the network
  SerialMon.print(F("Setting SSID/password..."));
  if (!modem.networkConnect(wifiSSID, wifiPass)) {
    SerialMon.println(" fail");
    delay(10000);
    return;
  }
  SerialMon.println(" success");
#endif

#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_XBEE
  // The XBee must run the gprsConnect function BEFORE waiting for network!
  modem.gprsConnect(apn, gprsUser, gprsPass);
#endif

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

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

#if TINY_GSM_USE_GPRS
  // GPRS connection parameters are usually set after network registration
  SerialMon.print(F("Connecting to "));
  SerialMon.print(apn);
  if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
    SerialMon.println(" fail");
    delay(10000);
    return;
  }
  SerialMon.println(" success");

  if (modem.isGprsConnected()) { SerialMon.println("GPRS connected"); }
#endif

  SerialMon.print(F("Connecting to "));
  SerialMon.println(" success");
 client.beginRequest();
    //  client.sendHeader("Url"," HTTP/1.0");
  client.get(resource);
  client.sendHeader("User-Agent","tiny-gsm-module");
  client.sendHeader("Accept","application/vnd.github.v3.raw");
  client.sendHeader("Authorization","Bearer ghp_778xXOGCw81UZ08x");
  client.sendHeader("X-GitHub-Api-Version","2022-11-28");
  client.endRequest(); 

        int status_code = client.responseStatusCode();
        
        
        Serial.print("Status code: ");
        Serial.println(status_code);
     
  SerialMon.println(F("Waiting for response header"));
  // Let's see what the entire elapsed time is, from after we send the request.
  uint32_t timeElapsed = millis();
  // While we are still looking for the end of the header (i.e. empty line
  // FOLLOWED by a newline), continue to read data into the buffer, parsing each
  // line (data FOLLOWED by a newline). If it takes too long to get data from
  // the client, we need to exit.

  const uint32_t clientReadTimeout   = 5000;
  uint32_t       clientReadStartTime = millis();
  String         headerBuffer;
  bool           finishedHeader = false;
  uint32_t       contentLength  = 0;

  while (!finishedHeader) {
    int nlPos;

    if (client.available()) {
      clientReadStartTime = millis();
      while (client.available()) {
        char c = client.read();
        headerBuffer += c;
   
        // Let's exit and process if we find a new line
        if (headerBuffer.indexOf(F("\r\n")) >= 0) break;
      }
    } else {
      if (millis() - clientReadStartTime > clientReadTimeout) {
        // Time-out waiting for data from client
        SerialMon.println(F(">>> Client Timeout !"));
        break;
      }
    }

    // See if we have a new line.
    nlPos = headerBuffer.indexOf(F("\r\n"));

    if (nlPos > 0) {
      headerBuffer.toLowerCase();
      // Check if line contains content-length
      if (headerBuffer.startsWith(F("content-length:"))) {
        contentLength =
            headerBuffer.substring(headerBuffer.indexOf(':') + 1).toInt();
        // SerialMon.print(F("Got Content Length: "));  // uncomment for
        // SerialMon.println(contentLength);            // confirmation
      }

      headerBuffer.remove(0, nlPos + 2);  // remove the line
    } else if (nlPos == 0) {
      // if the new line is empty (i.e. "\r\n" is at the beginning of the line),
      // we are done with the header.
      finishedHeader = true;
    }
  }

  // The two cases which are not managed properly are as follows:
  // 1. The client doesn't provide data quickly enough to keep up with this
  // loop.
  // 2. If the client data is segmented in the middle of the 'Content-Length: '
  // header,
  //    then that header may be missed/damaged.
  //

  uint32_t readLength = 0;
  CRC32    crc;

  if (finishedHeader) {
    SerialMon.println(F("Reading response data"));
    clientReadStartTime = millis();

    printPercent(readLength, contentLength);
    while (readLength < contentLength && client.connected() &&
           millis() - clientReadStartTime < clientReadTimeout) {
      while (client.available()) {
        uint8_t c = client.read();
        // SerialMon.print(reinterpret_cast<char>c);  // Uncomment this to show
        // data
        crc.update(c);
        readLength++;
        if (readLength % (contentLength / 13) == 0) {
          printPercent(readLength, contentLength);
        }
        clientReadStartTime = millis();
      }
    }
    printPercent(readLength, contentLength);
  }

  timeElapsed = millis() - timeElapsed;
  SerialMon.println();

  // Shutdown

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

#if TINY_GSM_USE_WIFI
  modem.networkDisconnect();
  SerialMon.println(F("WiFi disconnected"));
#endif
#if TINY_GSM_USE_GPRS
  modem.gprsDisconnect();
  SerialMon.println(F("GPRS disconnected"));
#endif

  float duration = float(timeElapsed) / 1000;

  SerialMon.println();
  SerialMon.print("Content-Length: ");
  SerialMon.println(contentLength);
  SerialMon.print("Actually read:  ");
  SerialMon.println(readLength);
  SerialMon.print("Calc. CRC32:    0x");
  SerialMon.println(headerBuffer);
  SerialMon.print("Duration:       ");
  SerialMon.print(duration);
  SerialMon.println("s");

  // Do nothing forevermore
  while (true) { delay(1000); }
}

this is a output
Wait...

Initializing modem...

[11051] ### TinyGSM Version: 0.11.7

[11051] ### TinyGSM Compiled Module: TinyGsmClientSIM7600

[13661] ### Modem:

[13661] ### Modem:

Modem Info: Manufacturer: INCORPORATED Model: A7670C-LASL Revision: A7670M7_V1.11.1 IMEI: 866651062719516 +GCAP: +CGSM,+FCLASS,+DS

Waiting for network... success

Network connected

Connecting to jionet success

GPRS connected

Connecting to success
Status code: 200

Waiting for response header

Reading response data

0.00%[26045] ### Unhandled: PB DONE

7.69%
15.38%
23.08%
30.77%
38.46%[66720] ### Closed: 0

44.62%
Server disconnected
GPRS disconnected

Content-Length: 922928
Actually read: 411824
Calc. CRC32: 0x95F9C35A

Duration: 48.13s


so I get the bin file file but how to update the firmware by update.h library and I don't get
complete file
… please help anyone have any suggestion or solution

@Juraj
@J-M-L
@horace

I'm on my iPhone so can't really look at the code

there are tutorials showing how to download a bin and install it, like this one

did you look into that?

yes I did this …..successfully with Wi-Fi
but I want to use gsm A7670C module cellular network for this I tried
AT command for https get request
AT command for SSL+HTTPs
In both method I didn't succeed

  1. TinyGSM+ [SSLClient]+(GitHub - OPEnSLab-OSU/SSLClient: 🔒Add SSL/TLS functionality to any Arduino library)+ ArduinoHttpClient for https request i have problem as I have mentioned above recent post. how to read the bin file and update the firmware because these function are not available in
    these library
    httpUpdate.updateSpiffs(client, link) that use getStreamptr() function that available in HTTPClient library .

so your issue is not directly related to OTA but more how to download a file from GitHub using your gsm A7670C module cellular using AT commands.

I don't have that module ...

yeah!!!!!!!!!!

I have successfully download the file from GitHub but I got error to update the firmware...

#include <ArduinoHttpClient.h>
 #define TINY_GSM_MODEM_SIM7600

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

// Set serial for AT commands (to the module)
// Use Hardware Serial on Mega, Leonardo, Micro
#ifndef __AVR_ATmega328P__
#define SerialAT Serial1

// or Software Serial on Uno, Nano
#else
#include <HardwareSerial.h>
HardwareSerial SerialAT(2);  // RX, TX
#endif

#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 1024
#endif

#define TINY_GSM_DEBUG SerialMon
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false

// set GSM PIN, if any
#define GSM_PIN ""

// Your GPRS credentials, if any
const char apn[]      = "jionet";
const char gprsUser[] = "";
const char gprsPass[] = "";

// Server details

const int  port= 443;

#include <TinyGsmClient.h>
#include <CRC32.h>
#include <ArduinoHttpClient.h>
#include "SSLClient.h"
#include <SPIFFS.h>
#include <FS.h>
#include "FS.h"
#include "SPIFFS.h"
#include<Update.h>

// Just in case someone defined the wrong thing..
#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS false
#define TINY_GSM_USE_WIFI true
#endif
#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
#endif
const char server[] = "api.github.com";
const char resource[] = "/repos/Nazneenashrafi/ota/contents/sketch_may21b.ino.bin";

#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm        modem(debugger);
#else
TinyGsm        modem(SerialAT);
#endif
TinyGsmClient base_client(modem, 0);
SSLClient secure_layer(&base_client);
HttpClient client = HttpClient( secure_layer, server, port);
const char * root_ca = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIEojCCBEigAwIBAgIRAIvcD/9Udy+q0XMnPyM2Kq8wCgYIKoZIzj0EAwIwgY8x\n"
"CzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNV\n"
"BAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDE3MDUGA1UEAxMu\n"
"U2VjdGlnbyBFQ0MgRG9tYWluIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTAe\n"
"Fw0yNDAzMDcwMDAwMDBaFw0yNTAzMDcyMzU5NTlaMBcxFTATBgNVBAMMDCouZ2l0\n"
"aHViLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHADGElEmwEKQTOjCTeZ\n"
"EQ+YFacbykIKQ+I0OI2NQqjTnlj+3zpJ/j8XYiau+kL+Wz5r97U8Q+qZYaDQ2A6I\n"
"bzKjggL6MIIC9jAfBgNVHSMEGDAWgBT2hQo7EYbhBH0Oqgss0u7MZHt7rjAdBgNV\n"
"HQ4EFgQULNWfMkiYavm5W71lUenpddcgsZYwDgYDVR0PAQH/BAQDAgeAMAwGA1Ud\n"
"EwEB/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEkGA1UdIARC\n"
"MEAwNAYLKwYBBAGyMQECAgcwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdv\n"
"LmNvbS9DUFMwCAYGZ4EMAQIBMIGEBggrBgEFBQcBAQR4MHYwTwYIKwYBBQUHMAKG\n"
"Q2h0dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb0VDQ0RvbWFpblZhbGlkYXRp\n"
"b25TZWN1cmVTZXJ2ZXJDQS5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNl\n"
"Y3RpZ28uY29tMIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdQDPEVbu1S58r/OH\n"
"W9lpLpvpGnFnSrAX7KwB0lt3zsw7CAAAAY4WRSJBAAAEAwBGMEQCIEn9RPTj/mWa\n"
"DNJYWLd5adwMh7lQLd0H4U687S0Vgw+IAiAWGZTh1I6KUlgK6RI2mF1Q+xxZvOIg\n"
"8FYfjyZYiosoewB3AKLjCuRF772tm3447Udnd1PXgluElNcrXhssxLlQpEfnAAAB\n"
"jhZFIbkAAAQDAEgwRgIhALcwnByZOMSyk9PPja2cXaA5/xwHG3n9zTUeVnErWwcS\n"
"AiEA5+cybN+U4jFHP/uWSLhtrysqmW+SUJnFxS2HNgjr25QAdgBOdaMnXJoQwzhb\n"
"bNTfP1LrHfDgjhuNacCx+mSxYpo53wAAAY4WRSG4AAAEAwBHMEUCIQD630D/OUY5\n"
"I0wDRYh6rvYhIaFYSHArqdPkP7vtRZa4EgIgOVY+ffcboNA/3MVjV4HbPJcFPntP\n"
"uh2wvdD4mlcFFkEwIwYDVR0RBBwwGoIMKi5naXRodWIuY29tggpnaXRodWIuY29t\n"
"MAoGCCqGSM49BAMCA0gAMEUCIQC4ntULeA2fj3hbPy2Ho1aUiJLU/qZe568a8//X\n"
"5PNAOAIgbK6eh8zyKr7uYc1/1qRMeDrvekcpOGOFnOHd3aOXVqc=\n"
"-----END CERTIFICATE-----\n"
"-----BEGIN CERTIFICATE-----\n" \
"MIIDqDCCAy6gAwIBAgIRAPNkTmtuAFAjfglGvXvh9R0wCgYIKoZIzj0EAwMwgYgx\n"
"CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtKZXJz\n"
"ZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYDVQQD\n"
"EyVVU0VSVHJ1c3QgRUNDIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE4MTEw\n"
"MjAwMDAwMFoXDTMwMTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAkdCMRswGQYDVQQI\n"
"ExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoT\n"
"D1NlY3RpZ28gTGltaXRlZDE3MDUGA1UEAxMuU2VjdGlnbyBFQ0MgRG9tYWluIFZh\n"
"bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEH\n"
"A0IABHkYk8qfbZ5sVwAjBTcLXw9YWsTef1Wj6R7W2SUKiKAgSh16TwUwimNJE4xk\n"
"IQeV/To14UrOkPAY9z2vaKb71EijggFuMIIBajAfBgNVHSMEGDAWgBQ64QmG1M8Z\n"
"wpZ2dEl23OA1xmNjmjAdBgNVHQ4EFgQU9oUKOxGG4QR9DqoLLNLuzGR7e64wDgYD\n"
"VR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYB\n"
"BQUHAwEGCCsGAQUFBwMCMBsGA1UdIAQUMBIwBgYEVR0gADAIBgZngQwBAgEwUAYD\n"
"VR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVz\n"
"dEVDQ0NlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMHYGCCsGAQUFBwEBBGowaDA/\n"
"BggrBgEFBQcwAoYzaHR0cDovL2NydC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdEVD\n"
"Q0FkZFRydXN0Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1\n"
"c3QuY29tMAoGCCqGSM49BAMDA2gAMGUCMEvnx3FcsVwJbZpCYF9z6fDWJtS1UVRs\n"
"cS0chWBNKPFNpvDKdrdKRe+oAkr2jU+ubgIxAODheSr2XhcA7oz9HmedGdMhlrd9\n"
"4ToKFbZl+/OnFFzqnvOhcjHvClECEQcKmc8fmA==\n"
"-----END CERTIFICATE-----\n"
"-----BEGIN CERTIFICATE-----\n" \
"MIID0zCCArugAwIBAgIQVmcdBOpPmUxvEIFHWdJ1lDANBgkqhkiG9w0BAQwFADB7\n"
"MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD\n"
"VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE\n"
"AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTE5MDMxMjAwMDAwMFoXDTI4\n"
"MTIzMTIzNTk1OVowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5\n"
"MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBO\n"
"ZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgRUNDIENlcnRpZmljYXRpb24gQXV0\n"
"aG9yaXR5MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEGqxUWqn5aCPnetUkb1PGWthL\n"
"q8bVttHmc3Gu3ZzWDGH926CJA7gFFOxXzu5dP+Ihs8731Ip54KODfi2X0GHE8Znc\n"
"JZFjq38wo7Rw4sehM5zzvy5cU7Ffs30yf4o043l5o4HyMIHvMB8GA1UdIwQYMBaA\n"
"FKARCiM+lvEH7OKvKe+CpX/QMKS0MB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1\n"
"xmNjmjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zARBgNVHSAECjAI\n"
"MAYGBFUdIAAwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5j\n"
"b20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEEKDAmMCQG\n"
"CCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQEM\n"
"BQADggEBABns652JLCALBIAdGN5CmXKZFjK9Dpx1WywV4ilAbe7/ctvbq5AfjJXy\n"
"ij0IckKJUAfiORVsAYfZFhr1wHUrxeZWEQff2Ji8fJ8ZOd+LygBkc7xGEJuTI42+\n"
"FsMuCIKchjN0djsoTI0DQoWz4rIjQtUfenVqGtF8qmchxDM6OW1TyaLtYiKou+JV\n"
"bJlsQ2uRl9EMC5MCHdK8aXdJ5htN978UeAOwproLtOGFfy/cQjutdAFI3tZs4RmY\n"
"CV4Ks2dH/hzg1cEo70qLRDEmBDeNiXQ2Lu+lIg+DdEmSx/cQwgwp+7e9un/jX9Wf\n\n"
"8qn0dNW44bOwgeThpWOjzOoEeJBuv/c=\n"
"-----END CERTIFICATE-----\n";

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

  // !!!!!!!!!!!
  // Set your reset, enable, power pins here
  // !!!!!!!!!!!

  SerialMon.println("Wait...");

  // Set GSM module baud rate
  SerialAT.begin(115200,SERIAL_8N1,16,17);
  delay(6000);
 if (!SPIFFS.begin(true))
    {
        SerialMon.println("SPIFFS Mount Failed");
        return;
    }
    SPIFFS.format();
    listDir(SPIFFS, "/", 0);
  // Restart takes quite some time
  // To skip it, call init() instead of restart()
  SerialMon.println("Initializing modem...");
  modem.restart();
  // modem.init();

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

#if TINY_GSM_USE_GPRS
  // Unlock your SIM card with a PIN if needed
  if (GSM_PIN && modem.getSimStatus() != 3) { modem.simUnlock(GSM_PIN); }
#endif
secure_layer.setCACert(root_ca);
}

void printPercent(uint32_t readLength, uint32_t contentLength) {
  // If we know the total length
  if (contentLength != (uint32_t)-1) {
    SerialMon.print("\r ");
    SerialMon.print((100.0 * readLength) / contentLength);
    SerialMon.print('%');
  } else {
    SerialMon.println(readLength);
  }

}

void loop() {
#if TINY_GSM_USE_WIFI
  // Wifi connection parameters must be set before waiting for the network
  SerialMon.print(F("Setting SSID/password..."));
  if (!modem.networkConnect(wifiSSID, wifiPass)) {
    SerialMon.println(" fail");
    delay(10000);
    return;
  }
  SerialMon.println(" success");
#endif

#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_XBEE
  // The XBee must run the gprsConnect function BEFORE waiting for network!
  modem.gprsConnect(apn, gprsUser, gprsPass);
#endif

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

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

#if TINY_GSM_USE_GPRS
  // GPRS connection parameters are usually set after network registration
  SerialMon.print(F("Connecting to "));
  SerialMon.print(apn);
  if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
    SerialMon.println(" fail");
    delay(10000);
    return;
  }
  SerialMon.println(" success");

  if (modem.isGprsConnected()) { SerialMon.println("GPRS connected"); }
#endif

  SerialMon.print(F("Connecting to "));
  SerialMon.println(" success");
 client.beginRequest();
    //  client.sendHeader("Url"," HTTP/1.0");
  client.get(resource);
  client.sendHeader("User-Agent","tiny-gsm-module");
  client.sendHeader("Accept","application/vnd.github.v3.raw");
  client.sendHeader("Authorization","Bearer ghp_77Cw81UZ08x");
  client.sendHeader("X-GitHub-Api-Version","2022-11-28");
  client.endRequest(); 

        int status_code = client.responseStatusCode();
        
        
        Serial.print("Status code: ");
        Serial.println(status_code);
  File file = SPIFFS.open("/update.bin", FILE_APPEND);
  SerialMon.println(F("Waiting for response header"));
  // Let's see what the entire elapsed time is, from after we send the request.
  uint32_t timeElapsed = millis();
  // While we are still looking for the end of the header (i.e. empty line
  // FOLLOWED by a newline), continue to read data into the buffer, parsing each
  // line (data FOLLOWED by a newline). If it takes too long to get data from
  // the client, we need to exit.

  const uint32_t clientReadTimeout   = 5000;
  uint32_t       clientReadStartTime = millis();
  String         headerBuffer;
  bool           finishedHeader = false;
  uint32_t       contentLength  = 0;

  while (!finishedHeader) {
    int nlPos;

    if (client.available()) {
      clientReadStartTime = millis();
      while (client.available()) {
        char c = client.read();
        headerBuffer += c;
  // Let's exit and process if we find a new line
  if (headerBuffer.indexOf(F("\r\n")) >= 0) break;
  
      }
    } else {
      if (millis() - clientReadStartTime > clientReadTimeout) {
        // Time-out waiting for data from client
        SerialMon.println(F(">>> Client Timeout !"));
        break;
      }
    }

    // See if we have a new line.
    nlPos = headerBuffer.indexOf(F("\r\n"));

    if (nlPos > 0) {
      headerBuffer.toLowerCase();
      // Check if line contains content-length
      if (headerBuffer.startsWith(F("content-length:"))) {
        contentLength =
            headerBuffer.substring(headerBuffer.indexOf(':') + 1).toInt();
        // SerialMon.print(F("Got Content Length: "));  // uncomment for
        // SerialMon.println(contentLength);            // confirmation
      }

      headerBuffer.remove(0, nlPos + 2);  // remove the line
    } else if (nlPos == 0) {
      // if the new line is empty (i.e. "\r\n" is at the beginning of the line),
      // we are done with the header.
      finishedHeader = true;
    }
  }

  // The two cases which are not managed properly are as follows:
  // 1. The client doesn't provide data quickly enough to keep up with this
  // loop.
  // 2. If the client data is segmented in the middle of the 'Content-Length: '
  // header,
  //    then that header may be missed/damaged.
  //

  uint32_t readLength = 0;
  CRC32    crc;

  if (finishedHeader) {
    SerialMon.println(F("Reading response data"));
    clientReadStartTime = millis();

    printPercent(readLength, contentLength);
    while (readLength < contentLength && client.connected() &&
           millis() - clientReadStartTime < clientReadTimeout) {
      while (client.available()) {
     if (!file.print(char(client.read())))
            {
                Serial.println("Appending file");
            }
        crc.update(char(client.read()));
        readLength++;
        if (readLength % (contentLength / 13) == 0) {
          printPercent(readLength, contentLength);
        }
        clientReadStartTime = millis();
      }
    }
    printPercent(readLength, contentLength);
  }
 file.close();
  timeElapsed = millis() - timeElapsed;
  SerialMon.println();

  // Shutdown

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

#if TINY_GSM_USE_WIFI
  modem.networkDisconnect();
  SerialMon.println(F("WiFi disconnected"));
#endif
#if TINY_GSM_USE_GPRS
  modem.gprsDisconnect();
  SerialMon.println(F("GPRS disconnected"));
#endif
  updateFromFS();
  float duration = float(timeElapsed) / 1000;

  SerialMon.println();
  SerialMon.print("Content-Length: ");
  SerialMon.println(contentLength);
  SerialMon.print("Actually read:  ");
  SerialMon.println(readLength);
  SerialMon.print("Calc. CRC32:    0x");
  SerialMon.println(crc.finalize(), HEX);

  // SerialMon.print("Known CRC32:    0x");
  // SerialMon.println(knownCRC32, HEX);
  SerialMon.println(headerBuffer);
  SerialMon.print("Duration:       ");
  SerialMon.print(duration);
  SerialMon.println("s");

  // Do nothing forevermore
  while (true) { delay(1000); }
}

void appendFile(fs::FS &fs, const char *path, const char *message)
{
    Serial.printf("Appending to file: %s\n", path);

    File file = fs.open(path, FILE_APPEND);
    if (!file)
    {
        Serial.println("Failed to open file for appending");
        return;
    }
    if (file.print(message))
    {
        Serial.println("APOK");
    }
    else
    {
        Serial.println("APX");
    }
}

void readFile(fs::FS &fs, const char *path)
{
    Serial.printf("Reading file: %s\n", path);

    File file = fs.open(path);
    if (!file || file.isDirectory())
    {
        Serial.println("Failed to open file for reading");
        return;
    }

    Serial.print("Read from file: ");
    while (file.available())
    {
        Serial.write(file.read());
        delayMicroseconds(100);
    }
}

void writeFile(fs::FS &fs, const char *path, const char *message)
{
    Serial.printf("Writing file: %s\n", path);

    File file = fs.open(path, FILE_WRITE);
    if (!file)
    {
        Serial.println("Failed to open file for writing");
        return;
    }
    if (file.print(message))
    {
        Serial.println("File written");
    }
    else
    {
        Serial.println("Write failed");
    }
}

void listDir(fs::FS &fs, const char *dirname, uint8_t levels)
{
    Serial.printf("Listing directory: %s\n", dirname);

    File root = fs.open(dirname);
    if (!root)
    {
        Serial.println("Failed to open directory");
        return;
    }
    if (!root.isDirectory())
    {
        Serial.println("Not a directory");
        return;
    }

    File file = root.openNextFile();
    while (file)
    {
        if (file.isDirectory())
        {
            Serial.print("  DIR : ");
            Serial.println(file.name());
            if (levels)
            {
                listDir(fs, file.name(), levels - 1);
            }
        }
        else
        {
            Serial.print("  FILE: ");
            Serial.print(file.name());
            Serial.print("  SIZE: ");
            Serial.println(file.size());
        }
        file = root.openNextFile();
    }
}

void deleteFile(fs::FS &fs, const char *path)
{
    Serial.printf("Deleting file: %s\n", path);
    if (fs.remove(path))
    {
        Serial.println("File deleted");
    }
    else
    {
        Serial.println("Delete failed");
    }
}

void updateFromFS()
{
    File updateBin = SPIFFS.open("/update.bin");
    if (updateBin)
    {
        if (updateBin.isDirectory())
        {
            Serial.println("Directory error");
            updateBin.close();
            return;
        }

        size_t updateSize = updateBin.size();

        if (updateSize > 0)
        {
            Serial.println("Starting update");
            performUpdate(updateBin, updateSize);
        }
        else
        {
            Serial.println("Error, archivo vacío");
        }

        updateBin.close();

        // when finished remove the binary from sd card to indicate end of the process
        //fs.remove("/update.bin");
    }
    else
    {
        Serial.println("no such binary");
    }
}

void performUpdate(Stream &updateSource, size_t updateSize)
{
    if (Update.begin(updateSize))
    {      
        size_t written = Update.writeStream(updateSource);
        if (written == updateSize)
        {
            Serial.println("Writes : " + String(written) + " successfully");
        }
        else
        {
            Serial.println("Written only : " + String(written) + "/" + String(updateSize) + ". Retry?");
        }
        if (Update.end())
        {
            Serial.println("OTA finished!");
            if (Update.isFinished())
            {
                Serial.println("Restart ESP device!");
                ESP.restart();
            }
            else
            {
                Serial.println("OTA not fiished");
            }
        }
        else
        {
            Serial.println("Error occured #: " + String(Update.getError()));
        }
    }
    else
    {
        Serial.println("Cannot beggin update");
    }
}


file is not big
what is the issue

you perform two reads here so you save in the file only every other byte and calculate the crc with every other byte as well...

yes, is there a issue?