|  | 
|  | 1 | +/* | 
|  | 2 | +   Flash Format | 
|  | 3 | +
 | 
|  | 4 | +   The sketch formats the board flash storage as follows: | 
|  | 5 | +
 | 
|  | 6 | + * Partition 1  1MB: used for network certificates | 
|  | 7 | + * Partition 2  5MB: OTA | 
|  | 8 | + * Partition 3  1MB: Provisioning KVStore | 
|  | 9 | + * Partition 4  7MB: User data | 
|  | 10 | +
 | 
|  | 11 | + This example code is in the public domain. | 
|  | 12 | + */ | 
|  | 13 | + | 
|  | 14 | +#include <Arduino.h> | 
|  | 15 | +#include <zephyr/fs/fs.h> | 
|  | 16 | +#include <zephyr/storage/flash_map.h> | 
|  | 17 | +#include "certificates.h" | 
|  | 18 | + | 
|  | 19 | +// MBR structures | 
|  | 20 | +struct __attribute__((packed)) mbrEntry { | 
|  | 21 | +    uint8_t status; | 
|  | 22 | +    uint8_t chsStart[3]; | 
|  | 23 | +    uint8_t type; | 
|  | 24 | +    uint8_t chsStop[3]; | 
|  | 25 | +    uint32_t lbaOffset; | 
|  | 26 | +    uint32_t lbaSize; | 
|  | 27 | +}; | 
|  | 28 | + | 
|  | 29 | +struct __attribute__((packed)) mbrTable { | 
|  | 30 | +    mbrEntry entries[4]; | 
|  | 31 | +    uint8_t signature[2]; | 
|  | 32 | +}; | 
|  | 33 | + | 
|  | 34 | +bool waitResponse() { | 
|  | 35 | +    bool proceed = false; | 
|  | 36 | +    bool confirmation = false; | 
|  | 37 | + | 
|  | 38 | +    while (confirmation == false) { | 
|  | 39 | +        if (!Serial.available()) { | 
|  | 40 | +            continue; | 
|  | 41 | +        } | 
|  | 42 | + | 
|  | 43 | +       switch (Serial.read()) { | 
|  | 44 | +           case 'y': | 
|  | 45 | +           case 'Y': | 
|  | 46 | +               confirmation = true; | 
|  | 47 | +               proceed = true; | 
|  | 48 | +               break; | 
|  | 49 | +           case 'n': | 
|  | 50 | +           case 'N': | 
|  | 51 | +               confirmation = true; | 
|  | 52 | +               proceed = false; | 
|  | 53 | +               break; | 
|  | 54 | +           default: | 
|  | 55 | +               continue; | 
|  | 56 | +       } | 
|  | 57 | +    } | 
|  | 58 | + | 
|  | 59 | +    return proceed; | 
|  | 60 | +} | 
|  | 61 | + | 
|  | 62 | +int formatPartition(unsigned int partition_id, int fs_type) { | 
|  | 63 | +    Serial.print("Formatting partition as "); | 
|  | 64 | +    Serial.println(fs_type == FS_FATFS ? "FAT..." : "LittleFS..."); | 
|  | 65 | + | 
|  | 66 | +    int rc = 0; | 
|  | 67 | +    if (fs_type == FS_FATFS) { | 
|  | 68 | +        rc = fs_mkfs(FS_FATFS, partition_id, NULL, 0); | 
|  | 69 | +    } else { | 
|  | 70 | +        rc = fs_mkfs(FS_LITTLEFS, partition_id, NULL, 0); | 
|  | 71 | +    } | 
|  | 72 | + | 
|  | 73 | +    if (rc < 0) { | 
|  | 74 | +        Serial.print("Error formatting partition: "); | 
|  | 75 | +        Serial.println(rc); | 
|  | 76 | +        return rc; | 
|  | 77 | +    } | 
|  | 78 | + | 
|  | 79 | +    Serial.print("Partition formatted successfully!"); | 
|  | 80 | +    return 0; | 
|  | 81 | +} | 
|  | 82 | + | 
|  | 83 | +int flashCertificates() { | 
|  | 84 | +    Serial.print("Certificate size: "); | 
|  | 85 | +    Serial.print(cacert_pem_len); | 
|  | 86 | +    Serial.println(" bytes"); | 
|  | 87 | + | 
|  | 88 | +    struct fs_file_t file; | 
|  | 89 | +    fs_file_t_init(&file); | 
|  | 90 | + | 
|  | 91 | +    Serial.println("Opening /wlan:/cacert.pem for writing..."); | 
|  | 92 | +    int rc = fs_open(&file, "/wlan:/cacert.pem", FS_O_CREATE | FS_O_WRITE); | 
|  | 93 | +    if (rc != 0) { | 
|  | 94 | +        Serial.print("Error opening cacert.pem: "); | 
|  | 95 | +        Serial.println(rc); | 
|  | 96 | +        return rc; | 
|  | 97 | +    } | 
|  | 98 | + | 
|  | 99 | +    Serial.println("Writing certificates..."); | 
|  | 100 | +    ssize_t ret = fs_write(&file, cacert_pem, cacert_pem_len); | 
|  | 101 | +    if (ret < 0) { | 
|  | 102 | +        Serial.print("Error writing certificates: "); | 
|  | 103 | +        Serial.println(ret); | 
|  | 104 | +        fs_close(&file); | 
|  | 105 | +        return rc; | 
|  | 106 | +    } | 
|  | 107 | + | 
|  | 108 | +    rc = fs_sync(&file); | 
|  | 109 | +    if (rc != 0) { | 
|  | 110 | +        Serial.print("Warning: fs_sync failed: "); | 
|  | 111 | +        Serial.println(rc); | 
|  | 112 | +        fs_close(&file); | 
|  | 113 | +        return rc; | 
|  | 114 | +    } | 
|  | 115 | + | 
|  | 116 | +    fs_close(&file); | 
|  | 117 | +    Serial.println("Certificates written successfully!"); | 
|  | 118 | +    return 0; | 
|  | 119 | +} | 
|  | 120 | + | 
|  | 121 | +int flashMBR() { | 
|  | 122 | +    Serial.println("Creating MBR partition table..."); | 
|  | 123 | + | 
|  | 124 | +    // Open the MBR partition | 
|  | 125 | +    const struct flash_area *fa; | 
|  | 126 | +    int ret = flash_area_open(FIXED_PARTITION_ID(mbr_partition), &fa); | 
|  | 127 | +    if (ret) { | 
|  | 128 | +        Serial.print("Error opening MBR partition: "); | 
|  | 129 | +        Serial.println(ret); | 
|  | 130 | +        return ret; | 
|  | 131 | +    } | 
|  | 132 | + | 
|  | 133 | +    // Prepare 512-byte sector with MBR | 
|  | 134 | +    uint8_t sector[512]; | 
|  | 135 | +    memset(sector, 0, 512); | 
|  | 136 | + | 
|  | 137 | +    // MBR partition table starts at offset 446 | 
|  | 138 | +    mbrTable *table = (mbrTable*)§or[446]; | 
|  | 139 | + | 
|  | 140 | +    // Note: lbaSize is in units of 4096-byte blocks (not standard 512-byte sectors) | 
|  | 141 | +    // This matches the original Arduino implementation which uses 4KB sectors | 
|  | 142 | + | 
|  | 143 | +    // Partition 1: WLAN (FAT32) - starts at 0x1000, size ~1020KB (255 blocks of 4KB) | 
|  | 144 | +    table->entries[0].status = 0x00;       // Not bootable | 
|  | 145 | +    table->entries[0].chsStart[0] = 0; | 
|  | 146 | +    table->entries[0].chsStart[1] = 2; | 
|  | 147 | +    table->entries[0].chsStart[2] = 0; | 
|  | 148 | +    table->entries[0].type = 0x0B;         // FAT32 | 
|  | 149 | +    table->entries[0].chsStop[0] = 4; | 
|  | 150 | +    table->entries[0].chsStop[1] = 0; | 
|  | 151 | +    table->entries[0].chsStop[2] = 0; | 
|  | 152 | +    table->entries[0].lbaOffset = 1;       // 0x1000 / 4096 = 1 | 
|  | 153 | +    table->entries[0].lbaSize = 255;       // (1MB - 4KB) / 4096 = 255 | 
|  | 154 | + | 
|  | 155 | +    // Partition 2: OTA (FAT32) - starts at 0x100000, size 5MB (1280 blocks of 4KB) | 
|  | 156 | +    table->entries[1].status = 0x00; | 
|  | 157 | +    table->entries[1].chsStart[0] = 4; | 
|  | 158 | +    table->entries[1].chsStart[1] = 1; | 
|  | 159 | +    table->entries[1].chsStart[2] = 0; | 
|  | 160 | +    table->entries[1].type = 0x0B;         // FAT32 | 
|  | 161 | +    table->entries[1].chsStop[0] = 24; | 
|  | 162 | +    table->entries[1].chsStop[1] = 0; | 
|  | 163 | +    table->entries[1].chsStop[2] = 0; | 
|  | 164 | +    table->entries[1].lbaOffset = 256;     // 0x100000 / 4096 = 256 | 
|  | 165 | +    table->entries[1].lbaSize = 1280;      // 5MB / 4096 = 1280 | 
|  | 166 | + | 
|  | 167 | +    // Partition 3: KVS (FAT32) - starts at 0x600000, size 1MB (256 blocks of 4KB) | 
|  | 168 | +    table->entries[2].status = 0x00; | 
|  | 169 | +    table->entries[2].chsStart[0] = 24; | 
|  | 170 | +    table->entries[2].chsStart[1] = 1; | 
|  | 171 | +    table->entries[2].chsStart[2] = 0; | 
|  | 172 | +    table->entries[2].type = 0x0B;         // FAT32 | 
|  | 173 | +    table->entries[2].chsStop[0] = 28; | 
|  | 174 | +    table->entries[2].chsStop[1] = 0; | 
|  | 175 | +    table->entries[2].chsStop[2] = 0; | 
|  | 176 | +    table->entries[2].lbaOffset = 1536;    // 0x600000 / 4096 = 1536 | 
|  | 177 | +    table->entries[2].lbaSize = 256;       // 1MB / 4096 = 256 | 
|  | 178 | + | 
|  | 179 | +    // Partition 4: Storage (LittleFS/FAT32) - starts at 0x700000, size 7MB (1792 blocks of 4KB) | 
|  | 180 | +    table->entries[3].status = 0x00; | 
|  | 181 | +    table->entries[3].chsStart[0] = 28; | 
|  | 182 | +    table->entries[3].chsStart[1] = 1; | 
|  | 183 | +    table->entries[3].chsStart[2] = 0; | 
|  | 184 | +    table->entries[3].type = 0x0B;         // FAT32 (could be 0x83 for LittleFS) | 
|  | 185 | +    table->entries[3].chsStop[0] = 56; | 
|  | 186 | +    table->entries[3].chsStop[1] = 0; | 
|  | 187 | +    table->entries[3].chsStop[2] = 0; | 
|  | 188 | +    table->entries[3].lbaOffset = 1792;    // 0x700000 / 4096 = 1792 | 
|  | 189 | +    table->entries[3].lbaSize = 1792;      // 7MB / 4096 = 1792 | 
|  | 190 | + | 
|  | 191 | +    // MBR signature | 
|  | 192 | +    table->signature[0] = 0x55; | 
|  | 193 | +    table->signature[1] = 0xAA; | 
|  | 194 | + | 
|  | 195 | +    // Erase the MBR partition | 
|  | 196 | +    ret = flash_area_erase(fa, 0, fa->fa_size); | 
|  | 197 | +    if (ret) { | 
|  | 198 | +        Serial.print("Error erasing MBR partition: "); | 
|  | 199 | +        Serial.println(ret); | 
|  | 200 | +        flash_area_close(fa); | 
|  | 201 | +        return ret; | 
|  | 202 | +    } | 
|  | 203 | + | 
|  | 204 | +    // Write the MBR sector | 
|  | 205 | +    ret = flash_area_write(fa, 0, sector, 512); | 
|  | 206 | +    if (ret) { | 
|  | 207 | +        Serial.print("Error writing MBR: "); | 
|  | 208 | +        Serial.println(ret); | 
|  | 209 | +        flash_area_close(fa); | 
|  | 210 | +        return ret; | 
|  | 211 | +    } | 
|  | 212 | + | 
|  | 213 | +    flash_area_close(fa); | 
|  | 214 | +    Serial.println("MBR created successfully!"); | 
|  | 215 | +    return 0; | 
|  | 216 | +} | 
|  | 217 | + | 
|  | 218 | +void setup() { | 
|  | 219 | +    Serial.begin(115200); | 
|  | 220 | +    while (!Serial); | 
|  | 221 | + | 
|  | 222 | +    Serial.println("\nWARNING! Running the sketch all the content of the flash storage will be erased."); | 
|  | 223 | +    Serial.println("The following partitions will be created:"); | 
|  | 224 | +    Serial.println("Partition 1: Network certificates 1MB"); | 
|  | 225 | +    Serial.println("Partition 2: OTA 5MB"); | 
|  | 226 | +    Serial.println("Partition 3: Provisioning KVStore 1MB"); | 
|  | 227 | +    Serial.println("Partition 4: User data 7MB"); | 
|  | 228 | +    Serial.println("Do you want to proceed? Y/[n]"); | 
|  | 229 | + | 
|  | 230 | +    if (!waitResponse()) { | 
|  | 231 | +        return; | 
|  | 232 | +    } | 
|  | 233 | + | 
|  | 234 | +    // Create MBR partition table FIRST before formatting | 
|  | 235 | +    if (flashMBR()) { | 
|  | 236 | +        return; | 
|  | 237 | +    } | 
|  | 238 | + | 
|  | 239 | +    // Format Partition 1: WLAN | 
|  | 240 | +    Serial.println("\nPartition 1 (/wlan:) will be formatted as FAT."); | 
|  | 241 | +    Serial.println("Do you want to format it? Y/[n]"); | 
|  | 242 | +    bool format_wlan = waitResponse(); | 
|  | 243 | + | 
|  | 244 | +    if (format_wlan) { | 
|  | 245 | +        if (formatPartition((uintptr_t) "wlan:", FS_FATFS)) { | 
|  | 246 | +            return; | 
|  | 247 | +        } | 
|  | 248 | + | 
|  | 249 | +        Serial.println("\nDo you want to restore the TLS certificates? Y/[n]"); | 
|  | 250 | +        if (waitResponse() && flashCertificates()) { | 
|  | 251 | +            return; | 
|  | 252 | +        } | 
|  | 253 | +    } | 
|  | 254 | + | 
|  | 255 | +    // Format Partition 2: OTA | 
|  | 256 | +    Serial.println("\nPartition 2 (/ota:) will be formatted as FAT."); | 
|  | 257 | +    Serial.println("Do you want to format it? Y/[n]"); | 
|  | 258 | +    if (waitResponse()) { | 
|  | 259 | +        if (formatPartition((uintptr_t) "ota:", FS_FATFS)) { | 
|  | 260 | +            return; | 
|  | 261 | +        } | 
|  | 262 | +    } | 
|  | 263 | + | 
|  | 264 | +    // Format Partition 4: Storage | 
|  | 265 | +    Serial.println("\nDo you want to use LittleFS to format user data partition? Y/[n]"); | 
|  | 266 | +    Serial.println("If No, FatFS will be used to format user partition."); | 
|  | 267 | +    bool useLittleFS = waitResponse(); | 
|  | 268 | + | 
|  | 269 | +    Serial.println("\nPartition 4 (/storage) will be formatted."); | 
|  | 270 | +    Serial.println("Do you want to format it? Y/[n]"); | 
|  | 271 | +    if (waitResponse()) { | 
|  | 272 | +        if (useLittleFS) { | 
|  | 273 | +            unsigned int partition_id = DT_FIXED_PARTITION_ID(DT_PROP(DT_NODELABEL(storage_fs), partition)); | 
|  | 274 | +            if (formatPartition(partition_id, FS_LITTLEFS)) { | 
|  | 275 | +                return; | 
|  | 276 | +            } | 
|  | 277 | +        } else { | 
|  | 278 | +            Serial.println("FatFS for storage partition not implemented in this example."); | 
|  | 279 | +            Serial.println("Please use LittleFS or update device tree configuration."); | 
|  | 280 | +        } | 
|  | 281 | +    } | 
|  | 282 | + | 
|  | 283 | +    Serial.println("\nFlash storage formatted!"); | 
|  | 284 | +    Serial.println("It's now safe to reboot or disconnect your board."); | 
|  | 285 | +} | 
|  | 286 | + | 
|  | 287 | +void loop() { | 
|  | 288 | +    delay(1000); | 
|  | 289 | +} | 
0 commit comments