title: Trying to understand ESP32 OTA over the GSM code
body: <p>I am performing ESP32 OTA over the GSM. I have found some example code online, modified it slightly based on my board connections and was able to sucesfully perform a firmware update.</p>
  <p>I use lilygo ESP32 GSM development board.</p>
  <p>However, it frustrates me that I am not capable of understanding what really happens in the code at the low level. I really want to understand before I go any further with my project.</p>
  <p>Full code for my OTA update:</p>
  <pre><code>#include &lt;Update.h&gt;
  #define TINY_GSM_MODEM_SIM800
  // Increase RX buffer
  #define TINY_GSM_RX_BUFFER 1030
  
  const char apn[] = &quot;omnitel&quot;;
  const char user[] = &quot;omni&quot;;
  const char pass[] = &quot;omni&quot;;
  
  
  
  #define MODEM_RST             5
  #define MODEM_PWRKEY          4
  #define MODEM_POWER_ON       23
  #define MODEM_TX             27
  #define MODEM_RX             26
  #define LED_GPIO             13
  #define LED_ON               HIGH
  #define LED_OFF              LOW
  
  
  #define SerialAT  Serial1
  
  #include &lt;TinyGsmClient.h&gt;
  #include &lt;CRC32.h&gt;
  
  #include &quot;FS.h&quot;
  #include &quot;SPIFFS.h&quot;
  
  #ifdef DUMP_AT_COMMANDS
  #include &lt;StreamDebugger.h&gt;
  StreamDebugger debugger(SerialAT, Serial);
  TinyGsm modem(debugger);
  #else
  TinyGsm modem(SerialAT);
  #endif
  
  
  TinyGsmClient client(modem);
  
  const char server[] = &quot;myesptestserver.ddns.net&quot;;
  const int port = 80;
  const char resource[] = &quot;/esp.bin&quot;; //here de bin file
  
  uint32_t knownCRC32 = 0x6f50d767;
  uint32_t knownFileSize = 1024; // In case server does not send it
  
  void setup()
  {
      SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX);
      // Set console baud rate
      Serial.begin(115200);
  
      setupModem();
      delay(10);
  
      if (!SPIFFS.begin(true))
      {
          Serial.println(&quot;SPIFFS Mount Failed&quot;);
          return;
      }
      SPIFFS.format();
      listDir(SPIFFS, &quot;/&quot;, 0);
  
      // Set GSM module baud rate
      
      delay(3000);
  
      // Restart takes quite some time
      // To skip it, call init() instead of restart()
      Serial.println(&quot;Initializing modem...&quot;);
      modem.restart();
  
      String modemInfo = modem.getModemInfo();
      Serial.print(&quot;Modem: &quot;);
      Serial.println(modemInfo);
  
      // Unlock your SIM card with a PIN
      //modem.simUnlock(&quot;1234&quot;);
  }
  
  
  
  void loop()
  {
      Serial.print(&quot;Waiting for network...&quot;);
      if (!modem.waitForNetwork())
      {
          Serial.println(&quot; fail&quot;);
          delay(10000);
          return;
      }
      Serial.println(&quot; OK&quot;);
  
      Serial.print(&quot;Connecting to &quot;);
      Serial.print(apn);
      if (!modem.gprsConnect(apn, user, pass))
      {
          Serial.println(&quot; fail&quot;);
          delay(10000);
          return;
      }
      Serial.println(&quot; OK&quot;);
  
      Serial.print(&quot;Connecting to &quot;);
      Serial.print(server);
  
      // if you get a connection, report back via serial:
      if (!client.connect(server, port))
      {
          Serial.println(&quot; fail&quot;);
          delay(10000);
          return;
      }
      Serial.println(&quot; OK&quot;);
      
      // Make a HTTP request:
      client.print(String(&quot;GET &quot;) + resource + &quot; HTTP/1.0\r\n&quot;);
      client.print(String(&quot;Host: &quot;) + server + &quot;\r\n&quot;);
      client.print(&quot;Connection: close\r\n\r\n&quot;);
  
      long timeout = millis();
      while (client.available() == 0)
      {
          if (millis() - timeout &gt; 5000L)
          {
              Serial.println(&quot;&gt;&gt;&gt; Client Timeout !&quot;);
              client.stop();
              delay(10000L);
              return;
          }
      }
  
      Serial.println(&quot;Reading header&quot;);
      uint32_t contentLength = knownFileSize;
  
      File file = SPIFFS.open(&quot;/update.bin&quot;, FILE_APPEND);
  
  
      while (client.available())
      {
          String line = client.readStringUntil('\n');
          line.trim();
          //Serial.println(line);    // Uncomment this to show response header
          line.toLowerCase();
          if (line.startsWith(&quot;content-length:&quot;))
          {
              contentLength = line.substring(line.lastIndexOf(':') + 1).toInt();
          }
          else if (line.length() == 0)
          {
              break;
          }
      }
  
  
      timeout = millis();
      uint32_t readLength = 0;
      CRC32 crc;
  
      unsigned long timeElapsed = millis();
      printPercent(readLength, contentLength);
      
  
      while (readLength &lt; contentLength &amp;&amp; client.connected() &amp;&amp; millis() - timeout &lt; 10000L)
      {
          int i = 0;
          while (client.available())
          {
                  // read file data to spiffs
              if (!file.print(char(client.read())))
              {
                  Serial.println(&quot;Appending file&quot;);
              }
              //Serial.print((char)c);       // Uncomment this to show data
              //crc.update(c);
              readLength++;
  
              if (readLength % (contentLength / 13) == 0)
              {
                  printPercent(readLength, contentLength);
              }
              timeout = millis();
          }
      }
  
      file.close();
  
      printPercent(readLength, contentLength);
      timeElapsed = millis() - timeElapsed;
      Serial.println();
  
      client.stop();
      Serial.println(&quot;stop client&quot;);
  
      modem.gprsDisconnect();
      Serial.println(&quot;gprs disconnect&quot;);
      Serial.println();
  
      float duration = float(timeElapsed) / 1000;
    /*
      Serial.print(&quot;Tamaño de Archivo: &quot;);
      Serial.println(contentLength);
      Serial.print(&quot;Leido:  &quot;);
      Serial.println(readLength);
      Serial.print(&quot;Calculado. CRC32:    0x&quot;);
      Serial.println(crc.finalize(), HEX);
      Serial.print(&quot;Conocido CRC32:    0x&quot;);
      Serial.println(knownCRC32, HEX);
      Serial.print(&quot;Bajado en:       &quot;);
      Serial.print(duration);
      Serial.println(&quot;s&quot;);
  
      Serial.println(&quot;Se genera una espera de 3 segundos&quot;);
      for (int i = 0; i &lt; 3; i++)
      {
          Serial.print(String(i) + &quot;...&quot;);
          delay(1000);
      }
    */
      //readFile(SPIFFS, &quot;/update.bin&quot;);
  
      updateFromFS();
  
      // Do nothing forevermore
      while (true)
      {
          delay(1000);
      }
  }
  
  
  
  
  
  
  
  void appendFile(fs::FS &amp;fs, const char *path, const char *message)
  {
      Serial.printf(&quot;Appending to file: %s\n&quot;, path);
  
      File file = fs.open(path, FILE_APPEND);
      if (!file)
      {
          Serial.println(&quot;Failed to open file for appending&quot;);
          return;
      }
      if (file.print(message))
      {
          Serial.println(&quot;APOK&quot;);
      }
      else
      {
          Serial.println(&quot;APX&quot;);
      }
  }
  
  void readFile(fs::FS &amp;fs, const char *path)
  {
      Serial.printf(&quot;Reading file: %s\n&quot;, path);
  
      File file = fs.open(path);
      if (!file || file.isDirectory())
      {
          Serial.println(&quot;Failed to open file for reading&quot;);
          return;
      }
  
      Serial.print(&quot;Read from file: &quot;);
      while (file.available())
      {
          Serial.write(file.read());
          delayMicroseconds(100);
      }
  }
  
  void writeFile(fs::FS &amp;fs, const char *path, const char *message)
  {
      Serial.printf(&quot;Writing file: %s\n&quot;, path);
  
      File file = fs.open(path, FILE_WRITE);
      if (!file)
      {
          Serial.println(&quot;Failed to open file for writing&quot;);
          return;
      }
      if (file.print(message))
      {
          Serial.println(&quot;File written&quot;);
      }
      else
      {
          Serial.println(&quot;Write failed&quot;);
      }
  }
  
  void listDir(fs::FS &amp;fs, const char *dirname, uint8_t levels)
  {
      Serial.printf(&quot;Listing directory: %s\n&quot;, dirname);
  
      File root = fs.open(dirname);
      if (!root)
      {
          Serial.println(&quot;Failed to open directory&quot;);
          return;
      }
      if (!root.isDirectory())
      {
          Serial.println(&quot;Not a directory&quot;);
          return;
      }
  
      File file = root.openNextFile();
      while (file)
      {
          if (file.isDirectory())
          {
              Serial.print(&quot;  DIR : &quot;);
              Serial.println(file.name());
              if (levels)
              {
                  listDir(fs, file.name(), levels - 1);
              }
          }
          else
          {
              Serial.print(&quot;  FILE: &quot;);
              Serial.print(file.name());
              Serial.print(&quot;  SIZE: &quot;);
              Serial.println(file.size());
          }
          file = root.openNextFile();
      }
  }
  
  void deleteFile(fs::FS &amp;fs, const char *path)
  {
      Serial.printf(&quot;Deleting file: %s\n&quot;, path);
      if (fs.remove(path))
      {
          Serial.println(&quot;File deleted&quot;);
      }
      else
      {
          Serial.println(&quot;Delete failed&quot;);
      }
  }
  
  void updateFromFS()
  {
      File updateBin = SPIFFS.open(&quot;/update.bin&quot;);
      if (updateBin)
      {
          if (updateBin.isDirectory())
          {
              Serial.println(&quot;Directory error&quot;);
              updateBin.close();
              return;
          }
  
          size_t updateSize = updateBin.size();
  
          if (updateSize &gt; 0)
          {
              Serial.println(&quot;Starting update&quot;);
              performUpdate(updateBin, updateSize);
          }
          else
          {
              Serial.println(&quot;Error, archivo vacío&quot;);
          }
  
          updateBin.close();
  
          // whe finished remove the binary from sd card to indicate end of the process
          //fs.remove(&quot;/update.bin&quot;);
      }
      else
      {
          Serial.println(&quot;no such binary&quot;);
      }
  }
  
  void performUpdate(Stream &amp;updateSource, size_t updateSize)
  {
      if (Update.begin(updateSize))
      {
          size_t written = Update.writeStream(updateSource);
          if (written == updateSize)
          {
              Serial.println(&quot;Writes : &quot; + String(written) + &quot; successfully&quot;);
          }
          else
          {
              Serial.println(&quot;Written only : &quot; + String(written) + &quot;/&quot; + String(updateSize) + &quot;. Retry?&quot;);
          }
          if (Update.end())
          {
              Serial.println(&quot;OTA finished!&quot;);
              if (Update.isFinished())
              {
                  Serial.println(&quot;Restart ESP device!&quot;);
                  ESP.restart();
              }
              else
              {
                  Serial.println(&quot;OTA not fiished&quot;);
              }
          }
          else
          {
              Serial.println(&quot;Error occured #: &quot; + String(Update.getError()));
          }
      }
      else
      {
          Serial.println(&quot;Cannot beggin update&quot;);
      }
  }
  
  
  
  
  
  void printPercent(uint32_t readLength, uint32_t contentLength)
  {
      // If we know the total length
      if (contentLength != -1)
      {
          Serial.print(&quot;\r &quot;);
          Serial.print((100.0 * readLength) / contentLength);
          Serial.print('%');
      }
      else
      {
          Serial.println(readLength);
      }
  }
  
  
  
  void setupModem()
  {
  #ifdef MODEM_RST
      // Keep reset high
      pinMode(MODEM_RST, OUTPUT);
      digitalWrite(MODEM_RST, HIGH);
  #endif
  
      pinMode(MODEM_PWRKEY, OUTPUT);
      pinMode(MODEM_POWER_ON, OUTPUT);
  
      // Turn on the Modem power first
      digitalWrite(MODEM_POWER_ON, HIGH);
  
      // Pull down PWRKEY for more than 1 second according to manual requirements
      digitalWrite(MODEM_PWRKEY, HIGH);
      delay(100);
      digitalWrite(MODEM_PWRKEY, LOW);
      delay(1000);
      digitalWrite(MODEM_PWRKEY, HIGH);
  
      // Initialize the indicator as an output
      pinMode(LED_GPIO, OUTPUT);
      digitalWrite(LED_GPIO, LED_OFF);
  }
  </code></pre>
  <p>From what I understand, the following part of the code reads my binary file that is stored in the server and read it into SPIFFS memory:</p>
  <pre><code>
      while (readLength &lt; contentLength &amp;&amp; client.connected() &amp;&amp; millis() - timeout &lt; 10000L)
      {
          int i = 0;
          while (client.available())
          {
                  // read file data to spiffs
              if (!file.print(char(client.read())))
              {
                  Serial.println(&quot;Appending file&quot;);
              }
              //Serial.print((char)c);       // Uncomment this to show data
              //crc.update(c);
              readLength++;
  
              if (readLength % (contentLength / 13) == 0)
              {
                  printPercent(readLength, contentLength);
              }
              timeout = millis();
          }
      }
  
  </code></pre>
  <p>Then the updateFromFS() function is executed, which checks whether the binary is all good and then calls another function performUpdate(updateBin, updateSize);</p>
  <pre><code>void updateFromFS()
  {
      File updateBin = SPIFFS.open(&quot;/update.bin&quot;);
      if (updateBin)
      {
          if (updateBin.isDirectory())
          {
              Serial.println(&quot;Directory error&quot;);
              updateBin.close();
              return;
          }
  
          size_t updateSize = updateBin.size();
  
          if (updateSize &gt; 0)
          {
              Serial.println(&quot;Starting update&quot;);
              performUpdate(updateBin, updateSize);
          }
          else
          {
              Serial.println(&quot;Error, archivo vacío&quot;);
          }
  
          updateBin.close();
  
          // whe finished remove the binary from sd card to indicate end of the process
          //fs.remove(&quot;/update.bin&quot;);
      }
      else
      {
          Serial.println(&quot;no such binary&quot;);
      }
  }
  </code></pre>
  <p>in the performUpdate(updateBin, updateSize), 2 functions from Arduino <strong>Update</strong> Library are being called ( Update.begin(updateSize) and Update.writeStream(updateSource) ) and that is where I cant follow anymore...</p>
  <pre><code>
  void performUpdate(Stream &amp;updateSource, size_t updateSize)
  {
      if (Update.begin(updateSize))
      {
          size_t written = Update.writeStream(updateSource);
          if (written == updateSize)
          {
              Serial.println(&quot;Writes : &quot; + String(written) + &quot; successfully&quot;);
          }
          else
          {
              Serial.println(&quot;Written only : &quot; + String(written) + &quot;/&quot; + String(updateSize) + &quot;. Retry?&quot;);
          }
          if (Update.end())
          {
              Serial.println(&quot;OTA finished!&quot;);
              if (Update.isFinished())
              {
                  Serial.println(&quot;Restart ESP device!&quot;);
                  ESP.restart();
              }
              else
              {
                  Serial.println(&quot;OTA not fiished&quot;);
              }
          }
          else
          {
              Serial.println(&quot;Error occured #: &quot; + String(Update.getError()));
          }
      }
      else
      {
          Serial.println(&quot;Cannot beggin update&quot;);
      }
  }
  </code></pre>
  <p>I have found this Update library source code :
  <a href="https://github.com/espressif/arduino-esp32/blob/master/libraries/Update/src/Updater.cpp" rel="nofollow noreferrer">https://github.com/espressif/arduino-esp32/blob/master/libraries/Update/src/Updater.cpp</a></p>
  <p>But it seems that this is beyond my knowledge to understand what really is going on in this code.</p>
  <p>First of all, we pass only 1 variable to .begin method, but the function prototype requires more than that:</p>
  <pre><code>bool UpdateClass::begin(size_t size, int command, int ledPin, uint8_t ledOn, const char *label) 
  </code></pre>
  <p>And then I am trying to understand what happens in function Update.writeStream(updateSource) but cant..</p>
  <p>If anyone could shed some light here I would appreciate a lot! Thanks in advance...</p>