ExplorInk
GitHub

development

Development: what is built, and when it landed

ExplorInk is a working prototype under active development, built in the open against real hardware. This page is the evidence: a dated log of what changed, and the full status list of what runs on the device today. Numbers here come off the device or off the code, and the entries say which.

the log

Dated entries, newest first.

  1. The map screen keeps answering while it draws

    Drawing a map on electronic paper takes seconds. Until now the device spent those seconds deaf: a button pressed while the map was redrawing was never noticed, the menu did not open until the picture finished, and a phone talking to the device could have a command go missing entirely.

    The picture is now assembled alongside everything else instead of in front of it. A press that lands mid-draw is held and carried out the moment the panel is free, the menu appears as soon as the map lands, and nothing the phone sends is dropped while the screen is busy. The drawing itself is no faster, and we are not claiming it is: what changed is that the rest of the device no longer waits for it.

    Getting there took two rounds of review over the finished work and five passes on real hardware. The reviews found twelve problems that a session of hand testing had walked straight past, most of them only possible when two things happen inside the same few milliseconds. The thirteenth was found by a question about a number that looked too good, which is its own lesson.

  2. The tile server started maintaining itself

    Our tile server has always built map tiles on demand: a device asks for ground nobody has covered yet, the server notices, builds it, and the next time anyone passes through it is there. What it never did was go back. Once a tile existed, nothing ever asked for it again, so every later improvement to how tiles are drawn reached only the ground covered after it. The published map had drifted into seven different vintages of its own drawing rules.

    It now works through them on its own, one small area at a time, always behind whatever a device is actually waiting for. It waits for the drawing rules to hold still before it starts, so a day of cartography work does not send it chasing a moving target, and it only replaces a tile whose contents genuinely changed, which is what keeps the cost of all this off the devices.

    The coverage page shows it happening: how many rule sets the published map is split across, which areas are behind, and how far the current pass has got.

  3. One railway, not eight lines of it

    A station approach is not one track. It is four, six, eight of them side by side, and OpenStreetMap holds each one as its own line, correctly. Drawn at their true positions on a screen this size they stop being tracks and become a black band across the map. The tag that separates a yard from a main line does not help here: every one of those lines is a main line.

    The tile builder now works out which lines belong to the same corridor and draws one of them. A single track out in open country has nothing beside it, so nothing happens to it. Built and measured on the laptop this week. It reaches a device when the tiles carrying it are rebuilt.

  4. Railways stopped shouting

    The old railway mark was loud enough to read as a warning. That is wrong on a map you glance at from a moving vehicle: a railway is something you cross, not something you follow, and the heavy line was taking attention away from the roads around it.

    It draws as a ladder now. Short teeth crossing the line on both sides, thin, at the width of a minor road. It still reads as track at a glance and it no longer competes with the road you are actually on. Confirmed on the device.

  5. The tiles stopped carrying clutter nobody can walk on

    Two kinds of junk were reaching the screen. Corridors inside buildings, which are tagged exactly like an outdoor footpath, and short scraps of path that connect to nothing: a driveway, a digitising artefact, the stub of something never finished. A park could read as a thicket of grey hair.

    Both are gone before a tile is written. The hard part was telling a scrap from a real trail that mapping had cut into many short pieces, so pieces are joined back into one path first and the length test runs on the whole thing. On one city area the tiles came out about 6 percent smaller with nothing worth drawing lost. Live on the tile server the same day.

  6. The map stopped hiding things behind its own buttons

    The screen draws its own furniture last and on top: the compass, the scale bar, the row of button labels. That is fine for a road, which still reads as a road running off the edge. It is not fine for a pin, which simply vanishes.

    The screen now keeps a list of what its furniture occupies, and anything placed onto the map asks first. A pin that would land behind a button comes back as an arrow at the edge instead. A place name moves to the other side of its dot. The corners past the outermost buttons stay usable, which is why the list holds each button separately instead of the whole row.

    Testing it on a device turned up something we had never looked at: the numbers under the scale bar had no outline, so on built-up ground with roads through them they were barely readable. They have one now.

  7. Place names stopped landing on the roads

    A town name used to take the first free spot beside its dot. That put plenty of them straight across a road, and a name is drawn with a white outline, so whatever sat under it was gone.

    The reader now looks at the frame it has already drawn, counts how much of it each of eight possible spots would cover, and takes the clearest one. Across eight test views every one of them erases less map per name, at the widest zoom 26 pixels against 53.

    Measured on an Xteink X3: it costs 12 ms out of a frame that takes 3.8 seconds to draw.

  8. Points now find their own way onto the card

    Water, shelters, huts and the rest of the nearby layer used to reach a reader only if someone copied the file there by hand. The reader now asks for what it is missing, the phone fetches it from the tile server and pushes it over the same link tiles already use.

    Confirmed on a real reader near Trnava: it asked, the phone answered, all four missing files arrived and the reader could read them back.

  9. Ground the map server had not built could get stuck for good

    The map server builds a square the first time a reader asks for it, but it gives up asking after a day if nothing ever arrived. Until now that square just sat there marked given up, with no way back short of dropping the whole saved area and asking for it again from scratch.

    The area screen can now retry exactly that ground, one area at a time, without touching anything already on the reader or anything stuck for a different reason retrying cannot fix. Confirmed on real hardware: squares stuck for days started building again within a minute of asking.

  10. Two readers, one memory of what each one has

    The phone keeps a note for every map square it has sent a reader: was it received, and does the file on the card match what went out. Until now that note did not say which reader answered. Send a square to one reader, then connect a second one, and the phone said the second reader already had it too, because the same note covered both.

    Found by swapping two readers mid test: an area confirmed against the first dropped straight back to unsent the moment the second connected, because its own log had never heard from that square before. The note now names the reader that answered, and a square confirmed on one no longer counts for another. The area screen can also be renamed now, and it can tell you on the spot how much of a saved area the reader you are connected to right now has actually confirmed.

  11. We followed our own install guide, and step one was broken

    Yesterday's entry ended by saying nobody had ever written our firmware onto a reader still carrying the factory software. That is no longer true. We put an X4 Pro back to Xteink's own firmware, from a full copy of its flash taken before we ever touched it, and then installed ExplorInk using nothing but what the install page says. Not our build tool, not a command we remembered. The page.

    The firmware half works: the four files, their checksums, the write, the reboot into ExplorInk. One command did not. The very first one, the backup, died a fifth of the way through. It needed a flag the page did not print.

    That is the step the page itself calls the point of no return, and the way it failed is worse than the failure. It still leaves a file behind, with the right name and a fraction of the contents. Anyone who does not check would see a backup that is not one, write over the original, and have nothing to go back to. The page now prints the flag, says how large a finished backup is, and says not to continue until it matches.

    The flag was never missing from our own notes. It was lost when the command was copied onto the website, and nothing was comparing the two copies. A recipe you can run is a second copy of that recipe, and it goes stale on its own.

  12. A screen that shows you the sky, and admits when there is none

    Waiting for a position used to be a blank panel and the word searching. That cannot tell a wait that will finish from one that will not, and on this hardware the difference is real: one ride took 526 seconds to a first fix, and a 15-minute walk got none at all while the receiver was tracking a single satellite the whole time.

    So the wait has its own screen now. The satellites are drawn where they actually are, azimuth left to right over a horizon and elevation upwards, filled when the antenna hears one and an outline when it knows one is up there and cannot. Under it: how many are heard, the best signal, how long this has been going on, and a limit after which the map opens by itself. A glance answers the only question a person has while waiting, which is whether to keep waiting or walk into the open.

    This is for the LilyGo T5 S3 Pro, the development board with its own receiver. The X4 and the X4 Pro take their position from the phone and never show it. Published today as 0.2.1-alpha, and one thing about it is worth saying out loud: the plot has never drawn a real sky. Every pass was indoors, where the receiver locates nothing, and the day it went outside the weather gave nothing stable. It was judged against a synthetic pattern instead, which is also what the last frame in the gallery shows.

  13. A download for a third reader, and the first one on a different chip

    The Xteink X4 Pro has a firmware build of its own, and it is on the install page next to the one for the X4 and the X3. Until today every image we published was for the ESP32-C3. The X4 Pro is an ESP32-S3, so this is a second binary rather than a wider one: a separate download, four files instead of three, and a cable most people will not have thought about, because the device has no USB socket and puts its data on the pogo pins.

    What is published is the exact image that ran on a real X4 Pro that morning, not a rebuild of it. On the device: it boots, the panel draws, the card mounts, the touch layer and the home key answer, the frontlight lights both its warm and its cool channel, and a map frame rendered in 1,183 milliseconds. Free memory with the map up is 172 kB, against 58 kB on the X4, which changes what the map is allowed to hold on this device and we have not begun to use that.

    Four things the day did not settle, and they are written on the device page rather than left out: whether the frontlight flashes once at boot, the light’s PWM frequency on this board, turning touch off from the settings menu, and what memory does over a long session. One more is worth saying here. Nobody has yet written any of our images onto a reader still carrying the factory firmware. Every flash we have done went onto a device that already ran ours.

  14. A second reader runs our firmware, and the map drew a city on it

    We flashed an Xteink X3 for the first time. It boots, recognises which model it is without being told, mounts its card and renders at 528 by 792 pixels. Position came from a stand-in for the phone app over Bluetooth, and the map tiles it was missing arrived the same way, at about 6 kB a second. That rate is worth stating plainly: a detailed tile is half a megabyte, so it takes over a minute and a half to walk across the wire. The map itself reads well, and the old town it drew is dense enough to be a fair test of the style.

    Three things sit in the wrong place on that screen. The button hints run off the bottom edge and over both sides, the scale bar and the place name are drawn on the same pixels, and the compass overlaps a hint box. None of it shows on the X4, because all three are tuned to a screen 48 pixels narrower and 8 pixels taller. One good surprise in the same shot: the position marker landed 4 pixels from the centre of the panel, which is the first time that piece of arithmetic has been checked on real hardware rather than in a simulator.

    An hour went into a device that was not broken. After writing the firmware, the usual reset over USB did not restart the chip at all: it stayed in the flashing loader while the screen kept the picture the factory software had left. A watchdog reset started it. Our own notes had promised the opposite, so they now say what actually happened.

  15. The map on the device got smaller and nothing about it looks different

    A road in map data is not one road. It arrives in pieces, split wherever the people who mapped it happened to stop and start, and until today we stored every piece as its own record. One bridge over the Danube was twelve records for a line that is straight to the centimetre, and it stayed twelve however far you zoomed out, because the tool that thins a line out is not allowed to throw away the place where one piece ends and the next begins.

    Now the pieces are joined back into whole lines before any of that happens. A tile lost between 4 and 34 percent of its size depending on the zoom, and the coarsest one, the one the device reads whenever you pull back to see where you are, lost a third.

    On the device the card read got a sixth shorter and drawing the roads got an eighth faster. And of the 384,000 pixels on the screen, 1,142 changed. That is two in a thousand, and it is one road sitting a pixel to the side. The map is the same picture for a third less data, which is the outcome we wanted and not the one we would have bet on: the fragments were never what made a road look rough.

  16. The device we tell people to buy finally runs our firmware, and its screen chip is not the one printed on it

    The X4 Pro is the device this site recommends, and until today nothing of ours had ever run on it. The first flash went in cleanly, the board booted, the card mounted, the log looked healthy, and the screen kept showing the bookshelf the factory firmware had left there. E-ink holds its last picture with the power off, so a frozen screen is not evidence that anything is wrong with the old software. It is evidence that nobody repainted it.

    The cause was one line that had never been written. These readers do not all use the same screen controller: the same model ships with parts from different suppliers depending on when it was built, so the firmware is supposed to ask the screen which chip it is before talking to it. That question was only being asked on the older chip family. On this one the code simply assumed, and it assumed wrong, so it spent the whole boot speaking a language the screen does not answer in.

    With the question restored, the same build draws. And the answer is worth writing down: the identifier inside the factory firmware names one controller, every unit anyone had measured before ours had a second one, and ours turned out to be a third. Nothing may read that chip off a label again.

    Not done: the touchscreen, the buttons, the frontlight and a map have all yet to be tried on it.

  17. A fix that was confirmed, then quietly went missing

    The SD card library the firmware builds on lives in its own repository, and the firmware records only which version of it to use. One line. A commit about a frontlight setting moved that line backwards, and for three days the build was missing two of our own fixes, one of them signed off by a hardware test four days earlier. One line in a diff is easy not to see.

    So we put it back and then went at it properly. The file that had crashed the board before, 733 kB served over the device's own web server, twice, identical byte for byte both times, and the device never restarted. Card writes as well. The repository now says so out loud whenever that one line moves, and keeps a log of every time it has.

  18. Pressing the buttons from a laptop

    The firmware takes a serial command that injects a real button press, so a whole walk through the interface can be driven from a desk: the home menu, the map, its options, the look-around mode, and a long press that zooms where a short one pans. Every step is read back as a screenshot. It is a development build only, and never reaches a released one. The reason it exists is dull and it matters: a screen nobody can reach without holding the device gets looked at once, and then never again.

  19. Two buttons, and one of them had nothing to do

    The validation board has four switches and only two the firmware can read. One of them, the power button, did nothing on a short press, which meant a screen could be opened and not left without touching the glass. A short press now steps back, and sleep moved to a longer hold so the two gestures can be told apart by a gloved thumb. The other button holds to walk the frontlight through its levels, one step every half second, and it stops where the thumb lets go. The level is a setting, so the light comes back the way it was left.

  20. A download that never says what it is downloading, until it does

    Pushing a batch of map tiles the device never asked for used to draw as a flat count of squares, because the phone announces how many files are coming and nothing about where they are. Each file's own start-of-transfer message does name a real tile, though, and that was already flowing over the wire unused. The screen now watches for it: the count-only placeholder holds until the first file begins, then the same nested map-square view a planned download uses takes over, filling in as files arrive instead of counting down.

    Managing saved pins now also works from the tile-sync screen, not only from the map -- the phone drives it the same way either screen is open.

  21. The desk without a device still has a screen

    The desktop simulator builds the firmware as a native binary and draws the panel in a window, so a change can be looked at without a device on the desk. It had stopped compiling: the simulator replaces the hardware layer, and two things the firmware had grown since were missing from it. Both are back.

    It also draws with no window at all now, which is how a scripted run takes a picture of a screen without stealing the desktop. That path used to finish quietly and write nothing. The map on it is the real map code reading real tiles, not a mock.

  22. A file that downloaded as a single byte

    Pulling a log off the device gave a normal reply: the request succeeded, the header stated the right size, and then one byte of file arrived. Not a flaky network, which is what it had looked like for six months. A refactor in the project we build on had wrapped the file handle in a type that no longer matched the function doing the sending, so the compiler quietly picked a different one and wrote a single byte. No warning, because every step of that substitution is legal.

    Fixing it let a large file transfer for the first time, and the board reset in the middle of it. The loop doing the sending never let the rest of the system breathe, so the part of the firmware that has to report it is still alive could not, and the chip restarted itself. That one lives a layer lower, in the hardware library, and it had been sitting there unreachable behind the first bug.

    The same file now arrives whole, and three times faster than the broken version managed before it gave up. Both fixes went back to the projects they came from.

  23. A link that names a place does not say where it is

    You find a spot in Google Maps, share it to the app, and the reader gets a pin. That was the idea. What the share sheet actually hands over is a shortened link with no position anywhere in it, and only one of the app’s two windows knew to open that link and look. The same share worked in one screen and stopped dead in the other. Both open it now.

    The second half was stranger. Search for a place, share it, and the link Google writes carries an identifier for that place and no coordinates at all. Nothing can read a position out of it, because there is not one in there. The app used to answer with a complaint about degrees and minutes, which was the parser finding numbers inside hexadecimal and believing them. It now says what is true: this link names a place but does not say where it is, so press and hold the spot in Google Maps to drop a pin, and share that. Drop a pin and the coordinates are right there in the link.

    One more thing came out of it. Both screens redraw once a second, and any message they wrote was wiped by the next redraw before it could be read. That is why a refusal looked like nothing happening at all.

  24. A screen you can drive, and a screen you can switch off

    The 4.7in board has a touch panel, and until now that meant the on-screen button labels vanished and every pixel was live. Fine in a chair. Less fine with the device in a bag, or in rain, or under a palm resting on the glass while you read the map.

    Touch now has three modes. The whole screen live, as before. Only the six labelled boxes, where a tap acts as the button whose name is printed in it and the rest of the glass is dead. Or off, and the panel is ignored entirely. The capacitive key below the screen switches between locked and not, and a small padlock at the bottom says which it is, because buttons being absent could mean either.

    The interesting part was not the feature. Getting one key to tell a tap from a double tap from a hold turned out to be hard for a structural reason: the firmware recognises those gestures in six separate places, and one recogniser feeds its answer to the next as if it were a button being pressed. That is written down now, along with what would replace it.

  25. The dot stops pretending it knows things it does not

    The marker on the map is a ring with a dot in it and, until this week, a heading mark that was always drawn. Always. A reader that had never been told which way you face drew a hand pointing north, because north is what the number defaults to. That is not a missing feature, it is the screen saying something untrue with a straight face.

    Now each half of the marker answers for itself. The ring is whole when the fix is good enough that you are somewhere underneath the dot, and breaks into eight arcs when it is not. The heading is a sharp arrow when there is a heading worth drawing, a wedge when only a neighbourhood is honest, and nothing at all when there is no heading to speak of. Standing still long enough that the last known direction has stopped describing the present counts as nothing at all.

    One number is worth stating because it was derived rather than picked. The ring has a radius of 27 pixels and the closest zoom draws one metre of ground per pixel, so an error under about 25 metres still fits inside the marker that is drawn for it. The marker cannot express a precision finer than itself, and that is the only non-arbitrary place to put the line. A sharp arrow, for the same reason, was already claiming to know your direction to within 11 degrees, whether or not anyone decided that.

    What does not work yet. None of these shapes has been on a real panel. They were drawn and measured in the desktop simulator, which proves they are drawn and that all of them are distinct, and proves nothing about how they read on electronic paper at arm’s length in sunlight. The wedge at the widest zoom is 25 pixels of ink and is the one we expect to argue about. The phone app that feeds it is published; the reader firmware is merged and unflashed.

  26. Load a whole city before you go, and transfers got three and a half times faster

    Until now the reader only ever got squares it had already failed to draw. You had to ride somewhere before the map for that place could arrive, which is the wrong way round for a trip you are planning at home. The phone app now takes an area: find the place in a maps app, share it back, pick a 10, 20 or 40 km box, and it tells you exactly what that costs in megabytes before anything is queued. The number is read from the server index rather than estimated, so it is the real size.

    The queue is written to the phone’s storage, so it survives the app being killed, the reader going to sleep and the link dropping. Walk away mid-transfer and it picks up where it stopped the next time the two are together. Squares the server has not built yet are not failures: they sit in the queue as waiting, and the app comes back for them on its own once the build lands.

    The link itself got a lot quicker in the same week: 3.9 to 13.6 kB/s, measured on a LilyGO T5 S3 Pro. Two changes, neither of them clever. The reader now offers a larger Bluetooth packet, and it asks the phone for one connection interval instead of a range. That second one was our own bug: the phone had already negotiated the fast interval, and our request handed back the slow one as an acceptable answer, so the link settled on the slow one for three weeks. Nobody noticed because the change that caused it was measured for power draw and not for speed.

    What does not work yet. Leaving the tile sync screen can reset the reader. It is a defect in the Bluetooth library we build on, reported upstream, and it has now been caught twice with the crash dump to prove it. The transfer itself is unaffected and the queue survives the reset, but a reset is a reset and it is fixed by nobody yet.

  27. The bench got a current meter, and it took five power cycles to read it

    Battery work here has been stuck on one thing: the device can report its own battery voltage, and voltage cannot tell two states apart when they differ by a few milliamps. A USB inline meter sits between the power source and the board and reports current directly, so a state can finally be priced against another state. One arrived this week and now reads live volts and milliamps straight into our own tooling.

    Getting there was not smooth. Every published way of driving this meter hung it: it stops answering, leaves the USB bus, and only comes back when both its cables are pulled. Four corrections later it streams, and three of them contradict the reference implementations everyone links to. The useful one for anybody else with the same meter: the stream stops on its own every few seconds, and what restarts it is resending the whole handshake, not the keep-going packet that is supposed to do that job.

    Nothing about the product changed today, and no board has been measured with it yet. This is an instrument coming online, not a result.

  28. The map stops having an edge on the server too

    The builder that makes a square when somebody rides onto ground nobody has built used to refuse everything outside a box drawn around Europe. The first reader owner who wrote to us was in the Philippines, outside it. The box came off the same day.

    It was never the thing holding the builder back. Ten squares a pass, a hundred and twenty areas a day, one build at a time and a day of cooldown per square already bound what an unattended job can do, whoever asks and from wherever. The box was a second lock on a door that was already locked.

    Eighteen places around the world were then asked for directly, the same way a reader asks: Stelvio, the Transfagarasan, the Nurburgring, the Pacific Coast Highway, the Rockies, the Cordillera above Baguio, Milford Sound, the Garden Route, and a handful of cities. Fifteen built. Three failed and it was not the ground: every public OpenStreetMap mirror was refusing by then, because a dozen areas queued together is enough to congest them for hours from one address. Building one area is twenty seconds of work and several minutes of waiting for somebody else's server, and that is the real ceiling.

  29. The tile server moved to a new map format, and started with an empty shelf

    The .tib tile format went from version 3 to version 4 this week, and the server that builds tiles for everyone now writes the new one. Version 4 exists to make the next change unnecessary: a tile’s identity no longer depends on which layers happen to be in it, so a new kind of map data can be added later without every device deciding its whole card went stale. It also lets a tile be wider than the old coordinate range could address, which is what the zoomed-out views needed.

    Nothing was carried across. The 193 areas built under version 3 were not copied, re-requested or rebuilt: the new shelf starts empty and fills only with squares somebody actually asks for. That was a deliberate call rather than a limitation, and it keeps the two versions honestly separate.

    If you are running ExplorInk today, nothing changed for you. Every published firmware reads version 3, and the version 3 tiles are still served, all 193 areas of them, untouched. A firmware release on the new format is the next step and it is deliberately not taken yet: the reader that would have tested it was lost off a motorbike on 22 August, and we are not shipping a format change to other people’s devices on the strength of a laptop test.

  30. Hills, and the edges you can fall off

    The hike map got the thing it was missing. Contour lines are cut from NASA elevation data and written into the tile itself: 20 m apart on the close views with every fifth one heavier, 100 m on the wide ones. The heavier lines carry their height as a number sitting on the line, rotated to follow it, with the digits’ top pointing uphill, so you can read which way the ground rises without counting lines.

    Rock faces are in there too, drawn as a line with combs hanging off the downhill side, which is how a paper map says this ends in a drop. The comb’s side and tooth thickness are part of the map style rather than baked into the code, because which of them survives on e-ink at one pixel wide is a question only the glass answers.

    And the trail data that was always in OpenStreetMap but never in our tiles: path difficulty, how visible a path is on the ground, and whether a path is part of a waymarked route, with the route’s own colour-and-shape symbol carried in six bits per path. A footpath tagged as demanding alpine terrain is no longer drawn the same as a park path.

    All of it is drawn by the device’s own renderer, compiled and run on a laptop and in a desktop simulator. None of it has been seen on a panel. Whether a one-pixel contour reads in daylight, whether a 12-pixel height number is legible in the hand and whether a seven-pixel comb survives are the questions a hardware pass has to answer, and there is no reader to answer them on right now.

  31. The whole firmware runs on a laptop now

    The project we forked publishes a desktop simulator that runs the real firmware UI natively, and we found it after building a narrower tool of our own, which is the right order to admit to. Our fork of it runs ExplorInk itself: the same activities, the same map screen, the same button handling, in a window.

    The window opens at three scales, and the third one is the interesting one: a device pixel drawn at the physical size it has on the real panel, because is this road a hairline in the hand is a question a 1:1 view on a desktop monitor cannot answer. A screenshot always comes out 480 by 800 device pixels whatever the window is doing, so a picture taken from the simulator is still evidence rather than a resample.

    It does not replace glass. Nothing about daylight, refresh behaviour or how grey looks on e-ink is in it. What it buys is that a change can be wrong on the laptop in two seconds instead of wrong on a device after a flash.

  32. Anyone can install it now: firmware and app, as downloads

    Until today, putting ExplorInk on a reader meant cloning a repository and building the firmware with PlatformIO. Now three firmware images and the Android APK are published files, with the flashing commands, the device table and a backup step next to them on the install page.

    The firmware image is not a fresh build made for the occasion: it is the exact binary that was confirmed working on an X4, same SHA-256, because a published build nobody ran on hardware is a worse thing to hand a stranger than no build. The flash offsets were checked against a real device’s own 16 MB dump, and the partition table we publish is byte for byte the one that device runs.

    Two limits are on the page rather than in the small print. The app is debug-signed, so the first properly signed build will not be able to update it and will need an uninstall first. And the plain X4 is the only device this was ever verified on: the X3 is compiled in but untested, and the X4 Pro is a different chip with no build at all.

  33. The map can tell you what is around you

    Drinking water, shelter, huts, lodging, fuel, medical, rescue, emergency phones and a way out without a vehicle. The device searches 25 km around your position, lists each kind with the distance to the nearest one, and draws the kind you ask for on the map. It reads them from its own small binary files on the card, tens of kilobytes per press, so the list is there before you have finished reading the menu.

    Two honesty rules are built into it rather than bolted on. A spring OSM says is not drinkable never reaches the device at all, because a square with a water glyph means candidate for drinking water and nothing else. And a point with a condition on it, a hut that is seasonal or a spring nobody has checked, carries a mark saying so: one mark for all conditions, because at this size a rider can tell that there is a condition and not which one. The detail screen names it.

    Getting there cost three device hangs, and two of them were older bugs this feature simply reached more often than anything before it. Both are fixed. One of them mattered more than the feature: with the firmware hung, no button on the device brings it back, so a bug of that shape ends a ride rather than annoying it.

  34. The site says what the phone does, and what this costs

    Two gaps closed on this site rather than on the device. There is a page for the phone app now: the three jobs it does, the send policy, the one power switch, and iOS stated as planned with no date attached. And there is an answer to the question every outdoor mapping product raises: no subscription for reading a map, ever, with the one thing a paid tier would ever cover named out loud.

    What the phone app does, and where iOS stands

  35. The site stopped being one URL

    Everything used to live on the homepage: product, hardware, the full status list, the log, early access. It reads fine for a person. For a crawler it is one document about everything, and Google had it as crawled and not indexed. It is a set of pages now, one subject each, assembled by a script from fragments, with a check that fails the build if a page loses its canonical, its description or its place in the sitemap.

  36. Pins: park here, camp here, and it is still there tomorrow

    Press and the reader saves where you are, with a name. The pin draws on the map, survives a reboot, and when it falls off the edge of the screen it turns into a direction and a distance at the border: base camp, 23 km that way. Verified on the panel, which changed three things about how they are drawn: a one-bit screen needs the icon and the label separated, and an arrow at the frame edge has to say which way without stealing map.

  37. Observation mode can zoom again, without a new button

    Panning the map on the buttons meant giving up zoom, because there are only four buttons and they were all busy. A hold got it back: tap to pan, hold to change scale. No new hardware, no menu trip, and the map still holds still while you read it.

    How panning and the zoom rungs work together

  38. A third off the power draw, from letting the radio sleep

    Two long runs with the map open and a phone connected: 35.6 mA in the first, 24.0 mA in the second with Bluetooth modem sleep on. That is a third less draw while the second run did about twice the panel work. Both numbers come from the battery’s own voltage slope against the cell’s rated capacity, not from an inline meter, so they are a range rather than a spec.

  39. A windowed update is not cheaper than a full-screen fast refresh

    The obvious optimisation is to redraw only the rectangle that changed. Measured on the panel, it is not cheaper than refreshing the whole screen in fast mode. The waveform pass dominates, and the window does not shorten it. The hypothesis is written down as refuted, because the next person to have this idea deserves the measurement rather than the idea.

    The refresh numbers this obeys

  40. A clock in the map header, and the cost of that choice

    The status strip carries the time now, top left of the map, so a glance answers two questions instead of one. The trade was stated when it was made and it stands on the record: it is the current time, not the time of the last position, so a dot that is twenty minutes old looks as current as a fresh one. Adding a staleness marker beside it is a small change. Nobody has ridden long enough with it to say whether it bites.

    What a dropped link actually looks like on screen

  41. The map says where you are, in words

    Until today the map drew a dot for every town and village and never a name. The dot tells you something is there; it does not tell you it is Modra. Names are on the glass now, in two sizes, towns bold, villages lighter, and at the widest zoom the reader fits a dozen of them across 24 by 40 kilometres.

    The hard part on a screen with no colour is keeping a name readable over a road network without blanking out the map to do it. Each name is outlined in white, letter by letter, so the roads and the forest keep running underneath. And a name is dropped rather than laid across your route, over another name, or under the buttons. The dot stays either way, so nothing goes missing, it just goes unnamed. Twelve names cost 163 milliseconds of a redraw that takes nearly four seconds.

    Place labels, in detail

  42. The phone app stops working when there is nothing to work for

    Turn the reader off and the phone app used to carry on: GPS at one fix a second, the processor held awake, and a notification saying it was still sending your position. It was not sending anything. There was nothing on the other end. Swipe the notification away and it came back within a second.

    All of it now hangs off one question: is the reader connected, or are you recording a ride? If neither, the app asks for no position, holds nothing awake, and after five minutes it shuts itself down. Opening the map on the reader starts it again on its own, so there is nothing to remember and nothing to press. The notification tells the truth in the meantime, and once dismissed it stays dismissed.

    The bridge, and its one power switch

  43. Closing a menu should not cost you the map

    The map's menu, the one behind the middle button, used to throw the map away when you closed it. Every dismissal read the map squares off the card again and repainted the whole screen, seconds of it, to arrive back at the picture that was already there. The reader now keeps the pixels the menu covers and puts them back, refreshing only that patch of glass. Nothing to wait for.

    The menu itself reads like the settings screen now: every line starts at the same edge, and the part you can change (ride mode, north up or heading up, and the rest) sits on the right in a filled box, so you can see what a line is set to without opening it. And the middle button no longer says “Select” on the map. It opens a menu, so it says “Options”.

    Keeping those pixels costs memory, and the reader has 54 kilobytes free with a map on screen, measured, not guessed. The menu was eating twenty of them, because the box grew a line taller with every option in it. It has a ceiling now: six lines on screen, the rest scrolls, and the box stops growing no matter how long the list gets. Nine kilobytes instead of twenty, and the map behind it stays visible.

  44. Two more steps out, for the day you ride a whole range

    The zoom buttons stopped at 20 metres per pixel, which is about ten kilometres of ground down the screen. Enough to see the next village, not enough to see where the hills you are riding actually go. There are two more steps now, and the widest shows 24 by 40 kilometres: the full width of the Malé Karpaty, both flanks, every road that crosses the ridge, on one screen.

    Zooming out that far is not free: the reader pulls twelve map squares off the card instead of six and draws them in 3.6 seconds. That is half of what the closest zoom already costs, and the map is only redrawn when you have moved far enough to need it.

    Two things had to stop being fixed sizes for this to feel right. The position marker: it is a fixed dot on the glass, so the further out you go the more ground it hides, two kilometres of it at the widest step. It is drawn smaller up there now. And the distance you have to travel before the marker is redrawn, which was a fixed number of pixels: eight metres of ground at the closest step, but three hundred and sixty at the widest, so the view that needed a steady trickle of updates got almost none. It follows the zoom now too.

    The zoom ladder, and what each rung costs

  45. Ride off the edge of the map and the map comes to you

    Until today, a square nobody had built yet stayed a hatched square. The reader asks the phone for it, the phone asks the tile server, the server does not have it, and that was the end of the story.

    Now that miss is the request. The tile server watches its own log of squares it could not serve, takes the ten most asked-for from the last day, and builds them from OpenStreetMap straight into what it serves. One build at a time, and the area around the square goes with it, so the ground you are riding into is there too. Measured on the server: about twenty seconds per area.

    The reader had to learn patience for that to be worth anything. It used to cross a square off for good once the phone said it could not get it. Sensible when a miss meant nobody had it, wrong now that the miss is what builds it. It waits and asks again instead, backing off from a minute and a half to an hour so a square that truly does not exist stops costing anyone data.

    Tested end to end on the reader, on a drive across ground the map did not cover: four squares missing, two arrived from the server, the other two were built from the reader’s own asking and drew a minute later, while it kept moving. Nothing on the server rewrites a square that already exists: a hole gets filled, everything else is left alone.

    How the tiles and the server work

  46. A scale bar, and a header that says where you are

    Two small things the map screen was missing. A classic scale bar, bottom-left: five alternating black/white segments, ticks, and a rounded distance: 100 m, 1 km, whatever the current zoom rounds to. Numbers that would run into each other at a loose zoom are dropped, not smeared; the endpoints always stay.

    The header is now a fixed white strip with a real 1px separator below it, so the map no longer draws underneath and gets covered, it stops at the line. Top-left, it now names the nearest place: read straight off the same tile data that already draws the place dots, no extra card read for it.

  47. Open the map and the phone starts sending, on its own

    Until today a ride started with a chore: unlock the phone, find the app, open it, wait for it to find the reader. Now the reader does it. Open the map on the device and the phone starts sending its position by itself: app closed, screen locked, nothing tapped. The signal was already there: the reader only talks over Bluetooth while the map is on screen, so the map opening is the announcement. What was missing was a phone that could hear it while shut down.

    The phone now pairs with one reader, once, in a system dialog. That fixed a second problem nobody had noticed: two of these readers look identical over the air, same name, same service. The app used to talk to whichever answered first, so a second device in the room was a coin toss, and the loser gets a stranger’s position on their map. It is now a fixed address, not a guess.

    Verified on the real device with a real phone: app killed, map opened, the phone woke itself, connected and sent a position with nobody touching it. Then again after restarting the phone, which is the case that usually breaks this kind of thing.

    Real hardware found a real bug in five minutes, again. The pairing screen sat empty while the app was connected to the very device it was looking for: the reader stops announcing itself once a phone connects, and the pairing screen can only offer what it can hear. So the app now steps off the link while you pair, and steps back on afterwards.

    How the wake works, and why not a background scan

