Skip to content

Repository files navigation

TrackSessionTimer GPS

A new-generation standalone track session timer and GPS lap timer for track-day and club motorsport use.

This repository is a clean hardware and firmware rebuild of the ideas proven in ctrmint/TrackSessionTimer. The new design keeps the original focus on an extremely readable session countdown, but adds a larger sunlight-capable display, high-rate GNSS lap timing, session logging and a software architecture intended to grow beyond the limits of the original RP2040/MicroPython platform.

This is not a CAN logging project. CAN bus integration is not part of the baseline scope.

Project status

Hardware bring-up in progress.

Running on the Waveshare board: the RM690B0 AMOLED panel over QSPI, FT6336 touch, the microSD card, the QMI8658 IMU with gyro-tracked attitude, and a gesture-driven UI whose track catalog is read from the card at boot. A session runs end to end - countdown, overrun and rest - records its duration and peak G on every axis, writes that to the card and survives a power cycle.

GNSS works. The receiver is connected over I2C, configured by the firmware at boot and delivering accepted fixes on hardware: 25 Hz as configured, every frame passing the quality gate, on 16 satellites. The chain is an I2C transport, a UBX parser, a fix-quality gate and a pipeline that counts every way a fix can go missing, and all of it is now exercised by a real receiver rather than only by host tests.

Those fixes now reach the timing task. The high-priority timing task, its engine, its lap state machine and its bounded queues were all built and host-tested long before a receiver existed, and until recently nothing called any of them - every fix the device had ever produced was read, validated, counted and thrown away. The queue has a consumer now, and lap times reach the screen.

