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.
topics
The pieces, written up properly
Five things took long enough, and taught us enough, to have their own page. Each one is a real problem, what we did about it, and what it measured afterwards.
.tib tiles, three levels of detail, and a server that builds what riders ask for.
Place labels
Town names over a road network, with a white halo instead of a label box.
Track-up navigation
Seven zoom rungs, a marker that moves inside a frozen frame, and a route that holds it.
Xteink X4, measured
Refresh timings, heap, grey levels — the hardware numbers everything else obeys.
the log
Dated entries, newest first.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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 — 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.
-
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.
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 that works runs on real hardware, not in a simulator. 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
.tibtiles, 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
- 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
- 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
- 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
- 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
Want it on your own reader before the installer exists? The firmware builds and flashes from source with PlatformIO — github.com/rfordinal/explorink has the build, flash and monitor commands, and the hardware page says what it runs on.