Created
April 9, 2026 12:57
-
-
Save ndoo/3f064f2530678d0601ff068a8bc1ee9d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Now I have all the information I need. Let me calculate the maximum file sizes: | |
| Files saved to LittleFS on STM32WL (with MESHTASTIC_EXCLUDE_BACKUP): | |
| /prefs/device.proto — meshtastic_DeviceState_size = 1737 bytes → 1 block (1 × 2048) | |
| fullAtomic write: during rename, .tmp (1 block) + final file (1 block) = 2 blocks peak | |
| /prefs/nodes.proto — meshtastic_NodeDatabase_size at runtime with MAX_NUM_NODES = 10 on STM32WL. Each node entry is 196 bytes, so 10 nodes plus minimal header overhead comes to roughly 2000 bytes total, fitting in 1 block. | |
| /prefs/config.proto — meshtastic_LocalConfig_size = 754 bytes → 1 block | |
| /prefs/module.proto — meshtastic_LocalModuleConfig_size = 820 bytes → 1 block | |
| /prefs/channels.proto — meshtastic_ChannelFile_size = 718 bytes → 1 block | |
| /prefs/transmit_history.dat has a 6-byte header and stores up to 16 entries at 7 bytes each, totaling 118 bytes — comfortably within a single block. | |
| /prefs/uiconfig.proto isn't written on STM32WL since HAS_SCREEN is disabled. | |
| Now I'm thinking through the LittleFS metadata overhead. LittleFS v1 allocates blocks 0 and 1 for its superblock, so with 14 total blocks available, that leaves 12 blocks for actual data storage. When SafeFile performs a fullAtomic write (which is used for device.proto), there's a temporary peak in space usage during the operation. | |
| For fullAtomic writes, the old file and the new temporary file both exist simultaneously, requiring 2 blocks at peak. For non-fullAtomic writes to other files, SafeFile also writes to a temporary file first before renaming, so the peak usage is similarly 2 blocks—the original file plus the temporary copy during the write operation. So the peak during a single file write is 6 steady-state files plus the temporary file being written, totaling 7 blocks of data plus 2 for LittleFS metadata—around 9 blocks. But I need to reconsider how LittleFS actually allocates blocks. It reserves blocks for the superblock and metadata, then allocates directory metadata blocks for each directory—the root and /prefs/ each get 2 blocks—and the actual file data sits on top of that. Let me trace through what files actually exist in /prefs/ after excluding backups. I'm realizing I miscounted—that's already 12 blocks out of 14 available, which leaves almost no room. Let me reconsider how LittleFS v1 actually structures this. The superblock itself serves as the root directory metadata, so blocks 0 and 1 handle both. When I create the /prefs/ subdirectory, that takes another pair of blocks. For the files themselves, smaller ones can be stored inline within the directory metadata, but once they exceed that threshold, they need their own data blocks—and I'm looking at six files that likely spill over into separate blocks. | |
| The previous analysis showed steady state at 7 blocks and peak at 8 blocks when writing device.proto, which suggests that with the backup excluded, I'm actually much closer to the limit than my current estimate of 12 blocks. | |
| Let me reconsider how LittleFS actually stores these files. The metadata blocks can inline small files directly, so I need to check which files would actually require separate data blocks versus being stored inline within the directory metadata. Looking at the file sizes—device.proto at 1737 bytes and nodes.proto around 2000 bytes would need their own blocks, but the smaller config, module, and channels files might fit inline. This could explain why the actual usage is only 7 blocks instead of the 9 I calculated. | |
| Rather than getting bogged down in the exact inline thresholds, I should trust the previous analysis that showed 7 blocks steady state and 8 blocks at peak when device.proto is being atomically written. With the MESHTASTIC_EXCLUDE_BACKUP flag removing the 2-block backup.proto file, I can work from that baseline. I need to think through the SafeFile write pattern more carefully—when backup.proto gets written, the peak usage happens during the temporary file phase, and I should calculate it as the steady state blocks plus the temporary blocks needed for the backup write, which would be 7 blocks (steady state) plus 2 blocks (backup.proto temporary), giving a peak of 9 blocks total during that operation. | |
| With the backup feature disabled via MESHTASTIC_EXCLUDE_BACKUP, the maximum drops to 8 blocks since backup.proto never exists. Now I'm wondering if we can reduce the total block_count from 14—currently there's 6 blocks of headroom during peak usage (14 total minus 8 peak), but I need to understand LittleFS's minimum requirements to see how low we can safely go. | |
| In LittleFS v1, the superblock occupies blocks 0 and 1 within the total block_count, so if peak usage is 8 blocks, the minimum block_count would be 8 plus some safety margin for metadata overhead, wear leveling, and potential file growth. I'm realizing I need to clarify whether that 7-block steady-state number from before was an empirical measurement or a theoretical calculation, since that affects how I think about the actual minimum. | |
| Now I'm working through the block allocation more carefully: the superblock and root directory share blocks 0 and 1, subdirectories each get their own metadata pair, and files either inline into directory metadata if they're small enough or consume separate data blocks. With a 2048-byte block size, I'm estimating that small files under roughly 200 bytes might fit inline, while larger files need external blocks. I'm realizing the metadata block itself stores directory entries, and in LittleFS v1, the inline storage capacity depends on how much space remains after accounting for entry headers—each file entry takes roughly 12-16 bytes for metadata, so with 6 files that's about 80 bytes, leaving around 1960 bytes available for inline file data within that single metadata block. | |
| Looking back at the previous session's empirical findings of 7 blocks steady state and 8 blocks peak, I should verify this by counting the actual block usage: the superblock takes 2 blocks, the /prefs/ directory metadata takes another 2 blocks, and then there are individual blocks for the proto files like channels.proto, config.proto, and module.proto. | |
| For the larger files like nodes.proto at 1960 bytes and device.proto at 1737 bytes, each fits within a single 2048-byte block. The question is whether transmit_history.dat gets inlined in the metadata or takes its own block—if it's inlined, that brings me to 9 blocks total, which exceeds the 7-block baseline. But reconsidering the LittleFS v1 structure, the root directory and /prefs/ directory are each metadata pairs occupying 2 blocks each, so that's 4 blocks for metadata alone, leaving room for 5 data files at 1 block each, which would match the observed count. | |
| Looking at the smaller proto files—channels.proto, config.proto, and module.proto—they might be inlined within the /prefs/ metadata block if their combined size plus directory overhead fits within 2048 bytes, though at 2292 bytes just for those three files, that seems unlikely unless LittleFS is doing something more aggressive with compression or only inlining one file per metadata block. | |
| Rather than getting stuck on the exact breakdown, I should focus on calculating a conservative minimum block count based on theoretical maximum usage and then add sufficient margin for safety. | |
| Looking at the worst-case scenario during a full atomic write of device.proto, I'm accounting for the superblock, metadata pairs, and all the proto files plus data files that could exist simultaneously—that puts us at around 10-11 blocks at peak usage. Now I need to factor this into the flash size and upload limits. | |
| With 14 total blocks available, that leaves only 3-4 blocks of headroom, which is tight. Dropping to 12 blocks would be risky with just 1-2 blocks spare. I should verify whether nodes.proto could actually exceed the 2048-byte block size, since with MAX_NUM_NODES set to 10 on STM32WL and each node entry taking about 196 bytes, the maximum should stay under 2000 bytes. | |
| Looking at the steady-state file layout, I'm mapping out the minimum block requirements: the superblock and directory metadata each need pairs, then each proto file and data file takes one block. That gives me a baseline of 10 blocks during normal operation, with an additional temporary block needed during atomic writes. | |
| But this conflicts with my earlier estimate of 7 blocks steady-state. I need to figure out whether the smaller files like config, module, and channels are being inlined into the metadata rather than stored separately. Let me check the LittleFS v1 source to understand how inline files work and whether the superblock actually serves as the root directory. | |
| Looking at the metadata structure, each directory block stores all its entries—both file and subdirectory references—as packed attributes. If a file is small enough, its data gets embedded directly in the metadata block rather than taking up separate storage. So I need to calculate whether the files in /prefs/ are small enough to be inlined, which would significantly reduce the actual block count. | |
| The six files total about 6157 bytes of data, which far exceeds the 2048-byte metadata block limit, so they can't all be inlined together. This means most or all of them will need separate block allocations outside the metadata. I'm trying to pin down the exact inline threshold for LittleFS v1—it depends on prog_size and block overhead, but with prog_size = block_size = 2048, the practical limit for inline storage is quite constrained, possibly leaving almost nothing eligible for inlining. | |
| Rather than getting lost in the LittleFS source code details, I should focus on what matters: the user wants to find the safe minimum filesystem size to free up more firmware space. I know the math checks out—14 blocks × 2048 bytes equals the 28,672 bytes currently allocated, which combined with the upload limit of 233,472 bytes fills the entire 256KB flash. So the question is whether we can reduce those 14 blocks without breaking functionality. | |
| Looking at the actual firmware usage, there's 172,496 bytes in use with 60,976 bytes of headroom available. If I reduced the filesystem to 10 blocks instead, that would give the firmware 241,664 bytes total—a significant gain. Now I'm working through what the absolute minimum block count needs to be by accounting for the steady-state storage: the superblock and root directory pair, the /prefs/ directory pair, and then individual blocks for each of the protocol buffer files like device.proto, nodes.proto, config.proto, and the others, plus space for transmit history. | |
| That gets me to 10 blocks under normal conditions. But during a SafeFile atomic write operation on device.proto, both the original file and the temporary file exist simultaneously while the write is being verified, which temporarily pushes the requirement to 11 blocks at peak. Actually, reconsidering the rename operation—when lfs_rename() atomically replaces the old file with the new one, the peak is really just 10 blocks since the old and new versions don't both persist through the entire operation. | |
| With 14 blocks available, that leaves 3 blocks of headroom. Going to 12 blocks would cut that down to just 1 block, which is too risky since LittleFS needs spare capacity for wear leveling and compaction. Even LittleFS v1 typically requires at least 1-2 free blocks for these operations, so 1 block of headroom isn't safe. Thirteen blocks would give me 1 block of headroom as well, which still feels precarious. | |
| Actually, I should double-check my block accounting. Directory metadata for /prefs/ takes up 2 blocks, but those blocks store the file entries themselves—they're not separate from the data blocks. The actual file data lives in its own blocks, and since all my files fit within 2048 bytes, each one should fit in a single data block. | |
| But now I'm realizing the previous session calculated 7 steady state and 8 peak, which is different from my 10 and 11. Let me look at whether some files might actually be getting inlined into the directory metadata itself. I'm checking the LittleFS v1 source to understand the inline threshold—it looks like small files can be stored directly within the directory blocks if they fit in the remaining space after accounting for other metadata entries. than 2048. | |
| Hmm, but since prog_size = block_size = 2048 in our current config, and LittleFS metadata blocks are exactly block_size bytes, there's essentially no room for inline file data alongside directory entries. | |
| Actually wait, let me re-read the LittleFS v1 inline logic more carefully. In LittleFS v1.6, files that are at most half the block size—so 1024 bytes—are candidates for inlining. That would include channels.proto at 718 bytes, config.proto at 754 bytes, and module.proto at 820 bytes, plus transmit_history.dat at 118 bytes. But device.proto at 1737 bytes exceeds that threshold and wouldn't be inlined. | |
| You've hit your limit · resets 10pm (Asia/Kuala_Lumpur) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment