ESP32 DirCon gateway library for BLE smart trainers over WiFi or Ethernet.
DirCon is an ESP32-based BLE-over-TCP gateway library for indoor bike trainers compatible with applications such as Zwift.
The project implements the Wahoo Direct Connect (_wahoo-fitness-tnp._tcp) concept by bridging BLE GATT trainer services to TCP clients over WiFi or Ethernet. It supports both proxying a real BLE smart trainer and simulating a virtual trainer entirely in software.
The library was developed primarily for indoor cycling applications where BLE reliability, radio interference, trainer accessibility, or protocol experimentation motivate the use of a network-based transport layer instead of a direct BLE connection.
DirCon is built around a layered and extensible architecture consisting of:
- a generic BLE backend abstraction
- a DirCon protocol manager
- a TCP/mDNS transport backend
- interchangeable BLE backend implementations
Current implementations include:
-
BleClientBackendConnects as a BLE GATT client to a real trainer and exposes it over DirCon. -
BleServerBackendSimulates a virtual trainer entirely in software for testing and experimentation.
The project supports:
- Cycling Power Service (CPS)
- Cycling Speed and Cadence Service (CSC)
- Fitness Machine Service (FTMS)
- Heart Rate Service (HRM)
- standard SIG UUIDs
- vendor-specific 128-bit BLE services
DirCon is designed specifically for ESP32-class devices using:
- Arduino-ESP32
- NimBLE-Arduino
- WiFi.h / ETH.h
- lwIP
- ESPmDNS
The implementation intentionally remains lightweight, polling-oriented, and Arduino-friendly while still supporting:
- automatic BLE reconnect
- network reconnect handling
- subscription restoration
- mDNS service advertisement
- WiFi and Ethernet transport
- FTMS Control Point procedures
- multi-client DirCon operation
The project has been tested with:
- Zwift
- ESP32 and ESP32-S3 hardware
- W5500 Ethernet adapters Waveshare ESP32-S3-ETH
- BLE trainer simulation backends
- real BLE indoor bike trainers
DirCon is intended for technically-oriented cyclists, embedded developers, protocol experimenters, and makers interested in BLE trainer interoperability and network-based trainer connectivity.
2. Features
- Connects as a BLE GATT client to a real indoor bike trainer
- Exposes the trainer through the Wahoo Direct Connect (
DirCon) TCP protocol - Allows applications such as Zwift to communicate with BLE trainers over WiFi or Ethernet
- Maintains a cached local representation of the remote BLE GATT database
-
Includes a fully software-based pseudo trainer backend
-
Simulates a BLE smart trainer without requiring a real BLE peripheral
-
Generates:
- Cycling Power (CPS)
- Cycling Speed & Cadence (CSC)
- Heart Rate (HRM)
- FTMS Indoor Bike Data
-
Supports FTMS Control Point procedures
-
Useful for:
- protocol testing
- Zwift interoperability testing
- development/debugging
- simulation environments
-
Discovers all services and characteristics after connection
-
Supports:
- standard SIG 16-bit UUIDs
- vendor-specific 128-bit UUIDs
-
Maintains canonical 128-bit UUID handling internally
-
Preserves service + characteristic identity internally for robustness
Current implementations support and/or simulate:
- Cycling Power Service (CPS)
- Cycling Speed and Cadence Service (CSC)
- Fitness Machine Service (FTMS)
- Heart Rate Service (HRM)
- Generic Access Service (GAS)
- Generic Attribute Service (GATT)
- Device Information Service (DIS)
Additional vendor-specific services are supported automatically through generic discovery.
- FTMS Control Point command handling
- FTMS indication/response procedures
- Indoor Bike Simulation support
- Target Power support
- Control ownership handling
- Compatible with Zwift FTMS workflows
Implements the Wahoo Direct Connect (wahoo-fitness-tnp) TCP protocol including:
- service discovery
- characteristic discovery
- characteristic read
- characteristic write
- notification enable/disable
- unsolicited notifications
- mDNS/DNS-SD advertisement
Supports both:
- native ESP32 WiFi
- native ESP32 Ethernet (
ETH.h)
Tested with:
- ESP32
- ESP32-S3
- W5500 Ethernet hardware Waveshare ESP32-S3-ETH
- Advertises DirCon service through mDNS
- Publishes BLE service UUID information
- Preserves vendor-specific 128-bit UUIDs
- Compatible with Zwift automatic discovery
- disconnect detection
- reconnect scheduling
- fast reconnect optimization
- fallback scanning
- subscription restoration
- network loss detection
- TCP service restart
- mDNS restart
- recovery after temporary Ethernet/WiFi interruptions
- Supports multiple simultaneous DirCon TCP clients
- Maintains per-client subscription state
- Avoids redundant BLE subscribe/unsubscribe operations
- polling-oriented Arduino-style design
- no RTOS required
- no asynchronous TCP framework required
- lightweight memory usage
- suitable for ESP32-class devices
The BLE layer is abstracted through BleBackend.
Custom backends can be implemented for:
- alternative trainer simulators
- custom BLE devices
- ANT+ bridges
- serial/network bridges
- protocol experimentation
The project has been tested successfully with:
- Zwift
- ESP32
- ESP32-S3
- W5500 Ethernet
- WiFi transport
- Ethernet transport
- real BLE trainers
- pseudo trainer backends
- FTMS/CPS/CSC/HRM simulation
3. Project Motivation
Modern indoor cycling platforms such as Zwift depend heavily on BLE connectivity to communicate with smart trainers. While BLE works well in many situations, practical real-world usage exposes several limitations that motivated the creation of this project.
Wahoo introduced Direct Connect (DirCon) as a way to transport BLE trainer communication over a stable TCP/IP network connection instead of relying solely on direct BLE radio communication.
The idea is simple:
- the trainer remains BLE-based internally
- the application communicates over TCP/IP
- an intermediate gateway bridges both worlds
This approach can significantly improve robustness and flexibility in challenging environments.
Indoor cycling setups are often electrically and radio-frequency noisy environments.
Common problems include:
- BLE interference from nearby devices
- unstable BLE connections
- trainer dropouts during rides
- crowded 2.4 GHz environments
- long distance between trainer and PC/tablet
- weak Bluetooth adapters
- operating system BLE instability
Particularly during long rides or races, temporary BLE interruptions can become extremely frustrating.
Using Ethernet or stable WiFi networking for the trainer connection can greatly improve reliability.
The ESP32 platform provides a unique combination of features:
- BLE support
- WiFi support
- Ethernet support
- low cost (See for example in Europe: Tinytronics)
- low power usage
- embedded flexibility
This makes the ESP32 an ideal platform for implementing a compact BLE-over-TCP gateway.
The project started as an experiment to understand:
- how Wahoo DirCon works
- how Zwift communicates with trainers
- how BLE trainer services are used in practice
- how robust trainer bridging could be implemented
Over time, the implementation evolved into a modular and extensible architecture supporting both:
- real BLE trainer proxying
- fully simulated virtual trainers
During development it became clear that the architecture could support much more than merely forwarding BLE packets.
The project evolved toward a layered architecture capable of:
- generic BLE service discovery
- vendor-specific service handling
- protocol experimentation
- trainer simulation
- FTMS testing
- reconnect robustness
- interchangeable BLE backends
The abstraction layers now allow completely different backend implementations to coexist behind the same DirCon protocol layer.
One of the earliest development challenges was testing DirCon behavior without depending on real trainer hardware.
This led to the development of BleServerBackend, a pseudo BLE trainer implementation that:
- simulates CPS/CSC/FTMS/HRM behavior
- generates realistic telemetry
- handles FTMS control procedures
- interoperates with Zwift
This proved extremely valuable for:
- protocol reverse engineering
- testing reconnect logic
- validating subscription handling
- experimenting with FTMS behavior
- debugging Zwift interoperability
The virtual backend eventually became a first-class architectural citizen alongside the real BLE client backend.
Several principles guided the architecture:
BLE logic, TCP networking, and DirCon protocol handling should remain independent.
New BLE backends should be easy to implement without modifying the core protocol layer.
The implementation should remain lightweight and Arduino-friendly.
Temporary BLE or network interruptions should recover automatically where possible.
The gateway should support:
- standard SIG services
- vendor-specific 128-bit services
without hardcoding trainer-specific assumptions.
DirCon is intended for:
- technically-oriented cyclists
- embedded developers
- ESP32 enthusiasts
- protocol experimenters
- trainer reverse engineers
- makers building custom trainer infrastructure
The project intentionally emphasizes:
- transparency
- architectural clarity
- protocol understanding
- practical interoperability
rather than hiding implementation details behind heavy abstractions.
4. Architecture Overview
DirCon is built as a layered and modular architecture that cleanly separates:
- BLE functionality
- DirCon protocol handling
- TCP transport handling
- network initialization
This separation allows the project to support multiple BLE backend implementations while keeping the DirCon protocol layer independent from BLE-specific details.
[ Remote BLE Trainer ]
^
|
BleClientBackend
^
|
DirConBleAdapter
^
|
DirConManager
^
|
EspTcpBackend
^
|
WiFi / Ethernet / lwIP
^
|
DirCon TCP Clients
(Zwift, etc.)
Alternative backend configuration:
BleServerBackend
^
|
DirConBleAdapter
^
|
DirConManager
^
|
EspTcpBackend
^
|
Zwift
In this configuration no real BLE trainer exists. The backend simulates a virtual trainer entirely in software.
BleBackend defines the abstract BLE/backend interface used by the architecture.
It isolates the DirCon protocol layer from backend-specific BLE implementation details.
- connects to a real BLE trainer
- performs GATT discovery
- proxies BLE operations
- forwards notifications
- simulates a virtual trainer
- generates telemetry internally
- implements FTMS/CPS/CSC/HRM behavior
- expose services and characteristics
- handle reads and writes
- manage subscriptions
- generate notifications
- maintain backend state
- notify observers of backend events
- DirCon protocol parsing
- TCP networking
- mDNS publication
Bridge layer between the BLE backend and the DirCon protocol layer.
- translate DirCon requests into backend operations
- resolve characteristic UUID → service UUID
- validate characteristics
- forward notifications upward
- forward backend connect/disconnect events
The adapter absorbs an important protocol mismatch:
- DirCon operations usually contain only a characteristic UUID
- internally the backend architecture uses:
(service UUID + characteristic UUID)
for robust identity handling.
Core DirCon protocol implementation and TCP client manager.
- parse and encode DirCon messages
- process protocol requests
- manage TCP clients
- maintain subscription state
- forward notifications
- coordinate reconnect handling
- publish mDNS services
- discover services
- discover characteristics
- read characteristic
- write characteristic
- enable/disable notifications
- unsolicited notifications
DirConManager intentionally contains:
- no direct BLE logic
- no NimBLE usage
- no hardware/network initialization
TCP/mDNS transport backend for ESP32.
- TCP server management
- client connection handling
- polling-based socket access
- mDNS/DNS-SD publication
- runtime network status monitoring
Built on:
- WiFi.h
- ETH.h
- lwIP
- ESPmDNS
- WiFi
- Ethernet
Both transports share the same underlying ESP32/lwIP TCP/IP stack.
One-time network initialization helper.
- initialize WiFi or Ethernet hardware
- configure SPI/PHY/chip settings
- start DHCP/static networking
- configure hostnames
- wait for network availability
NetworkBootstrap performs:
startup initialization
while EspTcpBackend performs:
runtime transport management
BLE notifications propagate upward through an observer chain:
BleBackend
->
DirConBleAdapter
->
DirConManager
->
TCP Clients
This keeps BLE/backend logic decoupled from the DirCon protocol layer.
One of the central design goals is interchangeable backend implementations.
Because DirConManager communicates only through:
BleBackendnew backends can be introduced without modifying the DirCon protocol layer.
Potential future backends could include:
- ANT+ bridges
- serial trainer bridges
- custom telemetry generators
- testing/mocking backends
- hybrid trainer implementations
The architecture intentionally remains lightweight and Arduino-friendly.
The main application loop typically looks like:
void loop()
{
bleBackend.update();
dirconManager.update();
}No RTOS or asynchronous TCP framework is required.
BLE notifications themselves still arrive asynchronously through NimBLE callbacks, but the higher-level architecture remains polling-oriented.
Internally, UUIDs are canonicalized into 128-bit string form:
uuid.to128().toString()This avoids inconsistencies between:
- 16-bit SIG UUID formatting
- full 128-bit UUID formatting
The architecture intentionally preserves:
(service UUID + characteristic UUID)
as the internal identity model to avoid ambiguity when characteristic UUIDs are reused under multiple services.
The architecture treats temporary interruptions as recoverable events.
Supported recovery behavior includes:
- disconnect detection
- reconnect scheduling
- fast reconnect attempts
- subscription restoration
- link-loss detection
- TCP service restart
- mDNS restart
- client reconnection handling
This allows rides to continue across short communication interruptions with minimal disruption.
5. Core Concepts
This section explains several important architectural and protocol concepts used throughout the DirCon project.
Understanding these concepts helps clarify many of the implementation decisions in the codebase.
DirCon is fundamentally a BLE-over-TCP protocol.
A DirCon TCP client such as Zwift communicates with the DirCon gateway as if it were interacting with a BLE trainer.
The gateway translates these TCP protocol messages into BLE/backend operations.
An important DirCon design characteristic is that protocol messages generally contain only a single UUID field.
The meaning of that UUID depends on the message context.
Examples:
DIRCON_MSGID_DISCOVER_SERVICES
The UUID represents a service UUID context.
READ_CHARACTERISTIC
WRITE_CHARACTERISTIC
ENABLE_CHARACTERISTIC_NOTIFICATIONS
The UUID represents a characteristic UUID.
DirCon itself assumes characteristic UUID uniqueness and does not generally carry an explicit service UUID for characteristic operations.
This is an important protocol constraint that strongly influenced the architecture.
Although DirCon uses mostly characteristic-only identity, the internal backend architecture intentionally preserves:
(service UUID + characteristic UUID)
as the true internal identity.
This avoids ambiguity when the same characteristic UUID appears under multiple services.
This design is especially important for:
- vendor-specific services
- custom trainer protocols
- future extensibility
The adapter layer resolves the protocol simplification.
BLE UUID handling contains an important practical complication.
The same UUID may appear in multiple textual forms:
0x1818
00001818-0000-1000-8000-00805f9b34fb
NimBLE frequently uses compact 16-bit formatting internally, while DirCon commonly transports 128-bit UUIDs.
Without normalization, lookups can fail unexpectedly.
Internally, UUIDs are canonicalized using:
uuid.to128().toString()This guarantees consistent lookup behavior across:
- standard SIG UUIDs
- vendor-specific UUIDs
- NimBLE formatting variations
- DirCon formatting
This became one of the key stability improvements in the project.
The architecture intentionally separates:
BLE/backend behavior
from:
DirCon protocol handling
through the abstract:
BleBackendinterface.
This allows multiple interchangeable backend implementations.
Acts as a BLE GATT client connected to a real trainer.
Responsibilities include:
- scanning
- connecting
- discovery
- reads/writes
- subscription handling
- reconnect logic
Acts as a virtual trainer simulator.
Responsibilities include:
- telemetry generation
- FTMS simulation
- CPS/CSC/HRM simulation
- control point handling
No real BLE radio communication occurs.
DirConBleAdapter exists specifically to isolate protocol simplifications from backend robustness.
The adapter:
- receives characteristic-only DirCon requests
- resolves service UUID context
- forwards robust backend operations
This allows:
DirCon simplicity
and:
backend robustness
to coexist cleanly.
The BLE client backend intentionally performs full discovery immediately after connecting.
Discovery sequence:
scan
->
connect
->
discover services
->
discover characteristics
->
cache everything
After discovery completes, DirCon operations work entirely against the cached database.
This means:
- DirCon timing is decoupled from BLE discovery timing
- lookups become fast and deterministic
- reconnect recovery becomes easier
The project maintains two distinct subscription layers.
Managed by the backend.
Represents actual BLE subscription state toward the trainer/backend.
Example:
gc.isSubscribedManaged by DirConManager.
Represents which TCP clients subscribed to which characteristics.
These are intentionally separate concepts.
When multiple DirCon clients subscribe to the same characteristic:
multiple DirCon subscriptions
->
single BLE subscription
The backend subscribes only once.
Similarly:
- BLE unsubscribe occurs only when the final DirCon subscriber disappears
This prevents redundant BLE operations.
Some protocol flows require notifications or indications even when the DirCon client did not explicitly subscribe.
Most important example:
FTMS Control Point responses
Zwift expects FTMS indication responses after control writes even though it may not subscribe through DirCon.
To support this, notifications may bypass normal DirCon subscription filtering using:
requireDirConSubscription == falseThis became an important interoperability concept.
The project intentionally avoids heavy asynchronous frameworks.
Main execution model:
void loop()
{
bleBackend.update();
dirconManager.update();
}Advantages:
- Arduino simplicity
- deterministic behavior
- easier debugging
- lower complexity
- lightweight memory usage
BLE notifications themselves still arrive asynchronously through NimBLE callbacks.
Temporary interruptions are treated as recoverable events.
The architecture supports:
- disconnect detection
- reconnect scheduling
- fast reconnect
- fallback scanning
- subscription restoration
- link-loss detection
- TCP restart
- mDNS restart
- client reconnection
The goal is ride continuity across short interruptions.
WiFi and Ethernet both ultimately use the ESP32 lwIP TCP/IP stack.
The architecture therefore uses a unified transport backend:
EspTcpBackendwith runtime transport checks such as:
WiFi.status()
ETH.linkUp()This greatly simplified the networking architecture once native ETH.h support was adopted.
Several core principles guided the implementation:
BLE, protocol, and networking layers remain independent.
Keep the architecture lightweight and Arduino-friendly.
Recover gracefully from temporary failures.
Allow alternative backends without rewriting the protocol layer.
Keep protocol behavior and architectural decisions visible and understandable.
6. Repository Structure
The repository is organized to keep the core DirCon protocol library stable and reusable while allowing BLE backend implementations to remain flexible and application-specific.
The architecture intentionally separates:
- reusable library components
- example backend implementations
- hardware/network configuration
- experimental sketches
This makes it easier to:
- create custom BLE backends
- test alternative trainer implementations
- support multiple ESP32 hardware targets
- keep the core protocol layer independent from application logic
DirCon/
│
├── src/
│ ├── DirCon.h
│ │
│ ├── BleBackend.h
│ ├── BleBackendObserver.h
│ ├── GatewayCharacteristic.h
│ │
│ ├── DirConBleAdapter.h
│ ├── DirConManager.h
│ ├── DirConMessage.h
│ │
│ ├── NetworkBackend.h
│ ├── EspTcpBackend.h
│ ├── NetworkBootstrap.h
│ │
│ ├── configNetwork.h
│ ├── debug.h
│ └── ...
│
├── examples/
│ │
│ ├── DirConWithBleClientBackend/
│ │ ├── DirConWithBleClientBackend.ino
│ │ ├── BleClientBackend.h
│ │ └── BleClientBackend.cpp
│ │
│ ├── DirConWithBleServerBackend/
│ │ ├── DirConWithBleServerBackend.ino
│ │ ├── BleServerBackend.h
│ │ └── BleServerBackend.cpp
│ │
│ └── ...
│
├── docs/
│ ├── FTMS.md
│ ├── RECONNECT.md
│ ├── BLE_DISCOVERY.md
│ ├── BACKEND_DEVELOPMENT.md
│ └── ...
│
├── library.properties
├── LICENSE
└── README.md
Contains the reusable and stable core library components.
These files implement:
- DirCon protocol handling
- BLE abstraction layers
- TCP transport abstraction
- network startup helpers
- common utility structures
The src/ folder intentionally does NOT contain trainer-specific backend implementations.
Defines the abstract BLE backend interface.
All BLE implementations derive from this interface.
Observer interface used to forward:
- notifications
- indications
- backend connect/disconnect events
upward into the DirCon protocol layer.
Internal cached representation of a BLE characteristic including:
- UUIDs
- properties
- subscription state
- advertisement metadata
Bridge layer between:
- BLE backend implementations
- DirConManager
Handles service-resolution and notification forwarding.
Core DirCon protocol implementation.
Handles:
- TCP clients
- subscriptions
- DirCon message processing
- notification forwarding
- reconnect handling
DirCon protocol message representation and serializer/parser.
Abstract TCP transport interface.
ESP32 TCP/mDNS implementation using:
- WiFi.h
- ETH.h
- lwIP
- ESPmDNS
Supports both WiFi and Ethernet transport.
One-time startup helper responsible for:
- WiFi startup
- Ethernet startup
- DHCP/static IP acquisition
- hostname configuration
Central compile-time network configuration file.
Typical configuration includes:
- WiFi/Ethernet selection
- W5500 pin definitions
- hostname settings
- client limits
Contains complete working example applications.
This is intentionally where backend implementations live.
The rationale is that BLE backends are expected to be:
- modified
- customized
- replaced
- experimented with
by users.
Example BLE trainer proxy gateway.
Features:
- connects to a real BLE trainer
- exposes trainer over DirCon
- supports reconnect handling
- supports subscription restoration
Typical use case:
Real trainer
->
ESP32 gateway
->
Zwift over WiFi/Ethernet
Example pseudo trainer simulator.
Features:
- software-generated trainer telemetry
- FTMS simulation
- CPS/CSC/HRM simulation
- protocol experimentation
Typical use case:
ESP32 virtual trainer
->
Zwift
Optional extended technical documentation.
Recommended topics include:
- FTMS protocol notes
- reconnect behavior
- Network setup
- et cetera
This keeps the main README manageable while still preserving deep technical documentation.
The repository structure intentionally reflects the architectural layering.
The core library:
src/
is intended to remain relatively stable.
Backend implementations inside:
examples/
are expected to evolve and be customized.
This separation avoids over-constraining experimentation.
The library provides:
- the protocol framework
- transport framework
- architectural abstractions
while users provide:
- trainer behavior
- backend specialization
- telemetry generation
- custom integrations
The structure intentionally remains:
- Arduino-friendly
- transparent
- lightweight
- hackable
without introducing:
- excessive framework complexity
- heavy build systems
- hidden code generation
The goal is to keep the code understandable and modifiable for embedded developers and technically-oriented cyclists.
7. Supported Hardware
DirCon is designed primarily for ESP32-class microcontrollers with BLE and TCP/IP networking support.
The architecture has been tested with both:
- WiFi transport
- Ethernet transport
using native ESP32 networking stacks.
The original ESP32 platform is fully suitable for:
- BLE trainer proxying
- WiFi DirCon gateways
- pseudo trainer simulation
Typical use cases:
- BLE trainer → WiFi DirCon bridge
- virtual trainer simulation
- protocol experimentation
The ESP32-S3 is strongly recommended for more advanced configurations.
Advantages include:
- more memory
- improved USB support
- improved peripheral support
- excellent Ethernet integration possibilities
The ESP32-S3 has been used extensively during development and testing.
Supported through:
WiFi.h
Features:
- DHCP support
- mDNS/DNS-SD advertisement
- TCP server operation
- automatic reconnect handling
WiFi operation works well for:
- nearby Zwift clients
- simple installations
- portable setups
Supported through native ESP32:
ETH.h
and the ESP32 lwIP stack.
Ethernet support provides:
- highly stable trainer connectivity
- reduced radio interference
- lower latency
- improved ride robustness
This became one of the primary motivations for the project.
Tested successfully with W5500:
W5500 SPI Ethernet: a hardwired TCP/IP stack internet controller chip
Features tested:
- DHCP
- TCP server operation
- mDNS advertisement
- Zwift discovery
- reconnect handling
- temporary cable disconnect recovery
Extensively tested development platform Waveshare ESP32-S3-ETH.
Configuration:
- ESP32-S3
- W5500 Ethernet
- SPI-connected Ethernet controller
This board is currently one of the best-supported configurations within the project.
Many ESP32 Ethernet boards supported by Arduino-ESP32 can use:
ETH.begin();directly.
Custom boards may require explicit configuration of:
- PHY/chip type
- SPI pins
- chip select pin
- interrupt pin
- reset pin
Example:
SPI.begin(ETH_SPI_SCK, ETH_SPI_MISO, ETH_SPI_MOSI);
ETH.begin(
ETH_PHY_TYPE,
ETH_PHY_ADDR,
ETH_PHY_CS,
ETH_PHY_IRQ,
ETH_PHY_RST,
SPI);The architecture intentionally supports both approaches.
DirCon relies on:
NimBLE-Arduino
for BLE functionality.
The BLE hardware must support:
- BLE Central role
- GATT client operation
- notifications/indications
- stable BLE connections
ESP32 and ESP32-S3 platforms satisfy these requirements well.
The project has been tested successfully with:
- Arduino-ESP32
- NimBLE-Arduino
- WiFi.h
- ETH.h
- ESPmDNS
The primary interoperability target.
Successfully tested features include:
- service discovery
- characteristic discovery
- CPS
- CSC
- HRM
- FTMS
- FTMS Control Point
- Indoor Bike Simulation
- reconnect behavior
- Ethernet transport
- WiFi transport
The BLE client backend targets indoor bike trainers advertising:
Cycling Power Service (0x1818)
After connection, the implementation performs generic discovery and supports additional services automatically.
Typical trainer services may include:
- CPS
- CSC
- FTMS
- HRM
- DIS
- GATT
- GAS
- vendor-specific 128-bit services
The architecture is lightweight, but BLE discovery caching and multiple TCP clients still consume memory.
Recommended platforms:
| Platform | Recommendation |
|---|---|
| ESP32 | Good |
| ESP32-S3 | Excellent |
| Ethernet-enabled ESP32-S3 | Recommended |
ESP32-S3 boards are particularly well suited for:
- Ethernet operation
- large BLE databases
- future protocol expansion
The following are currently untested or unsupported:
- ESP8266
- non-ESP32 Arduino platforms
- non-lwIP TCP stacks
- Classic Bluetooth-only devices
- non-NimBLE BLE stacks
The architecture currently assumes:
- ESP32 networking APIs
- ESP32 lwIP integration
- NimBLE-Arduino compatibility
The project intentionally favors:
- inexpensive hardware
- open embedded platforms
- easily obtainable components
- transparent networking stacks
rather than proprietary trainer bridge hardware.
The goal is to provide a flexible and understandable platform for BLE trainer interoperability experimentation and robust indoor cycling connectivity.
8. Software Dependencies
DirCon is built on top of the ESP32 Arduino ecosystem and intentionally keeps external dependencies lightweight, transparent, and ESP32-native.
The project primarily depends on:
- Arduino-ESP32
- NimBLE-Arduino
- native ESP32 networking libraries
- ESP32 lwIP networking integration
The architecture intentionally avoids:
- heavy asynchronous frameworks
- RTOS-dependent application design
- external TCP frameworks
- complex middleware layers
This keeps the project:
- Arduino-friendly
- lightweight
- easy to debug
- transparent for experimentation
Recommended:
- Arduino IDE 2.x
The project is developed and tested primarily within the standard Arduino environment.
Provided by Espressif Systems.
This is the most important platform dependency and provides:
- ESP32 hardware abstraction
- WiFi support
- Ethernet support
- SPI support
- lwIP networking stack
- FreeRTOS integration
- ESPmDNS support
- event handling infrastructure
DirCon depends heavily on the native ESP32 networking architecture.
Maintained by h2zero.
Provides all BLE functionality including:
- BLE central/client support
- GATT discovery
- notifications
- indications
- BLE reconnect handling
- UUID handling
DirCon uses NimBLE-Arduino for both:
BleClientBackend- BLE-related parts of
BleServerBackend
The following libraries are included automatically through Arduino-ESP32.
Used for:
- WiFi station mode
- DHCP
- TCP/IP connectivity
- network status monitoring
Example:
#include <WiFi.h>Used for:
- native ESP32 Ethernet support
- W5500 support
- Ethernet link monitoring
- unified lwIP integration
Example:
#include <ETH.h>The project intentionally uses native:
ETH.h
instead of generic Arduino Ethernet libraries.
This proved essential for:
- proper mDNS support
- stable ESP32 networking
- unified lwIP integration
Used for automatic DirCon discovery through:
_wahoo-fitness-tnp._tcp
service advertisement.
Example:
#include <ESPmDNS.h>This allows Zwift to discover the DirCon server automatically on the local network.
Although not directly included manually, the project also relies heavily on internal ESP32 components.
The ESP32 TCP/IP stack.
Both:
- WiFi
- Ethernet
ultimately use the same underlying:
lwIP netif
architecture.
This realization eventually led to the unified:
EspTcpBackendtransport implementation.
ESP32 Arduino internally uses FreeRTOS.
DirCon itself intentionally remains:
polling-oriented
and does not explicitly create RTOS tasks.
Example:
depends=NimBLE-Arduino
WiFi and Ethernet libraries do not need explicit installation because they are already part of Arduino-ESP32.
| Component | Recommended |
|---|---|
| Arduino IDE | 2.x |
| Arduino-ESP32 | recent stable |
| NimBLE-Arduino | recent stable |
DirCon is intentionally optimized for:
ESP32-class devices
Supported:
- ESP32
- ESP32-S3
Currently unsupported:
- ESP8266
- AVR Arduino boards
- STM32 Arduino cores
- Linux SBCs
- non-NimBLE BLE stacks
The original ESP32 BLE library was intentionally avoided in favor of NimBLE-Arduino because NimBLE offers:
- lower RAM usage
- improved BLE stability
- cleaner GATT client behavior
- better reconnect handling
- improved scalability
This became especially important for:
- BLE discovery caching
- FTMS indication handling
- reconnect robustness
- subscription restoration
The project originally experimented with:
Arduino Ethernet.h
using W5500 hardware.
Although basic TCP functionality worked, this approach eventually proved insufficient because:
- mDNS interoperability was problematic
- ESP32 network integration was incomplete
- lwIP integration was bypassed
The architecture later migrated fully to:
native ETH.h
which integrates correctly with:
- ESP32 networking
- ESPmDNS
- lwIP
- unified network state handling
This became a major architectural improvement.
The project intentionally favors:
- ESP32-native components
- lightweight dependencies
- transparent networking stacks
- well-supported open-source libraries
rather than introducing:
- large middleware frameworks
- heavy abstraction layers
- complex asynchronous runtimes
The goal is to keep the complete communication chain:
BLE
->
DirCon
->
TCP/IP
->
Zwift
understandable, inspectable, and modifiable for developers and technically-oriented cyclists.
9. Installation
This section explains how to install and build the DirCon library and example applications using the Arduino IDE and ESP32 platform.
DirCon is intended primarily for ESP32-class devices using:
- Arduino-ESP32
- NimBLE-Arduino
- WiFi.h / ETH.h
- ESPmDNS
Before installing DirCon, ensure the following software components are available.
Recommended:
- Arduino IDE 2.x
Older Arduino IDE versions may also work, but were not extensively tested.
Install the official Arduino-ESP32 package from Espressif.
https://espressif.github.io/arduino-esp32/package_esp32_index.json
Arduino IDE:
File
->
Preferences
->
Additional Boards Manager URLs
Add the URL above.
Then install:
Tools
->
Board
->
Boards Manager
->
esp32
Install the latest stable version from Espressif.
Install:
NimBLE-Arduino
through the Arduino Library Manager.
Sketch
->
Include Library
->
Manage Libraries
Search for:
NimBLE-Arduino
and install the latest stable version.
Clone the GitHub repository:
git clone https://github.com/Berg0162/DirCon.gitor download the ZIP archive directly from GitHub.
Place the repository into your Arduino libraries folder.
Typical locations:
Documents/Arduino/libraries/
~/Arduino/libraries/
~/Documents/Arduino/libraries/
Resulting structure:
Arduino/libraries/DirCon/
The repository contains example applications inside:
examples/
Typical examples:
DirConWithBleClientBackend/
DirConWithBleServerBackend/
Open the desired .ino sketch from the Arduino IDE.
Arduino IDE:
Tools
->
Board
Select your target board.
Examples:
- ESP32 Dev Module
- ESP32-S3 Dev Module
- Waveshare ESP32-S3-ETH See Settings
Copy configNetwork.example.h to configNetwork.h and fill in your local settings.
Edit:
configNetwork.h
Enable WiFi mode:
#define DIRCON_USE_WIFIConfigure credentials:
#define WIFI_SSID "your-wifi-ssid"
#define WIFI_PASSWORD "your-password"Enable Ethernet mode:
#define DIRCON_USE_ETHERNETFor supported Arduino ESP32 Ethernet boards:
ETH.begin();may be sufficient.
Custom Ethernet boards may require explicit SPI and PHY/chip configuration.
Example:
#define ETH_PHY_TYPE ETH_PHY_W5500
#define ETH_PHY_CS 14
#define ETH_PHY_IRQ 10
#define ETH_PHY_RST 9
#define ETH_SPI_SCK 13
#define ETH_SPI_MISO 12
#define ETH_SPI_MOSI 11Connect the ESP32 board through USB.
Then:
Sketch
->
Upload
or use the Upload button.
After upload, open:
Tools
->
Serial Monitor
Recommended baud rate:
115200
Typical startup sequence:
Setup & Run BLE Server and DirCon Network!
Starting DirCon TCP service
DirCon service running on port 36866
For BLE client mode:
BleClientBackend: Scanning for CPS trainer...
BleClientBackend: Connected
BleClientBackend: Ready
For Ethernet:
ETH MAC: xx:xx:xx:xx:xx:xx
IPv4: 192.168.x.x
Zwift should automatically discover the DirCon server through:
_wahoo-fitness-tnp._tcp
mDNS advertisement.
Typical advertised hostname:
DirCon-Server.local
Expected behavior:
Real Trainer
->
ESP32 DirCon Gateway
->
Zwift
Zwift should:
- discover the DirCon server
- connect through TCP
- subscribe to characteristics
- receive telemetry
- control the trainer
Expected behavior:
ESP32 Virtual Trainer
->
Zwift
Zwift should recognize the virtual trainer and receive:
- power data
- cadence data
- heart rate data
- FTMS responses
Verify:
- trainer is awake
- trainer advertises CPS (0x1818)
- BLE is enabled
Verify:
- ESP32 has a valid IP address
- mDNS started successfully
- WiFi/Ethernet network is operational
- PC/tablet and ESP32 are on the same subnet
Verify:
- W5500 SPI pins
- PHY/chip configuration
- Ethernet cable/link LEDs
- DHCP operation
Verify:
- NimBLE-Arduino installed
- ESP32 board package installed
- correct board selected
For first experiments:
| Use case | Recommended example |
|---|---|
| Real trainer bridge | DirConWithBleClientBackend |
| Protocol testing / simulation | DirConWithBleServerBackend |
| Ethernet experimentation | ESP32-S3 + W5500 |
| Simplest setup | WiFi backend |
The project intentionally keeps installation lightweight:
- no RTOS setup
- no external build system
- no code generation
- no cloud dependencies
Everything builds directly inside the standard Arduino-ESP32 ecosystem.
10. Quick Start
This section provides a minimal introduction to running DirCon in its two primary configurations:
- BLE trainer proxy gateway
- virtual trainer simulator
The goal is to get a working setup with Zwift as quickly as possible.
DirCon currently provides two main example backends:
| Backend | Purpose |
|---|---|
BleClientBackend |
Connect to a real BLE trainer and expose it over DirCon |
BleServerBackend |
Simulate a virtual trainer entirely in software |
Both examples use the same core architecture:
BLE Backend
->
DirConBleAdapter
->
DirConManager
->
EspTcpBackend
->
Zwift
This configuration bridges a real BLE trainer to Zwift over WiFi or Ethernet.
Real BLE Trainer
->
ESP32 DirCon Gateway
->
WiFi / Ethernet
->
Zwift
Open:
examples/DirConWithBleClientBackend/
Copy configNetwork.example.h to configNetwork.h and fill in your local settings.
Edit:
configNetwork.h
#define DIRCON_USE_WIFI
#define WIFI_SSID "your-wifi-ssid"
#define WIFI_PASSWORD "your-password"#define DIRCON_USE_ETHERNETConfigure W5500 pins if required.
Compile and upload the sketch.
Open the Serial Monitor at:
115200 baud
Ensure the trainer is awake and advertising:
Cycling Power Service (0x1818)
Typical startup sequence:
BleClientBackend: Scanning for CPS trainer...
BleClientBackend: Connected
BleClientBackend: Ready
DirCon TCP service started successfully
Zwift should automatically discover the DirCon server through mDNS.
Typical pairing flow:
Zwift
->
Pairing Screen
->
Power Source
->
DirCon-Server
After pairing:
- power data should appear
- cadence should update
- FTMS control should function
- riding should begin normally
This configuration simulates a trainer entirely in software.
No real trainer hardware is required.
ESP32 Virtual Trainer
->
WiFi / Ethernet
->
Zwift
Open:
examples/DirConWithBleServerBackend/
Copy configNetwork.example.h to configNetwork.h and fill in your local settings.
Edit:
configNetwork.h
Configure either:
- WiFi
- Ethernet
Compile and upload the sketch.
Typical startup output:
BleServerBackend: starting pseudo BLE server backend
DirCon TCP service started successfully
Zwift should discover the simulated trainer automatically.
Expected functionality:
- simulated power
- cadence
- heart rate
- FTMS communication
- virtual riding
The BLE client backend performs:
scan
->
connect
->
service discovery
->
characteristic discovery
->
cache creation
before becoming operational.
The backend also supports:
- reconnect handling
- fast reconnect optimization
- subscription restoration
The BLE server backend simulates:
- CPS
- CSC
- HRM
- FTMS
including:
- FTMS Control Point
- Indoor Bike Simulation
- Target Power
This backend is especially useful for:
- protocol development
- debugging
- Zwift interoperability testing
WiFi connected
IP address: 192.168.x.x
Starting DirCon TCP service
ETH MAC: xx:xx:xx:xx:xx:xx
IPv4: 192.168.x.x
Subscribe to Characteristic 0x2A63
Subscribe to Characteristic 0x2AD2
Subscribe to Characteristic 0x2A37
Request Control!
Set Indoor Bike Simulation Parameters!
For easiest success:
| Setup | Recommendation |
|---|---|
| Simplest hardware | ESP32 + WiFi |
| Most stable networking | ESP32-S3 + W5500 Ethernet |
| Simplest testing | BleServerBackend |
| Real trainer bridging | BleClientBackend |
Zwift discovers the server through:
_wahoo-fitness-tnp._tcp
mDNS advertisement.
The BLE client backend currently scans for trainers advertising:
Cycling Power Service (0x1818)
Some FTMS procedures intentionally bypass DirCon subscription filtering to match Zwift behavior expectations.
This is normal and intentional.
After basic operation succeeds, useful next experiments include:
- Ethernet operation
- reconnect testing
- FTMS simulation
- custom telemetry generation
- vendor-specific BLE services
- custom backend development
The architecture is intentionally designed to encourage experimentation and extension.
11. Example Backends
DirCon separates the stable protocol/networking library from concrete BLE backend implementations.
The core library defines the abstract interface:
BleBackendConcrete backend implementations can then provide different trainer behaviors without modifying:
DirConManagerDirConBleAdapterEspTcpBackendDirConMessage
This makes the examples an important part of the project: they demonstrate how to implement and plug in real backend behavior.
A backend is responsible for exposing BLE-like services and characteristics to the DirCon stack.
BleBackend
^
|
-------------------------
| |
BleClientBackend BleServerBackend
The rest of the DirCon architecture does not need to know whether the backend is:
- proxying a real BLE trainer
- simulating a virtual trainer
- generating test data
- implementing custom protocol behavior
BleClientBackend is the real BLE trainer proxy implementation.
It connects to an actual BLE indoor trainer and exposes that trainer over DirCon.
Real BLE Trainer
->
ESP32 running BleClientBackend
->
DirCon over WiFi/Ethernet
->
Zwift
BleClientBackend handles:
- BLE scanning
- trainer discovery
- BLE connection management
- GATT service discovery
- GATT characteristic discovery
- GATT cache creation
- read/write forwarding
- BLE subscribe/unsubscribe handling
- notification/indication forwarding
- BLE disconnect/reconnect recovery
- backend subscription restoration
The backend scans for trainers advertising:
Cycling Power Service (0x1818)
After connection, discovery becomes generic.
All available services and characteristics are discovered and cached, including:
- CPS
- CSC
- FTMS
- HRM
- DIS
- GAS
- GATT
- vendor-specific 128-bit services
Use BleClientBackend when you want to:
- connect Zwift to a real BLE trainer over WiFi or Ethernet
- test DirCon with actual trainer hardware
- improve network stability compared with direct BLE pairing
- experiment with vendor-specific BLE trainer services
BleServerBackend is a pseudo BLE trainer implementation.
It does not expose a real BLE GATT server over the air. Instead, it behaves like a virtual BLE trainer internally and exposes that behavior through DirCon.
ESP32 running BleServerBackend
->
DirCon over WiFi/Ethernet
->
Zwift
No real trainer hardware is required.
BleServerBackend handles:
- virtual service creation
- virtual characteristic creation
- simulated telemetry generation
- read/write handling
- subscription handling
- CPS measurement generation
- CSC measurement generation
- HRM measurement generation
- FTMS Indoor Bike Data generation
- FTMS Control Point handling
- FTMS Status notifications
The backend typically simulates:
Cycling Power Service 0x1818
Cycling Speed and Cadence 0x1816
Fitness Machine Service 0x1826
Heart Rate Service 0x180D
Use BleServerBackend when you want to:
- test DirCon without a real trainer
- debug Zwift pairing behavior
- experiment with FTMS commands
- develop trainer simulation logic
- validate networking and mDNS behavior
- test reconnect handling
- create controlled trainer data streams
Both example backends implement the same interface:
BleBackendThis means the application composition remains almost identical.
#include <DirCon.h>
#include "BleServerBackend.h"
BleServerBackend bleBackend;
DirConBleAdapter adapter(&bleBackend);
NetworkBootstrap bootstrap;
EspTcpBackend networkBackend;
DirConManager dirconManager(networkBackend);#include <DirCon.h>
#include "BleClientBackend.h"
BleClientBackend bleBackend;
DirConBleAdapter adapter(&bleBackend);
NetworkBootstrap bootstrap;
EspTcpBackend networkBackend;
DirConManager dirconManager(networkBackend);Only the backend implementation changes.
The backend implementations are intentionally kept outside the stable core library.
This is because backends are expected to be:
- modified
- extended
- replaced
- specialized for hardware
- specialized for trainer behavior
- used as starting points for experimentation
The core library remains focused on:
- DirCon protocol handling
- TCP/mDNS transport
- backend abstraction
- subscription management
- reconnect behavior
A custom backend can be created by implementing:
class MyBackend : public BleBackendThe backend must provide:
- service list
- characteristic list
- read behavior
- write behavior
- subscribe behavior
- notification generation
- readiness state
Notifications are forwarded through:
BleBackendObserverExample backends are deliberately readable and explicit.
They are intended to serve as:
- working examples
- test harnesses
- reference implementations
- starting points for custom trainer projects
The project encourages users to modify these backends for their own cycling, hardware, and protocol experiments.
12. Debugging
DirCon includes extensive serial debug output to make development and troubleshooting easier.
The project is intentionally transparent: most connection, discovery, subscription, network, and FTMS events can be inspected through the Arduino Serial Monitor.
DirCon uses a lightweight compile-time debug logging system.
Debug output is controlled through two levels:
| Symbol | Purpose |
|---|---|
GLOBAL_DEBUG |
Global master switch for the entire project |
DEBUG |
Enables logging for an individual source file |
The global debug switch is defined in:
debug.hEnable global debug logging by uncommenting:
#define GLOBAL_DEBUGDisable all debug logging globally by commenting it out:
//#define GLOBAL_DEBUGWhen disabled, all logging macros compile away completely with zero runtime overhead.
Source files that should generate debug output typically contain:
#define DEBUG
#include "debug.h"This allows selective participation in the global debug system.
If GLOBAL_DEBUG is disabled, these local DEBUG definitions have no effect.
The following macros are available:
LOG(...)
LOGF(...)
LOGLN(...)Examples:
LOGLN("Starting DirCon TCP service");
LOGF("Connected client %d\n", clientIndex);During development:
#define GLOBAL_DEBUGDuring deployment or release builds:
//#define GLOBAL_DEBUGThis immediately suppresses all debug logging throughout the project.
115200 baud
A typical successful startup shows:
Setup & Run BLE Server and DirCon Network!
BleServerBackend: ready
Ethernet: Connected, IP=192.168.x.x
Starting DirCon TCP service
Successfully added mDNS service
DirCon TCP service started successfully
For a BLE client backend:
BleClientBackend: Scanning for CPS trainer...
BleClientBackend: Connected
BleClientBackend: Ready
Check:
- ESP32 has a valid IP address
- mDNS started successfully
- client and ESP32 are on the same subnet
- WiFi network does not block multicast/mDNS
- Ethernet uses native
ETH.h, not ArduinoEthernet.h
Look for:
Adding DirCon mDNS service: _wahoo-fitness-tnp._tcp
Successfully added mDNS service
Check:
- TCP server is listening on port
36866 EspTcpBackenduses native ESP32WiFiServer/WiFiClientnet.poll()is called throughdirconManager.update()- firewall/VLAN isolation is not blocking traffic
Expected log:
New client connected
Check:
- trainer is awake
- trainer advertises CPS
0x1818 - trainer is not already connected to another client
- ESP32 BLE is enabled and stable
Expected log:
Scanning for CPS trainer...
Connected
Ready
This usually means FTMS Control Point responses are not reaching Zwift correctly.
Check:
- FTMS Control Point
0x2AD9exists - writes to
0x2AD9are decoded - response indication is emitted through
onNotification(...) requireDirConSubscription == falseis used for Control Point responses
Expected log:
Request Control!
FTMS Control Point response
Check CSC Measurement payload.
Cadence requires crank revolution data, not only wheel revolution data.
CSC flags should include crank data:
0x03 = wheel data + crank data present
Check whether subscriptions are incorrectly removed during network loss.
Temporary network loss should preserve DirCon subscription state.
Regular client disconnects may remove subscriptions, but network outages should not.
Look for:
Network lost
Network restored
TCP server listening
mDNS started
Look for:
New client connected
Client disconnected
addSubscription
removeAllSubscriptions
Look for:
Backend ready
Backend disconnected
Reconnect attempt
Reconnected and ready
Look for:
Request Control
Set Indoor Bike Simulation Parameters
Set Target Power
Reset
Use a Bonjour/mDNS browser to verify:
_wahoo-fitness-tnp._tcp
is visible on the network.
From another computer on the same network:
telnet <esp32-ip-address> 36866or:
Test-NetConnection <esp32-ip-address> -Port 36866During a ride:
unplug Ethernet cable for 5 seconds
reconnect cable
observe recovery
Expected:
Network lost
DirCon service stopped
Network restored
DirCon service restarted
Zwift reconnects
Temporarily switch off or disconnect the trainer.
Expected:
BLE disconnected
Reconnect attempt
Ready
restore subscriptions
notifications resume
The most useful debugging rule is to identify which layer failed:
Network discovery?
TCP connection?
DirCon protocol?
BLE discovery?
Subscriptions?
FTMS control?
Telemetry payload?
Because the architecture is layered, most issues can be isolated by following the serial log from bottom to top.
13. Credits and References
DirCon was developed through extensive experimentation with BLE trainer interoperability, ESP32 networking, and the Wahoo Direct Connect protocol.
The project benefited greatly from the work, ideas, and publicly available source code of several open-source projects and contributors in the indoor cycling and embedded BLE community.
Special thanks to the following projects and authors whose work provided inspiration, technical insight, protocol understanding, and practical implementation examples during the development of DirCon.
by Anthony Doud
An outstanding open-source smart trainer control project and an important source of practical FTMS and BLE trainer implementation knowledge.
Particularly valuable for:
- FTMS behavior understanding
- trainer control concepts
- ESP32 BLE implementation ideas
- indoor cycling interoperability experimentation
by Juergen Leber
A highly relevant and technically insightful DirCon-related project.
Particularly valuable for:
- understanding Wahoo Direct Connect behavior
- DirCon message handling concepts
- FTMS Control Point response handling
- practical interoperability observations
Several architectural and protocol insights were derived while studying SHIFTR behavior and implementation strategies.
by Roberto Viola
A remarkably extensive indoor fitness interoperability project supporting many trainers, protocols, and platforms.
Particularly valuable for:
- FTMS interoperability knowledge
- trainer protocol experimentation
- practical Zwift behavior observations
- general indoor cycling ecosystem understanding
DirCon also builds heavily upon the excellent work of the following projects and communities.
by Espressif Systems
Provides:
- ESP32 Arduino core
- WiFi stack integration
- ETH.h integration
- lwIP integration
- ESPmDNS support
The native ESP32 networking stack proved essential for robust WiFi and Ethernet DirCon support.
by h2zero
Provides the lightweight BLE stack used throughout the project.
Particularly valuable for:
- BLE GATT client support
- BLE notifications/indications
- ESP32 BLE robustness
- lower memory usage compared to the original ESP32 BLE stack
The following Bluetooth SIG specifications and service definitions were important references during development.
The Wahoo Direct Connect (DirCon) protocol is not publicly documented in a complete official specification.
Large portions of the implementation were therefore developed through:
- interoperability testing
- packet inspection
- experimentation
- behavioral analysis
- reverse engineering observations
using real-world interaction with:
- Zwift
- BLE trainers
- existing open-source projects
by elfrances
Unofficial specification of the wahoo-fitness-tnp service and associated DirCon protocol
DirCon itself is released under:
GPL-3.0
The project strongly supports:
- open experimentation
- protocol transparency
- interoperability research
- community-driven development
The indoor cycling ecosystem benefits greatly from the willingness of developers and enthusiasts to openly share protocol knowledge, implementation ideas, and practical discoveries.
Special thanks to the broader communities around:
- ESP32 development
- BLE reverse engineering
- indoor cycling interoperability
- open-source fitness technology
whose shared knowledge and experimentation made projects such as DirCon possible.
More detailed technical notes and deep-dive discussions are available in the /docs folder.
This project is just starting! If you’re interested in testing, coding, writing docs, or just giving feedback, contributions are welcome in Discussions.
This project is developed and published in accordance with EU directives that recognize the right to study, test, and develop software components for the purpose of achieving interoperability (e.g., Directive 2009/24/EC on the legal protection of computer programs, Article 6).
No part of this project is intended to infringe upon intellectual property rights or violate technological protection measures. All content is shared in good faith under the belief that it falls within the bounds of legitimate research, reverse engineering for interoperability, and fair use under EU law.
Users must ensure their own compliance with national implementations of EU directives, and are responsible for how they apply or modify this code.