FlashPoint
- 9 Devlogs
- 35 Total hours
My fork of CrossInk, adds flashcard support to the XTeink X4
My fork of CrossInk, adds flashcard support to the XTeink X4
I have split the app into four parts - decks, review, sync, settings.
Currently just shows an ‘open’ button to review cards. Will eventually show a navigable deck tree.
For reviewing due cards.
Syncs with Anki desktop.
Settings and stuff… (haven’t decided yet)
Cards are now downloaded from Anki on my laptop when the app is opened
and loaded into memory for review.
I am running an anki plugin on my laptop called anki-connect which provides a REST API for interacting with cards, decks, notes etc…
The process looks like this:
Attached is a video showing 3 cards being synced.
Why: My current implementation loads the entire response body into
memory at once then deserialises it. Each card in the response is
roughly 1KB, so loading many at once would require more memory than the
ESP32 has.
Fix: Implement a streaming approach instead where cards are saved to the
SD card as they are received, reducing memory usage.
Why: I wanted a quick CardStore implementation to test the sync with so
cards are only stored in memory.
Fix: Implement a CardStore which saves them to the SD Card
Currently trying to get the XTeink to load cards from my laptop through the anki-connect plugin’s api. Here’s a screenshot of the log from it attempting to load the decks and cards. Sadly it crashed right after so I’m still working on that.
After I get this working I can start working on storing the cards so they can be reviewed offline.
I’ve made an interface called CardStore so I can implement multiple ways of storing cards. In future, I might use this to benchmark and compare different methods but for now I’m using an in-memory store so I can quickly test things out.
I’ve made a plan for the persistent card storage, but I’ll get to that later.
For now I’m going to work on the initial sync with Anki. This will be much easier than the partial sync (where cards have been added, edited, removed, and reviewed).
Anki allows you to export all your cards into a single .apkg file, which is essentially a zip file containing media files along with Anki’s SQLite database. I want it to be possible to import apkg files directly into flashpoint so I figured if I could get SQLite working on the XTeink X4 directly then this would be easy.
I have never used PlatformIO before, so I wasn’t sure how to actually include libraries. Initially, I tried just adding #include <sqlite.h> where I needed it, but this failed to build so I installed SQLite onto my laptop which didn’t work either. I discovered that since the XTeink X4 uses an ESP32 I needed specific libraries for it — I found siara-cc/Sqlite3Esp32 and added it to the dependencies section in platformio.ini (PlatformIO’s config file). This brings us to Issue #2.
The ESP32 only has 16MiB of flash memory. This is enough to store around 48 novels, 4 photos, or 1/20th of a YouTube video*. This memory is typically used to store configuration, assets, and firmware. Here is the partition scheme used in CrossInk (the project I forked).
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000, # 20KiB - For settings, like the WiFi password
otadata, data, ota, 0xe000, 0x2000, # 8KiB - Tracks OTA update progress
app0, app, ota_0, 0x10000, 0x640000, # 6.25MiB - First firmware partition
app1, app, ota_1, 0x650000,0x640000, # 6.25MiB - Second firmware partition
spiffs, data, spiffs, 0xc90000,0x360000, # 3.375MiB - Filesystem for assets, such as those in the webserver
coredump, data, coredump,0xFF0000,0x10000, # 64KiB - Crash reports for debugging
Overall, this reduces the space actually available for the firmware down to 6.25MiB. SQLite adds roughly 470KiB to the firmware image, bringing it beyond the 6.25MiB partition limit. To solve this I shrunk the spiffs partition, which allowed me to expand app0 and app1 to 7MiB and build an image with SQLite. However, I realised that I still couldn’t use SQLite yet.
Under many abstractions, it seems that CrossInk uses SdFat to manage the filesystem, rather than the simpler SD library. As I understand it, this means that files cannot be accessed using stdio.h commands like fopen() and instead must be accessed through the SdFat library. This means that for SQLite to work I would need to write a sort of ‘translation’ class which maps SQLite’s desired filesystem actions onto SdFat methods. This is called a VFS (Virtual File System), and looks very tedious to implement. In a last ditch effort, I tried to use the SD library and SdFat library simultaneously, which resulted in the whole device crashing whenever I opened my flashcards (pictured below) - this is not very useful. I’m sure I’m misunderstanding something and there’s probably an easier solution which works, but I’ve decided to try a different approach instead.
CrossInk already persists structured data in many places. For example, bookmarks appear to be stored in a binary file format using some sort of serialisation utility. I’ll look into this and probably implement my own storage solution which only stores the data I need rather than using an entire SQLite database.
In hindsight, SQLite was probably the wrong decision, but it led me to better understand PlatformIO, ESP32 partition tables, and CrossInk’s storage architecture.
Rough calculations: