Jump to content




Photo

YATSS - Yet Another Timing/Scoring System


  • Please log in to reply
11 replies to this topic

#1 tekrantz57

tekrantz57

    Rookie Keyboard Racer

  • Full Member
  • Pip
  • 13 posts
  • Joined: 20-July 26
  • Gender:Male
  • Location:NC

Posted 22 July 2026 - 11:49 AM

Hi all,

 

I wanted to share a project I’ve been working on called YATSS, short for Yet Another Timing/Scoring System.

YATSS is a home-built slot car lap timing and race-control system. It uses an Arduino Nano ESP32 at the track for sensor input and track power control, with a Windows race-control app handling lap counting, race flow, qualifying, heat races, timing display, reports, and serial logging.

 

The basic idea is that the microcontroller stays simple and reliable. It watches the lane sensors, timestamps debounced edges, and sends those events to the Windows app over USB serial. The Windows app does the higher-level work: lap validation, minimum lap time filtering, heat management, qualifying order, race reports, and track calls.

 

Current features include:

  • Practice mode
  • Heat race mode
  • Qualifying support
  • 2 to 8 active lanes
  • More racers than lanes
  • Configurable lane names and colors
  • Minimum lap time filtering
  • Controller-side debounce
  • Serial logging
  • Spoken countdowns / announcements
  • Track power cut and restore commands
  • Demo modes for testing without live hardware

The sensor circuit I’ve been testing uses dead strips feeding an optocoupler circuit. The controller sees a clean falling edge from each lane. Track power control is handled separately with relay-driver circuits, so the Arduino pin only drives a MOSFET gate while the MOSFET handles the relay coil current.

 

This is still very much an active project, but it has reached the point where the major pieces are coming together: sensor input, serial communication, Windows app logic, saved settings, race flow, and hardware power control.

 

The project is currently in a private GitHub repository. I’m using that mostly as a place to keep the code, notes, and documentation organized while development continues.

 

My goal is not to claim this is the one true way to build a timing system. It’s more of a practical, buildable system for my own track that may be useful or interesting to others who like combining slot cars, electronics, and software.

I’d be glad to discuss the design, hardware choices, lessons learned, and tradeoffs as the project continues to evolve.

 

Thanks for taking a look.


  • Cheater likes this
Tim Krantz 




#2 tekrantz57

tekrantz57

    Rookie Keyboard Racer

  • Full Member
  • Pip
  • 13 posts
  • Joined: 20-July 26
  • Gender:Male
  • Location:NC

Posted 22 July 2026 - 12:39 PM

Oh, and by the way, this will be fully open source when I get to that point.


  • Cheater and MSwiss like this
Tim Krantz 

#3 dalek

dalek

    Race Leader

  • Subscriber
  • PipPipPipPipPip
  • 947 posts
  • Joined: 25-October 11
  • Gender:Male
  • Location:Central FL

Posted 23 July 2026 - 07:02 AM

Will your software generate race reports and if so, to what extent and how would they be modifiable?



#4 tekrantz57

tekrantz57

    Rookie Keyboard Racer

  • Full Member
  • Pip
  • 13 posts
  • Joined: 20-July 26
  • Gender:Male
  • Location:NC

Posted 23 July 2026 - 10:21 AM

The software currently reports something like the attached HTML file. Not currently modifiable via the UI but I am certainly open for suggestions/additions. And since the software will be fully open source, anyone will be able to make changes.

 

Attached File  HeatRace_20260723_111200.html   19.35KB   13 downloads


Tim Krantz 

#5 tekrantz57

tekrantz57

    Rookie Keyboard Racer

  • Full Member
  • Pip
  • 13 posts
  • Joined: 20-July 26
  • Gender:Male
  • Location:NC

Posted 23 July 2026 - 12:43 PM

If there is any interest I can put together a Windows package of the entire Windows side of the software. It can do demo runs with no microcontroller at all. It will also produce sample race reports if you let a 'demo race' run to completion.
 
Let me know.


  • Cheater likes this
Tim Krantz 

#6 Bill Seitz

Bill Seitz

    always in last place

  • Subscriber
  • PipPipPipPipPip
  • 714 posts
  • Joined: 20-February 21
  • Gender:Male
  • Location:Tucson, AZ

Posted 23 July 2026 - 06:13 PM

Does the Arduino black box also provide the lap timing?



#7 dalek

dalek

    Race Leader

  • Subscriber
  • PipPipPipPipPip
  • 947 posts
  • Joined: 25-October 11
  • Gender:Male
  • Location:Central FL

Posted 23 July 2026 - 07:11 PM

The software currently reports something like the attached HTML file. Not currently modifiable via the UI but I am certainly open for suggestions/additions. And since the software will be fully open source, anyone will be able to make changes.

 

In a race report, it's handy to have not only the lap count for each driver for each lane they ran, but also to have the driver's best lap there with the lap count so two separate reports aren't needed.
 
There are examples of reports I made that show what I mean at: some race reports.


#8 tekrantz57

tekrantz57

    Rookie Keyboard Racer

  • Full Member
  • Pip
  • 13 posts
  • Joined: 20-July 26
  • Gender:Male
  • Location:NC

Posted 24 July 2026 - 12:29 PM

That is certainly doable. Seems a matter of preference. I would not call my way a sseparate report,, just a separate chart. The beauty of open source is, make it look however you like!
 
The more I think about it, I could certainly export raw info to a CSV file and you could fashion your own reports from there.
 
Edit:
I am in the process of allowing the export of completed race data in both JSON and CSV formats. I will have a document of the format shortly.


Tim Krantz 

#9 tekrantz57

tekrantz57

    Rookie Keyboard Racer

  • Full Member
  • Pip
  • 13 posts
  • Joined: 20-July 26
  • Gender:Male
  • Location:NC

Posted 24 July 2026 - 12:33 PM

Does the Arduino black box also provide the lap timing?

 

The architecture is such that the esp32 does exactly what it does best. Gets interrupts and measures the time. I also would not call it a black box since the source code for the firmware and the protocol between the esp32 and the front end are completely open source. If you are interested in the protocol doc, I can provide it. If anyone wants more info,  I can provide them access to the private Github. All you need it a Github account.


Tim Krantz 

#10 tekrantz57

tekrantz57

    Rookie Keyboard Racer

  • Full Member
  • Pip
  • 13 posts
  • Joined: 20-July 26
  • Gender:Male
  • Location:NC

Posted 24 July 2026 - 01:34 PM

In a race report, it's handy to have not only the lap count for each driver for each lane they ran, but also to have the driver's best lap there with the lap count so two separate reports aren't needed.
 
There are examples of reports I made that show what I mean at: some race reports.

 
I now have the ability to export race data at the completion of a race in eithr or boty JSON and CSV. The document below specifies the current format of the export:
 
# Race Reports and Data Exports

YATSS creates race artifacts when the final heat completes. The HTML report is the primary human-readable result. Optional JSON and CSV exports preserve the same scoring data for spreadsheets, custom reports, websites, and other tools.

## Configuration

Open `Configure` and use the `Race Reports` options:

- `Write JSON race archive` writes the complete, versioned machine-readable
  archive.
- `Write CSV data files` writes normalized tables for spreadsheet and script
  use.

Both options default to enabled and are persisted in the YATSS SQLite settings database. They can be enabled independently. The HTML report is always written and displayed when a race completes.

## Output Location and Names

Artifacts are written under:

```text
%USERPROFILE%\Documents\YATSS Race Reports
```

Every artifact from one race shares a timestamped basename:

```text
HeatRace_yyyyMMdd_HHmmss.html
HeatRace_yyyyMMdd_HHmmss.json
HeatRace_yyyyMMdd_HHmmss_results.csv
HeatRace_yyyyMMdd_HHmmss_laps.csv
HeatRace_yyyyMMdd_HHmmss_qualifying.csv
HeatRace_yyyyMMdd_HHmmss_adjustments.csv
```

Only the HTML file is present when both optional exports are disabled. Disabling
JSON does not affect CSV, and disabling CSV does not affect JSON.

## Scoring Boundaries

The archive records accepted race-scoring information, not every controller
message:

- A lane's first practice or first-heat edge establishes its timing baseline
  and is not exported as a lap.
- Accepted timed laps are exported individually.
- A counted lap without a duration is retained with a null/empty lap time.
- Laps rejected by minimum-time, raw-edge-lockout, sequence, or other validity
  checks are not race laps and remain documented in the serial log.
