| 
 | 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