Skip to content

FEATURE: Sector-crossed event callback with rich crossing context #32

Description

@FabioRoss

What problem are you trying to solve?

When a sector line or start/finish line is confirmed crossed, user code needs to
react immediately: update the display, log to SD, start an
animation. Today, the only feedback mechanism is the int return value of loop()
(0 = near a line, -1 = not near). This requires the user to:

  • Poll loop() return value every iteration
  • Track state themselves to know WHICH line was crossed (they get "near a line",
    not "which sector")
  • Compute direction context themselves

The user's update code is likely more complex than the lap timer itself. A callback
system lets the library deliver the right event to the right handler at the right
moment — after interpolation completes, not during buffering.

A second pain point: the callback fires AFTER interpolation, so it's the
authoritative signal that a valid crossing happened. If the user instead polls
loop() == 0, they don't know whether they're in the approach zone (still
buffering) or post-interpolation (confirmed).

Proposed solution

Add a user-registerable callback that fires once per confirmed sector crossing,
with rich context parameters.

CALLBACK TYPE:

typedef void (*SectorCrossedCallback)(
  int currentSector,   // sector the driver just ENTERED (0 = just started lap, 1/2/3)
  int lastSector,      // sector the driver just COMPLETED
  uint8_t sectorType   // SECTOR_TYPE_RACE / SECTOR_TYPE_SERVICE / SECTOR_TYPE_OTHER
);

void setSectorCrossedCallback(SectorCrossedCallback cb);

FIRING RULES:

  • Fires only when LINE_DETECT_COMPLETED is returned and crossingTime != 0
    (valid interpolated crossing confirmed)
  • Never fires during LINE_DETECT_IN_ZONE (approach buffering phase)
  • Fires before the crossing buffer is reset, so the callback can read
    any state that was updated as part of the crossing
  • Fires for BOTH start/finish AND sector lines (sectorType discriminates)

COMPANION: Service sector callback (see Issue #30):

typedef void (*ServiceSectorCallback)(uint8_t serviceType, bool isEntry);
void setServiceSectorCallback(ServiceSectorCallback cb);

WHY SEPARATE CALLBACKS?
Race sector crossings and service sector events have different contexts.
Race callbacks tell you lap progress; service callbacks tell you operational
status. Mixing them in one callback requires the user to switch on sectorType
constantly. Two clean callbacks is more ergonomic.

EXAMPLE USAGE:

lapTimer.setSectorCrossedCallback([](int cur, int last, uint8_t type) {
  Serial.printf("Sector %d complete, entering %d\n", last, cur);
  display.showSectorSplit(last, lapTimer.sectorTimes[last]);
});

Alternatives considered

  1. loop() return value (current approach) — tells caller "near a line" but NOT
    which line, and NOT whether it was approach or confirmed crossing. Caller
    must maintain its own state machine to detect the transition.

  2. Polling boolean flags (e.g. getCrossing()) — same problem: no event,
    no which-line context, still need user-side state machine.

  3. Single callback with all crossing types — workable but forces switch(sectorType)
    in every callback. Two callbacks is cleaner separation of concerns.

  4. Return struct from loop() instead of int — breaks API compatibility; also
    still requires the caller to detect the confirmed-vs-buffering transition.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions