From 69ebece5a72fa7b71c04535f9e9ef10d67dcf5a2 Mon Sep 17 00:00:00 2001 From: tobozo Date: Tue, 25 Oct 2022 15:13:01 +0200 Subject: [PATCH] 1.1.7 (RP2040 support) (#62) * added experimental RP2040 support * updated readme --- .github/workflows/LibraryBuild.yml | 4 + README.md | 5 +- examples/Test_tar_gz_tgz/Test_tar_gz_tgz.ino | 148 ++-- library.properties | 4 +- src/ESP32-targz-lib.cpp | 724 ++++++++++--------- src/ESP32-targz-lib.hpp | 73 +- src/ESP32-targz-log.hpp | 205 ++++++ src/ESP32-targz.h | 31 +- src/helpers/path_tools.h | 5 + 9 files changed, 740 insertions(+), 459 deletions(-) create mode 100644 src/ESP32-targz-log.hpp diff --git a/.github/workflows/LibraryBuild.yml b/.github/workflows/LibraryBuild.yml index 2de3a93..eede09b 100644 --- a/.github/workflows/LibraryBuild.yml +++ b/.github/workflows/LibraryBuild.yml @@ -30,6 +30,10 @@ jobs: platform-url: https://arduino.esp8266.com/stable/package_esp8266com_index.json required-libraries: ESP32-Chimera-Core,LovyanGFX + - arduino-boards-fqbn: rp2040:rp2040:rpipico + sketch-names: Test_tar_gz_tgz.ino + platform-url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json + fail-fast: false steps: diff --git a/README.md b/README.md index 5718d7b..f0af17d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 🗜️ ESP32-targz -## An ESP32/ESP8266 Arduino library to provide decompression support for .tar, .gz and .tar.gz files +## An ESP32/ESP8266/RP2040 Arduino library to provide decompression support for .tar, .gz and .tar.gz files [![arduino-library-badge](https://www.ardu-badge.com/badge/ESP32-targz.svg?)](https://www.ardu-badge.com/ESP32-targz) [![PlatformIO Registry](https://badges.registry.platformio.org/packages/tobozo/library/ESP32-targz.svg)](https://registry.platformio.org/packages/libraries/tobozo/ESP32-targz) @@ -54,7 +54,8 @@ Support Matrix | ESP32 | 1.0 | 1.0.5 | 1.0.5 | 1.0 | 1.0 | | | | | | | | | ESP8266 | builtin | 0.1.0 | 0.1.0 | n/a | n/a | - +| | | | | | | +| RP2040 | n/a | n/a | 2.0.0 | n/a | n/a | diff --git a/examples/Test_tar_gz_tgz/Test_tar_gz_tgz.ino b/examples/Test_tar_gz_tgz/Test_tar_gz_tgz.ino index dd002b5..d53ee47 100644 --- a/examples/Test_tar_gz_tgz/Test_tar_gz_tgz.ino +++ b/examples/Test_tar_gz_tgz/Test_tar_gz_tgz.ino @@ -40,6 +40,33 @@ #endif +#if defined ARDUINO_ARCH_RP2040 + + // SD config based on this gist: https://gist.github.com/tobozo/a68c3f37f5ed0c763e8513c11320f4fd + + /*\ + * + * SD Module | RP2040 + * -----------+----------- + * | + * GND | GND + * 3.3v | 3V3 + * CS | GPIO5 + * MOSI | GPIO3 + * SCK | GPIO2 + * MISO | GPIO4 + * | + \*/ + + const uint8_t SD_SCK = 2; + const uint8_t SD_MOSI = 3; + const uint8_t SD_MISO = 4; + const uint8_t SD_CS = 5; + +#endif + + + #include "test_tools.h" // Test #1: tarExpander() @@ -59,7 +86,7 @@ bool test_tarExpander() #if defined ESP32 TARUnpacker->setTarVerify( true ); // true = enables health checks but slows down the overall process #endif - #if defined ESP8266 + #if defined ESP8266 || defined ARDUINO_ARCH_RP2040 TARUnpacker->setTarVerify( false ); // true = enables health checks but slows down the overall process #endif @@ -97,7 +124,7 @@ bool test_tarStreamExpander() #if defined ESP32 TARUnpacker->setTarVerify( true ); // true = enables health checks but slows down the overall process #endif - #if defined ESP8266 + #if defined ESP8266 || defined ARDUINO_ARCH_RP2040 TARUnpacker->setTarVerify( false ); // true = enables health checks but slows down the overall process #endif @@ -214,7 +241,7 @@ bool test_tarGzExpander() #if defined ESP32 TARGZUnpacker->setTarVerify( true ); // true = enables health checks but slows down the overall process #endif - #if defined ESP8266 + #if defined ESP8266 || defined ARDUINO_ARCH_RP2040 TARGZUnpacker->setTarVerify( false ); // true = enables health checks but slows down the overall process #endif TARGZUnpacker->setupFSCallbacks( targzTotalBytesFn, targzFreeBytesFn ); // prevent the partition from exploding, recommended @@ -260,7 +287,7 @@ bool test_tarGzExpander_no_intermediate() #if defined ESP32 TARGZUnpacker->setTarVerify( true ); // true = enables health checks but slows down the overall process #endif - #if defined ESP8266 + #if defined ESP8266 || defined ARDUINO_ARCH_RP2040 TARGZUnpacker->setTarVerify( false ); // true = enables health checks but slows down the overall process //TARGZUnpacker->setTarStatusProgressCallback( BaseUnpacker::targzNullLoggerCallback ); // print the filenames as they're expanded #endif @@ -291,48 +318,57 @@ bool test_tarGzExpander_no_intermediate() } +#if defined HAS_OTA_SUPPORT -// Test #5: gzUpdater() and gzStreamUpdater() -// Requires: gzipped firmware file, filename must end with ".gz" -bool test_gzUpdater() -{ - bool ret = false; - #if defined ESP32 - const char* firmwareFile = "/firmware_example_esp32.gz"; - #endif - #if defined ESP8266 - const char* firmwareFile = "/firmware_example_esp8266.gz"; - #endif + // Test #5: gzUpdater() and gzStreamUpdater() + // Requires: gzipped firmware file, filename must end with ".gz" + bool test_gzUpdater() + { + bool ret = false; + #if defined ESP32 + const char* firmwareFile = "/firmware_example_esp32.gz"; + #endif + #if defined ESP8266 + const char* firmwareFile = "/firmware_example_esp8266.gz"; + #endif - SerialPrintCentered("Testing gzUpdater / gzStreamUpdater", false, true ); + SerialPrintCentered("Testing gzUpdater / gzStreamUpdater", false, true ); - GzUnpacker *GZUnpacker = new GzUnpacker(); + GzUnpacker *GZUnpacker = new GzUnpacker(); - GZUnpacker->haltOnError( true ); // stop on fail (manual restart/reset required) - GZUnpacker->setupFSCallbacks( targzTotalBytesFn, targzFreeBytesFn ); // prevent the partition from exploding, recommended - GZUnpacker->setGzProgressCallback( BaseUnpacker::defaultProgressCallback ); // targzNullProgressCallback or defaultProgressCallback - GZUnpacker->setLoggerCallback( BaseUnpacker::targzPrintLoggerCallback ); // gz log verbosity + GZUnpacker->haltOnError( true ); // stop on fail (manual restart/reset required) + GZUnpacker->setupFSCallbacks( targzTotalBytesFn, targzFreeBytesFn ); // prevent the partition from exploding, recommended + GZUnpacker->setGzProgressCallback( BaseUnpacker::defaultProgressCallback ); // targzNullProgressCallback or defaultProgressCallback + GZUnpacker->setLoggerCallback( BaseUnpacker::targzPrintLoggerCallback ); // gz log verbosity - #ifdef ESP32 - GZUnpacker->setPsram( true ); - #endif + #ifdef ESP32 + GZUnpacker->setPsram( true ); + #endif - if( ! GZUnpacker->gzUpdater( sourceFS, firmwareFile, U_FLASH, /*restart on update*/false ) ) { - Serial.println( OpenLine ); - SerialPrintfCentered("gzUpdater failed with return code #%d", GZUnpacker->tarGzGetError() ); - Serial.println( CloseLine ); - } else { - ret = true; + if( ! GZUnpacker->gzUpdater( sourceFS, firmwareFile, U_FLASH, /*restart on update*/false ) ) { + Serial.println( OpenLine ); + SerialPrintfCentered("gzUpdater failed with return code #%d", GZUnpacker->tarGzGetError() ); + Serial.println( CloseLine ); + } else { + ret = true; + } + return ret; } - return ret; -} +#endif -// NOT (yet?) working on ESP8266 +// NOT (yet?) working on ARDUINO_ARCH_RP2040 // Test #6 tarGzStreamExpander() // Requires: a valid stream (http or file) to a targz file bool test_tarGzStreamExpander() { + + #if defined ARDUINO_ARCH_RP2040 + // unsupported yet + SerialPrintCentered("tarGzStreamExpander not supported on RP2040", false, true ); + return true; + #endif + bool ret = false; const char* tarGzFile = "/targz_example.tar.gz"; myPackage.folder = "/"; // for md5 tests @@ -345,7 +381,7 @@ bool test_tarGzStreamExpander() #if defined ESP32 TARGZUnpacker->setTarVerify( true ); // true = enables health checks but slows down the overall process #endif - #if defined ESP8266 + #if defined ESP8266 || defined ARDUINO_ARCH_RP2040 TARGZUnpacker->setTarVerify( false ); // true = enables health checks but slows down the overall process #endif TARGZUnpacker->setupFSCallbacks( targzTotalBytesFn, targzFreeBytesFn ); // prevent the partition from exploding, recommended @@ -474,6 +510,10 @@ void setup() int max_tests = 7; #endif + #if defined ARDUINO_ARCH_RP2040 + // compensate for the dumb port detection + while(!Serial.available() ) yield(); + #endif delay(1000); Serial.println(); @@ -484,10 +524,20 @@ void setup() Serial.println( MiddleLine ); #if defined DEST_FS_USES_SD - #if defined ESP8266 + #if defined ESP8266 // || defined ARDUINO_ARCH_RP2040 SDFSConfig sdConf(4, SD_SCK_MHZ(20) ); tarGzFS.setConfig(sdConf); if (!tarGzFS.begin()) + #elif defined ARDUINO_ARCH_RP2040 + SPI.setRX( SD_MISO ); + SPI.setCS( SD_CS ); + SPI.setSCK( SD_SCK ); + SPI.setTX( SD_MOSI ); + SPI.begin(); + SDFSConfig fileSystemConfig( SD_CS, SD_SCK_MHZ(20) ); + // fileSystemConfig.setSPI( SPI0 ); // default is SPI0 + tarGzFS.setConfig( fileSystemConfig ); + if (!tarGzFS.begin()) #else // ESP32 specific SD settings // if (!tarGzFS.begin( TFCARD_CS_PIN, 16000000 )) if (!tarGzFS.begin()) @@ -512,12 +562,15 @@ void setup() } #endif + + _test_begin: + if( testNum < max_tests ) { Serial.println( MiddleLine ); - SerialPrintfCentered("System Available heap: %d bytes", ESP.getFreeHeap() ); + SerialPrintfCentered("System Available heap: %d bytes", HEAP_AVAILABLE() ); Serial.println( MiddleLine ); - SerialPrintfCentered("ESP ready for test #%d", testNum+1 ); + SerialPrintfCentered("Device ready for test #%d", testNum+1 ); Serial.println( MiddleLine ); } @@ -535,9 +588,11 @@ void setup() case 2: test_succeeded = test_tarGzExpander(); break; case 3: test_succeeded = test_tarGzExpander_no_intermediate(); break; case 4: test_succeeded = test_tarGzStreamExpander(); break; - case 5: test_succeeded = test_gzUpdater(); break; - #if defined ESP32 && !__has_include() - case 6: test_succeeded = test_tarGzStreamUpdater(); break; + #if defined HAS_OTA_SUPPORT + case 5: test_succeeded = test_gzUpdater(); break; + #if defined ESP32 && !__has_include() + case 6: test_succeeded = test_tarGzStreamUpdater(); break; + #endif #endif default: tests_finished = true; @@ -547,9 +602,9 @@ void setup() if( tests_finished ) { EEPROM.write(0, 0 ); EEPROM.commit(); - #if !__has_include() - BaseUnpacker *Unpacker = new BaseUnpacker(); - SPIFFS.begin(); + #if !__has_include() && __has_include() + BaseUnpacker *Unpacker = new BaseUnpacker(); + SPIFFS.begin(); Unpacker->tarGzListDir( SPIFFS, "/", 3 ); #endif SerialPrintCentered("All tests performed, press reset to restart", false, true ); @@ -557,10 +612,13 @@ void setup() if( test_succeeded ) { Serial.println( OpenLine ); - SerialPrintfCentered("Test #%d succeeded, will restart to proceed to the next test", testNum+1); + SerialPrintfCentered("Test #%d finished, will proceed to the next test", testNum+1); Serial.println( CloseLine ); + + testNum++; + goto _test_begin; // go on with next test - ESP.restart(); + // DEVICE_RESTART(); } } diff --git a/library.properties b/library.properties index a11299d..c6f7c0f 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,10 @@ name=ESP32-targz -version=1.1.6 +version=1.1.7 author=tobozo maintainer=tobozo sentence=A library to unpack/uncompress tar, gz, and tar.gz files on ESP32 and ESP8266 paragraph=ESP32-targz is a wrapper for TinyUntar and uzLib to use with fs::FS. It supports streaming and will use ~36KB ram. category=Data Processing url=https://github.com/tobozo/ESP32-targz/ -architectures=esp32,esp8266 +architectures=esp32,esp8266,rp2040,mbed_rp2040 includes=ESP32-targz.h diff --git a/src/ESP32-targz-lib.cpp b/src/ESP32-targz-lib.cpp index 85662b4..b60f492 100644 --- a/src/ESP32-targz-lib.cpp +++ b/src/ESP32-targz-lib.cpp @@ -106,15 +106,19 @@ static uint32_t output_position = 0; //position in output_buffer static uint16_t blockmod = GZIP_BUFF_SIZE / TAR_BLOCK_SIZE; static uint16_t gzTarBlockPos = 0; static size_t tarReadGzStreamBytes = 0; -static bool tarBlockIsUpdateData = false; +#if defined HAS_OTA_SUPPORT + static bool tarBlockIsUpdateData = false; +#endif size_t min_output_buffer_size = 512; #if defined ESP32 static bool unTarDoHealthChecks = true; // set to false for faster writes -#endif -#if defined ESP8266 +#elif defined ESP8266 || defined ARDUINO_ARCH_RP2040 static bool unTarDoHealthChecks = false; // ESP8266 is unstable with health checks void vTaskDelay(int ms) { delay(ms); } // ESP8266 has no OS +#else + static bool unTarDoHealthChecks = false; + void vTaskDelay(int ms) { delay(ms); } #endif @@ -647,7 +651,7 @@ int TarUnpacker::tarHeaderCallBack( TAR::header_translated_t *header, CC_UNUSED untarredFile = tarFS->open(file_path, FILE_WRITE); if(!untarredFile) { - log_e("[ERROR] in tarHeaderCallBack: Could not open [%s] for write.", file_path); + log_e("[ERROR] in tarHeaderCallBack: Could not open [%s] for write, filesystem full?", file_path); setError( ESP32_TARGZ_FS_ERROR ); return ESP32_TARGZ_FS_ERROR; } @@ -742,7 +746,7 @@ int TarUnpacker::tarEndCallBack( TAR::header_translated_t *header, CC_UNUSED int } tarProgressCallback( 100 ); - log_d("Total expanded bytes: %d, heap free: %d", (int)tarTotalSize, ESP.getFreeHeap() ); + log_d("Total expanded bytes: %d, heap free: %d", (int)tarTotalSize, HEAP_AVAILABLE() ); tarMessageCallback( "%s", header->filename ); @@ -760,129 +764,130 @@ int TarUnpacker::tarEndCallBack( TAR::header_translated_t *header, CC_UNUSED int +#if defined HAS_OTA_SUPPORT -int TarUnpacker::tarHeaderUpdateCallBack(TAR::header_translated_t *header, int entry_index, void *context_data) -{ - dump_header(header); - - // https://github.com/tobozo/ESP32-targz/issues/33 - if( header->type == TAR::T_NORMAL || header->type == TAR::T_EXTENDED ) { + int TarUnpacker::tarHeaderUpdateCallBack(TAR::header_translated_t *header, int entry_index, void *context_data) + { + dump_header(header); - int partition = -1; + // https://github.com/tobozo/ESP32-targz/issues/33 + if( header->type == TAR::T_NORMAL || header->type == TAR::T_EXTENDED ) { - if( String( header->filename ).endsWith("ino.bin") ) { - // partition = app - partition = U_FLASH; - } else if( String( header->filename ).endsWith("spiffs.bin") - || String( header->filename ).endsWith("littlefs.bin") - || String( header->filename ).endsWith("ffat.bin") ) { - partition = U_PART; - } else { - // not relevant to Update - // TODO: provide action callbacks (e.g. file has meta info) - return ESP32_TARGZ_OK; - } + int partition = -1; - // check that header->filesize is smaller than partition available size - if( !Update.begin( header->filesize/*UPDATE_SIZE_UNKNOWN*/, partition ) ) { - setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id - return (Update.getError()-20); - } - - tarCurrentFileSize = header->filesize; // for progress - tarCurrentFileSizeProgress = 0; // for progress - tarBlockIsUpdateData = true; + if( String( header->filename ).endsWith("ino.bin") ) { + // partition = app + partition = U_FLASH; + } else if( String( header->filename ).endsWith("spiffs.bin") + || String( header->filename ).endsWith("littlefs.bin") + || String( header->filename ).endsWith("ffat.bin") ) { + partition = U_PART; + } else { + // not relevant to Update + // TODO: provide action callbacks (e.g. file has meta info) + return ESP32_TARGZ_OK; + } - tarTotalSize += header->filesize; - if( tarStatusProgressCallback ) { - tarStatusProgressCallback( header->filename, header->filesize, tarTotalSize ); - } - if( tarTotalSize == header->filesize ) - tarProgressCallback( 0 ); + // check that header->filesize is smaller than partition available size + if( !Update.begin( header->filesize/*UPDATE_SIZE_UNKNOWN*/, partition ) ) { + setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id + return (Update.getError()-20); + } - }/* else { + tarCurrentFileSize = header->filesize; // for progress + tarCurrentFileSizeProgress = 0; // for progress + tarBlockIsUpdateData = true; - switch( header->type ) { - case TAR::T_HARDLINK: log_d("Ignoring hard link to %s.", header->filename); break; - case TAR::T_SYMBOLIC: log_d("Ignoring sym link to %s.", header->filename); break; - case TAR::T_CHARSPECIAL: log_d("Ignoring special char."); break; - case TAR::T_BLOCKSPECIAL: log_d("Ignoring special block."); break; - case TAR::T_DIRECTORY: log_d("Entering %s directory.", header->filename); - //tarMessageCallback( "Entering %s directory\n", header->filename ); - if( tarStatusProgressCallback ) { - tarStatusProgressCallback( header->filename, 0, tarTotalSize ); - } - totalFolders++; - break; - case TAR::T_FIFO: log_d("Ignoring FIFO request."); break; - case TAR::T_CONTIGUOUS: log_d("Ignoring contiguous data to %s.", header->filename); break; - case TAR::T_GLOBALEXTENDED: log_d("Ignoring global extended data."); break; - case TAR::T_EXTENDED: log_d("Ignoring extended data."); break; - case TAR::T_OTHER: default: log_d("Ignoring unrelevant data."); break; - } + tarTotalSize += header->filesize; + if( tarStatusProgressCallback ) { + tarStatusProgressCallback( header->filename, header->filesize, tarTotalSize ); + } + if( tarTotalSize == header->filesize ) + tarProgressCallback( 0 ); + + }/* else { + + switch( header->type ) { + case TAR::T_HARDLINK: log_d("Ignoring hard link to %s.", header->filename); break; + case TAR::T_SYMBOLIC: log_d("Ignoring sym link to %s.", header->filename); break; + case TAR::T_CHARSPECIAL: log_d("Ignoring special char."); break; + case TAR::T_BLOCKSPECIAL: log_d("Ignoring special block."); break; + case TAR::T_DIRECTORY: log_d("Entering %s directory.", header->filename); + //tarMessageCallback( "Entering %s directory\n", header->filename ); + if( tarStatusProgressCallback ) { + tarStatusProgressCallback( header->filename, 0, tarTotalSize ); + } + totalFolders++; + break; + case TAR::T_FIFO: log_d("Ignoring FIFO request."); break; + case TAR::T_CONTIGUOUS: log_d("Ignoring contiguous data to %s.", header->filename); break; + case TAR::T_GLOBALEXTENDED: log_d("Ignoring global extended data."); break; + case TAR::T_EXTENDED: log_d("Ignoring extended data."); break; + case TAR::T_OTHER: default: log_d("Ignoring unrelevant data."); break; + } - }*/ + }*/ - return ESP32_TARGZ_OK; -} + return ESP32_TARGZ_OK; + } -int TarUnpacker::tarEndUpdateCallBack( TAR::header_translated_t *header, int entry_index, void *context_data) -{ - int ret = ESP32_TARGZ_OK; + int TarUnpacker::tarEndUpdateCallBack( TAR::header_translated_t *header, int entry_index, void *context_data) + { + int ret = ESP32_TARGZ_OK; - if ( !Update.end( true ) ) { - Update.printError(Serial); - log_e( "Update Error Occurred. Error #: %u", Update.getError() ); - setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id - return (Update.getError()-20); - } + if ( !Update.end( true ) ) { + Update.printError(Serial); + log_e( "Update Error Occurred. Error #: %u", Update.getError() ); + setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id + return (Update.getError()-20); + } - if ( !Update.isFinished() ) { - log_e("Update incomplete"); - setError( ESP32_TARGZ_UPDATE_INCOMPLETE ); - return ESP32_TARGZ_UPDATE_INCOMPLETE; - } + if ( !Update.isFinished() ) { + log_e("Update incomplete"); + setError( ESP32_TARGZ_UPDATE_INCOMPLETE ); + return ESP32_TARGZ_UPDATE_INCOMPLETE; + } - log_d("Update finished !"); - Update.end(); + log_d("Update finished !"); + Update.end(); - tarBlockIsUpdateData = false; - tarProgressCallback( 100 ); - //log_d("Total expanded bytes: %d, heap free: %d", (int)tarTotalSize, ESP.getFreeHeap() ); - tarMessageCallback( "%s", header->filename ); - totalFiles++; + tarBlockIsUpdateData = false; + tarProgressCallback( 100 ); + //log_d("Total expanded bytes: %d, heap free: %d", (int)tarTotalSize, HEAP_AVAILABLE() ); + tarMessageCallback( "%s", header->filename ); + totalFiles++; - return ret; -} + return ret; + } -int TarUnpacker::tarStreamWriteUpdateCallback(TAR::header_translated_t *header, int entry_index, void *context_data, unsigned char *block, int length) -{ - if( tarBlockIsUpdateData ) { - int wlen = Update.write( block, length ); - if( wlen != length ) { - //tgzLogger("\n"); - log_e("[TAR ERROR] Written length differs from buffer length (unpacked bytes:%d, expected: %d, returned: %d)!\n", untarredBytesCount, length, wlen ); - return ESP32_TARGZ_FS_ERROR; - } - untarredBytesCount+=wlen; - // file unpack progress - log_v("[TAR INFO] tarStreamWriteCallback wrote %d bytes to %s", length, header->filename ); - tarCurrentFileSizeProgress += wlen; - if( tarCurrentFileSize > 0 ) { - // this is a per-file progress, not an overall progress ! - int32_t progress = (100*tarCurrentFileSizeProgress) / tarCurrentFileSize; - if( progress != 100 && progress != 0 ) { - tarProgressCallback( progress ); + int TarUnpacker::tarStreamWriteUpdateCallback(TAR::header_translated_t *header, int entry_index, void *context_data, unsigned char *block, int length) + { + if( tarBlockIsUpdateData ) { + int wlen = Update.write( block, length ); + if( wlen != length ) { + //tgzLogger("\n"); + log_e("[TAR ERROR] Written length differs from buffer length (unpacked bytes:%d, expected: %d, returned: %d)!\n", untarredBytesCount, length, wlen ); + return ESP32_TARGZ_FS_ERROR; + } + untarredBytesCount+=wlen; + // file unpack progress + log_v("[TAR INFO] tarStreamWriteCallback wrote %d bytes to %s", length, header->filename ); + tarCurrentFileSizeProgress += wlen; + if( tarCurrentFileSize > 0 ) { + // this is a per-file progress, not an overall progress ! + int32_t progress = (100*tarCurrentFileSizeProgress) / tarCurrentFileSize; + if( progress != 100 && progress != 0 ) { + tarProgressCallback( progress ); + } } } + return ESP32_TARGZ_OK; } - return ESP32_TARGZ_OK; -} - +#endif @@ -1049,17 +1054,21 @@ void GzUnpacker::gzExpanderCleanup() } -// gzWriteCallback -bool GzUnpacker::gzUpdateWriteCallback( unsigned char* buff, size_t buffsize ) -{ - if( Update.write( buff, buffsize ) ) { - log_v("Wrote %d bytes", buffsize ); - return true; - } else { - log_e("Failed to write %d bytes", buffsize ); +#if defined HAS_OTA_SUPPORT + + // gzWriteCallback + bool GzUnpacker::gzUpdateWriteCallback( unsigned char* buff, size_t buffsize ) + { + if( Update.write( buff, buffsize ) ) { + log_v("Wrote %d bytes", buffsize ); + return true; + } else { + log_e("Failed to write %d bytes", buffsize ); + } + return false; } - return false; -} + +#endif // gzWriteCallback @@ -1199,8 +1208,7 @@ int GzUnpacker::gzUncompress( bool isupdate, bool stream_to_tar, bool use_dict, #if defined ESP32 size_t output_buffer_size = SPI_FLASH_SEC_SIZE; // SPI_FLASH_SEC_SIZE = 4Kb - #endif - #if defined ESP8266 + #elif defined ESP8266 || defined ARDUINO_ARCH_RP2040 size_t output_buffer_size = min_output_buffer_size; // must be a multiple of 512 (tar block size) #endif @@ -1218,14 +1226,14 @@ int GzUnpacker::gzUncompress( bool isupdate, bool stream_to_tar, bool use_dict, } if( uzlib_gzip_dict == NULL ) { - log_e("[ERROR] can't alloc %d bytes for gzip dict (%d bytes free)", GZIP_DICT_SIZE, ESP.getFreeHeap() ); + log_e("[ERROR] can't alloc %d bytes for gzip dict (%d bytes free)", GZIP_DICT_SIZE, HEAP_AVAILABLE() ); gzExpanderCleanup(); return ESP32_TARGZ_UZLIB_MALLOC_FAIL; // TODO : Number this error } uzlib_dict_size = GZIP_DICT_SIZE; uzLibDecompressor.readDestByte = NULL; - log_v("[INFO] gzUncompress tradeoff: faster, used %d bytes of ram (heap after alloc: %d)", GZIP_DICT_SIZE+output_buffer_size, ESP.getFreeHeap()); - //log_w("[%d] alloc() done", ESP.getFreeHeap() ); + log_v("[INFO] gzUncompress tradeoff: faster, used %d bytes of ram (heap after alloc: %d)", GZIP_DICT_SIZE+output_buffer_size, HEAP_AVAILABLE()); + //log_w("[%d] alloc() done", HEAP_AVAILABLE() ); } else { if( stream_to_tar ) { log_e("[ERROR] gz->tar->filesystem streaming requires a gzip dictionnnary"); @@ -1235,7 +1243,7 @@ int GzUnpacker::gzUncompress( bool isupdate, bool stream_to_tar, bool use_dict, log_v("[INFO] gz output is file"); } //output_buffer_size = SPI_FLASH_SEC_SIZE; - log_v("[INFO] gzUncompress tradeoff: slower will use %d bytes of ram (heap before alloc: %d)", output_buffer_size, ESP.getFreeHeap()); + log_v("[INFO] gzUncompress tradeoff: slower will use %d bytes of ram (heap before alloc: %d)", output_buffer_size, HEAP_AVAILABLE()); uzlib_gzip_dict = NULL; uzlib_dict_size = 0; } @@ -1375,19 +1383,19 @@ bool GzUnpacker::gzExpander( fs::FS sourceFS, const char* sourceFile, fs::FS des bool stream_to_tar = false; bool gz_use_dict = true; - if( ESP.getFreeHeap() < GZIP_DICT_SIZE+GZIP_BUFF_SIZE ) { - size_t free_min_heap_blocks = ESP.getFreeHeap() / 512; // leave 1k heap, eat all the rest ! + if( HEAP_AVAILABLE() < GZIP_DICT_SIZE+GZIP_BUFF_SIZE ) { + size_t free_min_heap_blocks = HEAP_AVAILABLE() / 512; // leave 1k heap, eat all the rest ! if( free_min_heap_blocks <1 ) { setError( ESP32_TARGZ_HEAP_TOO_LOW ); return false; } min_output_buffer_size = free_min_heap_blocks * 512; if( min_output_buffer_size > GZIP_BUFF_SIZE ) min_output_buffer_size = GZIP_BUFF_SIZE; - log_w("Disabling GZIP Dictionary (heap wanted:%d, available: %d, buffer: %d bytes), writes will be slow", ESP.getFreeHeap(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE, min_output_buffer_size ); + log_w("Disabling GZIP Dictionary (heap wanted:%d, available: %d, buffer: %d bytes), writes will be slow", HEAP_AVAILABLE(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE, min_output_buffer_size ); gz_use_dict = false; // } else { - log_d("Current heap budget (available:%d, needed:%d)", ESP.getFreeHeap(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE ); + log_d("Current heap budget (available:%d, needed:%d)", HEAP_AVAILABLE(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE ); } if( destFile == nullptr ) { @@ -1483,20 +1491,19 @@ bool GzUnpacker::gzStreamExpander( Stream *stream, size_t gz_size ) return false; } // TODO: ESP8266 support - #if defined ESP8266 - log_e("gz stream expanding not implemented on ESP8266"); + #if defined ESP8266 || defined ARDUINO_ARCH_RP2040 + log_e("gz stream expanding not implemented on ESP8266/RP2040"); return false; - #endif - #if defined ESP32 + #elif defined ESP32 bool show_progress = false; bool use_dict = true; bool isupdate = false; bool stream_to_tar = false; - if( ESP.getFreeHeap() < GZIP_DICT_SIZE+GZIP_BUFF_SIZE ) { - // log_w("Disabling gzip dictionnary (havailable:%d, needed:%d)", ESP.getFreeHeap(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE ); - log_w("Insufficient heap to decompress (available:%d, needed:%d), aborting", ESP.getFreeHeap(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE ); + if( HEAP_AVAILABLE() < GZIP_DICT_SIZE+GZIP_BUFF_SIZE ) { + // log_w("Disabling gzip dictionnary (havailable:%d, needed:%d)", HEAP_AVAILABLE(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE ); + log_w("Insufficient heap to decompress (available:%d, needed:%d), aborting", HEAP_AVAILABLE(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE ); setError( ESP32_TARGZ_HEAP_TOO_LOW ); return false; } @@ -1530,216 +1537,216 @@ bool GzUnpacker::gzStreamExpander( Stream *stream, size_t gz_size ) +#if defined HAS_OTA_SUPPORT + // uncompress gz file to flash (expected to be a valid gzipped firmware) + bool GzUnpacker::gzUpdater( fs::FS &fs, const char* gz_filename, int partition, bool restart_on_update ) + { + tarGzClearError(); + initFSCallbacks(); + if (!tgzLogger ) { + setLoggerCallback( targzPrintLoggerCallback ); + } + // ESP8266 does not need such check as the unpacker is in the bootloader -// uncompress gz file to flash (expected to be a valid gzipped firmware) -bool GzUnpacker::gzUpdater( fs::FS &fs, const char* gz_filename, int partition, bool restart_on_update ) -{ - tarGzClearError(); - initFSCallbacks(); - if (!tgzLogger ) { - setLoggerCallback( targzPrintLoggerCallback ); - } - // ESP8266 does not need such check as the unpacker is in the bootloader - - if( !fs.exists( gz_filename ) ) { - log_e("[ERROR] in gzUpdater: %s does not exist", gz_filename); - setError( ESP32_TARGZ_UZLIB_INVALID_FILE ); - return false; + if( !fs.exists( gz_filename ) ) { + log_e("[ERROR] in gzUpdater: %s does not exist", gz_filename); + setError( ESP32_TARGZ_UZLIB_INVALID_FILE ); + return false; + } + log_v("uzLib SPIFFS Updater start!"); + fs::File gz = fs.open( gz_filename, FILE_READ ); + #if defined ESP8266 + int update_size = gz.size(); + #endif + #if defined ESP32 + int update_size = UPDATE_SIZE_UNKNOWN; + #endif + return gzStreamUpdater( (Stream*)&gz, update_size, partition, restart_on_update ); } - log_v("uzLib SPIFFS Updater start!"); - fs::File gz = fs.open( gz_filename, FILE_READ ); - #if defined ESP8266 - int update_size = gz.size(); - #endif - #if defined ESP32 - int update_size = UPDATE_SIZE_UNKNOWN; - #endif - return gzStreamUpdater( (Stream*)&gz, update_size, partition, restart_on_update ); -} -// uncompress gz stream (file or HTTP) to flash (expected to be a valid Arduino compiled binary sketch) -bool GzUnpacker::gzStreamUpdater( Stream *stream, size_t update_size, int partition, bool restart_on_update ) -{ - if( !gzProgressCallback ) { - setGzProgressCallback( defaultProgressCallback ); - } - if( !tgzLogger ) { - setLoggerCallback( targzPrintLoggerCallback ); - } - - size_t size = stream->available(); - if( ! size ) { - log_e("Bad stream, aborting"); - setError( ESP32_TARGZ_STREAM_ERROR ); - return false; - } - - #ifdef ESP8266 - // ESP8266 has built-in uzlib so no need to uncompress, just update with gzData - bool use_buffered_writes = false; // use buffered writes when the gz size is unknown, otherwise use stream writes - size_t stream_size = 0; - - if( int( update_size ) < 1 ) { - use_buffered_writes = true; - stream_size = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; - log_d("Stream size is unknown, aligning update to the partition available size: %d ", stream_size ); - } else { - Update.runAsync( true ); - stream_size = update_size; - log_v("Stream size is %d", stream_size ); + // uncompress gz stream (file or HTTP) to flash (expected to be a valid Arduino compiled binary sketch) + bool GzUnpacker::gzStreamUpdater( Stream *stream, size_t update_size, int partition, bool restart_on_update ) + { + if( !gzProgressCallback ) { + setGzProgressCallback( defaultProgressCallback ); + } + if( !tgzLogger ) { + setLoggerCallback( targzPrintLoggerCallback ); } - if ( !Update.begin( stream_size, partition )) { // U_FLASH or U_PART - log_e("Can't begin update"); - setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id + size_t size = stream->available(); + if( ! size ) { + log_e("Bad stream, aborting"); + setError( ESP32_TARGZ_STREAM_ERROR ); return false; } - if( !use_buffered_writes ) { - // stream method - static bool finished = false; - // async progress - Update.onProgress([]( size_t done, size_t total ) { - size_t progress = (100*done)/total; - if(! finished ) gzProgressCallback( progress ); - if( progress == 100 ) finished = true; - }); - // walk stream - while( stream->available() ) { - if(! Update.writeStream( *stream ) ) { - log_e("Update couldn't read stream"); - setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id - return false; - } - if( finished ) break; - yield(); + #ifdef ESP8266 + // ESP8266 has built-in uzlib so no need to uncompress, just update with gzData + bool use_buffered_writes = false; // use buffered writes when the gz size is unknown, otherwise use stream writes + size_t stream_size = 0; + + if( int( update_size ) < 1 ) { + use_buffered_writes = true; + stream_size = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; + log_d("Stream size is unknown, aligning update to the partition available size: %d ", stream_size ); + } else { + Update.runAsync( true ); + stream_size = update_size; + log_v("Stream size is %d", stream_size ); } - } else { - // buffered writes method, uses 4KB ram - // TODO: make this adjustable - size_t buffsize = 4096; - uint8_t *buffer = new uint8_t[4096]; - if( !buffer) { - log_e("Could not allocate %d bytes for stream read", buffsize ); - setError( ESP32_TARGZ_HEAP_TOO_LOW ); + + if ( !Update.begin( stream_size, partition )) { // U_FLASH or U_PART + log_e("Can't begin update"); + setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id return false; } - uint8_t progress = 0; - gzProgressCallback( progress ); - - while( stream->available() ) { - size_t len = stream->readBytes( buffer, buffsize ); - if( len < 1 ) break; // end of stream - if (Update.write(buffer, len) != len) { - log_e("Updater could not write %d bytes", len ); - setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id + + if( !use_buffered_writes ) { + // stream method + static bool finished = false; + // async progress + Update.onProgress([]( size_t done, size_t total ) { + size_t progress = (100*done)/total; + if(! finished ) gzProgressCallback( progress ); + if( progress == 100 ) finished = true; + }); + // walk stream + while( stream->available() ) { + if(! Update.writeStream( *stream ) ) { + log_e("Update couldn't read stream"); + setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id + return false; + } + if( finished ) break; + yield(); + } + } else { + // buffered writes method, uses 4KB ram + // TODO: make this adjustable + size_t buffsize = 4096; + uint8_t *buffer = new uint8_t[4096]; + if( !buffer) { + log_e("Could not allocate %d bytes for stream read", buffsize ); + setError( ESP32_TARGZ_HEAP_TOO_LOW ); return false; - } else { - progress = (Update.progress()*100)/Update.size(); - gzProgressCallback( progress ); } - yield(); - } - if( progress != 100 ) { - gzProgressCallback( 100 ); + uint8_t progress = 0; + gzProgressCallback( progress ); + + while( stream->available() ) { + size_t len = stream->readBytes( buffer, buffsize ); + if( len < 1 ) break; // end of stream + if (Update.write(buffer, len) != len) { + log_e("Updater could not write %d bytes", len ); + setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id + return false; + } else { + progress = (Update.progress()*100)/Update.size(); + gzProgressCallback( progress ); + } + yield(); + } + if( progress != 100 ) { + gzProgressCallback( 100 ); + } + delete buffer; } - delete buffer; - } - if ( !Update.end( true ) ) { - Update.printError(Serial); - log_e( "Update Error Occurred. Error #: %u", Update.getError() ); - setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id - return false; - } + if ( !Update.end( true ) ) { + Update.printError(Serial); + log_e( "Update Error Occurred. Error #: %u", Update.getError() ); + setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id + return false; + } - if ( !Update.isFinished() ) { - log_e("Update incomplete"); - setError( ESP32_TARGZ_UPDATE_INCOMPLETE ); - return false; - } + if ( !Update.isFinished() ) { + log_e("Update incomplete"); + setError( ESP32_TARGZ_UPDATE_INCOMPLETE ); + return false; + } - log_v("Update finished !"); - if( restart_on_update ) ESP.restart(); - #endif // ifdef ESP8266 + log_v("Update finished !"); + if( restart_on_update ) ESP.restart(); + #endif // ifdef ESP8266 - #if defined ESP32 - // unfortunately ESP32 doesn't handle gzipped firmware from the bootloader - bool show_progress = false; - bool use_dict = true; - bool isupdate = true; - bool stream_to_tar = false; + #if defined ESP32 + // unfortunately ESP32 doesn't handle gzipped firmware from the bootloader + bool show_progress = false; + bool use_dict = true; + bool isupdate = true; + bool stream_to_tar = false; - if( ESP.getFreeHeap() < GZIP_DICT_SIZE+GZIP_BUFF_SIZE ) { - // log_w("Disabling gzip dictionnary (havailable:%d, needed:%d)", ESP.getFreeHeap(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE ); - log_w("Insufficient heap to decompress (available:%d, needed:%d), aborting", ESP.getFreeHeap(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE ); - setError( ESP32_TARGZ_HEAP_TOO_LOW ); - return false; - } + if( HEAP_AVAILABLE() < GZIP_DICT_SIZE+GZIP_BUFF_SIZE ) { + // log_w("Disabling gzip dictionnary (havailable:%d, needed:%d)", HEAP_AVAILABLE(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE ); + log_w("Insufficient heap to decompress (available:%d, needed:%d), aborting", HEAP_AVAILABLE(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE ); + setError( ESP32_TARGZ_HEAP_TOO_LOW ); + return false; + } - tarGzIO.gz = stream; - if( gzWriteCallback == nullptr ) { - setStreamWriter( gzUpdateWriteCallback ); - } + tarGzIO.gz = stream; + if( gzWriteCallback == nullptr ) { + setStreamWriter( gzUpdateWriteCallback ); + } - Update.onProgress([]( size_t done, size_t total ) { - gzProgressCallback( (100*done)/total ); - }); + Update.onProgress([]( size_t done, size_t total ) { + gzProgressCallback( (100*done)/total ); + }); - if( int( update_size ) < 1 || update_size == UPDATE_SIZE_UNKNOWN ) { - tgzLogger("[GZUpdater] Starting update with unknown binary size\n"); - if( !Update.begin( UPDATE_SIZE_UNKNOWN, partition ) ) { - setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id - return false; + if( int( update_size ) < 1 || update_size == UPDATE_SIZE_UNKNOWN ) { + tgzLogger("[GZUpdater] Starting update with unknown binary size\n"); + if( !Update.begin( UPDATE_SIZE_UNKNOWN, partition ) ) { + setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id + return false; + } + } else { + tgzLogger("[GZUpdater] Starting update\n"); + if( !Update.begin( ( ( update_size + SPI_FLASH_SEC_SIZE-1 ) & ~( SPI_FLASH_SEC_SIZE-1 ) ), partition ) ) { + setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id + return false; + } } - } else { - tgzLogger("[GZUpdater] Starting update\n"); - if( !Update.begin( ( ( update_size + SPI_FLASH_SEC_SIZE-1 ) & ~( SPI_FLASH_SEC_SIZE-1 ) ), partition ) ) { - setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id + // process with unzipping + int ret = gzUncompress( isupdate, stream_to_tar, use_dict, show_progress ); + + // unzipping ended + if( ret!=0 ) { + log_e("gzHTTPUpdater returned error code %d", ret); + setError( (tarGzErrorCode)ret ); return false; } - } - // process with unzipping - int ret = gzUncompress( isupdate, stream_to_tar, use_dict, show_progress ); - // unzipping ended - if( ret!=0 ) { - log_e("gzHTTPUpdater returned error code %d", ret); - setError( (tarGzErrorCode)ret ); - return false; - } - - if ( Update.end( true ) ) { - log_v( "OTA done!" ); - if ( Update.isFinished() ) { - // yay - log_v("Update finished !"); - gzProgressCallback( 100 ); - if( restart_on_update ) ESP.restart(); + if ( Update.end( true ) ) { + log_v( "OTA done!" ); + if ( Update.isFinished() ) { + // yay + log_v("Update finished !"); + gzProgressCallback( 100 ); + if( restart_on_update ) ESP.restart(); + } else { + log_e( "Update not finished? Something went wrong!" ); + setError( ESP32_TARGZ_UPDATE_INCOMPLETE ); + return false; + } } else { - log_e( "Update not finished? Something went wrong!" ); - setError( ESP32_TARGZ_UPDATE_INCOMPLETE ); + log_e( "Update Error Occurred. Error #: %u", Update.getError() ); + setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id return false; } - } else { - log_e( "Update Error Occurred. Error #: %u", Update.getError() ); - setError( (tarGzErrorCode)(Update.getError()-20) ); // "-20" offset is Update error id to esp32-targz error id - return false; - } - log_v("uzLib filesystem Updater finished!"); - setError( (tarGzErrorCode)ret ); - #endif // ifdef ESP32 - - return true; -} + log_v("uzLib filesystem Updater finished!"); + setError( (tarGzErrorCode)ret ); + #endif // ifdef ESP32 + return true; + } +#endif // defined HAS_OTA_SUPPORT @@ -1876,12 +1883,12 @@ bool TarGzUnpacker::tarGzExpanderNoTempFile( fs::FS sourceFS, const char* source setGzProgressCallback( targzNullProgressCallback ); } - if( ESP.getFreeHeap() < GZIP_DICT_SIZE+GZIP_BUFF_SIZE ) { - log_e("Insufficient heap to decompress (available:%d, needed:%d), aborting", ESP.getFreeHeap(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE ); + if( HEAP_AVAILABLE() < GZIP_DICT_SIZE+GZIP_BUFF_SIZE ) { + log_e("Insufficient heap to decompress (available:%d, needed:%d), aborting", HEAP_AVAILABLE(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE ); setError( ESP32_TARGZ_HEAP_TOO_LOW ); return false; } else { - log_d("Current heap budget (available:%d, needed:%d)", ESP.getFreeHeap(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE ); + log_d("Current heap budget (available:%d, needed:%d)", HEAP_AVAILABLE(), GZIP_DICT_SIZE+GZIP_BUFF_SIZE ); } if( !sourceFS.exists( sourceFile ) ) { log_e("gzip file %s does not exist", sourceFile); @@ -1967,7 +1974,7 @@ bool TarGzUnpacker::tarGzExpander( fs::FS sourceFS, const char* sourceFile, fs:: mkdirp( &destFS, tempFile ); if( gzExpander(sourceFS, sourceFile, destFS, tempFile) ) { - log_d("[INFO] heap before tar-expanding: %d)", ESP.getFreeHeap()); + log_d("[INFO] heap before tar-expanding: %d)", HEAP_AVAILABLE()); if( tarExpander(destFS, tempFile, destFS, destFolder) ) { // yay } @@ -1988,58 +1995,63 @@ bool TarGzUnpacker::tarGzExpander( fs::FS sourceFS, const char* sourceFile, fs:: } } -bool TarGzUnpacker::tarGzStreamUpdater( Stream *stream ) -{ - if( !stream->available() ) { - log_e("Bad stream, aborting"); - setError( ESP32_TARGZ_STREAM_ERROR ); - return false; - } - if( !gzProgressCallback ) { - setGzProgressCallback( defaultProgressCallback ); - } - tarGzIO.gz = stream; - tarFS = nullptr; - untarredBytesCount = 0; - gzTarBlockPos = 0; - tarCallbacks = { - tarHeaderUpdateCallBack, - gzFeedTarBuffer, - tarStreamWriteUpdateCallback, - tarEndUpdateCallBack - }; +#if defined HAS_OTA_SUPPORT - TAR::tar_error_logger = tgzLogger; // targzPrintLoggerCallback or tgzLogger - TAR::tar_debug_logger = tgzLogger; // comment this out if too verbose + bool TarGzUnpacker::tarGzStreamUpdater( Stream *stream ) + { + if( !stream->available() ) { + log_e("Bad stream, aborting"); + setError( ESP32_TARGZ_STREAM_ERROR ); + return false; + } + if( !gzProgressCallback ) { + setGzProgressCallback( defaultProgressCallback ); + } + tarGzIO.gz = stream; + tarFS = nullptr; + untarredBytesCount = 0; + gzTarBlockPos = 0; - if( gzWriteCallback == nullptr ) { - setStreamWriter( gzProcessTarBuffer ); - } - //gzWriteCallback = &gzProcessTarBuffer; + tarCallbacks = { + tarHeaderUpdateCallBack, + gzFeedTarBuffer, + tarStreamWriteUpdateCallback, + tarEndUpdateCallBack + }; - totalFiles = 0; - totalFolders = 0; + TAR::tar_error_logger = tgzLogger; // targzPrintLoggerCallback or tgzLogger + TAR::tar_debug_logger = tgzLogger; // comment this out if too verbose - firstblock = true; // trigger TAR setup from gzUncompress callback - lastblock = false; + if( gzWriteCallback == nullptr ) { + setStreamWriter( gzProcessTarBuffer ); + } + //gzWriteCallback = &gzProcessTarBuffer; - bool isupdate = true; - bool stream_to_tar = false; - bool use_dict = true; - bool show_progress = false; + totalFiles = 0; + totalFolders = 0; - int ret = gzUncompress( isupdate, stream_to_tar, use_dict, show_progress ); + firstblock = true; // trigger TAR setup from gzUncompress callback + lastblock = false; - if( ret!=0 ) { - log_e("gzUncompress returned error code %d", ret); - setError( (tarGzErrorCode)ret ); - return false; - } + bool isupdate = true; + bool stream_to_tar = false; + bool use_dict = true; + bool show_progress = false; - return true; + int ret = gzUncompress( isupdate, stream_to_tar, use_dict, show_progress ); -} + if( ret!=0 ) { + log_e("gzUncompress returned error code %d", ret); + setError( (tarGzErrorCode)ret ); + return false; + } + + return true; + + } + +#endif // defined HAS_OTA_SUPPORT // uncompress tar+gz stream (file or HTTP) to filesystem without intermediate tar file @@ -2110,13 +2122,13 @@ bool TarGzUnpacker::tarGzStreamExpander( Stream *stream, fs::FS &destFS, const c - #if defined ESP8266 + #if defined ESP8266 || defined ARDUINO_ARCH_RP2040 // calculate minimal ram for gzip - int available_heap = (ESP.getFreeHeap()-(GZIP_DICT_SIZE+min_output_buffer_size+2048)); // leave 1k heap for the stack + int available_heap = (HEAP_AVAILABLE()-(GZIP_DICT_SIZE+min_output_buffer_size+2048)); // leave 1k heap for the stack if( available_heap < 0 ) { use_dict = false; - available_heap = (ESP.getFreeHeap()-(min_output_buffer_size+2048)); // leave 1k heap for the stack + available_heap = (HEAP_AVAILABLE()-(min_output_buffer_size+2048)); // leave 1k heap for the stack if( available_heap < 0 ) { setError( ESP32_TARGZ_HEAP_TOO_LOW ); return false; @@ -2133,7 +2145,7 @@ bool TarGzUnpacker::tarGzStreamExpander( Stream *stream, fs::FS &destFS, const c min_output_buffer_size = free_min_heap_blocks * 1024; if( min_output_buffer_size > GZIP_BUFF_SIZE ) min_output_buffer_size = GZIP_BUFF_SIZE; // cap to 4096 - min_output_buffer_size = 4096; + //min_output_buffer_size = 4096; blockmod = min_output_buffer_size / TAR_BLOCK_SIZE; tgzLogger("tarGzStreamExpander will unpack stream to %s folder using %d buffered bytes and %s dictionary\n", destFolder, min_output_buffer_size, use_dict ? "36Kb for the" : "NO" ); @@ -2237,7 +2249,7 @@ bool TarGzUnpacker::tarGzStreamExpander( Stream *stream, fs::FS &destFS, const c int ret = gzUnpacker.gzUncompress( true/*isupdate*/, false/*stream_to_tar*/, true/*use_dict*/, false/*show_progress*/ ); if( ret!=0 ) { - log_e("gzUncompress returned error code %d (free heap=%d bytes)", ret, ESP.getFreeHeap() ); + log_e("gzUncompress returned error code %d (free heap=%d bytes)", ret, HEAP_AVAILABLE() ); //gzUnpacker.setError( (tarGzErrorCode)ret ); return 0; } diff --git a/src/ESP32-targz-lib.hpp b/src/ESP32-targz-lib.hpp index 7739d09..d45b574 100644 --- a/src/ESP32-targz-lib.hpp +++ b/src/ESP32-targz-lib.hpp @@ -42,15 +42,18 @@ #include - #if defined( ESP32 ) #include + #define HAS_OTA_SUPPORT #elif defined( ESP8266 ) //#ifdef USE_LittleFS // #define SPIFFS LittleFS // #include //#endif #include + #define HAS_OTA_SUPPORT +#elif defined ARDUINO_ARCH_RP2040 + // TODO: RP2040 OTA implementation? #else #error Unsupported architecture #endif @@ -186,10 +189,10 @@ struct BaseUnpacker void setLoggerCallback( genericLoggerCallback cb ); void setupFSCallbacks( fsTotalBytesCb cbt, fsFreeBytesCb cbf ); // setup abstract filesystem helpers (totalBytes, freeBytes) #ifdef ESP8266 - void printDirectory(fs::FS &fs, File dir, int numTabs, uint8_t levels, bool hexDump); + void printDirectory(fs::FS &fs, File dir, int numTabs, uint8_t levels, bool hexDump); #endif #ifdef ESP32 - bool setPsram( bool enable ); + bool setPsram( bool enable ); #endif static const char* targzFSFilePath( fs::File *file ) { #if defined ESP_IDF_VERSION_MAJOR && ESP_IDF_VERSION_MAJOR >= 4 @@ -229,13 +232,15 @@ struct TarUnpacker : virtual public BaseUnpacker static int tarStreamReadCallback( unsigned char* buff, size_t buffsize ); static int tarStreamWriteCallback( TAR::header_translated_t *header, int entry_index, void *context_data, unsigned char *block, int length); - static int tarStreamWriteUpdateCallback(TAR::header_translated_t *header, int entry_index, void *context_data, unsigned char *block, int length); static int tarHeaderCallBack(TAR::header_translated_t *header, int entry_index, void *context_data); static int tarEndCallBack( TAR::header_translated_t *header, int entry_index, void *context_data); - static int tarHeaderUpdateCallBack(TAR::header_translated_t *header, int entry_index, void *context_data); - static int tarEndUpdateCallBack( TAR::header_translated_t *header, int entry_index, void *context_data); + #if defined HAS_OTA_SUPPORT + static int tarHeaderUpdateCallBack(TAR::header_translated_t *header, int entry_index, void *context_data); + static int tarEndUpdateCallBack( TAR::header_translated_t *header, int entry_index, void *context_data); + static int tarStreamWriteUpdateCallback(TAR::header_translated_t *header, int entry_index, void *context_data, unsigned char *block, int length); + #endif }; @@ -246,8 +251,6 @@ struct GzUnpacker : virtual public BaseUnpacker bool gzExpander( fs::FS sourceFS, const char* sourceFile, fs::FS destFS, const char* destFile = nullptr ); //TODO: gzStreamExpander( Stream* sourceStream, fs::FS destFS, const char* destFile ); bool gzStreamExpander( Stream *stream, size_t gz_size = 0 ); // use with setStreamWriter - bool gzUpdater( fs::FS &sourceFS, const char* gz_filename, int partition = U_FLASH, bool restart_on_update = true ); // flashes the ESP with the content of a *gzipped* file - bool gzStreamUpdater( Stream *stream, size_t update_size = 0, int partition = U_FLASH, bool restart_on_update = true ); // flashes the ESP from a gzip stream (file or http), no progress callbacks void setGzProgressCallback( genericProgressCallback cb ); void setGzMessageCallback( genericLoggerCallback cb ); //void setStreamReader( gzStreamReader cb ); // optional, use with gzStreamExpander @@ -255,13 +258,16 @@ struct GzUnpacker : virtual public BaseUnpacker void setDestByteReader( gzDestByteReader cb ); void gzExpanderCleanup(); int gzUncompress( bool isupdate = false, bool stream_to_tar = false, bool use_dict = true, bool show_progress = true ); - - static bool gzUpdateWriteCallback( unsigned char* buff, size_t buffsize ); static bool gzStreamWriteCallback( unsigned char* buff, size_t buffsize ); static bool gzReadHeader(fs::File &gzFile); static uint8_t gzReadByte(fs::File &gzFile, const int32_t addr, fs::SeekMode mode=fs::SeekSet); static unsigned int gzReadDestByteFS(int offset, unsigned char *out); static unsigned int gzReadSourceByte(struct GZ::TINF_DATA *data, unsigned char *out); + #if defined HAS_OTA_SUPPORT + bool gzUpdater( fs::FS &sourceFS, const char* gz_filename, int partition = U_FLASH, bool restart_on_update = true ); // flashes the ESP with the content of a *gzipped* file + bool gzStreamUpdater( Stream *stream, size_t update_size = 0, int partition = U_FLASH, bool restart_on_update = true ); // flashes the ESP from a gzip stream, no progress callback + static bool gzUpdateWriteCallback( unsigned char* buff, size_t buffsize ); + #endif }; @@ -273,14 +279,18 @@ struct TarGzUnpacker : public TarUnpacker, public GzUnpacker bool tarGzExpander( fs::FS sourceFS, const char* sourceFile, fs::FS destFS, const char* destFolder="/tmp", const char* tempFile = "/tmp/data.tar" ); // same as tarGzExpander but without intermediate file bool tarGzExpanderNoTempFile( fs::FS sourceFS, const char* sourceFile, fs::FS destFS, const char* destFolder="/tmp" ); - // requirements: targz archive must contain files with names suffixed by ".ino.bin" and/or ".spiffs.bin" - bool tarGzStreamUpdater( Stream *stream ); // unpack stream://fileName.tar.gz contents to destFS::/destFolder/ bool tarGzStreamExpander( Stream *stream, fs::FS &destFs, const char* destFolder = "/", int64_t streamSize = -1 ); static bool gzProcessTarBuffer( unsigned char* buff, size_t buffsize ); static int tarReadGzStream( unsigned char* buff, size_t buffsize ); static int gzFeedTarBuffer( unsigned char* buff, size_t buffsize ); + + #if defined HAS_OTA_SUPPORT + // requirements: targz archive must contain files with names suffixed by ".ino.bin" and/or ".spiffs.bin" + bool tarGzStreamUpdater( Stream *stream ); + #endif + }; @@ -288,7 +298,6 @@ struct TarGzUnpacker : public TarUnpacker, public GzUnpacker #if defined ESP32 - // this class was inspired by https://github.com/vortigont/esp32-flashz class GzUpdateClass : public UpdateClass { @@ -367,44 +376,6 @@ struct TarGzUnpacker : public TarUnpacker, public GzUnpacker #endif - -#ifdef ESP8266 - // some ESP32 => ESP8266 syntax shim - - #define U_PART U_FS - #define ARDUHAL_LOG_FORMAT(letter, format) "[" #letter "][%s:%u] %s(): " format "\r\n", __FILE__, __LINE__, __FUNCTION__ - - #if defined DEBUG_ESP_PORT - // always show errors/warnings when debug port is there - #define log_n(format, ...) DEBUG_ESP_PORT.printf(ARDUHAL_LOG_FORMAT(N, format), ##__VA_ARGS__); - #define log_e(format, ...) DEBUG_ESP_PORT.printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__); - #define log_w(format, ...) DEBUG_ESP_PORT.printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__); - - #if defined DEBUG_ESP_CORE - // be verbose - #define log_i(format, ...) DEBUG_ESP_PORT.printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__); - #define log_d(format, ...) DEBUG_ESP_PORT.printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__); - #define log_v(format, ...) DEBUG_ESP_PORT.printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__); - #else - // don't be verbose, only errors+warnings - #define log_i BaseUnpacker::targzNullLoggerCallback - #define log_d BaseUnpacker::targzNullLoggerCallback - #define log_v BaseUnpacker::targzNullLoggerCallback - #endif - - #else - #define log_n BaseUnpacker::targzNullLoggerCallback - #define log_e BaseUnpacker::targzNullLoggerCallback - #define log_w BaseUnpacker::targzNullLoggerCallback - #define log_i BaseUnpacker::targzNullLoggerCallback - #define log_d BaseUnpacker::targzNullLoggerCallback - #define log_v BaseUnpacker::targzNullLoggerCallback - #endif - -#else - #define U_PART U_SPIFFS -#endif - // md5sum (essentially for debug) #include "helpers/md5_sum.h" // helpers: mkdir, mkpath, dirname diff --git a/src/ESP32-targz-log.hpp b/src/ESP32-targz-log.hpp new file mode 100644 index 0000000..ec69a9c --- /dev/null +++ b/src/ESP32-targz-log.hpp @@ -0,0 +1,205 @@ +#pragma once +/* + * + * ESP32-yaml + * Project Page: https://github.com/tobozo/esp32-yaml + * + * Copyright 2022 tobozo http://github.com/tobozo + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files ("ESP32-yaml"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +// Emit logs in arduino style at software level rather than firmware level + +#pragma once + + +#if defined ESP8266 || defined ESP32 + // those have OTA and common device API + #define HEAP_AVAILABLE() ESP.getFreeHeap() + #define DEVICE_RESTART() ESP.restart() + + #ifdef ESP8266 + // some ESP32 => ESP8266 syntax shim + + #define U_PART U_FS + #define ARDUHAL_LOG_FORMAT(letter, format) "[" #letter "][%s:%u] %s(): " format "\r\n", __FILE__, __LINE__, __FUNCTION__ + + #if defined DEBUG_ESP_PORT + // always show errors/warnings when debug port is there + #define log_n(format, ...) DEBUG_ESP_PORT.printf(ARDUHAL_LOG_FORMAT(N, format), ##__VA_ARGS__); + #define log_e(format, ...) DEBUG_ESP_PORT.printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__); + #define log_w(format, ...) DEBUG_ESP_PORT.printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__); + + #if defined DEBUG_ESP_CORE + // be verbose + #define log_i(format, ...) DEBUG_ESP_PORT.printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__); + #define log_d(format, ...) DEBUG_ESP_PORT.printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__); + #define log_v(format, ...) DEBUG_ESP_PORT.printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__); + #else + // don't be verbose, only errors+warnings + #define log_i BaseUnpacker::targzNullLoggerCallback + #define log_d BaseUnpacker::targzNullLoggerCallback + #define log_v BaseUnpacker::targzNullLoggerCallback + #endif + + #else + #define log_n BaseUnpacker::targzNullLoggerCallback + #define log_e BaseUnpacker::targzNullLoggerCallback + #define log_w BaseUnpacker::targzNullLoggerCallback + #define log_i BaseUnpacker::targzNullLoggerCallback + #define log_d BaseUnpacker::targzNullLoggerCallback + #define log_v BaseUnpacker::targzNullLoggerCallback + #endif + + #else + #define U_PART U_SPIFFS + #endif + +#elif defined ARDUINO_ARCH_RP2040 + // no OTA support + #define DEVICE_RESTART() rp2040.restart() + #define HEAP_AVAILABLE() rp2040.getFreeHeap() + + // ESP like log functions turned to macros to allow gathering of file name, log level, etc + #define log_v(format, ...) TGZ::LOG(__FILE__, __LINE__, TGZ::LogLevelVerbose, format, ##__VA_ARGS__) + #define log_d(format, ...) TGZ::LOG(__FILE__, __LINE__, TGZ::LogLevelDebug, format, ##__VA_ARGS__) + #define log_i(format, ...) TGZ::LOG(__FILE__, __LINE__, TGZ::LogLevelInfo, format, ##__VA_ARGS__) + #define log_w(format, ...) TGZ::LOG(__FILE__, __LINE__, TGZ::LogLevelWarning, format, ##__VA_ARGS__) + #define log_e(format, ...) TGZ::LOG(__FILE__, __LINE__, TGZ::LogLevelError, format, ##__VA_ARGS__) + #define log_n(format, ...) TGZ::LOG(__FILE__, __LINE__, TGZ::LogLevelNone, format, ##__VA_ARGS__) + + #include + #define LOG_PRINTF Serial.printf + + #if !defined TGZ_PATHNAME + #define TGZ_PATHNAME _pathToFileName + static const char * _pathToFileName(const char * path) + { + size_t i = 0, pos = 0; + char * p = (char *)path; + while(*p){ + i++; + if(*p == '/' || *p == '\\'){ + pos = i; + } + p++; + } + return path+pos; + } + #endif + + #if !defined LOG_PRINTF + #define LOG_PRINTF printf + #endif + + #if !defined TGZ_DEFAULT_LOG_LEVEL + #define TGZ_DEFAULT_LOG_LEVEL LogLevelWarning + #endif + + namespace TGZ + { + // maximum size of log string + #define LOG_MAXLENGTH 215 + #define TGZ_LOGGER_attr __attribute__((unused)) static + + // logger function signature + typedef void (*TGZ_LOGGER_t)(const char* path, int line, int loglevel, const char* fmr, ...); + + // supported log levels, inspired from esp32 arduhal + enum LogLevel_t + { + LogLevelNone, // no logging + LogLevelError, // err + LogLevelWarning, // err+warn + LogLevelInfo, // err+warn+info + LogLevelDebug, // err+warn+info+debug + LogLevelVerbose // err+warn+info+debug+verbose + }; + // log levels names + TGZ_LOGGER_attr const char* levelNames[6] = {"None","Error","Warning","Info","Debug","Verbose"}; + // the default logging function + TGZ_LOGGER_attr void _LOG(const char* path, int line, int loglevel, const char* fmr, ...); + // the pointer to the logging function (can be overloaded with a custom logger) + TGZ_LOGGER_attr void (*LOG)(const char* path, int line, int loglevel, const char* fmr, ...) = _LOG; + // log level setter + TGZ_LOGGER_attr void setLogLevel( LogLevel_t level ); + // the logging function setter + TGZ_LOGGER_attr void setLoggerFunc( TGZ_LOGGER_t fn ); + // default log level + TGZ_LOGGER_attr LogLevel_t _LOG_LEVEL = TGZ_DEFAULT_LOG_LEVEL; + // log level getter (int) + TGZ_LOGGER_attr LogLevel_t logLevelInt(); + // log level getter (string) + TGZ_LOGGER_attr const char* logLevelStr(); + + LogLevel_t logLevelInt() + { + return _LOG_LEVEL; + } + + const char* logLevelStr() + { + return levelNames[_LOG_LEVEL]; + } + + void setLogLevel( LogLevel_t level ) + { + TGZ::_LOG_LEVEL = level; + log_n("New log level: %d", level ); + } + + void setLoggerFunc( TGZ_LOGGER_t fn ) + { + LOG = fn; + } + + void _LOG(const char* path, int line, int loglevel, const char* fmr, ...) + { + if( loglevel <= TGZ::_LOG_LEVEL ) { + using namespace TGZ; + char log_buffer[LOG_MAXLENGTH+1] = {0}; + va_list arg; + va_start(arg, fmr); + vsnprintf(log_buffer, LOG_MAXLENGTH, fmr, arg); + va_end(arg); + if( log_buffer[0] != '\0' ) { + switch( loglevel ) { + case LogLevelVerbose: LOG_PRINTF("[V][%d][%s:%d] %s\r\n", HEAP_AVAILABLE(), TGZ_PATHNAME(path), line, log_buffer); break; + case LogLevelDebug: LOG_PRINTF("[D][%d][%s:%d] %s\r\n", HEAP_AVAILABLE(), TGZ_PATHNAME(path), line, log_buffer); break; + case LogLevelInfo: LOG_PRINTF("[I][%d][%s:%d] %s\r\n", HEAP_AVAILABLE(), TGZ_PATHNAME(path), line, log_buffer); break; + case LogLevelWarning: LOG_PRINTF("[W][%d][%s:%d] %s\r\n", HEAP_AVAILABLE(), TGZ_PATHNAME(path), line, log_buffer); break; + case LogLevelError: LOG_PRINTF("[E][%d][%s:%d] %s\r\n", HEAP_AVAILABLE(), TGZ_PATHNAME(path), line, log_buffer); break; + case LogLevelNone: LOG_PRINTF("[N][%d][%s:%d] %s\r\n", HEAP_AVAILABLE(), TGZ_PATHNAME(path), line, log_buffer); break; + } + } + } + } + + }; + +#else + + #error "Only ESP32, ESP8266 and RP2040 architectures are supported" + +#endif diff --git a/src/ESP32-targz.h b/src/ESP32-targz.h index 0cf3cea..139b19e 100644 --- a/src/ESP32-targz.h +++ b/src/ESP32-targz.h @@ -90,9 +90,34 @@ FSInfo fsinfo; + +#elif defined ARDUINO_ARCH_RP2040 + + #pragma message "Experimental RP2040 support" + #include "ESP32-targz-log.hpp" + + #undef DEST_FS_USES_SD_MMC // unsupported + #undef DEST_FS_USES_FFAT // unsupported + #undef DEST_FS_USES_SPIFFS // unsupported + + #if defined DEST_FS_USES_SD + #include + #define tarGzFS SDFS + #define FS_NAME "SD" + #elif defined DEST_FS_USES_LITTLEFS + //#include + #include + #define tarGzFS LittleFS + #define FS_NAME "LITTLEFS (picolib)" + #else + #error "Unspecified or invalid destination filesystem, please #define one of these before including the library: DEST_FS_USES_LITTLEFS, DEST_FS_USES_SD" + #endif + + FSInfo fsinfo; + #else - #error "Only ESP32 and ESP8266 architectures are supported" + #error "Only ESP32, ESP8266 and RP2040 architectures are supported" #endif @@ -107,7 +132,7 @@ __attribute__((unused)) static size_t targzFreeBytesFn() { #if defined DEST_FS_USES_SPIFFS || defined DEST_FS_USES_SD || defined DEST_FS_USES_SD_MMC || defined DEST_FS_USES_LITTLEFS || defined DEST_FS_USES_PSRAMFS #if defined ESP32 return tarGzFS.totalBytes() - tarGzFS.usedBytes(); - #elif defined ESP8266 + #elif defined ESP8266 || defined ARDUINO_ARCH_RP2040 if( tarGzFS.info( fsinfo ) ) { return fsinfo.totalBytes - fsinfo.usedBytes; } else { @@ -129,7 +154,7 @@ __attribute__((unused)) static size_t targzTotalBytesFn() { #if defined DEST_FS_USES_SPIFFS || defined DEST_FS_USES_SD || defined DEST_FS_USES_SD_MMC || defined DEST_FS_USES_LITTLEFS || defined DEST_FS_USES_FFAT || defined DEST_FS_USES_PSRAMFS #if defined ESP32 return tarGzFS.totalBytes(); - #elif defined ESP8266 + #elif defined ESP8266 || defined ARDUINO_ARCH_RP2040 if( tarGzFS.info( fsinfo ) ) { return fsinfo.totalBytes; } else { diff --git a/src/helpers/path_tools.h b/src/helpers/path_tools.h index 61130eb..986cc35 100644 --- a/src/helpers/path_tools.h +++ b/src/helpers/path_tools.h @@ -1,6 +1,11 @@ #ifndef _ESP32_TARGZ_PATHTOOLS_ #define _ESP32_TARGZ_PATHTOOLS_ +#if !defined ESP32 || !defined ESP8266 + #include "ESP32-targz-log.hpp" +#endif + + /* dirname - return directory part of PATH. Copyright (C) 1996-2014 Free Software Foundation, Inc. This file is part of the GNU C Library.