Skip to content

Berg0162/DirCon

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DirCon

ESP32 DirCon gateway library for BLE smart trainers over WiFi or Ethernet.

1. Introduction

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:

  • BleClientBackend Connects as a BLE GATT client to a real trainer and exposes it over DirCon.

  • BleServerBackend Simulates 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

BLE Trainer Proxy Gateway

  • 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

Virtual Trainer / Simulator Backend

  • 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

Generic BLE Discovery

  • 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


Supported BLE Services

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 Support

  • FTMS Control Point command handling
  • FTMS indication/response procedures
  • Indoor Bike Simulation support
  • Target Power support
  • Control ownership handling
  • Compatible with Zwift FTMS workflows

DirCon Protocol Support

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

WiFi and Ethernet Networking

Supports both:

  • native ESP32 WiFi
  • native ESP32 Ethernet (ETH.h)

Tested with:


mDNS / DNS-SD Advertisement

  • Advertises DirCon service through mDNS
  • Publishes BLE service UUID information
  • Preserves vendor-specific 128-bit UUIDs
  • Compatible with Zwift automatic discovery

Robust Connection Handling

BLE robustness

  • disconnect detection
  • reconnect scheduling
  • fast reconnect optimization
  • fallback scanning
  • subscription restoration

Network robustness

  • network loss detection
  • TCP service restart
  • mDNS restart
  • recovery after temporary Ethernet/WiFi interruptions

Multi-Client Support

  • Supports multiple simultaneous DirCon TCP clients
  • Maintains per-client subscription state
  • Avoids redundant BLE subscribe/unsubscribe operations

Lightweight Embedded Architecture

  • polling-oriented Arduino-style design
  • no RTOS required
  • no asynchronous TCP framework required
  • lightweight memory usage
  • suitable for ESP32-class devices

Extensible Backend Architecture

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

Tested Configurations

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.

Why DirCon?

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.


Practical BLE Limitations

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.


Motivation for an ESP32-Based Gateway

The ESP32 platform provides a unique combination of features:

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

Beyond Simple Proxying

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.


Why a Virtual Trainer Backend?

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.


Design Philosophy

Several principles guided the architecture:

Layer separation

BLE logic, TCP networking, and DirCon protocol handling should remain independent.

Extensibility

New BLE backends should be easy to implement without modifying the core protocol layer.

Embedded simplicity

The implementation should remain lightweight and Arduino-friendly.

Robustness

Temporary BLE or network interruptions should recover automatically where possible.

Generic BLE support

The gateway should support:

  • standard SIG services
  • vendor-specific 128-bit services

without hardcoding trainer-specific assumptions.


Intended Audience

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.


