diff --git a/.github/workflows/PlatformioBuild.yml b/.github/workflows/PlatformioBuild.yml index 35c9c305..8f3ac4b0 100644 --- a/.github/workflows/PlatformioBuild.yml +++ b/.github/workflows/PlatformioBuild.yml @@ -105,4 +105,3 @@ jobs: # install local version of the library pio pkg install -e test --no-save --library file://$(realpath ../../../) pio run -e test - diff --git a/README.md b/README.md index 85a01237..7da59b2c 100644 --- a/README.md +++ b/README.md @@ -72,11 +72,12 @@ All those are available in the [Arduino Library Manager](https://www.arduinolibr
-**2) Download the [SD-Content :floppy_disk:](https://github.com/tobozo/M5Stack-SD-Updater/releases/download/v0.4.1/SD-Apps-Folder.zip) folder from the release page and unzip it into the root of the SD Card.** Then put the SD Card into the M5Stack. This zip file comes preloaded with [precompiled apps](https://github.com/tobozo/M5Stack-SD-Updater/tree/master/examples/M5Stack-SD-Menu/SD-Apps) and the relative meta information for the menu. +**outdated binaries** +~~**2) Download the [SD-Content :floppy_disk:](https://github.com/tobozo/M5Stack-SD-Updater/releases/download/v0.4.1/SD-Apps-Folder.zip) folder from the release page and unzip it into the root of the SD Card.** Then put the SD Card into the M5Stack. This zip file comes preloaded with [precompiled apps](https://github.com/tobozo/M5Stack-SD-Updater/tree/master/examples/M5Stack-SD-Menu/SD-Apps) and the relative meta information for the menu.~~
-**3) Compile and flash the `M5Stack-SD-Menu.ino` example.**
+**2) Compile and flash the `M5Stack-SD-Menu.ino` example.**
This sketch is the **menu** app. It shoul reside in the root directory of a micro SD card for persistence and also executed once. Once flashed it will **copy itself** on OTA2 partition and on the SDCard, then rolled back and executed from the OTA2 partition. @@ -86,7 +87,7 @@ Thanks to @Lovyan03 this self-propagation logic is very convenient: by residing
-**4) Make application sketches compatible with the SD-Updater Menu .**
+**3) Make application sketches compatible with the SD-Updater Menu .**
The snippet of code in the `M5Stack-SDLoader-Snippet.ino` sketch can be used as a model to make any ESP32 sketch compatible with the SD-Updater menu. diff --git a/examples/M5Stack-FW-Menu/src/main.cpp b/examples/M5Stack-FW-Menu/src/main.cpp index ca15882e..036df4d2 100644 --- a/examples/M5Stack-FW-Menu/src/main.cpp +++ b/examples/M5Stack-FW-Menu/src/main.cpp @@ -401,7 +401,8 @@ void menuItemLoadFW() { auto ota_num = slotPicker("Run Flash App", false); // load apps if( ota_num > 0 ) { - SDUpdater::_message("Booting partition"); + String msg = "Booting partition " + String(ota_num); + SDUpdater::_message(msg); if( !Flash::bootPartition( ota_num ) ) { SDUpdater::_error("Partition unbootable :("); } @@ -468,10 +469,10 @@ void menuItemPartitionsInfo() M5.Lcd.printf("Slot: %d (%s)\n", nvs_part->ota_num, AppName.c_str()); M5.Lcd.printf("Type: 0x%02x\n", part->type); M5.Lcd.printf("SType: 0x%02x\n", part->subtype); - M5.Lcd.printf("Addr: 0x%06x\n", part->address); - M5.Lcd.printf("Size: %d\n", part->size); + M5.Lcd.printf("Addr: 0x%06lx\n", part->address); + M5.Lcd.printf("Size: %lu\n", part->size); M5.Lcd.printf("Used: %s\n", meta.image_len>0 ? String(meta.image_len).c_str() : "n/a"); - M5.Lcd.printf("Desc: %s\n", nvs_part->desc[0]!=0?nvs_part->desc:"none"); + //M5.Lcd.printf("Desc: %s\n", nvs_part->desc[0]!=0?nvs_part->desc:"none"); M5.Lcd.clearClipRect(); @@ -724,7 +725,7 @@ void printFlashPartition( Flash::Partition_t* sdu_partition ) } } - Serial.printf("%-8s 0x%02x 0x%02x 0x%06x %8d %8s %8s %8s\n", + Serial.printf("%-8s 0x%02x 0x%02x 0x%06lx %8lu %8s %8s %8s\n", String( part.label ).c_str(), part.type, part.subtype, @@ -746,6 +747,7 @@ void lsFlashPartitions() } } +#include "base64.h" void lsNVSpartitions() { @@ -758,6 +760,36 @@ void lsNVSpartitions() Serial.printf("[%d] %s slot\n", nvs_part->ota_num, i==0?"Reserved":"Available" ); } } + Serial.println("\nPartitions as CSV:"); + + + size_t blob_size = (sizeof(NVS::PartitionDesc_t)*NVS::Partitions.size()); + NVS::blob_partition_t *bPart = new NVS::blob_partition_t(blob_size); + + if( !bPart->blob) { + log_e("Can't allocate %d bytes for blob", blob_size ); + return; + } + size_t idx = 0; + for( int i=0; iblob[idx], part, sizeof(NVS::PartitionDesc_t) ); + } + + + // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/nvs_partition_gen.html#csv-file-format + String b64 = base64::encode( (const uint8_t*)bPart->blob, blob_size ); + Serial.printf("Sizeof PartitionDesc_t: %d bytes\n", sizeof(NVS::PartitionDesc_t) ); + Serial.printf("Sizeof NVS blob: %d bytes\n", blob_size ); + Serial.printf("Sizeof Base64: %d bytes\n", b64.length() ); + Serial.println(); + Serial.println("key,type,encoding,value");// <-- column header + Serial.printf("%s,namespace,,\n", NVS::PARTITION_NS ); // <-- First entry should be of type "namespace" + Serial.printf("%s,data,base64,%s\n", NVS::PARTITION_KEY, b64.c_str() ); + Serial.println(); + // key1,data,u8,1 + // key2,file,string,/path/to/file } @@ -775,6 +807,7 @@ bool checkFactoryStickyPartition() return false; } + SDUpdater::_message("Checking factory..."); // will compare running partition with factory, and update if necessary if( SDUpdater::saveSketchToFactory() ) { // sketch was just saved to factory partition, mark it as bootable and restart @@ -782,7 +815,7 @@ bool checkFactoryStickyPartition() log_e("Switching to factory app failed :-("); return false; } - + SDUpdater::_message("Checking partitions..."); if( !NVS::getPartitions() ) { log_w("Partitions not found on NVS, creating"); // first visit! PartitionManager::createPartitions(); @@ -812,7 +845,9 @@ bool checkOTAStickyPartition() const esp_partition_t* last_partition = esp_ota_get_next_update_partition(NULL); - if( !last_partition ) return false; + if( !last_partition ) { + return false; + } const esp_partition_t* next_partition = Flash::getNextAvailPartition( last_partition->type, last_partition->subtype ); @@ -830,15 +865,12 @@ bool checkOTAStickyPartition() } return check_for_migration; - } - - void setup() { using namespace AppTheme; @@ -860,7 +892,7 @@ void setup() SDU_UI::resetScroll(); - SDUpdater::_message("Booting factory..."); + //SDUpdater::_message("Booting factory..."); if( ! checkFactoryStickyPartition() ) { // print partitions for debug @@ -883,12 +915,6 @@ void setup() void loop() { - auto ota_num = slotPicker("Run Application", false); // load apps - if( ota_num > 0 ) { - SDUpdater::_message("Booting partition"); - if( !Flash::bootPartition( ota_num ) ) { - SDUpdater::_error("Partition unbootable :("); - } - } + menuItemLoadFW(); launcherPicker(); } diff --git a/src/M5StackUpdater.hpp b/src/M5StackUpdater.hpp index 0136bc5c..db163ed4 100644 --- a/src/M5StackUpdater.hpp +++ b/src/M5StackUpdater.hpp @@ -96,7 +96,7 @@ #define resetReason (int)rtc_get_reset_reason(0) // use `#define SDU_NO_PRAGMAS` to disable duplicate pragma messages -#if !defined SDU_NO_PRAGMAS +#if !defined SDU_NO_PRAGMAS && CORE_DEBUG_LEVEL>=ARDUHAL_LOG_LEVEL_ERROR #define SDU_STRINGIFY(a) #a #define SDU_PRAGMA_MESSAGE(msg) \ _Pragma( SDU_STRINGIFY( message msg ) ) diff --git a/src/PartitionManager/NVS/NVSUtils.cpp b/src/PartitionManager/NVS/NVSUtils.cpp index 82862c7f..a19ade97 100644 --- a/src/PartitionManager/NVS/NVSUtils.cpp +++ b/src/PartitionManager/NVS/NVSUtils.cpp @@ -81,13 +81,15 @@ namespace SDUpdaterNS } + + bool getPartitions() { if( Partitions.size()>0 ) Partitions.clear(); size_t blob_size; - char* out = nullptr; bool ret = false; + blob_partition_t *bPart = nullptr; auto err = nvs_open(PARTITION_NS, NVS_READONLY, &handle); if( err != ESP_OK ) { log_i("NVS Namespace not created yet"); @@ -100,22 +102,23 @@ namespace SDUpdaterNS goto _nvs_close; } - out = (char*)calloc(blob_size+1, sizeof(char)); - if (!out ) { - log_e("Could not alloc %d bytes", blob_size+1 ); + bPart = new blob_partition_t( blob_size ); + if (!bPart->blob ) { + log_e("Could not alloc %d bytes", blob_size ); goto _nvs_close; } - err = nvs_get_blob(handle, PARTITION_KEY, out, &blob_size); + + err = nvs_get_blob(handle, PARTITION_KEY, bPart->blob, &blob_size); if( err != ESP_OK ) { log_e("Could not read blob"); goto _nvs_close; } - ret = parsePartitions( out, blob_size-1 ); - free( out ); + ret = parsePartitions( bPart->blob, blob_size ); _nvs_close: nvs_close( handle ); + if( bPart!=nullptr ) delete bPart; return ret; } @@ -145,9 +148,10 @@ namespace SDUpdaterNS bool ret = true; if( Partitions.size() > 0 ) { log_d("Saving partitions"); - size_t blob_size = (sizeof(PartitionDesc_t)*Partitions.size()) + 1; - char* blob = (char*)calloc(blob_size, sizeof(char)); - if( !blob) { + size_t blob_size = (sizeof(PartitionDesc_t)*Partitions.size()); + blob_partition_t *bPart = new blob_partition_t(blob_size); + + if( !bPart->blob) { log_e("Can't allocate %d bytes for blob", blob_size ); return false; } @@ -155,7 +159,7 @@ namespace SDUpdaterNS for( int i=0; iblob[idx], part, sizeof(PartitionDesc_t) ); } auto err = nvs_open(PARTITION_NS, NVS_READWRITE, &handle); @@ -164,7 +168,7 @@ namespace SDUpdaterNS return false; } - err = nvs_set_blob(handle, PARTITION_KEY, blob, blob_size); + err = nvs_set_blob(handle, PARTITION_KEY, bPart->blob, blob_size); if( err != ESP_OK ) { log_e("Failed to save blob"); ret = false; @@ -174,7 +178,7 @@ namespace SDUpdaterNS } nvs_close( handle ); - free( blob ); + delete bPart; } return ret; } diff --git a/src/PartitionManager/NVS/NVSUtils.hpp b/src/PartitionManager/NVS/NVSUtils.hpp index a670b1f7..4d495dba 100644 --- a/src/PartitionManager/NVS/NVSUtils.hpp +++ b/src/PartitionManager/NVS/NVSUtils.hpp @@ -38,7 +38,28 @@ namespace SDUpdaterNS size_t bin_size{0}; // firmware size uint8_t digest[32]{0}; // firmware digest char name[40]{0}; // firmware name - char desc[40]{0}; // firmware desc + //char desc[40]{0}; // firmware desc + }; + + struct blob_partition_t + { + char* blob{nullptr}; + bool needs_free{false}; + blob_partition_t() : blob(nullptr), needs_free(false) { } + blob_partition_t( size_t blob_size ) : blob(nullptr), needs_free(false) + { + this->blob = (char*)calloc(blob_size+1, sizeof(char)); + if (!this->blob ) { + log_e("Could not alloc %d bytes", blob_size ); + } else { + needs_free = true; + } + } + ~blob_partition_t() + { + if( needs_free ) + free(this->blob); + } }; extern nvs_handle_t handle; diff --git a/src/PartitionManager/PartitionManager.cpp b/src/PartitionManager/PartitionManager.cpp index f4efda7f..916e75b5 100644 --- a/src/PartitionManager/PartitionManager.cpp +++ b/src/PartitionManager/PartitionManager.cpp @@ -25,11 +25,12 @@ namespace SDUpdaterNS if( Flash::metadataHasDigest( meta ) ) { nvs_part.bin_size = meta->image_len; memcpy( nvs_part.digest, meta->image_digest, 32 ); - log_d("Added flash digest to NVS::Partitions[]: %s", digests.toString( nvs_part.digest ) ); + log_d("Added flash digest to NVS::Partitions[%d]: %s", NVS::Partitions.size(), digests.toString( nvs_part.digest ) ); } NVS::Partitions.push_back( nvs_part ); } NVS::savePartitions(); + //debugPartitions(); log_i("Partition scheme has %d app slot(s)", NVS::Partitions.size() ); } @@ -62,6 +63,7 @@ namespace SDUpdaterNS if( needs_saving ) { NVS::savePartitions(); + //debugPartitions(); } } @@ -84,6 +86,7 @@ namespace SDUpdaterNS nvs_part->bin_size = 0; memset( nvs_part->digest, 0, 32 ); if( NVS::savePartitions() ) { + //debugPartitions(); // TODO: implement partitions reload instead of restart ESP.restart(); } @@ -123,7 +126,8 @@ namespace SDUpdaterNS log_e("WARN: partition has no sha"); //return false; } - return NVS::savePartitions(); + ret = NVS::savePartitions(); + //debugPartitions(); } return ret; } @@ -203,21 +207,83 @@ namespace SDUpdaterNS } + // void debugPartitions() + // { + // log_d("OTA, size, digest, name, desc"); + // Flash::digest_t digests; + // for( int i=0;ipart; + // auto meta = sdu_partition->meta; + // + // String AppName = "n/a"; + // + // if( Flash::partitionIsApp( &part ) ) { + // if( Flash::partitionIsFactory( &part ) ) { + // AppName = "Factory"; + // } else { + // AppName = "OTA" + String( part.subtype - ESP_PARTITION_SUBTYPE_APP_OTA_MIN ); + // } + // } + // + // log_d("%-8s 0x%02x 0x%02x 0x%06x %8d %8s %8s %8s", + // String( part.label ).c_str(), + // part.type, + // part.subtype, + // part.address, + // part.size, + // meta.image_len>0 ? String(meta.image_len).c_str() : "n/a", + // AppName.c_str(), + // Flash::partitionIsApp(&part)&&Flash::metadataHasDigest(&meta) ? digests.toString(meta.image_digest) : "n/a" + // ); + // } + // } + + bool erase( uint8_t ota_num ) { NVS::PartitionDesc_t* nvs_part = NVS::findPartition(ota_num); - if( !nvs_part ) return false; + if( !nvs_part ) { + log_e("Cannot erase partition, NVS::findPartition(%d) found nothing", ota_num); + return false; + } auto flash_part = Flash::getPartition( ota_num ); - if( !flash_part ) return false; + if( !flash_part ) { + log_e("Cannot erase partition, Flash::getPartition(%d) found nothing", ota_num); + return false; + } if( Flash::erase(ota_num) ) { + nvs_part->bin_size = 0; nvs_part->name[0] = 0; - nvs_part->desc[0] = 0; + //nvs_part->desc[0] = 0; for( int i=0;i<32;i++ ) nvs_part->digest[i] = 0; + + Flash::scan(); + //debugPartitions(); + if( NVS::savePartitions() ) { log_d("TODO: implement partitions reload instead of restart"); + //debugPartitions(); ESP.restart(); // force partition reload } + } else { + log_e("Could erase partition, Flash::erase(%d) failed", ota_num); } return false; } @@ -246,12 +312,17 @@ namespace SDUpdaterNS bool migrateSketch( const char* binFileName ) { assert(binFileName); + log_v("Checking %s for migration", binFileName); Flash::digest_t digests = Flash::digest_t(); if( !canMigrateToFactory() ) { return false; // need a factory partition scheme } + if( !NVS::getPartitions() ) { + return false; // need a NVS partition management already set + } + esp_image_metadata_t running_meta; esp_image_metadata_t dst_meta; @@ -262,35 +333,44 @@ namespace SDUpdaterNS size_t sksize = 0; String error = ""; + String msg = ""; Flash::running_partition = esp_ota_get_running_partition(); if( !Flash::running_partition ) { log_e("Flash inconsistency: running partition not found" ); return false; // uh-oh } + + if( Flash::running_partition->subtype != ESP_PARTITION_SUBTYPE_APP_OTA_MIN ) { + + } + + running_meta = Flash::getSketchMeta( Flash::running_partition ); NVSPart = NVS::findPartition( binFileName ); - if( !NVSPart ) { + if( !NVSPart /*|| (NVSPart && digests.isEmpty( NVSPart->digest))*/ ) { // No NVS partition found named [binFilename], but NVS may be wrong so try to also find by digest FlashPart = Flash::findDupePartition( &running_meta, Flash::running_partition->type, Flash::running_partition->subtype ); if( FlashPart ) { //log_d("Duplicate partition found: NVS has no entry named %s but partition meta %d exists", binFileName, FlashPart->part.subtype - ESP_PARTITION_SUBTYPE_APP_OTA_MIN ); - error = "Error: NVS dupe found"; + error = "Error: NVS dupe found, please erase NVS"; goto _error_nvs; } + log_d("NVS Partition named %s not found", binFileName ); // new slot, get next flashable partition Flash::nextupd_partition = Flash::getNextAvailPartition( Flash::running_partition->type, Flash::running_partition->subtype ); if( !Flash::nextupd_partition ) { - error = "Partitions full";//migration to new slot is not possible + error = "Migration canceled: partitions full";//migration to new slot is not possible goto _error_nvs; } // store NVS app number ota_num = Flash::nextupd_partition->subtype - ESP_PARTITION_SUBTYPE_APP_OTA_MIN; sksize = ESP.getSketchSize(); - SDUpdater::_message( String("Migrating to new slot") ); + msg = String("Migrating to new slot #") + String(ota_num); + SDUpdater::_message( msg ); // copy to next partition if( !Flash::copyPartition( Flash::nextupd_partition, Flash::running_partition, sksize) ) { @@ -337,20 +417,27 @@ namespace SDUpdaterNS FlashPart = Flash::findDupePartition( &running_meta, Flash::running_partition->type, Flash::running_partition->subtype ); if( FlashPart ) { // erase duplicate (will trigger a restart on success) - SDUpdater::_message( String("Erasing old partition") ); - if( !PartitionManager::erase( FlashPart->part.subtype -ESP_PARTITION_SUBTYPE_APP_OTA_MIN ) ) { + msg = String("Erasing old partition"); + SDUpdater::_message( msg ); + if( !PartitionManager::erase( FlashPart->part.subtype - ESP_PARTITION_SUBTYPE_APP_OTA_MIN ) ) { error = "Erasing failed!"; goto _error_nvs; } + } - log_d("this sketch is already running from the right partition according to NVS"); + log_i("this sketch is already running from the right partition (%d) according to NVS", ota_num); return false; } - // log_d("Current sketch is not running from its assigned partition (NVS want=%d, has=%d)", ota_num, Flash::running_partition->subtype - ESP_PARTITION_SUBTYPE_APP_OTA_MIN ); + log_w("Current sketch is not running from its assigned partition (NVS want=%d, has=%d)", ota_num, Flash::running_partition->subtype - ESP_PARTITION_SUBTYPE_APP_OTA_MIN ); // overwrite if digests differ if( ! digests.match( NVSPart->digest, running_meta.image_digest ) ) { + msg = "Overwriting slot (in="; + msg += digests.toString( NVSPart->digest ); + msg += ", out="; + msg += digests.toString( running_meta.image_digest ); + msg += ")"; SDUpdater::_message( String("Overwriting slot") ); sksize = ESP.getSketchSize(); if( !Flash::copyPartition( Flash::nextupd_partition, Flash::running_partition, sksize) ) { @@ -359,7 +446,7 @@ namespace SDUpdaterNS } goto _update_nvs; } else { - // log_d("names and digests match, no overwrite, just switch"); + log_d("names and digests match, no overwrite, just switch"); goto _boot_partition; } @@ -372,7 +459,9 @@ namespace SDUpdaterNS NVSPart->bin_size = running_meta.image_len; memcpy( NVSPart->digest, running_meta.image_digest, 32 ); snprintf( NVSPart->name, 39, "%s", binFileName ); + log_d("Updated NVSPart->name %s=%s", NVSPart->name, binFileName); NVS::savePartitions(); + //debugPartitions(); _boot_partition: diff --git a/src/PartitionManager/PartitionManager.hpp b/src/PartitionManager/PartitionManager.hpp index 2ee7ad04..0e107a66 100644 --- a/src/PartitionManager/PartitionManager.hpp +++ b/src/PartitionManager/PartitionManager.hpp @@ -18,6 +18,7 @@ namespace SDUpdaterNS void createPartitions(); void updatePartitions(); void processPartitions(); + void debugPartitions(); bool flashFactory(); bool migrateSketch( const char* binFileName ); diff --git a/src/PartitionManager/Partitions/PartitionUtils.cpp b/src/PartitionManager/Partitions/PartitionUtils.cpp index 174f1dcb..dbcdad1e 100644 --- a/src/PartitionManager/Partitions/PartitionUtils.cpp +++ b/src/PartitionManager/Partitions/PartitionUtils.cpp @@ -3,6 +3,10 @@ #include "../../ConfigManager/ConfigManager.hpp" +#if !defined SPI_FLASH_SEC_SIZE + #define SPI_FLASH_SEC_SIZE 4096 +#endif + namespace SDUpdaterNS { @@ -601,17 +605,23 @@ namespace SDUpdaterNS } - - bool erase( uint8_t ota_num ) + bool erase( const esp_partition_t *part ) { - auto part = getPartition( ota_num ); - log_d("Erasing ota partition %d (%#x)", ota_num, part->subtype ); + assert(part); + log_d("Erasing ota partition %#x", part->subtype ); if( part && ESP.partitionEraseRange(part, 0, part->size ) ) { return true; } - log_e("FATAL: can't erase running partition"); + log_e("FATAL: can't erase partition"); return false; } + + bool erase( uint8_t ota_num ) + { + auto part = getPartition( ota_num ); + return erase( part ); + } + }; }; diff --git a/src/PartitionManager/Partitions/PartitionUtils.hpp b/src/PartitionManager/Partitions/PartitionUtils.hpp index 5af7ea27..b636b9ab 100644 --- a/src/PartitionManager/Partitions/PartitionUtils.hpp +++ b/src/PartitionManager/Partitions/PartitionUtils.hpp @@ -3,6 +3,7 @@ #include #include #include +#include extern "C" { #include "esp_ota_ops.h" #include "esp_image_format.h" @@ -45,6 +46,7 @@ namespace SDUpdaterNS bool bootPartition( uint8_t ota_num ); bool erase( uint8_t ota_num ); + bool erase( const esp_partition_t *part ); bool copyPartition(fs::FS *fs, const char* binfilename); // copy from OTA0 to OTA1 and filesystem bool copyPartition(fs::File* dstFile, const esp_partition_t* src, size_t length); // copy from given partition to filesystem diff --git a/src/SDUpdater/SDUpdater_Class.cpp b/src/SDUpdater/SDUpdater_Class.cpp index f4af8cab..1f16e74f 100644 --- a/src/SDUpdater/SDUpdater_Class.cpp +++ b/src/SDUpdater/SDUpdater_Class.cpp @@ -312,6 +312,12 @@ namespace SDUpdaterNS { bool hasFileName = (fileName!=""); + // if using factory: disable "Save FW" action button when running partition isn't the first partition + SDUCfg.Buttons[2].enabled = cfg->rollBackToFactory + ? esp_ota_get_running_partition()->subtype==ESP_PARTITION_SUBTYPE_APP_OTA_MIN + : SDUCfg.Buttons[2].enabled + ; + if( cfg->onWaitForAction ) { int action = cfg->onWaitForAction( !hasFileName ? (char*)cfg->labelRollback : (char*)cfg->labelMenu, (char*)cfg->labelSkip, (char*)cfg->labelSave, SDUCfg.waitdelay ); @@ -335,7 +341,7 @@ namespace SDUpdaterNS log_v("Checking if %s needs saving", cfg->binFileName ); saveSketchToFS( *cfg->fs, cfg->binFileName, action != ConfigManager::SDU_BTNC_SAVE ); } else if( cfg->rollBackToFactory ) { - return PartitionManager::migrateSketch( fileName.c_str() ); + return PartitionManager::migrateSketch( cfg->binFileName ); } else if( action == ConfigManager::SDU_BTNC_SAVE ) { const char* msg[] = {"No valid filesystem", "selected!", "Cannot save", fileName.c_str()}; _error( msg, 4 );