Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to update specific partition #10516

Open
1 task done
hitecSmartHome opened this issue Oct 24, 2024 · 1 comment
Open
1 task done

Add option to update specific partition #10516

hitecSmartHome opened this issue Oct 24, 2024 · 1 comment
Labels
Type: Feature request Feature request for Arduino ESP32

Comments

@hitecSmartHome
Copy link

Related area

HTTPUpdate

Hardware specification

ESP32

Is your feature request related to a problem?

Yes. I can't update specific partition. I need to do a hack.

Describe the solution you'd like

HTTPUpdateResult HTTPUpdate::updateSpiffs(NetworkClient &client, const String &url, const String &currentVersion, HTTPUpdateRequestCB requestCB,  const char* partitionLabel) {
  HTTPClient http;
  if (!http.begin(client, url)) {
    return HTTP_UPDATE_FAILED;
  }
  return handleUpdate(http, currentVersion, true, requestCB, partitionLabel);
}

HTTPUpdateResult HTTPUpdate::handleUpdate(HTTPClient &http, const String &currentVersion, bool spiffs, HTTPUpdateRequestCB requestCB, const char* partitionLabel) {

  HTTPUpdateResult ret = HTTP_UPDATE_FAILED;

  // use HTTP/1.0 for update since the update handler not support any transfer Encoding
  http.useHTTP10(true);
  http.setTimeout(_httpClientTimeout);
  http.setFollowRedirects(_followRedirects);
  http.setUserAgent("ESP32-http-Update");
  http.addHeader("Cache-Control", "no-cache");
  http.addHeader("x-ESP32-BASE-MAC", Network.macAddress());
#if SOC_WIFI_SUPPORTED
  http.addHeader("x-ESP32-STA-MAC", WiFi.macAddress());
  http.addHeader("x-ESP32-AP-MAC", WiFi.softAPmacAddress());
#endif
  http.addHeader("x-ESP32-free-space", String(ESP.getFreeSketchSpace()));
  http.addHeader("x-ESP32-sketch-size", String(ESP.getSketchSize()));
  String sketchMD5 = ESP.getSketchMD5();
  if (sketchMD5.length() != 0) {
    http.addHeader("x-ESP32-sketch-md5", sketchMD5);
  }
  // Add also a SHA256
  String sketchSHA256 = getSketchSHA256();
  if (sketchSHA256.length() != 0) {
    http.addHeader("x-ESP32-sketch-sha256", sketchSHA256);
  }
  http.addHeader("x-ESP32-chip-size", String(ESP.getFlashChipSize()));
  http.addHeader("x-ESP32-sdk-version", ESP.getSdkVersion());

  if (spiffs) {
    http.addHeader("x-ESP32-mode", "spiffs");
  } else {
    http.addHeader("x-ESP32-mode", "sketch");
  }

  if (currentVersion && currentVersion[0] != 0x00) {
    http.addHeader("x-ESP32-version", currentVersion);
  }
  if (requestCB) {
    requestCB(&http);
  }

  if (!_user.isEmpty() && !_password.isEmpty()) {
    http.setAuthorization(_user.c_str(), _password.c_str());
  }

  if (!_auth.isEmpty()) {
    http.setAuthorization(_auth.c_str());
  }

  const char *headerkeys[] = {"x-MD5"};
  size_t headerkeyssize = sizeof(headerkeys) / sizeof(char *);

  // track these headers
  http.collectHeaders(headerkeys, headerkeyssize);

  int code = http.GET();
  int len = http.getSize();

  if (code <= 0) {
    log_e("HTTP error: %s\n", http.errorToString(code).c_str());
    _lastError = code;
    http.end();
    return HTTP_UPDATE_FAILED;
  }

  log_d("Header read fin.\n");
  log_d("Server header:\n");
  log_d(" - code: %d\n", code);
  log_d(" - len: %d\n", len);

  String md5;
  if (_md5Sum.length()) {
    md5 = _md5Sum;
  } else if (http.hasHeader("x-MD5")) {
    md5 = http.header("x-MD5");
  }
  if (md5.length()) {
    log_d(" - MD5: %s\n", md5.c_str());
  }

  log_d("ESP32 info:\n");
  log_d(" - free Space: %d\n", ESP.getFreeSketchSpace());
  log_d(" - current Sketch Size: %d\n", ESP.getSketchSize());

  if (currentVersion && currentVersion[0] != 0x00) {
    log_d(" - current version: %s\n", currentVersion.c_str());
  }

  switch (code) {
    case HTTP_CODE_OK:  ///< OK (Start Update)
      if (len > 0) {
        bool startUpdate = true;
        if (spiffs) {
          const esp_partition_t *_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, partitionLabel);
          if (!_partition) {
            _lastError = HTTP_UE_NO_PARTITION;
            return HTTP_UPDATE_FAILED;
          }

          printf("Partition label: %s\n", _partition->label);
          if (len > _partition->size) {
            printf("spiffsSize to low (%d) needed: %d\n", _partition->size, len);
            log_e("spiffsSize to low (%d) needed: %d\n", _partition->size, len);
            startUpdate = false;
          }
        } else {
          int sketchFreeSpace = ESP.getFreeSketchSpace();
          if (!sketchFreeSpace) {
            _lastError = HTTP_UE_NO_PARTITION;
            return HTTP_UPDATE_FAILED;
          }

          if (len > sketchFreeSpace) {
            log_e("FreeSketchSpace to low (%d) needed: %d\n", sketchFreeSpace, len);
            startUpdate = false;
          }
        }

        if (!startUpdate) {
          _lastError = HTTP_UE_TOO_LESS_SPACE;
          ret = HTTP_UPDATE_FAILED;
        } else {
          // Warn main app we're starting up...
          if (_cbStart) {
            _cbStart();
          }

          NetworkClient *tcp = http.getStreamPtr();

          // To do?                NetworkUDP::stopAll();
          // To do?                NetworkClient::stopAllExcept(tcp);

          delay(100);

          int command;

          if (spiffs) {
            command = U_SPIFFS;
            log_d("runUpdate spiffs...\n");
          } else {
            command = U_FLASH;
            log_d("runUpdate flash...\n");
          }

          if (!spiffs) {
            /* To do
                    uint8_t buf[4];
                    if(tcp->peekBytes(&buf[0], 4) != 4) {
                        log_e("peekBytes magic header failed\n");
                        _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED;
                        http.end();
                        return HTTP_UPDATE_FAILED;
                    }
*/

            // check for valid first magic byte
            //                    if(buf[0] != 0xE9) {
            if (tcp->peek() != 0xE9) {
              log_e("Magic header does not start with 0xE9\n");
              _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED;
              http.end();
              return HTTP_UPDATE_FAILED;
            }
            /* To do
                    uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4);

                    // check if new bin fits to SPI flash
                    if(bin_flash_size > ESP.getFlashChipRealSize()) {
                        log_e("New binary does not fit SPI Flash size\n");
                        _lastError = HTTP_UE_BIN_FOR_WRONG_FLASH;
                        http.end();
                        return HTTP_UPDATE_FAILED;
                    }
*/
          }
          if (runUpdate(*tcp, len, md5, command, partitionLabel) {
            ret = HTTP_UPDATE_OK;
            log_d("Update ok\n");
            http.end();
            // Warn main app we're all done
            if (_cbEnd) {
              _cbEnd();
            }

            if (_rebootOnUpdate && !spiffs) {
              ESP.restart();
            }

          } else {
            ret = HTTP_UPDATE_FAILED;
            log_e("Update failed\n");
          }
        }
      } else {
        _lastError = HTTP_UE_SERVER_NOT_REPORT_SIZE;
        ret = HTTP_UPDATE_FAILED;
        log_e("Content-Length was 0 or wasn't set by Server?!\n");
      }
      break;
    case HTTP_CODE_NOT_MODIFIED:
      ///< Not Modified (No updates)
      ret = HTTP_UPDATE_NO_UPDATES;
      break;
    case HTTP_CODE_NOT_FOUND:
      _lastError = HTTP_UE_SERVER_FILE_NOT_FOUND;
      ret = HTTP_UPDATE_FAILED;
      break;
    case HTTP_CODE_FORBIDDEN:
      _lastError = HTTP_UE_SERVER_FORBIDDEN;
      ret = HTTP_UPDATE_FAILED;
      break;
    default:
      _lastError = HTTP_UE_SERVER_WRONG_HTTP_CODE;
      ret = HTTP_UPDATE_FAILED;
      log_e("HTTP Code is (%d)\n", code);
      break;
  }

  http.end();
  return ret;
}

