You uploaded the WiFi firmware updater to your dual-core vision board, and the code just wrote the same 421,098 bytes to two different addresses on a 16 MiB QSPI flash: once as a file inside a small FAT partition at the bottom of the chip, and once as raw data at a fixed offset near the top. The offset — 15 × 1024 × 1024 + 1024 × 512 — looks arbitrary the first time you read it. It is not, and knowing why is the difference between a clean update and a corrupted data volume. This explainer walks the flash memory map of that updater byte by byte: what each region holds, why the firmware is stored twice, and what to confirm before you reboot. The same layout logic applies to any embedded vision machine whose host board carries a wireless module next to its board-level camera.
Where does the WiFi firmware actually live on the flash?
In two separate regions: the file copy goes to /wlan/4343WA1.BIN inside a 1 MiB FAT partition that starts at byte 0, and the raw copy goes to byte 16,252,928 in the 512 KiB tail at the top of the 16 MiB chip.
The sketch reaches the chip through QSPIFBlockDevice root(...), clocked at 40 MHz (40000000) in mode QSPIF_POLARITY_MODE_1. Its first storage step carves the partition with mbed::MBRBlockDevice::partition(&root, 1, 0x0B, 0, 1024 * 1024), wraps it as mbed::MBRBlockDevice wifi_data(&root, 1), and mounts it through mbed::FATFileSystem wifi_data_fs("wlan"). The firmware file lands at /wlan/4343WA1.BIN, the certificates at /wlan/cacert.pem.
Then comes the copy that confuses everyone: root.program(wifi_firmware_image_data, offset + byte_count, chunck_size) writes the identical bytes at a constant address near the top of the chip. Both writes run unconditionally in setup(); loop() is empty. So "which place does the firmware use?" has one answer — both — and the full map is:
| Byte range | Size | What lives there |
|---|---|---|
| 0 – 1,048,575 | 1 MiB | MBR partition 1, FAT32 (type 0x0B): /wlan/4343WA1.BIN (421,098 bytes) and /wlan/cacert.pem
|
| 1,048,576 – 15,728,639 | 14 MiB | Application data; partition 2 starts at 1 MiB in the stock layout |
| 15,728,640 – 16,252,927 | 512 KiB | Head of the top region; the updater never touches it |
| 16,252,928 – 16,777,215 | 512 KiB | Raw, memory-mapped copy of the WiFi firmware (the 15.5 MiB – 16 MiB region) |
Add the sizes: 1 + 14 + 0.5 + 0.5 = 16 MiB, the whole chip, with no overlap.
Why is the raw copy written at 15.5 MB?
Because the top 512 KiB of the 16 MiB chip is reserved for firmware the CPU reads directly through the QSPI controller's memory-mapped mode, and 15 × 1024 × 1024 + 1024 × 512 places the first byte exactly at the start of that region.
The comment in the updater states the intent in one line: "use space from 15.5MB to 16 MB for another fw, memory mapped". Memory-mapped mode is why a fixed byte offset matters at all: the QSPI controller translates ordinary CPU reads into serial transactions, so firmware at flash offset 16,252,928 is readable at one constant address with no filesystem, no mount step and no RAM buffer. Code that streams the module's firmware simply points at that address.
The region must also sit outside every FAT volume. A filesystem allocates clusters wherever it likes, so a raw write inside a mounted volume is invisible to FAT and gets destroyed by the next file allocation. An unpartitioned tail keeps the two users apart: files below, memory-mapped bytes above. If reading a processor's memory map is new territory for you, finding BIOS, flash and SDRAM ranges in an embedded Linux memory map walks the same exercise on Linux hosts.
Why is the FAT partition only 1 MB when the firmware is 421 kB?
One MiB is the smallest round volume that holds the 421,098-byte firmware file, the certificate file and FAT overhead with margin, so the staging area stays small and the remaining 15 MiB stays free for the application.
The staging volume spans bytes 0 to 1,048,575. Inside it, the file write moves 1,024 bytes at a time: 421,098 = 411 × 1,024 + 234, so 411 full chunks and one 234-byte tail chunk. Certificates go out in 128-byte chunks; their length lives in certificates.h, not in the updater. A root-certificate store is a few kilobytes, so file, certificates and FAT structures share the 1 MiB with room left over — and the 14 MiB above stays untouched.
The mount logic protects first boots. If the mount call fails, the sketch reformats partition 1 once and prints that this usually means the firmware was never installed or was overwritten. If /wlan/4343WA1.BIN already exists, it asks Y/N before reformatting; answering N leaves everything exactly as it was.
What does partition type 0x0B actually select?
0x0B is the MBR partition-type byte for a FAT32 volume, and passing it to mbed::MBRBlockDevice::partition() is what lets mbed::FATFileSystem mount the slice as /wlan instead of treating the chip as one raw block device.
A master boot record spends sector 0 on four 16-byte partition entries. The call partition(&root, 1, 0x0B, 0, 1024 * 1024) writes entry 1 as a FAT32 volume starting at byte 0, 1,048,576 bytes long. MBRBlockDevice wifi_data(&root, 1) then presents exactly that slice as a block device, so FAT structures cannot wander into the space above. Raw chip, partition, filesystem, path — that is the whole storage stack, four objects deep.
Why does the sketch have to run on the M7 core?
The updater is built behind a guard: #ifndef CORE_CM7 raises an #error that says to upload to the M7 core instead of the M4, because only the core that owns the QSPI controller and the module's host interface may reprogram the flash.
The guard turns a wrong-core upload into a compile error rather than a runtime fault halfway through a write. On these dual-core boards the two cores share the package but not the peripherals, and the flash controller has exactly one owner. For how the cores split work in practice — one running the application and camera stack, the other handling IO or motion — read running a sketch on both cores of a dual-core vision board. During the update itself the rule is single ownership: while root.program() runs, no other code may read or write the QSPI bus, and nothing may execute from that flash.
How do you confirm both copies before you reboot?
Read both regions back and compare: list /wlan and confirm 4343WA1.BIN and cacert.pem are present, then read 421,098 bytes starting at 16,252,928 and compare them against the image compiled into the sketch.
The updater already prints progress every 10 percent at 115200 baud on Serial, echoes the certificate file back character for character, and ends by telling you it is safe to reboot. Four confirmations close the loop on your side:
- List
/wlan: both4343WA1.BINandcacert.pemmust appear. - Reopen the BIN with the sketch's
getFileSize()helper: the answer must be 421,098 bytes. - Check the certificate echo shows a PEM block, not an empty file.
- Compare the raw tail against the compiled-in image:
uint8_t back[1024] = {0}; // our read-back buffer, not from the updater
long done = 0; // bytes compared so far
long n = 0; // chunk length for this pass
bool ok = true; // true only if every byte matches
while (done < 421098 && ok) {
n = (421098 - done < 1024) ? 421098 - done : 1024;
root.read(back, 15 * 1024 * 1024 + 1024 * 512 + done, n);
ok = (memcmp(back, &wifi_firmware_image_data[done], n) == 0);
done += n;
}
// ok == true proves the raw copy matches the compiled-in image
The same read-back discipline is step one of our board camera evaluation checklist for inspection systems, and the approval-to-readback pattern is spelled out in the camera firmware update workflow.
When is the 15.5 MB offset the wrong answer?
When the chip density or the layout differs: the same number runs off the end of an 8 MiB part, and a board whose flash was reformatted by other code can have a filesystem sitting on byte 16,252,928, so the raw write would land inside it.
Three cases decide it. Density: an 8 MiB chip ends at byte 8,388,608, and offset 16,252,928 is past the end, so program() fails. Layout: a previous project may have partitioned the flash differently — the stock layout tool also ships a non-default scheme with a smaller partition 2 — so read the MBR entries before trusting any fixed offset; if a volume reaches past 15.5 MiB, rebuild the stock layout first. Size: the tail holds 524,288 bytes and the image is 421,098, leaving 103,190 bytes, so a release more than about 24 percent larger no longer fits and needs a new layout, not a squeeze.
The decision: on any board of unknown history, dump the partition table before you write, and treat the fixed offset as valid only against the stock 16 MiB map.
What should you check before you power-cycle?
Run the six checks below; each one catches a failure that otherwise surfaces later as a module that will not associate, a TLS handshake that fails, or a camera application that cannot find its data partition.
- The sketch compiled with
CORE_CM7defined — the build stops otherwise. -
mbed::MBRBlockDevice::partition(&root, 1, 0x0B, 0, 1024 * 1024)ran, and/wlanmounted or reformatted exactly once on a virgin chip. -
/wlanlists4343WA1.BINandcacert.pem, and the BIN reads back 421,098 bytes. - The 421,098 bytes at offset 16,252,928 compare equal to the compiled-in image.
- No other core or task touched the QSPI bus while the writes ran.
- The antenna is connected before the radio comes up.
With the module side settled, pick the camera that shares its frame. A 1.6 MP GigE board-level camera such as the MV-CB016-10GM-M-W or a 3.1 MP USB3 board-level camera like the LS-BCM-0003 keeps image traffic on wire, so the WiFi module carries telemetry only. Where the camera hides inside a tight mechanism, a miniature 1.3 MP GigE camera with an M12 lens mount such as the LS-BCM-0061 fits where a C-mount body will not. Browse the board-level cameras category, or start at the camera step of the system configurator.
- Sensor format
- 1/2.9"
- Lens mount
- M12
- Sensor format
- 1/1.8"
- Lens mount
- C-mount
Questions
Does the WiFi firmware update touch images my application stores on the same flash?
No, not on the stock layout. The updater writes partition 1 (bytes 0 to 1,048,575) and the reserved tail (bytes 16,252,928 to 16,777,215); application data lives in the middle region between 1 MiB and 15 MiB and is never opened. The only destructive step is the one-time reformat of partition 1 itself, which holds only the firmware file and certificates.
Can you skip the file copy and program only the memory-mapped image?
You can force it, but you lose TLS: the root certificates live only in the FAT volume as /wlan/cacert.pem, and the updater treats an existing 4343WA1.BIN as proof the module is already provisioned. Write both copies every time; the raw program erases and rewrites the same region, so repeating it on an already-updated board takes well under a minute and removes all doubt.
What happens if the board loses power partway through the raw write?
The tail then holds a partial image with no checksum: some chunks are new, the rest is erased or stale, and the module may fail to start or associate. Nothing else is damaged. Rerun the updater from the beginning — each program() pass erases before it writes, so the partial copy is cleared, and the file side is rewritten from the image compiled into the sketch.
Is the 15.5 MB offset 15,500,000 bytes?
No. Flash addressing on this board uses binary units: 15 × 1,048,576 + 512 × 1,024 = 16,252,928 bytes. A decimal 15.5 MB would be 15,500,000 bytes, which sits inside the middle data region and would corrupt it. When you translate the updater's arithmetic for another tool, carry 1024-based units end to end; a single decimal slip lands the write 752,928 bytes low.
Can the reserved tail store anything else in its spare 103,190 bytes?
Not safely. The 103,190 bytes left after the 421,098-byte image are slack so a slightly longer firmware release still fits in the 524,288-byte region; if you park files there, the next updater run erases them without warning. Put application files on the /wlan volume or the middle partition, where a filesystem, not a fixed offset, tracks what belongs to whom.