High-Level Architecture

         [ 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.


Architectural Layers


BleBackend

Purpose

BleBackend defines the abstract BLE/backend interface used by the architecture.

It isolates the DirCon protocol layer from backend-specific BLE implementation details.

Current implementations

BleClientBackend

  • connects to a real BLE trainer
  • performs GATT discovery
  • proxies BLE operations
  • forwards notifications

BleServerBackend

  • simulates a virtual trainer
  • generates telemetry internally
  • implements FTMS/CPS/CSC/HRM behavior

Responsibilities

  • expose services and characteristics
  • handle reads and writes
  • manage subscriptions
  • generate notifications
  • maintain backend state
  • notify observers of backend events

Non-responsibilities

  • DirCon protocol parsing
  • TCP networking
  • mDNS publication

DirConBleAdapter

Purpose

Bridge layer between the BLE backend and the DirCon protocol layer.

Responsibilities

  • translate DirCon requests into backend operations
  • resolve characteristic UUID → service UUID
  • validate characteristics
  • forward notifications upward
  • forward backend connect/disconnect events

Design role

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.


DirConManager

Purpose

Core DirCon protocol implementation and TCP client manager.

Responsibilities

  • parse and encode DirCon messages
  • process protocol requests
  • manage TCP clients
  • maintain subscription state
  • forward notifications
  • coordinate reconnect handling
  • publish mDNS services

Supported protocol operations

  • discover services
  • discover characteristics
  • read characteristic
  • write characteristic
  • enable/disable notifications
  • unsolicited notifications

Design notes

DirConManager intentionally contains:

  • no direct BLE logic
  • no NimBLE usage
  • no hardware/network initialization

EspTcpBackend

Purpose

TCP/mDNS transport backend for ESP32.

Responsibilities

  • TCP server management
  • client connection handling
  • polling-based socket access
  • mDNS/DNS-SD publication
  • runtime network status monitoring

Networking stack

Built on:

  • WiFi.h
  • ETH.h
  • lwIP
  • ESPmDNS

Supported transports

  • WiFi
  • Ethernet

Both transports share the same underlying ESP32/lwIP TCP/IP stack.


NetworkBootstrap

Purpose

One-time network initialization helper.

Responsibilities

  • initialize WiFi or Ethernet hardware
  • configure SPI/PHY/chip settings
  • start DHCP/static networking
  • configure hostnames
  • wait for network availability

Important distinction

NetworkBootstrap performs:

startup initialization

while EspTcpBackend performs:

runtime transport management

Observer Chain

BLE notifications propagate upward through an observer chain:

BleBackend
    ->
DirConBleAdapter
    ->
DirConManager
    ->
TCP Clients

This keeps BLE/backend logic decoupled from the DirCon protocol layer.


Backend Interchangeability

One of the central design goals is interchangeable backend implementations.

Because DirConManager communicates only through:

BleBackend

new 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

Polling-Oriented Design

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.


UUID Handling Strategy

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.


Reconnect Philosophy

The architecture treats temporary interruptions as recoverable events.

Supported recovery behavior includes:

BLE side

  • disconnect detection
  • reconnect scheduling
  • fast reconnect attempts
  • subscription restoration

Network side

  • 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 Protocol Model

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.


One UUID Per Message

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:

Service discovery

DIRCON_MSGID_DISCOVER_SERVICES

The UUID represents a service UUID context.

Characteristic operations

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.


Internal Identity Model

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.


UUID Canonicalization

BLE UUID handling contains an important practical complication.

The same UUID may appear in multiple textual forms:

16-bit form

0x1818

Full 128-bit form

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.


Canonical UUID Strategy

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.


BLE Backend Abstraction

The architecture intentionally separates:

BLE/backend behavior

from:

DirCon protocol handling

through the abstract:

BleBackend

interface.

This allows multiple interchangeable backend implementations.


BleClientBackend

Acts as a BLE GATT client connected to a real trainer.

Responsibilities include:

  • scanning
  • connecting
  • discovery
  • reads/writes
  • subscription handling
  • reconnect logic

BleServerBackend

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.


Adapter Pattern

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.


Cached Discovery Model

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

Subscription Model

The project maintains two distinct subscription layers.


BLE-side subscriptions

Managed by the backend.

Represents actual BLE subscription state toward the trainer/backend.

Example:

gc.isSubscribed

DirCon-side subscriptions

Managed by DirConManager.

Represents which TCP clients subscribed to which characteristics.

These are intentionally separate concepts.


Shared BLE Subscription Optimization

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.


Forced Protocol Indications

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 == false

This became an important interoperability concept.


Polling-Oriented Architecture

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.


Reconnect Philosophy

Temporary interruptions are treated as recoverable events.

The architecture supports:

BLE recovery

  • disconnect detection
  • reconnect scheduling
  • fast reconnect
  • fallback scanning
  • subscription restoration

Network recovery

  • link-loss detection
  • TCP restart
  • mDNS restart
  • client reconnection

The goal is ride continuity across short interruptions.


ESP32 Networking Model

WiFi and Ethernet both ultimately use the ESP32 lwIP TCP/IP stack.

The architecture therefore uses a unified transport backend:

EspTcpBackend

with runtime transport checks such as:

WiFi.status()
ETH.linkUp()

This greatly simplified the networking architecture once native ETH.h support was adopted.


Design Philosophy Summary

Several core principles guided the implementation:

separation of concerns

BLE, protocol, and networking layers remain independent.

embedded simplicity

Keep the architecture lightweight and Arduino-friendly.

robustness

Recover gracefully from temporary failures.

extensibility

Allow alternative backends without rewriting the protocol layer.

transparency

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

Typical Repository Layout

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

src/

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.


Core BLE Abstraction

BleBackend.h

Defines the abstract BLE backend interface.

All BLE implementations derive from this interface.


BleBackendObserver.h

Observer interface used to forward:

  • notifications
  • indications
  • backend connect/disconnect events

upward into the DirCon protocol layer.


GatewayCharacteristic.h

Internal cached representation of a BLE characteristic including:

  • UUIDs
  • properties
  • subscription state
  • advertisement metadata

DirCon Protocol Layer

DirConBleAdapter.h

Bridge layer between:

  • BLE backend implementations
  • DirConManager

Handles service-resolution and notification forwarding.


DirConManager.h

Core DirCon protocol implementation.

Handles:

  • TCP clients
  • subscriptions
  • DirCon message processing
  • notification forwarding
  • reconnect handling

DirConMessage.h

DirCon protocol message representation and serializer/parser.


Network Layer

NetworkBackend.h

Abstract TCP transport interface.


EspTcpBackend.h

ESP32 TCP/mDNS implementation using:

  • WiFi.h
  • ETH.h
  • lwIP
  • ESPmDNS

Supports both WiFi and Ethernet transport.


NetworkBootstrap.h

One-time startup helper responsible for:

  • WiFi startup
  • Ethernet startup
  • DHCP/static IP acquisition
  • hostname configuration

Configuration

configNetwork.h

Central compile-time network configuration file.

Typical configuration includes:

  • WiFi/Ethernet selection
  • W5500 pin definitions
  • hostname settings
  • client limits

examples/

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.


DirConWithBleClientBackend

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

DirConWithBleServerBackend

Example pseudo trainer simulator.

Features:

  • software-generated trainer telemetry
  • FTMS simulation
  • CPS/CSC/HRM simulation
  • protocol experimentation

Typical use case:

ESP32 virtual trainer
    ->
Zwift

docs/

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.


Design Philosophy Behind the Structure

The repository structure intentionally reflects the architectural layering.


Stable Core vs Experimental Backends

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.


Library Role

The library provides:

  • the protocol framework
  • transport framework
  • architectural abstractions

while users provide:

  • trainer behavior
  • backend specialization
  • telemetry generation
  • custom integrations

Embedded Simplicity

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.


Supported ESP32 Platforms

ESP32

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

ESP32-S3

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 Networking Hardware


Native ESP32 WiFi

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

Ethernet Support

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.


Supported Ethernet Hardware


W5500 Ethernet

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

Waveshare ESP32-S3-ETH

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.


Custom Ethernet Boards

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.


BLE Hardware Requirements

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.


Tested Software Environment

The project has been tested successfully with:

  • Arduino-ESP32
  • NimBLE-Arduino
  • WiFi.h
  • ETH.h
  • ESPmDNS

Tested Client Applications

Zwift

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

Trainer Compatibility

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

Memory Considerations

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

Unsupported / Untested Platforms

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

Hardware Philosophy

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

Required Software Components

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

Native ESP32 Libraries Used

The following libraries are included automatically through Arduino-ESP32.


WiFi.h

Used for:

  • WiFi station mode
  • DHCP
  • TCP/IP connectivity
  • network status monitoring

Example:

#include <WiFi.h>

ETH.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

ESPmDNS

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.


Internal ESP32 Stack Components

Although not directly included manually, the project also relies heavily on internal ESP32 components.


lwIP

The ESP32 TCP/IP stack.

Both:

  • WiFi
  • Ethernet

ultimately use the same underlying:

lwIP netif

architecture.

This realization eventually led to the unified:

EspTcpBackend

transport implementation.


FreeRTOS

ESP32 Arduino internally uses FreeRTOS.

DirCon itself intentionally remains:

polling-oriented

and does not explicitly create RTOS tasks.


Typical library.properties

Example:

depends=NimBLE-Arduino

WiFi and Ethernet libraries do not need explicit installation because they are already part of Arduino-ESP32.


Recommended Software Versions

Component Recommended
Arduino IDE 2.x
Arduino-ESP32 recent stable
NimBLE-Arduino recent stable

Platform Scope

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

Why NimBLE-Arduino?

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

Ethernet Library Considerations

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.


Dependency Philosophy

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

Requirements

Before installing DirCon, ensure the following software components are available.


Arduino IDE

Recommended:

  • Arduino IDE 2.x

Older Arduino IDE versions may also work, but were not extensively tested.


ESP32 Board Support Package

Install the official Arduino-ESP32 package from Espressif.

Boards Manager URL

https://espressif.github.io/arduino-esp32/package_esp32_index.json

Installation steps

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.


NimBLE-Arduino

Install:

NimBLE-Arduino

through the Arduino Library Manager.

Installation steps

Sketch
    ->
Include Library
    ->
Manage Libraries

Search for:

NimBLE-Arduino

and install the latest stable version.


Obtain the DirCon Repository

Clone the GitHub repository:

git clone https://github.com/Berg0162/DirCon.git

or download the ZIP archive directly from GitHub.


Install as Arduino Library

Place the repository into your Arduino libraries folder.

Typical locations:

Windows

Documents/Arduino/libraries/

Linux

~/Arduino/libraries/

macOS

~/Documents/Arduino/libraries/

Resulting structure:

Arduino/libraries/DirCon/

Open an Example Sketch

The repository contains example applications inside:

examples/

Typical examples:

DirConWithBleClientBackend/
DirConWithBleServerBackend/

Open the desired .ino sketch from the Arduino IDE.


Select the ESP32 Board

Arduino IDE:

Tools
    ->
Board

Select your target board.

Examples:

  • ESP32 Dev Module
  • ESP32-S3 Dev Module
  • Waveshare ESP32-S3-ETH See Settings

Configure Networking

Copy configNetwork.example.h to configNetwork.h and fill in your local settings.

Edit:

configNetwork.h

WiFi Configuration

Enable WiFi mode:

#define DIRCON_USE_WIFI

Configure credentials:

#define WIFI_SSID     "your-wifi-ssid"
#define WIFI_PASSWORD "your-password"

Ethernet Configuration

Enable Ethernet mode:

#define DIRCON_USE_ETHERNET

For supported Arduino ESP32 Ethernet boards:

ETH.begin();

may be sufficient.


Custom W5500 Configuration

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  11

Compile and Upload

Connect the ESP32 board through USB.

Then:

Sketch
    ->
Upload

or use the Upload button.


Serial Monitor

After upload, open:

Tools
    ->
Serial Monitor

Recommended baud rate:

115200

Expected Startup Output

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

Verify mDNS Advertisement

Zwift should automatically discover the DirCon server through:

_wahoo-fitness-tnp._tcp

mDNS advertisement.

Typical advertised hostname:

DirCon-Server.local

First Zwift Test


BLE Client Backend

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

BLE Server Backend

Expected behavior:

ESP32 Virtual Trainer
    ->
Zwift

Zwift should recognize the virtual trainer and receive:

  • power data
  • cadence data
  • heart rate data
  • FTMS responses

Troubleshooting


Trainer not discovered

Verify:

  • trainer is awake
  • trainer advertises CPS (0x1818)
  • BLE is enabled

Zwift does not discover DirCon server

Verify:

  • ESP32 has a valid IP address
  • mDNS started successfully
  • WiFi/Ethernet network is operational
  • PC/tablet and ESP32 are on the same subnet

Ethernet issues

Verify:

  • W5500 SPI pins
  • PHY/chip configuration
  • Ethernet cable/link LEDs
  • DHCP operation

Compile errors

Verify:

  • NimBLE-Arduino installed
  • ESP32 board package installed
  • correct board selected

Recommended Starting Point

For first experiments:

Use case Recommended example
Real trainer bridge DirConWithBleClientBackend
Protocol testing / simulation DirConWithBleServerBackend
Ethernet experimentation ESP32-S3 + W5500
Simplest setup WiFi backend

Installation Philosophy

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.


Quick Start Overview

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

Example 1 — BLE Trainer Proxy Gateway

This configuration bridges a real BLE trainer to Zwift over WiFi or Ethernet.


Typical Use Case

Real BLE Trainer
       ->
ESP32 DirCon Gateway
       ->
WiFi / Ethernet
       ->
Zwift

Step 1 — Open the Example

Open:

examples/DirConWithBleClientBackend/

Step 2 — Configure Networking

Copy configNetwork.example.h to configNetwork.h and fill in your local settings.

Edit:

configNetwork.h

WiFi example

#define DIRCON_USE_WIFI

#define WIFI_SSID     "your-wifi-ssid"
#define WIFI_PASSWORD "your-password"

Ethernet example

#define DIRCON_USE_ETHERNET

Configure W5500 pins if required.


Step 3 — Upload to ESP32

Compile and upload the sketch.

Open the Serial Monitor at:

115200 baud

Step 4 — Wake the Trainer

Ensure the trainer is awake and advertising:

Cycling Power Service (0x1818)

Step 5 — Observe Startup

Typical startup sequence:

BleClientBackend: Scanning for CPS trainer...
BleClientBackend: Connected
BleClientBackend: Ready

DirCon TCP service started successfully

Step 6 — Pair in Zwift

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

Example 2 — Virtual Trainer Simulator

This configuration simulates a trainer entirely in software.

No real trainer hardware is required.


Typical Use Case

ESP32 Virtual Trainer
       ->
WiFi / Ethernet
       ->
Zwift

Step 1 — Open the Example

Open:

examples/DirConWithBleServerBackend/

Step 2 — Configure Networking

Copy configNetwork.example.h to configNetwork.h and fill in your local settings.

Edit:

configNetwork.h

Configure either:

  • WiFi
  • Ethernet

Step 3 — Upload to ESP32

Compile and upload the sketch.


Step 4 — Observe Startup

Typical startup output:

BleServerBackend: starting pseudo BLE server backend
DirCon TCP service started successfully

Step 5 — Pair in Zwift

Zwift should discover the simulated trainer automatically.

Expected functionality:

  • simulated power
  • cadence
  • heart rate
  • FTMS communication
  • virtual riding

BLE Client Backend Notes

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

BLE Server Backend Notes

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

Typical Serial Output


WiFi startup

WiFi connected
IP address: 192.168.x.x
Starting DirCon TCP service

Ethernet startup

ETH MAC: xx:xx:xx:xx:xx:xx
IPv4: 192.168.x.x

Zwift subscription activity

Subscribe to Characteristic 0x2A63
Subscribe to Characteristic 0x2AD2
Subscribe to Characteristic 0x2A37

FTMS Control Point activity

Request Control!
Set Indoor Bike Simulation Parameters!

Recommended First Setup

For easiest success:

Setup Recommendation
Simplest hardware ESP32 + WiFi
Most stable networking ESP32-S3 + W5500 Ethernet
Simplest testing BleServerBackend
Real trainer bridging BleClientBackend

Important Notes


DirCon discovery

Zwift discovers the server through:

_wahoo-fitness-tnp._tcp

mDNS advertisement.


BLE trainer requirement

The BLE client backend currently scans for trainers advertising:

Cycling Power Service (0x1818)

FTMS behavior

Some FTMS procedures intentionally bypass DirCon subscription filtering to match Zwift behavior expectations.

This is normal and intentional.


What to Explore Next

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:

BleBackend

Concrete backend implementations can then provide different trainer behaviors without modifying:

  • DirConManager
  • DirConBleAdapter
  • EspTcpBackend
  • DirConMessage

This makes the examples an important part of the project: they demonstrate how to implement and plug in real backend behavior.


Backend Role in the Architecture

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

BleClientBackend is the real BLE trainer proxy implementation.

It connects to an actual BLE indoor trainer and exposes that trainer over DirCon.


Typical Use Case

Real BLE Trainer
       ->
ESP32 running BleClientBackend
       ->
DirCon over WiFi/Ethernet
       ->
Zwift

Responsibilities

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

Discovery Strategy

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

When to Use

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

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.


Typical Use Case

ESP32 running BleServerBackend
       ->
DirCon over WiFi/Ethernet
       ->
Zwift

No real trainer hardware is required.


Responsibilities

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

Simulated Services

The backend typically simulates:

Cycling Power Service       0x1818
Cycling Speed and Cadence   0x1816
Fitness Machine Service     0x1826
Heart Rate Service          0x180D

When to Use

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

Backend Interchangeability

Both example backends implement the same interface:

BleBackend

This means the application composition remains almost identical.


Example: Virtual Trainer

#include <DirCon.h>
#include "BleServerBackend.h"

BleServerBackend bleBackend;
DirConBleAdapter adapter(&bleBackend);

NetworkBootstrap bootstrap;
EspTcpBackend networkBackend;
DirConManager dirconManager(networkBackend);

Example: Real Trainer Proxy

#include <DirCon.h>
#include "BleClientBackend.h"

BleClientBackend bleBackend;
DirConBleAdapter adapter(&bleBackend);

NetworkBootstrap bootstrap;
EspTcpBackend networkBackend;
DirConManager dirconManager(networkBackend);

Only the backend implementation changes.


Why Backends Are Kept in Examples

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

Creating New Backends

A custom backend can be created by implementing:

class MyBackend : public BleBackend

The backend must provide:

  • service list
  • characteristic list
  • read behavior
  • write behavior
  • subscribe behavior
  • notification generation
  • readiness state

Notifications are forwarded through:

BleBackendObserver

Backend Development Philosophy

Example 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.


Enable Debug Output

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

Global Debug Master Switch

The global debug switch is defined in:

debug.h

Enable global debug logging by uncommenting:

#define GLOBAL_DEBUG

Disable all debug logging globally by commenting it out:

//#define GLOBAL_DEBUG

When disabled, all logging macros compile away completely with zero runtime overhead.


Per-File Debug Participation

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.


Available Logging Macros

The following macros are available:

LOG(...)
LOGF(...)
LOGLN(...)

Examples:

LOGLN("Starting DirCon TCP service");

LOGF("Connected client %d\n", clientIndex);

Recommended Usage

During development:

#define GLOBAL_DEBUG

During deployment or release builds:

//#define GLOBAL_DEBUG

This immediately suppresses all debug logging throughout the project.

Recommended Serial Monitor speed:

115200 baud

Normal Startup Sequence

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

Common Problems

Zwift does not discover the DirCon server

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 Arduino Ethernet.h

Look for:

Adding DirCon mDNS service: _wahoo-fitness-tnp._tcp
Successfully added mDNS service

Zwift sees the server but does not connect

Check:

  • TCP server is listening on port 36866
  • EspTcpBackend uses native ESP32 WiFiServer/WiFiClient
  • net.poll() is called through dirconManager.update()
  • firewall/VLAN isolation is not blocking traffic

Expected log:

New client connected

BLE trainer is not found

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

Zwift keeps sending “Request Control”

This usually means FTMS Control Point responses are not reaching Zwift correctly.

Check:

  • FTMS Control Point 0x2AD9 exists
  • writes to 0x2AD9 are decoded
  • response indication is emitted through onNotification(...)
  • requireDirConSubscription == false is used for Control Point responses

Expected log:

Request Control!
FTMS Control Point response

Power works but cadence does not

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

Ride stops after network hiccup

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.


Useful Debug Areas

Network

Look for:

Network lost
Network restored
TCP server listening
mDNS started

Clients

Look for:

New client connected
Client disconnected
addSubscription
removeAllSubscriptions

BLE backend

Look for:

Backend ready
Backend disconnected
Reconnect attempt
Reconnected and ready

FTMS

Look for:

Request Control
Set Indoor Bike Simulation Parameters
Set Target Power
Reset

Recommended Diagnostic Tests

mDNS test

Use a Bonjour/mDNS browser to verify:

_wahoo-fitness-tnp._tcp

is visible on the network.


TCP test

From another computer on the same network:

telnet <esp32-ip-address> 36866

or:

Test-NetConnection <esp32-ip-address> -Port 36866

Ethernet hiccup test

During 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

BLE hiccup test

Temporarily switch off or disconnect the trainer.

Expected:

BLE disconnected
Reconnect attempt
Ready
restore subscriptions
notifications resume

Debugging Philosophy

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.


Acknowledgements

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

Additional Technologies and Libraries

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

Protocol References

The following Bluetooth SIG specifications and service definitions were important references during development.






DirCon / Wahoo Notes

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


Open Source Philosophy

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.


Final Thanks

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.


💡 Additional Documentation

More detailed technical notes and deep-dive discussions are available in the /docs folder.


❤️ Contributing

This project is just starting! If you’re interested in testing, coding, writing docs, or just giving feedback, contributions are welcome in Discussions.

⚖️ Legal Notice (EU Context)

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.