bool HTTPUpdate::runUpdate(Stream &in, uint32_t size, String md5, int command, const char* partitionLabel) {

  StreamString error;

  if (_cbProgress) {
    Update.onProgress(_cbProgress);
  }

  if (!Update.begin(size, command, _ledPin, _ledOn, partitionLabel)) {
    _lastError = Update.getError();
    Update.printError(error);
    error.trim();  // remove line ending
    log_e("Update.begin failed! (%s)\n", error.c_str());
    return false;
  }

  if (_cbProgress) {
    _cbProgress(0, size);
  }

  if (md5.length()) {
    if (!Update.setMD5(md5.c_str())) {
      _lastError = HTTP_UE_SERVER_FAULTY_MD5;
      log_e("Update.setMD5 failed! (%s)\n", md5.c_str());
      return false;
    }
  }

  // To do: the SHA256 could be checked if the server sends it

  if (Update.writeStream(in) != size) {
    _lastError = Update.getError();
    Update.printError(error);
    error.trim();  // remove line ending
    log_e("Update.writeStream failed! (%s)\n", error.c_str());
    return false;
  }

  if (_cbProgress) {
    _cbProgress(size, size);
  }

  if (!Update.end()) {
    _lastError = Update.getError();
    Update.printError(error);
    error.trim();  // remove line ending
    log_e("Update.end failed! (%s)\n", error.c_str());
    return false;
  }

  return true;
}

Describe alternatives you've considered

Now I have to manually modify the partition label on every firmware change because the new one overwrites my changes

Additional context

I have checked existing list of Feature requests and the Contribution Guide

  • I confirm I have checked existing list of Feature requests and Contribution Guide.
@hitecSmartHome hitecSmartHome added the Type: Feature request Feature request for Arduino ESP32 label Oct 24, 2024
@hitecSmartHome
Copy link
Author

Update already supports it so basically

HTTPUpdate.h

t_httpUpdate_return updateSpiffs(NetworkClient &client, const String &url, const String &currentVersion = "", HTTPUpdateRequestCB requestCB = NULL, const char* partitionLabel = "spiffs");

t_httpUpdate_return update(HTTPClient &httpClient, const String &currentVersion = "", HTTPUpdateRequestCB requestCB = NULL);

t_httpUpdate_return updateSpiffs(HTTPClient &httpClient, const String &currentVersion = "", HTTPUpdateRequestCB requestCB = NULL, const char* partitionLabel = "spiffs");

t_httpUpdate_return handleUpdate(HTTPClient &http, const String &currentVersion, bool spiffs = false, HTTPUpdateRequestCB requestCB = NULL, const char* partitionLabel = NULL);

bool runUpdate(Stream &in, uint32_t size, String md5, int command = U_FLASH, const char* partitionLabel = NULL);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Feature request Feature request for Arduino ESP32
Projects
None yet
Development

No branches or pull requests

1 participant