What remains before a lap can actually be timed is a circuit the engine will accept. No definition on the card is timing-ready: all 24 UK layouts are provisional (#139), so the device loads them and runs timer-only. #184's test loop is the route to a first real lap. With nothing attached GPS Only reports "NO RECEIVER", which is the truth rather than a fault.

Four modes are built on those fixes. 0-X times a standing start to a target speed, interpolating both ends rather than quantising them to the fix rate. Compass shows course over ground on a fixed card, and hides its marker entirely when the car is too slow for the receiver to know which way it is going - a parked vehicle has no heading, and the board has no magnetometer to supply one. Speed Only shows road speed in mph at 248 px - two thirds of the panel's height - with a stationary car reading 0 and an absent or unfixed receiver reading --- rather than a believable zero. Clock mode: a 24-hour GMT clock taken from the receiver's own UTC time, fixed to GMT for now and labelled so, since for half the year that is an hour behind every other clock in the UK. It shows dashes rather than a time whenever the receiver has not resolved one, and free-runs visibly rather than freezing if the receiver goes quiet - on a clock, a believable wrong answer is the worst available failure.

See ADR-005 for the staged I2C-then-UART plan: I2C is solderless and so is what bring-up uses, and UART is pushed by DMA and so is what the car uses. The RTC is not driven yet.

The repository additionally holds the project plan, architecture, hardware bill of materials, GNSS timing design, UI requirements, test plan and issue backlog.

Target hardware

The baseline prototype is built around:

  • Waveshare ESP32-S3-Touch-AMOLED-2.41-B
    • 2.41 inch AMOLED
    • 600 x 450 pixels
    • 800 cd/m2 stated brightness
    • ESP32-S3R8, 8 MB PSRAM, 16 MB flash
    • capacitive touch
    • onboard QMI8658 IMU
    • RTC and microSD/TF support
    • protective case supplied with the -B variant
  • u-blox NEO-M9N GNSS, on the SparkFun GPS-17285 SMA Qwiic breakout
    • 25 Hz position update rate across all five constellation configurations
    • UBX protocol, over I2C for bring-up and UART before any vehicle testing (ADR-005)
    • SMA antenna connector, chosen to keep a fragile U.FL joint out of a vibrating car
    • optional timepulse/PPS input to the ESP32 for timing diagnostics
  • External active GNSS antenna covering GPS, Galileo, GLONASS and BeiDou, with a clear view of the sky. The band matters: many "GNSS" antennas start at 1575 MHz and sit above BeiDou B1 at 1561.098 MHz, losing a quarter of the constellations silently
  • microSD card for high-rate session logs

The product target is a single dashboard-mounted unit with the display at the front and a compact GNSS/power extension behind it. The development build may use the Waveshare case plus an add-on rear pod before a final enclosure is designed.

See docs/HARDWARE_BOM.md and hardware/README.md.

Primary goals

  1. Preserve the original TrackSessionTimer session countdown workflow.
  2. Improve at-a-glance readability in a moving vehicle.
  3. Add genuine 20 Hz minimum GNSS, with 25 Hz preferred on the selected M9N hardware.
  4. Detect start/finish crossings using a directional virtual line, not a simple radius/geofence trigger.
  5. Calculate lap crossing time from GNSS measurement time and interpolate the line intersection between fixes.
  6. Record current, previous and best lap times.
  7. Add session trace logging to microSD.
  8. Retain IMU-based G measurement and launch-related functionality where useful.
  9. Operate fully offline at the circuit.
  10. Degrade safely if GNSS, SD or IMU functionality becomes unavailable.

Explicit non-goals for the first release

  • CAN bus acquisition or ECU logging
  • cloud dependency
  • official race timing or transponder replacement
  • RTK centimetre-level positioning
  • telemetry streaming to a pit wall
  • phone dependency during a session
  • complex map navigation on the device

These may be evaluated later, but they must not delay the core track timer.

Firmware direction

The planned firmware stack is C++ on ESP-IDF, with LVGL for the user interface once the Waveshare display driver is proven.

The rationale is documented in docs/decisions/ADR-001-firmware-platform.md. The original MicroPython project is valuable as a behavioural reference, but the new system has a larger framebuffer, a continuous 20/25 Hz GNSS stream, SD logging and more demanding timing/UI workloads.

The repository starts with a minimal ESP-IDF application under firmware/ so the toolchain can be validated before display-specific dependencies are added.

High-level architecture

                  External active GNSS antenna
                              |
                              v
                       +-------------+
                       | u-blox M9N  |
                       | 20/25 Hz    |
                       +------+------+ 
                              | UART / UBX
                              | optional PPS
                              v
+----------------------------------------------------------------+
|                   ESP32-S3 TrackSessionTimer                    |
|                                                                |
|  +-------------+   +---------------+   +-------------------+   |
|  | GNSS ingest |-->| Timing engine |-->| Session state     |   |
|  +-------------+   +---------------+   +-------------------+   |
|          |                 |                     |              |
|          v                 v                     v              |
|  +-------------+   +---------------+   +-------------------+   |
|  | Track match |   | Lap / delta   |   | UI / LVGL         |   |
|  +-------------+   +---------------+   +-------------------+   |
|          |                 |                     |              |
|          +-----------------+----------+----------+              |
|                                       |                         |
|                                       v                         |
|                               +---------------+                 |
|                               | microSD log   |                 |
|                               +---------------+                 |
|                                                                |
|   QMI8658 IMU -> G meter / launch / orientation                |
+----------------------------------------------------------------+

See docs/ARCHITECTURE.md.

Lap timing principle

The receiver produces a sequence of timestamped positions. A track file defines directed start, finish, pit-entry, and pit-exit lines. Start or finish is selected as the repeating lap boundary; the two pit gates remain independent typed events.

For every new GNSS fix, the timing engine examines the segment between the previous and current position against all four finite lines. A valid crossing fraction is calculated geometrically and applied to the GNSS timestamps on either side of the line.

Example:

fix A time        12:43:17.320
fix B time        12:43:17.360
crossing fraction 0.62

crossing time = 17.320 + (0.62 x 40 ms)
              = 17.3448

This avoids quantising every lap to the next 40 ms receiver update. It does not remove GNSS position error, so the design still uses quality checks, direction checks, hysteresis, minimum-lap-time rules and trace validation.

See docs/GNSS_AND_LAP_TIMING.md.

Repository layout

.github/                  GitHub issue and PR templates
firmware/                 ESP-IDF firmware project
simulator/                600 x 450 LVGL/SDL desktop device simulator
hardware/                 BOM, wiring and enclosure notes
data/tracks/              Track file schema and synthetic example
data/track-packs/         Provenance-gated offline pack source manifests
planning/                 Export of the live milestone/issue backlog
tests/                    Host-side algorithm tests
tools/                    Development and replay utilities
docs/                     Architecture and project documentation
PROJECT_PLAN.md            Milestones, sequencing and acceptance gates
ROADMAP.md                 Product roadmap beyond the first usable build
CONTRIBUTING.md            Development workflow and quality rules

First development steps

  1. Buy or assemble the baseline hardware in docs/HARDWARE_BOM.md.
  2. Install a supported ESP-IDF toolchain.
  3. Build and flash the minimal firmware skeleton.
  4. Prove display initialization and full-screen refresh performance.
  5. Prove touch, microSD and QMI8658 access.
  6. Connect the NEO-M9N on a dedicated UART.
  7. Configure the M9N for 20 or 25 Hz UBX output and record raw traces.
  8. Develop the lap-crossing engine against recorded traces on a host computer before relying on it on track.

Detailed sequencing is in PROJECT_PLAN.md.

Quick firmware bootstrap

The supported firmware baseline is ESP-IDF v6.0.2. From an activated v6.0.2 shell:

cd firmware
idf.py set-target esp32s3
idf.py build
idf.py -p /dev/ttyACM0 flash monitor

Without a native IDF installation, run make firmware-container-build from the repository root. See docs/DEVELOPMENT_SETUP.md for the pinned Python environment, clean-clone checks, and flashing notes.

Build the deterministic UK offline track archive from the repository root with make uk-track-pack, and the functional test fixtures with make test-track-pack. The archives are written under build/track-pack/ and are ignored by Git. Public-map geometry is timer-only until it is validated: two definitions in the UK pack have been, Donington Park GP and National, and the remaining 22 have not. See docs/TRACK_DATABASE.md for what promotion requires.

Hardware-independent screen simulator

Frozen. No longer developed. The simulator lags the device and cannot exercise the gesture-driven interaction model the firmware uses. It is kept in CI as a regression guard over the shared firmware components it compiles. See CONTRIBUTING.md.

The 600 x 450 LVGL/SDL simulator runs the device presentation model without the Waveshare board. On a Linux host with CMake, Ninja, and SDL2 development headers:

make simulator-test
make simulator-run

The interactive window starts on the ready dashboard. Its large Start, Setup, Review, and Diagnostics controls accept pointer/touch or keyboard activation and use the same tested navigation model. Setup provides track selection and transactional editors for every release-one device option, including explicit Save, Cancel, and confirmed Defaults actions. The track screen explains all matching/readiness states, requires confirmation of suggestions, and preserves timer-only operation. Simulator settings are stored below the operating system temporary directory rather than in the repository. Deterministic active, GNSS-loss/recovery, and storage-failure/recovery states remain available through command-line scenarios. Deterministic backends provide 20/25 Hz synthetic or fixture-driven GNSS, 100 Hz IMU, RTC, touch, bounded queues, and missing/full/slow/write-failed storage. A containerized headless check is available with make simulator-container-test. See simulator/README.md for replay commands, fixture validation, diagnostics, and the boundary between simulated and physical acceptance.

Development backlog

The live GitHub issues and milestones are the source of truth. A reviewable epic summary is in planning/INITIAL_ISSUES.md, with machine-readable snapshots in planning/issues.csv and planning/labels.csv.

tools/create_issues.py and tools/create_labels.py preview the corresponding GitHub CLI commands. Execute mode skips existing issue titles and forces label updates.

make issue-preview
make label-preview

Use --repo OWNER/REPO when seeding another repository. Native sub-issue and blocked-by relationships are not reconstructed by the CSV preview scripts.

Design rules

  • Timing uses GNSS measurement time, not message-arrival time.
  • The active session timer must never depend on Wi-Fi or a phone.
  • A GNSS failure must not stop the basic session countdown.
  • SD failure must not stop timing.
  • UI work must not block GNSS ingestion.
  • GNSS raw/derived data must be replayable on a host computer.
  • Driver-facing screens prioritise readability over information density.
  • No hidden automatic behaviour may alter a completed lap time after it is shown.
  • On-track interaction must be minimal.

Licence

GPL-3.0. The previous TrackSessionTimer repository is GPL-3.0 and this project is expected to reuse or port parts of that work. See docs/LICENSING.md.

Source references

Hardware claims and baseline component choices were checked against manufacturer/vendor documentation on 2026-08-12. See docs/SOURCE_REFERENCES.md.

Releases

Packages

Used by

Contributors

Languages