TrailInk
GitHub

Heavy development · nothing here is finished

A map you can
read in the sun.

TrailInk turns a pocket e-ink reader into an offline map display for motorcycles and trails. No backlight to wash out, no battery gone by lunch. One still picture: where you are, where the route goes, what is around you.

  • 4.3″e-ink, 480×800, 220 PPI
  • 10 h+power only when the picture changes
  • 0bytes of network needed on the trail

Built on CrossPoint Reader. Map data © OpenStreetMap contributors.

The X4 panel drawing the road network around a coordinate
real panel roads off the SD card, 313 ms per layer

01 — why e-ink

A phone is a bad navigation display on a motorbike.

The screen washes out in direct sun. It cooks in a handlebar mount. The battery does not last a day of riding. E-ink has the opposite properties — and one hard cost you have to design around.

Readable in full sun

Reflective panel, no backlight. The brighter the day, the better it looks. Visor down, glance, done.

Days, not hours

Holding an image costs nothing. Power goes only into changing the picture, so the X4 runs for tens of hours.

Slow, and grey

A full refresh takes about 1.7 s; four grey levels cost an extra waveform pass, and lose their grey to the next one. That rules out a moving map.

So: a still picture

Position, route, surroundings — redrawn rarely, readable at a glance at speed. No rerouting, no voice, no traffic. The route is planned before you leave.

02 — the screen

Real framebuffers, not mockups.

Everything in the first row came off the panel over USB. Send a coordinate; the device draws the roads around it from tiles on its SD card. The second row is the renderer running on a laptop against the same tiles — that is how styling gets tuned without reflashing.

Honest caveat: the device renderer follows most of the style spec now — road widths, casings, buildings, forest, built-up and water areas all draw on the panel. Place labels, the route line and junction dots exist in the spec and in the laptop preview, not yet on the glass.

03 — how it is built

Three parts. All three exist.

  1. 01

    Map builder laptop

    Fetches an area from OpenStreetMap, projects and clips it, routes along real roads, and writes binary .tib tiles at three zoom levels. A browser tuning tool renders a pixel-exact 480×800 preview, so the styling gets judged as e-ink instead of as a pretty vector map.

  2. 02

    Firmware TrailInk, on the X4

    A fork of CrossPoint Reader. Map screen, BLE position receiver, tiles off the SD card, zoom and marker height on the physical buttons, ride / hike / cycle filter, and a command console over USB serial and BLE. The e-reader stack comes out; maps go in.

  3. 03

    Phone app Android

    Sends the phone's GPS fix to the device over BLE every few seconds and records every fix and packet for replay. One window, no settings screen, foreground service — it keeps sending with the phone locked in a bag. A GPS liveness gate throws away network fixes that would jump you across town.

OpenStreetMap map builder SD card .tib tiles X4 panel phone GPS over BLE

04 — status

What works today.

Formats, the render spec and the BLE protocol all still change without notice. Do not depend on any of it 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 zoom levelsworks
  • 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 buttonsworks
  • 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
  • Renderer: place labels, route line, junction dotsin progress
  • Route following and off-route warning on the deviceplanned
  • Anything you could call a releaseplanned

05 — development log

Recent work, newest first.

  1. The map reads like a map

    Buildings, forest and built-up areas now draw on the panel, filled with a dither pattern rather than hatch lines — at map scale a house is a handful of pixels, and lines across it read as dirt. Roads take a width per class, and the panel confirmed both.

    Which of the two a village gets is decided when the tiles are built: individual buildings up close, one built-up area from 6 m/px out. The device never reads what it would not draw, and that shows in the numbers — a viewport reset takes 2506 ms reading 484 KB at 3 m/px, and 1088 ms reading 198 KB at 6 m/px.

  2. Grey works on the panel — and the map still won't use it

    All four grey levels render and are distinguishable by eye. A full grey frame measures ~2.1 s, and it has to be the last thing that touches the panel: a refresh issued in the same breath breaks the picture. A repeated nudge lightens the same pixel and adds grain, so grey cannot be topped up in place.

    Judged side by side on the glass, the existing 2×2 dither simply read better for area fills — and it survives any refresh, because it is ordinary black and white. So fills stay dithered and four-level grey stays out of the map. The support layer and its test screen remain, behind a build flag, for the next panel question.

  3. Style tuning without a flash

    The device's own renderer now compiles as a host binary and runs against the style being edited, so the webapp preview shows real device output. Two seconds per edit instead of a build-and-flash cycle.

  4. Areas, not just lines

    Forest and built-up land arrive in the tile format as aggregated areas. Buildings are drawn up close and replaced by a built-up tone further out — a build-time split, picked per level of detail rather than per style.

  5. A GPS liveness gate on the phone

    Network fixes were confirming implausible jumps. The app now ignores a network fix when satellite fixes are still alive. Validated on three real bike rides: 10–15 % of fixes filtered.

  6. The panel is not 1-bit

    Grey went on the table after the driver was read properly — four levels, one extra waveform pass each. A pile of code comments still said 1-bit; they were lying.

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. Read it on GitHub →

06 — the hardware

Xteink X4

A pocket e-ink reader, bought off the shelf. Not a dev board, not Android, no ADB — an ESP32-C3 with a display driver and physical buttons. Everything TrailInk does has to fit in that.

Panel
4.3″ e-ink, 480×800, 220 PPI, no touch
Grey
4 levels, ~1.7 s full refresh, ~0.5 s fast; map fills use 2×2 dither
SoC
ESP32-C3, WiFi + Bluetooth 5 LE
Storage
16 MB internal flash, maps on a separate SD card
Input
Physical buttons only
Wireless
BLE preferred over WiFi for everything
TrailInk boot screen on the X4: logo, name and version
Boot screen, firmware 1.5.0

07 — get in touch

Built by one person, in the open.

I ride, and I wanted a display that survives a sunny day on a handlebar. If you are doing something similar on Xteink hardware, or you want to try it, or you just want to tell me the map style is wrong — write to me.

Email

roman.fordinal@comsultia.com

Code

github.com/rfordinal/TrailInk

Firmware, docs and every hardware finding. MIT licensed.