A bridge service for AMB/MyLaps transponder timing systems. It connects to an AMB decoder over TCP, decodes the proprietary binary protocol, and forwards lap-passing events to a remote HTTP endpoint. Connected clients can subscribe to a live event stream via a simple TCP command interface, and a built-in web page shows the most recent lap times.
Runs on Linux (Raspberry Pi, tested on Pi 3B / aarch64 Debian) and Windows. Primary deployment target is a self-contained .NET 9 binary — no runtime installation required on the Pi.
the service is running in our club for over 10 years now without any problems but I lost the source code, so this repository contains a decompiled project from the latest running program.
I upgraded the project to .NET 9, fixed one bug and added this README with the help of claude code, otherwise it's just a ~12 year old source without any guarantees ;)
To run the program with audio lap time output you need additional audio files in the Media_[lang]/ folder. You need wav files from 0 to 99 and comma.wav. You can see in Talk.cs how the audio files are loaded.
# development build
dotnet build AMB-Listener.sln
# self-contained publish for Raspberry Pi (arm64)
dotnet publish AMB-Listener/AMB-Listener.csproj \
-r linux-arm64 --self-contained true -c Release -o ./publishCopy the contents of ./publish to the Pi, along with the Media_*/ audio directories (see below).
Configuration is read from AMB-Listener.dll.config (generated from App.config during build) in the same directory as the executable.
| Key | Required | Default | Description |
|---|---|---|---|
AMB_IP |
yes | — | IP address of the AMB decoder |
AMB_PORT |
yes | 5403 | TCP port of the AMB decoder |
REMOTE_HTTP_URL |
yes | — | URL to POST lap JSON to |
CMD_PORT |
no | 3456 | TCP port for the command server |
HTTPD_PORT |
no | 8181 | Port for the status web page |
MIN_LAP_TIME |
no | 20 | Minimum valid lap time in seconds |
MAX_LAP_TIME |
no | 90 | Maximum valid lap time in seconds |
TALK_ENABLE |
no | true | Enable audio readout of lap times |
TALK_LANGUAGE |
no | de | Audio language: de, en, or jp |
SND_IF |
no | PCM | ALSA mixer interface name for volume control |
VOLUME |
no | 80 | Initial volume (0–100) |
SESSION_SAVE_FILE |
no | — | Path for session persistence file |
VERBOSE |
no | false | Enable verbose console logging |
Lap times are read aloud using WAV files. Place audio directories alongside the executable:
Media_de/ 0.wav, 1.wav, ... 99.wav, Komma.wav
Media_en/
On Linux, playback uses /usr/bin/aplay (standard on Raspberry Pi OS). Volume is controlled via /usr/bin/amixer. On Windows, System.Media.SoundPlayer is used instead.
Connect with any TCP client (e.g. telnet, nc):
| Command | Alias | Description |
|---|---|---|
/listen |
/l |
Subscribe this connection to live lap events |
/stoplisten |
/sl |
Unsubscribe from lap events |
/volume <0-100> |
/vol |
Set playback volume |
/talk <seconds> |
/t |
Speak a lap time immediately |
/enabletalk |
/et |
Enable audio readout |
/disabletalk |
/dt |
Disable audio readout |
/language <de|en> |
/lng |
Change audio language |
/ambip |
Show the configured AMB IP address | |
/testupload |
/tu |
Send a test lap to the remote HTTP server |
/verbose |
/v |
Toggle verbose logging |
/closeapplication |
Shut down the service | |
/exit |
/e |
Close the TCP connection |
/help |
/h |
List all commands |
Lap events delivered to subscribed clients are JSON, one per line.
The application is split into four projects:
| Project | Role |
|---|---|
| AMB-Listener | Main executable. Wires up all components, reads config, handles the command protocol, uploads laps via HTTP. |
| AMB-Receiver | TCP connection to the AMB device. Parses the binary frame format (start/end markers 0x8E/0x8F, CRC16 validation, escape sequences) and emits typed records. |
| AMB-Server | Async TCP server (APM pattern) for the command interface. Manages multiple client connections. |
| AMB-WebServer | Minimal HttpListener-based web server that serves a live lap summary page. |
Data flow:
AMBConnectorreads raw bytes from the AMB device and enqueues byte frames intoOriginalMessageQueue.AMBQueueManagerruns a 10 ms poll loop: dequeues frames, validates CRC, parses them intoAMBRecordPASSING/AMBRecordSTATUSrecords.- Valid passing records are matched in pairs per transponder to calculate the lap time, then filtered against the configured min/max bounds.
- Accepted laps are posted as JSON to
REMOTE_HTTP_URL, broadcast to subscribed TCP clients, and read aloud. - A 25-second watchdog timer triggers reconnection if no STATUS heartbeat is received from the AMB device.
Lap JSON is serialized with System.Text.Json from the public properties of AMBRecordPASSING (transponder ID, lap time in ms, UTC time, signal strength, voltage, temperature, session label, etc.).