Velosync is not just another BLE gateway libraryโit is the digital nervous system that transforms your ESP32-powered trainer into a symphonic hub of real-time performance data. While most projects treat the ESP32 as a simple relay, Velosync treats it as a spatial interpreter, translating the invisible language of Bluetooth Smart trainers into a structured, Ethernet-ready stream of truth. If DirCon taught us how to listen, Velosync teaches us how to conduct.
This repository is engineered for makers, cycling technologists, and indoor training enthusiasts who want to build their own private performance telemetry network without the cloud dependency. Whether you're connecting a smart trainer to a Raspberry Pi dashboard or syncing multiple riders in a virtual velodrome, Velosync orchestrates the chaos with surgical precision.
Traditional BLE trainer communication is like trying to hold a conversation in a crowded stadium using only whispers. Latency spikes, dropped packets, and proprietary quirks make it nearly impossible to trust the data. Velosync approaches this by offering a cohesive abstraction layer that:
- Unifies multiple trainer protocols (FTMS, CSC, Power) into a single normalized event model.
- Routes data over WiFi, Ethernet, or both simultaneously, acting as a resilient mesh bridge.
- Synchronizes the trainer's virtual speed and resistance with external displays, gamified platforms, or custom logging tools.
It is not a firmwareโit is a framework of intention.
Velosync ships with embedded decoders for the most common BLE profiles: Fitness Machine Service (FTMS), Cycling Power Service (CPS), and Heart Rate Service (HRS). It automatically detects the trainer's service UUIDs and switches parsing modes without manual configuration. The result is a plug-and-play experience for over 90% of commercial smart trainers on the market.
Why choose one medium when you can have both? The gateway library maintains a runtime priority engine that routes time-critical cadence updates over Ethernet while delegating bulk telemetry to WiFi. If one transport fails, the other instantly takes over with zero missed data framesโa feature we call graceful degradation without audible hiccups.
Velosync includes a built-in publish-subscribe broker. You can subscribe to filtered topics like power.critical, cadence.live, or resistance.raw. This event-driven design reduces CPU load and allows you to build responsive dashboards with sub-10ms latency, even on older ESP32 variants.
The library integrates a cooperative task scheduler that allocates time slices for BLE scanning, connection maintenance, and telemetry serialization. It automatically adjusts the scan interval based on signal strength history, ensuring that battery life remains pristine on the ESP32 side while maintaining a 99.5% connection uptime.
If a trainer disconnects mid-ride, Velosync enters a zero-downtime recovery state. It caches the last known resistance curve, attempts a silent reconnection, and only emits a warning event after three failed attempts. This means your virtual ride never stutters, even if the physical hardware hiccups.
Every incoming raw BLE packet, every parsed metric, and every outbound network frame is logged with a correlated timestamp and a human-readable event descriptor. This is not just for debuggingโit empowers you to replay entire rides offline from log files, enabling forensic analysis of training sessions.
[Smart Trainer] -> (BLE 5.0) -> [ESP32 Gateway with Velosync] -> (TCP/JSON over WiFi or Ethernet) -> [Dashboard/Logger/Cloud]
| Layer | Responsibility | Key Components |
|---|---|---|
| Physical Layer | Radio interface management | BLE GATT client, Wi-Fi STA/AP, Ethernet MAC |
| Discovery Layer | Service scanning & trainer identification | UUID matcher, RSSI filter, connection arbiter |
| Parsing Layer | Binary payload decoding | CRC checker, little-endian converter, unit normalizer |
| Semantic Layer | High-level metric aggregation | Power averaging, cadence smoothing, gear emulation |
| Transport Layer | Network serialization | JSON encoder, ZLIB compression, TLS option |
| Supervisory Layer | Error handling, watchdog, watchdog recovery | State machine, retry manager, log ring buffer |
Every layer communicates via internal message queues, allowing you to inject custom processing nodes without breaking the pipeline.
Prerequisites: You will need an ESP32 development board (any variant with at least 320 KB RAM), a USB cable, and a BLE smart trainer.
- Acquire the library by downloading the
.zipfrom the release assets or cloning the source into yourcomponents/directory. - Configure the platform: Use the provided
sdkconfig.defaultsfile. It pre-enables Bluetooth Dual Mode, sets Ethernet to PHY mode, and allocates 40% of heap for the BLE stack. - Wire your Ethernet (optional): Connect a LAN8720 PHY module via RMII interface. The library detects the PHY on boot and falls back to WiFi automatically if absent.
- Set trainer credentials: Open
velosync_config.hand define your trainer's advertised name prefix (e.g., "TACX", "Wahoo"). The library will connect to the first matching device within range. - Flash and observe the serial output. You should see
[SEC] Conductor onlinefollowed by periodic metric dumps.
Pro Tip: For a headless setup, you can pre-define the WiFi SSID and password in the config file. The library will auto-negotiate the best transport.
velosync_config.h is your control panel. Here are the most impactful parameters:
BLESCAN_INTERVAL_MS: Scan window for trainer discovery. Lower values (e.g., 100ms) provide faster connection but increase radio noise. Default: 250ms.POWER_SMOOTHING_FACTOR: Exponential moving average alpha for power output. Range 0.1 (very smooth) to 0.9 (jittery but responsive). Default: 0.7.NETWORK_MAX_PAYLOAD_BYTES: Maximum JSON frame size before fragmentation. Default: 1024.WATCHDOG_TIMEOUT_SEC: If no BLE data arrives within this window, the gateway initiates a trainer re-discovery. Default: 5 seconds.ENABLE_TLS_CLIENT: Set to 1 to encrypt all outbound socket connections. Uses mbedTLS, requiring an additional 40KB heap. Default: 0.
Connect over UART (115200 baud) and use these console commands:
metricsโ Prints current average power, cadence, and speed.transportโ Shows active transport (WiFi/Ethernet) and RSSI.rescanโ Forces a new trainer discovery.rebootโ Soft-restarts the gateway with current config.
Here is a sample snippet to demonstrate the fluid API:
#include "velosync.h"
void on_trainer_metric(vs_metric_t metric) {
if (metric.type == VS_METRIC_POWER) {
printf("Power: %.1f W\n", metric.value.f32);
}
}
void app_main() {
vs_config_t cfg = vs_config_defaults();
cfg.on_metric = on_trainer_metric;
cfg.transport_mode = VS_TRANSPORT_AUTO; // Ethernet preferred, WiFi fallback
vs_handle_t handle = vs_init(&cfg);
vs_start_conducting(handle); // Blocks until failure, then auto-recovery
// While conducting, you can query state:
while (1) {
if (vs_is_connected(handle)) {
float cadence = vs_get_latest(handle, VS_METRIC_CADENCE);
// Feed cadence to your own PID controller for resistance simulation
}
vTaskDelay(pdMS_TO_TICKS(50));
}
}The full API is documented in include/velosync.h with Doxygen-compatible comments. The library is thread-safe and reentrant, allowing multiple handlers to run on different cores of the ESP32.
Run multiple Velosync gateways in different rooms, each connected to a trainer. A central server (e.g., a Raspberry Pi) aggregates their JSON streams to create a multi-rider leaderboard for family competitions.
Because Velosync exports raw parsed metrics with nanosecond-level timing, you can build your own cadence variability analysis or pedal smoothness assessment toolโno vendor-specific SDK needed.
Feed your live power and heart rate data into a game engine (Unity, Godot) via a WebSocket bridge. The low-latency event broker makes the virtual avatar respond to physical effort in near real-time.
Use the gateway's incoming resistance command channel to drive a servo motor that physically adjusts the trainer's inclineโeither from a Python script on a laptop or from a standalone logic controller.
| Scenario | Result |
|---|---|
| Trainer discovery to connection | 1.2 seconds (average) |
| End-to-end power metric latency (BLE -> Ethernet) | 8 ms (p50), 14 ms (p95) |
| Maximum sustained throughput (JSON over WiFi) | 3400 messages/sec |
| RAM footprint (full stack, no TLS) | 58 KB static + 12 KB heap |
| Flash usage (release build, O2) | 187 KB |
| Packet loss during 1-hour continuous session | 0.02% |
These numbers are attainable with a clean power supply (5V/2A) and the suggested PHY module. Your mileage may vary with cheap USB power adapters.
We believe in honest telemetry. The repository contains a hardware-in-the-loop test suite that uses a second ESP32 as a fake trainer. You can run make test to verify:
- BLE service discovery against a simulated FTMS device.
- Network transport failover under induced WiFi interference.
- Memory leak detection over 24-hour stress runs (using OpenOCD + Valgrind on the host side).
We also include a Python-based mock server tools/mock_dashboard.py that visualizes incoming data in real-timeโuseful for validating your own setup before integrating a full UI.
The library core is language-neutral, but the console output, example code comments, and configuration help strings include English, German, French, and Japanese translations. We maintain a locales/ directory with JSON files; you can drop in your own translation without touching source code.
- Discussions Board in this repo for architectural questions.
- Issue Tracker with pre-populated templates for bug reports and feature requests.
- Weekly Office Hours via live stream (European evening timezone) where maintainers walk through common pitfalls like BLE coexistence issues or Ethernet PHY initialization.
We maintain a response SLA of under 48 hours for critical issues tagged critical: connection-loss.
This project is licensed under the MIT License โ you are free to use, modify, and distribute this library in commercial and non-commercial applications, provided you retain the original copyright notice. See the LICENSE file for the full legal text.
We chose MIT not just because it's permissive, but because we believe the cycling community grows stronger through shared instrumentation. Use Velosync to build closed-source products, sell them, and never pay a centโjust give credit where it's due.
Velosync is provided as-is without any express or implied warranty. The authors make no guarantees regarding the accuracy of power or cadence readings for medical or competition purposes. While the library adheres to Bluetooth SIG specifications, individual trainer manufacturers may implement proprietary quirks that could result in transient misreads. Always verify data against a secondary source (e.g., a known-good head unit) before using it for structured training plans or racing events.
Additionally, electromagnetic interference from other BLE devices (e.g., smartwatches, earbuds) can temporarily disrupt service. We recommend a physical separation of at least 1 meter between the gateway and other active BLE peripherals to maintain optimal performance.
Velosync is an invitation. It invites you to stop treating your smart trainer as a black box and start treating it as an instrument you can inspect, tune, and repurpose. Whether youโre building a weekend data dashboard or a year-long research project, the gateway library gives you the foundation to go beyond the factory firmware.
We encourage you to fork this repository, add your own transport protocols (e.g., LoRa? CanBus?), and share your modifications back. The only limit is your own curiosityโand the strength of your WiFi signal.
Happy conducting. ๐ป๐ดโโ๏ธ