The full dated log lives in the repo, alongside one topic document per hardware finding: how the panel actually behaves, why the obvious approach fails, and what is still unmeasured. Read it on GitHub →

status

The full list, item by item.

Everything marked works runs on real hardware, not in a simulator. On the bench means built and tested on a laptop and not yet seen on a panel. Formats, the render spec and the BLE protocol still change without notice, so do not build on them yet.

  • USB serial access, flash backup and inspectionworks
  • Map builder: OSM fetch, projection, clipping, routing on real roadsworks
  • Map builder: browser tuning tool with device-exact previewworks
  • Binary .tib tiles, three levels of detailworks
  • Firmware: map screen and BLE position receiverworks
  • Firmware: tiles loaded off the SD cardworks
  • Firmware: command console over USB serial and BLEworks
  • Firmware: zoom and marker height on the buttons, seven zoom rungs from 1 to 45 m/pxworks
  • Firmware: observation mode: pan the map on the buttons instead of following the fix, zoom on a long pressworks
  • Firmware: north-up or frozen-heading map, switchable from Settings or the map menuworks
  • Firmware: ride / hike / cycle filterworks
  • Firmware: four grey levels, verified on the panel (map uses dither instead)works
  • Firmware: grey screenshots over USB serial, decoded on the laptopworks
  • Phone app: BLE position sender and ride recorderworks
  • Phone app: queue a whole area before a trip, resumable across reconnects, waits for squares the server has not built yetworks
  • Firmware: road widths and casings, buildings, forest, built-up and water areasworks
  • Rivers and lakes drawn as filled water with waves, not as a lineworks
  • Railways drawn the way a map draws them: a cased line in alternating blocksworks
  • Watercourses broken, so a stream cannot be mistaken for a roadworks
  • Zoomed-out views drop what you cannot use there: field tracks, ditches, rail sidingsworks
  • Firmware: map files pushed onto the SD card over BLEworks
  • Firmware: the marker follows the fix; the map only redraws when it has toworks
  • Firmware: track-up map, north indicator turns with itworks
  • Fetch missing tiles: the device asks, the phone brings them from the tile serverworks
  • Public tile server, live: maps served square by square, so nobody has to build oneworks
  • Map builder: build a route from waypoints, a GPX file, a recorded ride or the browser toolworks
  • Firmware: pick a route when the map opens, and see all of it at onceworks
  • Firmware: with a route loaded the map holds still, only the marker movesworks
  • Fill a gap without stopping: the map asks for a missing square the moment it hits oneworks
  • Nearby: drinking water, shelter, huts, fuel, medical, rescue and a way out, searched 25 km around you and drawn on the map. The reader now fetches what it is missing on its own, over the same link as tilesworks
  • Firmware: town and village names on the map, outlined in white, kept off the routeworks
  • Firmware: GPS/tile/BLE debug readout, off by default, in Settings and the map menuworks
  • Out-of-date squares: the device asks the phone whether the map it holds is still current, off by defaultworks
  • Open the map and the phone starts sending on its own, app closed, nothing tappedworks
  • The phone pairs with one reader, so someone else’s does not get your positionworks
  • Firmware: pins (park here, camp here) saved at your position, drawn on the map, and still there after a rebootworks
  • Firmware: a pin off the edge of the screen still says which way it is and how farworks
  • Phone app: read, save and delete the reader's pins from a coordinate, a geo: link or a shared map linkworks
  • Firmware: a classic scale bar on the map, ticks and rounded distancesworks
  • Firmware: fixed header strip with a separator, and the nearest named place shown top-leftworks
  • The tile server builds what riders ask for, unattended: a square nobody has built yet is on the server a minute laterworks
  • A published square is never rewritten, and the builder’s day is capped, so an unwatched job cannot run awayworks
  • Public downloads: the firmware images and the Android APK, with an install guide and a backup stepworks
  • Tile server: builds the version-4 tile format, and keeps serving version 3 for readers still on the older firmwareworks
  • Contour lines with height numbers on the hike map, read off NASA elevation dataon the bench
  • Rock faces drawn with combs on the downhill sideon the bench
  • Path difficulty, path visibility and waymarked routes carried in the tileson the bench
  • The whole firmware running in a desktop window, at three scales including real panel sizeon the bench
  • Renderer: junction dots and off-screen place markersplanned
  • Route following and off-route warning on the deviceplanned
  • A packaged release: a one-click flasher and a signed Android appplanned
  • Phone app for iOS: same four jobs, same BLE service, no firmware change neededplanned

Want it on your own reader? The install page has the firmware images, the APK and the commands, no toolchain needed. Building from source still works too: github.com/rfordinal/explorink carries the build, flash and monitor commands, and the hardware page says what it runs on.