feat: 8MB app + 7MB data partition, add recovery mode for OTA#951
feat: 8MB app + 7MB data partition, add recovery mode for OTA#951ngxson wants to merge 5 commits intocrosspoint-reader:masterfrom
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Before I proceed further, would like to tag @crosspoint-reader/firmware-maintainers for discussion. Would like to see if everyone is ok with this direction. |
|
I'm not a maintainer, but this sounds like a great direction to me. Keeping half of the flash empty most of the time plus an unused spiffs partition is leaving CrossPoint unnecessarily constrained. |
Is there enough room left to fit the Hebrew block (U+0590–U+05FF)? |
That's ~100 characters, we should have more than enough space.
However, I'm running into issue that it fails to link the ELF if the total program size is more than 8MB. I should be onto something undocumented... The official docs tells that 16MB mapping is possible, but I doubt if it's actually correct |
Update: that was not true. Turns out, the included |
|
Ok so after some more trials and error, I found out that:
Test code#define ROW_16_BYTES 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10
#define ROW_32_BYTES ROW_16_BYTES, ROW_16_BYTES
#define ROW_64_BYTES ROW_32_BYTES, ROW_32_BYTES
#define ROW_128_BYTES ROW_64_BYTES, ROW_64_BYTES
#define ROW_256_BYTES ROW_128_BYTES, ROW_128_BYTES
#define ROW_512_BYTES ROW_256_BYTES, ROW_256_BYTES
#define ROW_1KB ROW_512_BYTES, ROW_512_BYTES
#define ROW_2KB ROW_1KB, ROW_1KB
#define ROW_4KB ROW_2KB, ROW_2KB
#define ROW_8KB ROW_4KB, ROW_4KB
#define ROW_16KB ROW_8KB, ROW_8KB
#define ROW_32KB ROW_16KB, ROW_16KB
#define ROW_64KB ROW_32KB, ROW_32KB
#define ROW_128KB ROW_64KB, ROW_64KB
#define ROW_256KB ROW_128KB, ROW_128KB
static const char TEST[] = {
ROW_256KB ,
ROW_256KB ,
ROW_128KB ,
ROW_64KB ,
ROW_32KB ,
ROW_16KB , // add or remove them to see
};
void setup() {
t1 = millis();
assert(TEST[millis() % sizeof(TEST)] != 0); // make sure it's not optimized out by the compiler
... |
|
Ah, good find. This reference document, page 95, spells out that we get 8 MB for instruction bus and 8 MB for data bus. I bet it is possible for us to use |
|
@znelson yes I'm aware of the limitation of 8MB inst + 8MB data. I wanted to try force-loading the font as instruction by adding Re. I'm thinking about an alternative: We can have a notion of "logical partition" which allow app partition to contain both app + assets, something like: |
|
Alright, I put together a demo that allow concatenate I added Latin-Ext codepoints which already allow reading Vietnamese books. This was not possible if I compiled with the normal way (font bitmap as header), the linker won't allow linking due to running out of space on All 3 fonts take 5.71MB. No addition delay between page turns, even though I wasn't using mmap. I'll try experimenting with CJK next, a bit more complicated as explained above, as the current Demo vietnamese book (The story of a seagull and the cat who taught her to fly / Luis Sepúlveda):
|
|
I see. You're a few steps ahead 🙂 Edit: Oops, I was replying to your previous comment with "I don't want to have yet another partition". Seems like it wouldn't be so bad to have 8 MB executable + 8 MB data/fonts (or 7 MB executable + 1 MB recovery + 8 MB data/fonts). But either way I'm interested to see what you figure out. I hadn't seen your #596 until now. I pushed a quick (AI assisted) PoC of splitting fonts on to a data partition in #960. |
|
@znelson Just note that my poc can also allow putting font into dedicated partition. Indeed, esptool has a command called "merge-bin" that add a 0-padding between app/data partitions, so when upload they will be in the correct place. My poc work that way, the main difference is that it doesn't add any padding. Btw, one of my goals is to have app+data in one single file so it will be easier to validate the hash. |
|
After some more testings, I concluded that CJK is not fit-able inside the flash. The whole "core" CJK is still to big. Just one single variant of Noto Sans SC regular, 12pt already took 6MB. So fitting 4 combinations: I experimented with filtering only simplified chinese from CJK, but on second thought, it's not very practical. Also, CJK fonts are usually a dedicated font, they also don't have italic variant. So, it makes more sense to distribute them as user-installable font, something like: #455 However, given the benefit of the current PR is to allow using flash for other things: support latin-ext, icons, hyphenations, bluetooth stack, better image decoder, etc. I think it's still worth to proceed in this direction. Though I still would like to hear other maintainers' thoughts on this. |
|
One constraining factor I've kept to date is the ability to revert back to the official firmware quickly by retaining the same partition structure as XTOS and just re-applying the latest OTA. This would obviously relax that, and while it's something I'm definitely open to considering, I think we'd need to communicate the change, and update https://github.com/crosspoint-reader/xteink-flasher to suit. |
I do consider this, more specifically, I think you're referring to the ability of having crosspoint in app0 and stock firmware in app1, right? Indeed, this new partition layout will still work if you try to flash the stock firmware to offset Here is the command I used for flashing stock fw: The only thing stock firmware need is the SPIFFS partition to store settings, but I will add it to the partition table as @znelson suggested earlier, mostly because I want to avoid misconception that app can be larger than 8MB (will explain more about this a bit later) |
|
Given the recent improvement in font storage usage, I'm a bit doubt if it still worth proceeding with this PR. As explained in #951 (comment) and #1059 (comment) , it is not practically possible to add CJK to the firmware, mostly due to poor user experience. We could in theory, support just one CJK font with 4 variants I will pause this PR a bit to see what are other benefits of having a bigger flash size. Will eventually close it if I cannot find any. If you have any ideas, feel free to comment. |
|
I think it's worth continuing down this path a bit longer before calling it. Yes, we have ~2MB free right now — but that headroom tends to erode faster than expected. New features, UI additions, community contributions... it adds up. Getting the partition layout right while we have the chance is just good planning. And even if full CJK support may stays out of reach, a larger flash layout pays off in plenty of other ways:
Even if CJK never makes it onto flash, more headroom is a long-term win. Margin is rarely wasted. P.S. while I'm eagerly waiting for to custom user fonts, I do think we should always keep at least one font baked into the flash — and it should cover as much of Unicode as we can reasonably fit. You're doing amazing job! Thank you so much for your contributions. |
|
Papyrix supports CJK, arabic, thai, vietnamese in same firmware.bin, all while keeping same partition size. So it's doable |
|
@osteotek I believe Papyrix stores all CJK, Arabic and Thai fonts on the SD card, While having one reader font and one UI font embedded in the firmware. |
Right. I just flashed Papyrix and was able to open arabic epub but not chinese, looks like external font is required for CJK. One difference Papyrix has is they are keeping only one reader font in one size embedded in the firmware. We may have to do the same if we want as much Unicode support as possible out of the box |
|
IMHO, opendyslexic should be outside of the firmware once custom font is supported in the future. I feel like it may not help much on a device with such small screen like this. IIRC, my friend how has dyslexia only uses a ruler when reading text on A4 paper; reading on a mobile phone screen is fine. And from my own experience: opendyslexic on kindle looks good, but on xteink it looks too crammed |
|
With #1157, flash usage will be at
This really reinforces my earlier point about how urgently we need additional space. Expanding the layout sooner rather than later would give us breathing room and prevent us from constantly optimizing under pressure. |
|
Yes I'll go back to this after #1016 |
|
Annoyingly, it becomes too difficult to fit the recovery firmware into 1MB. I had to (almost) completely separate its source code into a sub-project, with a dedicated font (only ASCII, no unicode) And yet it's barely fit into 983KB And |
|
@Jason2866 tagging you since you said that you're the maintainer of pioarduino: is there any method to flash the custom recovery image (same idea as safeboot) to the correct |
|
@ngxson Add your own boards.json. This declutters the platformio.ini and you can flash everything you want. Tasmota does use a small factory (recovery) firmware for flashing. In https://github.com/arendst/Tasmota/blob/development/pio-tools/post_esp32.py the factory (safeboot) firmware is added for flash process Btw To have an recovery firmware under 1MB is no problem. Take a look at the size of the safeboot (recovery) firmware of Tasmota. Honestly the project includes debatable features for example WebDAV. This is for embedded devices a fail. Furthermore it more or less works only with Windows. Another edit: Factory firmware has to be at |
safeboot has the advantage to be a dedicated project, while here I'm trying to reuse as much code as possible to make a MVP. I can easily split it this into a dedicated minimal project that fit into < 800KB though.
agree, it's not broken, I said that "it's removed"
if I understand correctly, the info you present is the case where safeboot is already compiled to however, since this is an MVP, I need to compile and flash the recovery app in a simple way. now that you confirmed that there is no other way to get around the fact that ofc, other (proper) methods can be considered, but I'm doing a MVP, and simplicity is the main point here. |
|
The Tasmota scripts fetches precompiled safeboot bin if no locally build is found, and the post script combines them to a full factory image which gets automatically uploaded from VSC. The safeboot is a env and can be build as the normal firmware. |
|
@Jason2866 I highly recommend you to read the description of this PR, as long as #862 (reply in thread) for the context.
I did consider this way, given that we will surely need to combine the app+spiffs in the future. However, my point still stands: the ability to compile and flash only the recovery image of < 1MB makes the development much easier.
That's basically what I referred to by saying:
The main differences between safeboot and crosspoint's recovery mode are:
The TL;DR is that this is the same thing as recovery mode on android, hence the name |

Summary
This PR is currently a MVP to illustrate the discussion: #862 (reply in thread)
It allows allocating 15MB flash (8MB app + 7MB data) for crosspoint and 1MB for recovery, the same idea as "recovery mode" on android.
The reason why we cannot use the entire 15MB for app is because the linker refuse to link such app (I suppose it won't load-able either.) The 7MB data can be accessed either by doing mmap (demo in #596), or by using
esp_flash_read()To try this PR
defaultprofile in platformio, then flash itrecoveryprofile in platformio, then flash itThe procedure for OTA will be as followed:
firmware.binfirmware.binis picked up, verify hashesp_ota_mark_app_valid_cancel_rollbackis called to cancel rollback to recovery mode. If it's not called (the firmware failed to boot), it goes back to recoveryTo discuss:
TODO:
firmware.binBonus:
AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? NO