- Paused time and between-heat time are excluded from race elapsed timing.
- The first crossing after a lane rotation counts from the heat start but is
  marked ineligible for fastest-lap awards because it can represent a partial
  physical lap.
- Manual lap additions and subtractions change official totals but do not
  manufacture timing samples. They are exported as separate audit entries.

The serial log remains the source for raw frames, rejected edges, controller
diagnostics, and communication troubleshooting.

## HTML Report

The HTML report includes race settings, qualifying, finish order, fastest laps by lane, heat details, and any manual lap corrections. The qualifying table contains:

- Ranked position and racer
- Qualifying lane
- Configured and actual session duration
- Accepted-lap count
- Best qualifying lap
- Complete accepted qualifying-lap history

YATSS displays this report in an owned report window after completion. The window can also open the HTML in the default browser or reveal it in File Explorer.

## JSON Archive

The JSON file is the canonical lossless export. Property names use camel case, times are numeric milliseconds unless documented otherwise, and the file is UTF-8 without a byte-order mark.

### Top Level

| Property | Meaning |
| --- | --- |
| `schemaVersion` | Integer contract version. The initial version is `1`. |
| `applicationVersion` | YATSS assembly version that produced the archive. |
| `exportedAt` | ISO 8601 timestamp with UTC offset for archive creation. |
| `race` | Complete race-report object described below. |

Consumers should reject unsupported higher schema versions or ignore unknown properties. YATSS will increment `schemaVersion` when an incompatible contract change is introduced.

### Race Object

| Property | Meaning |
| --- | --- |
| `createdLocal` | Local date and time used for the artifact basename. |
| `raceName` | Configured race name; may be empty. |
| `heatLengthMinutes` | Configured active duration of each heat. |
| `betweenHeatsSeconds` | Configured intermission duration. |
| `trackLengthFeet` | Configured physical track length. |
| `totalHeats` | Number of scheduled heats. |
| `laneNames` | Active physical lane names in zero-based lane order. |
| `laneColorArgb` | Active lane colors as signed .NET ARGB integers. |
| `qualifyingResults` | Ranked qualifying session records. |
| `racers` | Final standings and per-racer aggregate results. |
| `laneResults` | One aggregate result for each occupied lane in each heat. |
| `laps` | Every accepted heat-race lap record. |
| `manualAdjustments` | Ordered manual correction audit entries. |
| `notes` | Human-readable scoring notes. |

JSON `laneIndex` values are zero based. CSV files expose one-based `LaneNumber` values for human-facing use.

### Qualifying Result

| Property | Meaning |
| --- | --- |
| `racerName` | Racer who ran the session. |
| `originalOrder` | Zero-based order before qualifying. |
| `bestLapMilliseconds` | Fastest accepted lap, or null when none was set. |
| `laneIndex` | Zero-based physical qualifying lane. |
| `configuredDurationSeconds` | Requested session duration. |
| `elapsedMilliseconds` | Actual controller elapsed time at completion. |
| `laps` | Accepted qualifying laps in crossing order. |

Each qualifying lap contains `lapNumber`, `lapMilliseconds`, and `sessionElapsedMilliseconds`. The elapsed value is measured from the start of that racer's qualifying session to the recorded crossing.

### Final Racer Result

Each item in `racers` contains `racerName`, `totalLaps`, `heatLaps`, and`bestLapByLaneMilliseconds`. The array is sorted in final finishing order. The heat-lap array uses zero-based array position for Heat 1, Heat 2, and so on. The best-lap array uses zero-based physical lane position.

### Lane Result

Each item in `laneResults` contains `heatNumber`, `laneIndex`, `laneName``racerName`, `heatLaps`, `totalLaps`, and `bestLapMilliseconds`. `totalLaps` is that racer's cumulative total at the end of the recorded heat.

### Heat Lap

Each item in `laps` contains:

| Property | Meaning |
| --- | --- |
| `heatNumber` | One-based heat number. |
| `laneIndex` / `laneName` | Physical lane identity. |
| `racerName` | Racer assigned to the lane for that heat. |
| `lapNumberInHeat` | One-based accepted crossing number in that heat. |
| `racerTotalLapNumber` | Racer's cumulative lap number at that crossing, before later manual corrections. |
| `lapMilliseconds` | Measured duration, or null for an untimed counted crossing. |
| `raceElapsedMilliseconds` | Active race time at the crossing, excluding pauses and intermissions. |
| `fastestLapEligible` | Whether this timing sample may win a fastest-lap award. |

