---
title: "A £41 badge knows when you should run for the bus"
locale: "en"
url: "https://irz.fr/en/articles/tfl-badger-departures-board-en"
markdown_url: "https://irz.fr/en/articles/tfl-badger-departures-board-en.md"
category: "tech"
tags: ["e-paper", "micropython", "embedded", "transport", "low-power"]
published_at: "2026-08-24T16:45:00.000Z"
author: "Jules Garnier"
translation: "https://irz.fr/fr/articles/tfl-badger-departures-board-fr.md"
---

# A £41 badge knows when you should run for the bus

Marcio Lopes turned a Pimoroni Badger 2350 e-ink badge into a live departures board for his London bus stop: no API key, no account, roughly a week per charge.

Hanging by the front door of a London flat, a pocket-sized badge has taken over a job most of us still give our phones: telling us whether to run for the bus. Six departure rows for the stop across the street, today's weather along the bottom, a small battery percentage in the corner. The thing has been running for a few weeks now on its built-in battery — about a week per charge, sometimes ten days.[1](https://forums.pimoroni.com/t/badger-2350-as-a-live-tfl-bus-arrivals-board/29051)[2](https://github.com/marciolopes93/tfl-bus-arrivals-badger2350)

The hardware is cheap and needs no soldering. Pimoroni's Badger 2350 is a 2.7-inch four-level greyscale e-paper badge driven by an RP2350, sold for £41.25 with battery, case and lanyard.[3](https://shop.pimoroni.com/products/badger-2350) Its owner, Marcio Lopes, published the full MicroPython app under an MIT licence on August 16, and Adafruit's write-up three days later has already brought it an audience.[1](https://forums.pimoroni.com/t/badger-2350-as-a-live-tfl-bus-arrivals-board/29051)[2](https://github.com/marciolopes93/tfl-bus-arrivals-badger2350)[4](https://blog.adafruit.com/2026/08/19/live-tfl-bus-arrivals-weather-departure-board-on-a-pimoroni-badger-2350-e-ink-badge/)

## The image is free

Everything starts with a physical property. An e-paper screen keeps its last image without spending a single milliamp. When no bus is due, the board can sit frozen for hours at zero cost — the exact opposite of an LCD, which needs its backlight on all the time to stay readable.

That free image comes with a trade-off: updates are slow and power-hungry, so every pixel counts. The panel measures 264 × 176 pixels — measured on the device itself, the project's documentation insists, because an estimate made from another model's spec sheet first cost him two bus rows on paper.[2](https://github.com/marciolopes93/tfl-bus-arrivals-badger2350) The font occupies 6 × 12 pixels, just enough room for six departures, a title bar and the weather line, and not one typographic luxury more.

The four grey levels had their own surprise in store. An early version striped the table rows for readability; the chosen grey rendered as solid black under the fast update mode, and the striping went straight in the bin. The final board is black on white, period.[2](https://github.com/marciolopes93/tfl-bus-arrivals-badger2350)

## No key, no account

Timetables aren't enough here: what makes the object useful is real time. So the app queries Transport for London's open API, which exposes arrival predictions stop by stop. TfL's official portal caps anonymous access at 50 requests per minute and offers a free key to raise that to 500.[5](https://api-portal.tfl.gov.uk/)

The badge refreshes every two minutes. About thirty requests per hour — roughly 1% of the anonymous ceiling — so no key, no account, no signup. A call to one of the project's configured stops on August 24 returned live predictions without any credentials at all.[2](https://github.com/marciolopes93/tfl-bus-arrivals-badger2350)

> The network budget
> **Far below the ceiling**
> - TfL's official anonymous rate limit: 50/min
> - requests actually sent by the badge: ~30/hour
> - between two refreshes of the board: 2 min
> Sources: TfL API portal; project documentation.

Weather takes a different route: the wttr.in service, queried once an hour over plain HTTP — the TLS handshake fails on this board, while the one to TfL works fine. A firmware quirk that cost someone, somewhere, an evening.[2](https://github.com/marciolopes93/tfl-bus-arrivals-badger2350)

## Sleep wipes RAM

Between refreshes the badge sleeps deeply. And there the firmware imposes a constraint that shaped the whole program: deep sleep amounts to a full reboot. Every wake clears RAM. The latest known departures, the weather, the timestamps must therefore be written to flash before sleeping, then read back on every wake.[2](https://github.com/marciolopes93/tfl-bus-arrivals-badger2350)

That permanent amnesia nearly killed the project. For about forty-eight hours, the badge sat frozen on its last frame: an exception while arming the wake alarm had been silently swallowed, and the device had gone to sleep with no way to wake up — after some 1,400 cycles. The fix now arms the timer with a verified retry: the device refuses to sleep until a wake source is confirmed.[2](https://github.com/marciolopes93/tfl-bus-arrivals-badger2350)

A second firmware trap: the function meant to report battery level only ever returns 100% or 0%. The app computes its own percentage from the measured voltage through a LiPo curve — and reads that voltage before switching the Wi-Fi radio on, since radio activity skews the measurement.[2](https://github.com/marciolopes93/tfl-bus-arrivals-badger2350)

## A slower night

From 10:30 pm to 5:30 am the badge stops chasing buses. It wakes once an hour to update the clock, the battery and the weather, then dozes off again. One press of button A buys thirty minutes of live times for night owls.[1](https://forums.pimoroni.com/t/badger-2350-as-a-live-tfl-bus-arrivals-board/29051)[2](https://github.com/marciolopes93/tfl-bus-arrivals-badger2350)

When the battery drops to 3.45 V, the app latches into a static low-battery screen, drawn once — e-paper keeps it visible for free — then wakes only every quarter hour to check for a charger. The logic fits in one line: when the badge can no longer vouch for its times, it shuts down with a warning rather than leaving misleading numbers on the wall. On the forum, Marcio Lopes owns that choice: without it, you'd sprint for a bus based on times that no longer mean anything.[1](https://forums.pimoroni.com/t/badger-2350-as-a-live-tfl-bus-arrivals-board/29051)

With all this restraint, reported battery life lands around a week per charge, sometimes ten days — figures reported by the author for his own usage, so treat them as exactly that.[1](https://forums.pimoroni.com/t/badger-2350-as-a-live-tfl-bus-arrivals-board/29051)

## The stop next door

In late July, roadworks closed his usual stop. The outage exposed a blind spot: the arrivals endpoint has no idea a stop is closed — it happily keeps serving times for a stop no bus will ever pull into again. Closures live on a separate endpoint, checked once an hour.[2](https://github.com/marciolopes93/tfl-bus-arrivals-badger2350)

So the app watches that endpoint, and when a closure is declared it draws an inverted "{stop} Closed" banner, retitles the header, and fetches departures from the neighbouring stop — the only ones you can act on. The internal documentation puts it better than anyone could: times for a stop no bus will reach are worse than no times, because they read as actionable.[2](https://github.com/marciolopes93/tfl-bus-arrivals-badger2350)

> **Two signals, two failure policies**
> - The footer disappears once it goes stale.: Weather
> - The alert holds until TfL answers with an official all-clear.: Closure
> - The cadence shared by both secondary checks.: Hourly
> Design documented in the repository's HANDOFF.md.

Hence a deliberate asymmetry in how failures are handled. If wttr.in goes down, the weather footer eventually disappears: missing information is honest, stale information is not. Conversely, a closure alert never clears on a mere network failure — it holds until TfL explicitly answers that everything is back to normal. Better a wasted walk to a reopened stop than standing in front of a phantom "closed" sign.

## What remains

In the forum thread, the very first comment is a reminder of how fragile this model is: a German user tells of building the same kind of board for his buses, right up until the API he relied on shut down. An ambient object lives entirely at the mercy of its data provider's openness policy — TfL could tighten its quotas or terms tomorrow.[1](https://forums.pimoroni.com/t/badger-2350-as-a-live-tfl-bus-arrivals-board/29051)

What remains is a transferable pattern. Label what's stale, drop what's dead, shout when you no longer know: three ordinary decisions in web software, vanishingly rare in an object expected to run for weeks unattended. This £41 badge does very little — six lines, a clock, one line of weather — which is more or less all anyone asks of it. It never misses a chance to say what it doesn't know.

## References

1. [Marcio Lopes, Badger 2350 as a live TfL bus arrivals board — Pimoroni forums, August 16, 2026](https://forums.pimoroni.com/t/badger-2350-as-a-live-tfl-bus-arrivals-board/29051)
2. [marciolopes93/tfl-bus-arrivals-badger2350 — GitHub repository (README and HANDOFF.md)](https://github.com/marciolopes93/tfl-bus-arrivals-badger2350)
3. [Pimoroni Badger 2350 — official product page](https://shop.pimoroni.com/products/badger-2350)
4. [Adafruit Blog, Live TfL bus arrivals + weather departure board on a Pimoroni Badger 2350, August 19, 2026](https://blog.adafruit.com/2026/08/19/live-tfl-bus-arrivals-weather-departure-board-on-a-pimoroni-badger-2350-e-ink-badge/)
5. [Transport for London — API portal and rate limits](https://api-portal.tfl.gov.uk/)