### Manual Adjustment

Each item in `manualAdjustments` contains the heat, lane, racer, signed `delta``resultingTotalLaps`, active `raceElapsedMilliseconds`, and an ISO 8601 `recordedAt` timestamp. Multiple changes are retained separately, including a later correction that reverses an earlier one.

## CSV Exports

CSV files use UTF-8 without a byte-order mark, invariant-culture numbers, lowercase `true`/`false`, comma delimiters, and doubled quotes inside quoted fields. Empty optional values are blank. A CSV with no applicable records still
contains its header row.

### Results CSV

One row is written for each occupied lane in each heat.

| Column | Meaning |
| --- | --- |
| `RaceName` | Configured race name. |
| `CreatedLocal` | ISO 8601 local race-report time. |
| `FinalPlace` | Racer's one-based final position. |
| `Heat` | One-based heat number. |
| `LaneNumber` / `LaneName` | One-based physical lane identity and configured name. |
| `Racer` | Racer assigned to that lane. |
| `HeatLaps` | Official laps credited during the heat, including manual corrections. |
| `TotalLaps` | Official cumulative total after the heat. |
| `BestLapMilliseconds` | Fastest eligible measured lap in that heat/lane, or blank. |

### Laps CSV

One row is written for every accepted heat-race lap. Columns are `RaceName``Heat`, `LaneNumber`, `LaneName`, `Racer`, `LapNumberInHeat``RacerTotalLapNumber`, `LapMilliseconds`, `RaceElapsedMilliseconds`, and
`FastestLapEligible`.

Manual additions do not appear as fabricated rows in this file. Use the adjustments CSV with the results CSV to audit how measured laps became official totals.

### Qualifying CSV

One row is written for every accepted qualifying lap. A qualifier with no valid lap still receives one row with blank lap fields. Columns are `RaceName``Position`, `OriginalOrder`, `LaneNumber`, `LaneName`, `Racer``ConfiguredDurationSeconds`, `ElapsedMilliseconds`, `LapNumber``LapMilliseconds`, `SessionElapsedMilliseconds`, and `IsBestLap`.

`Position`, `OriginalOrder`, and `LaneNumber` are one based in CSV. When equal lap times tie for the best time, every matching row has `IsBestLap=true`.

### Adjustments CSV

One row is written for each manual lap correction. Columns are `RaceName``Heat`, `LaneNumber`, `LaneName`, `Racer`, `Delta`, `ResultingTotalLaps``RaceElapsedMilliseconds`, and `RecordedAt`.

`Delta` is signed: positive values add laps and negative values subtract laps. `RecordedAt` is an ISO 8601 timestamp with UTC offset.

## Compatibility and Historical Reports

Complete lap and qualifying histories are captured for races run with this version or later. Existing HTML reports cannot be expanded into full archives because the older report model did not retain every timing sample. Existing
reports remain readable and are not moved or rewritten.


Tim Krantz 

#11 dalek

dalek

    Race Leader

  • Subscriber
  • PipPipPipPipPip
  • 947 posts
  • Joined: 25-October 11
  • Gender:Male
  • Location:Central FL

Posted 25 July 2026 - 08:49 AM

It looks like you really know your stuff – very cool.


  • Cheater and tekrantz57 like this

#12 tekrantz57

tekrantz57

    Rookie Keyboard Racer

  • Full Member
  • Pip
  • 13 posts
  • Joined: 20-July 26
  • Gender:Male
  • Location:NC

Posted 26 July 2026 - 04:54 PM

I did not get the amount of discussion that I had hoped for, but that is fine.  If I make this a public read-only repository is there anyone who would like to pull it, compile the Windows portion and run it? It can run in demo mode even without the microcontroller.

 

It is still in very actiive development, so it will likely still change day to day but the basic implementation seems sound.  All it takes is a (free) Github account, a (free) version of the 2026 community edition of visual studio. I have also compiled it with (free) visual studio code.  To compile the firmware it needs a (free) Arduino IDE. The software is of course free.

 

edit: come to think of it, no github account needed if I make the repository public...

 

Oh and BTW, the frontend runs perfectly under linux/wine (not completely tested but everything I have tried worked)


Tim Krantz 





Electric Dreams Online Shop