WattFlow gids en Bluetooth architectuur
Dit document bundelt de brede uitleg van WattFlow zoals de app nu werkt op 24 maart 2026: van tabbladen, workouts en trainingsmodi tot Strava, Apple Health en de diepe Bluetooth-laag. Het idee is dat je hier zowel een leesbare gebruikersgids als de technische architectuur terugvindt, zonder dat je eerst door Xcode hoeft te bladeren.
Start hier: kies je onderwerp
Deze HTML werkt nu als een soort WattFlow-startpagina. Je kunt hem lezen als gebruikershandleiding, als uitlegdocument voor teamleden, of als technische naslag. De kaarten hieronder brengen je direct naar het juiste niveau.
Zo werkt WattFlow als app
Een totaalplaatje van de 4 hoofdtabbladen, de normale gebruikersflow en waar je welke instellingen vindt.
Workouts en import
Hoe de workoutlijst werkt, hoe favorieten en filters werken, en wat er gebeurt bij import van .zwo, .fit en .tcx.
FTP, level, ERG en FreeRide
De praktische uitleg van WattFlow’s trainingsmodi, inclusief automatische FTP-suggesties en de logica achter level-modus.
Workout rijden, opslaan en terugkijken
Wat er op het live workoutscherm gebeurt, hoe samenvattingen worden opgebouwd en hoe activiteiten in Historie terechtkomen.
Strava-koppeling
Verbinden, workouts uploaden, recente ritten importeren en hoe WattFlow met FIT, TCX en Strava-streams omgaat.
Apple Health-koppeling
Welke gegevens de app leest uit Health, wat hij wegschrijft na een workout en hoe FTP- en gewichtsynchronisatie werken.
Bluetooth en sensoren
De diepe laag: FTMS, CPS, CSC, prioriteitsregels, PowerMatch, L/R-balans en kalibratie.
Zo werkt WattFlow als app
Op hoofdlijnen draait WattFlow om vier tabbladen: apparaten instellen, een workout kiezen, de workout rijden en daarna alles terugzien in Historie. De app is dus niet alleen een trainer-controller, maar ook een bibliotheek, workoutplayer, samenvattingsscherm en import/export-hub.
Instellingen
Hier koppel je trainer, powermeter, hartslag- en cadanssensoren. Ook kies je hier FTP- of level-modus, PowerMatch, FreeRide-gedrag, kalibratie en externe koppelingen zoals Strava en Apple Health.
Workouts
Dit is de trainingsbibliotheek. Je zoekt op naam, filtert op categorie of duur, markeert favorieten en importeert bestanden van buitenaf.
Workout
Hier rijd je de gekozen training echt. De app toont live vermogen, cadans en hartslag, bewaakt intervallen en laat je starten, pauzeren, hervatten en blokken overslaan.
Historie
Na afloop belanden workouts en geïmporteerde ritten hier. Vanuit deze tab kun je samenvattingen bekijken, delen, opnieuw exporteren en Strava-imports binnenhalen.
De kortste route voor een nieuwe gebruiker ziet er in de praktijk zo uit:
- Koppel je trainer en sensoren in Instellingen en controleer of de juiste bronnen actief zijn.
- Kies je trainingsbasis: FTP-modus als je met een vaste drempelwaarde werkt, of level-modus als je meer op gevoel wilt trainen.
- Kies of importeer een workout in de Workouts-tab.
- Rijd de sessie in de Workout-tab, waar WattFlow de trainer aanstuurt en elke seconde samples opneemt.
- Sla op en kijk terug in Historie, eventueel met upload naar Strava of sync naar Apple Health.
WorkoutSessionEngine op die live data leest, targets uitstuurt en samples bewaart.
Workouts en import
De Workouts-tab is tegelijk bibliotheek en importpunt. Je ziet ingebouwde trainingen en eigen imports samen in één lijst. De lijst is dus niet alleen een menu, maar ook de plek waar WattFlow zijn trainingsbestandencollectie beheert.
| Onderdeel | Wat de gebruiker ziet | Wat er technisch gebeurt |
|---|---|---|
| Zoekveld | Snel zoeken op naam van de workout | Filtert de reeds geïndexeerde workoutlijst live in de UI |
| Categorieën en duurfilter | Alleen relevante workouts tonen | De app bewaart de gekozen filterstappen in AppModel |
| Favorieten | Snelle terugkeer naar favoriete trainingen | Favorieten hangen aan de bestandsnaam, zodat ze ook na opnieuw indexeren behouden blijven |
| Importeren | Bestanden ophalen uit Bestanden, Mail of iCloud Drive | .zwo en workout-.fit gaan naar de workoutbibliotheek; activiteiten-.fit en .tcx gaan naar Historie |
| Ververs | Pull-to-refresh op de workoutlijst | Forceert een nieuwe index van bundle-workouts en document-workouts |
Een belangrijk detail is dat een .fit-bestand in WattFlow twee rollen kan hebben:
het kan een echte workout met stappen zijn, of een al gereden activiteit. De app probeert daarom
eerst te bepalen of het bestand workoutstappen bevat. Pas als dat niet zo is, behandelt WattFlow het als ritbestand
en stuurt het door naar de activiteitenimport.
FTP, level, ERG en FreeRide
WattFlow heeft in de kern twee trainingsmodi: FTP-modus en level-modus. Daarbovenop heb je workoutgedrag zoals ERG en FreeRide. Dat klinkt veel, maar in gewone taal betekent het vooral: wil je trainen op een vaste vermogensbasis, of wil je meer op gevoel en niveau rijden?
| Onderwerp | FTP-modus | Level-modus |
|---|---|---|
| Basis van de training | Je echte FTP in watt | Een virtuele FTP op basis van gewicht, geslacht en gekozen preset |
| Wat je invult | Een vaste FTP-waarde | Gewicht, geslacht en een startniveau zoals rustig, normaal of stevig |
| Wat je in de workout ziet | Doelvermogen en live vermogen | Huidig niveau plus live vermogen als feedback |
| Beste voor | Gestructureerde intervallen en nauwkeurige zone-training | Meer gevoel, meer speelruimte en rijden zonder constant op één exact wattgetal vast te zitten |
| Bijstellen tijdens de training | Doel volgt de workoutblokken | Je kunt niveaus omhoog of omlaag zetten; de app bewaart optioneel het laatst gebruikte niveau |
FTP in gewone taal
FTP is in WattFlow de referentiewaarde voor zones, intervaldoelen, IF, TSS en andere trainingsscores. Als je in FTP-modus rijdt, vertaalt de app workoutpercentages direct naar doelwatts. Na een testtraining vraagt WattFlow altijd of een nieuwe FTP moet worden overgenomen. Bij gewone trainingen doet de app dat alleen als de berekende kandidaat hoger is dan je huidige FTP.
Level-modus in gewone taal
Level-modus is meer “rijden op gevoel”. Onder water rekent WattFlow nog steeds met een virtuele FTP,
maar jij stuurt vooral via niveaus. De app gebruikt hiervoor een schaal van -3 tot 20,
waarbij niveau 1 de neutrale start is en niveau 0 expres wordt overgeslagen.
Hoe hoger het niveau, hoe hoger de factor die op de virtuele FTP wordt toegepast.
ERG en FreeRide
ERG is de stand waarin de trainer een doelvermogen probeert vast te houden. Dat is ideaal voor klassieke intervaltraining.
FreeRide werkt anders: dan stuurt WattFlow geen vaste wattdoelen, maar een weerstandsniveau.
De instellingen Start weerstand en Max weerstand bepalen hoe zwaar zo’n FreeRide-blok aanvoelt.
Workout rijden, opslaan en terugkijken
Zodra je een workout kiest, bouwt WattFlow een sessie op rond die training. Vanaf dat moment wordt de Workout-tab het live controlecentrum. Na afloop verschuift dezelfde sessie naar samenvatting, opslag en historie.
De levenscyclus van een workout is in de app grofweg als volgt:
- Selecteren: de gekozen workout wordt via
prepareWorkout(...)klaargezet met training mode, FTP/virtual FTP en PowerMatch-instellingen. - Starten: de gebruiker start via een overlay; tijdens de sessie kun je pauzeren, hervatten en blokken vooruit of achteruit zetten.
- Opnemen: de engine legt elke seconde samples vast met onder meer power, cadence, hartslag, targetinformatie en L/R-balans.
- Afronden: aan het einde bouwt de app een
ActivityRecord, exporteert naar TCX/FIT en maakt een samenvattingsscherm. - Bewaren of delen: daarna kun je de activiteit opslaan, naar Historie sturen en eventueel uploaden naar Strava.
Tijdens het rijden
De Workout-tab toont live vermogen, cadans, hartslag en de huidige intervalstatus. In level-modus staat het niveau centraal; in andere blokken vooral het doelvermogen.
Na afloop
De samenvatting laat onder meer duur, IF, TSS, vermogen, zones en eventueel PowerMatch-statistiek zien. Vanuit Historie kun je later dezelfde activiteit opnieuw bekijken of delen.
Historie is bovendien niet alleen voor app-workouts. Ook geïmporteerde .fit- en .tcx-activiteiten
komen hier terecht. Daardoor wordt Historie het centrale archief van alles wat je in WattFlow wilt bewaren of terugzien.
Strava-koppeling
De Strava-koppeling in WattFlow heeft twee hoofdrichtingen: uploaden van afgeronde activiteiten en importeren van recente ritten vanuit Strava naar je lokale historie.
WattFlow gebruikt Strava niet alleen om ritten te versturen, maar ook om recente Strava-activiteiten weer terug de app in te halen als lokale historie.
| Stap | Wat WattFlow doet | Wat dat voor de gebruiker betekent |
|---|---|---|
| Verbinden | Start een OAuth-flow via ASWebAuthenticationSession |
Je logt in bij Strava zonder de app handmatig te verlaten |
| Uploaden | Probeert eerst FIT te uploaden en valt alleen terug op TCX als FIT niet beschikbaar is | Normaal gesproken krijgt Strava dus de rijkste export die WattFlow heeft |
| Importeren | Haalt recente activiteiten op en leest streams voor tijd, vermogen, hartslag en cadans | Een Strava-rit kan daardoor in WattFlow als gewone lokale activiteit terugkomen |
| Loskoppelen | Verwijdert de lokale sessie en probeert ook remote te deauthoriseren | De gebruiker kan de koppeling volledig resetten |
Belangrijk om te weten: Strava-import is geen “live sync van alles”. WattFlow haalt bewust een beperkte set recente importeerbare ritten op. Bij import wordt een nieuwe lokale activiteit opgebouwd met samples, statistiek en een eventueel FTP-kandidaatadvies.
Apple Health-koppeling
Apple Health werkt in WattFlow vooral als betrouwbare gegevensuitwisseling met iOS: de app leest profielinformatie terug waar dat nuttig is, en schrijft afgeronde workouts weg zodat de training ook buiten WattFlow zichtbaar blijft.
Hier haalt WattFlow vooral profieldata zoals FTP en gewicht op, en hier schrijft de app na afloop je indoor cycling-workouts weer naar terug.
| Richting | Gegevens | Effect in WattFlow |
|---|---|---|
| Health -> WattFlow | Laatste lichaamsgewicht en laatste cycling FTP | Level-modus kan gewicht updaten; FTP in de app kan worden bijgewerkt naar de Health-waarde |
| WattFlow -> Health | Indoor cycling workout plus power, cadence, speed, distance, energie, hartslag en HRV waar beschikbaar | Afgeronde trainingen leven niet alleen in WattFlow maar ook in Apple Health |
| WattFlow -> Health | Nieuwe FTP-waarde | Als Health-sync aan staat, probeert WattFlow wijzigingen in FTP ook terug te schrijven |
| Import sync | Geïmporteerde activiteiten | Ook geïmporteerde ritten kunnen naar Health worden weggeschreven als sync aan staat |
Wanneer je Apple Health inschakelt, vraagt de app eerst toestemming. Als die toestemming ontbreekt of later wordt ingetrokken, schakelt WattFlow de sync niet blind door, maar zet hij die veilig terug. Zo voorkom je dat de UI “aan” zegt terwijl er onder water niets meer mag.
Bluetooth-verdieping
Vanaf hier schakelt het document een versnelling dieper. Dit deel beschrijft niet meer alleen wat je als gebruiker ziet, maar hoe WattFlow intern beslist welke sensor wint, welke data-stromen worden gebruikt en hoe trainersturing, PowerMatch, L/R-balans en kalibratie precies zijn opgezet.
In dit deel zie je hoe WattFlow tegelijk met FTMS, CPS, CSC en hartslagservices werkt en daarna beslist welke data op dat moment leidend is.
BLE, FTMS, CPS en CSC betekenen,
begin dan meteen hieronder bij WattFlow for dummies. Vanaf hoofdstuk 1 wordt het technisch preciezer.
0. WattFlow for dummies
Als je geen achtergrond hebt in Bluetooth of fietssensoren, dan helpt deze simpele vertaling: WattFlow probeert eigenlijk maar drie dingen tegelijk te doen:
- data lezen van sensoren, zoals vermogen, cadans en hartslag;
- beslissen welke sensor op dat moment het meest betrouwbaar is;
- de trainer opdrachten geven om zwaarder of lichter te worden.
BLE is de draadloze verbinding. FTMS is de taal waarmee WattFlow
een smart trainer kan besturen. CPS is de taal waarmee een powermeter zijn vermogen doorgeeft.
CSC is de taal voor snelheid en cadans. WattFlow luistert dus naar meerdere "talen" tegelijk.
In gewone mensentaal: iPhone met WattFlow <- leest watts van trainer of powermeter <- leest cadence van trainer, cadanssensor of powermeter <- leest hartslag van HR-band of trainer -> stuurt trainer zwaarder / lichter via FTMS Doel: de juiste live waarden tonen workouts correct opnemen de trainer op het juiste vermogen houden
0.1 Begrippenlijst
| Begrip | Betekent in gewone taal | Rol in WattFlow |
|---|---|---|
| BLE | Bluetooth Low Energy: de zuinige draadloze verbinding tussen iPhone en sensoren | De transportlaag waar alles overheen loopt |
| FTMS | Fitness Machine Service: de "smart trainer taal" | Wordt gebruikt om trainerdata te lezen en om ERG, weerstand en spin-down te sturen |
| CPS | Cycling Power Service: de "powermeter taal" | Levert vermogen, vaak ook cadans, soms ook pedal balance, en ondersteunt zero offset |
| CSC | Cycling Speed and Cadence: de taal voor snelheid- en cadanssensoren | Wordt vooral gebruikt om cadans uit crank-omwentelingen te halen |
| Service | Een categorie functies op een BLE-device | Bijvoorbeeld FTMS, CPS, CSC, Heart Rate of Battery |
| Characteristic | Een concreet datapunt of commando binnen zo'n service | Bijvoorbeeld "Indoor Bike Data" of "Control Point" |
| Advertising | Het korte visitekaartje dat een apparaat tijdens scannen uitzendt | Helpt WattFlow snel te raden wat een device waarschijnlijk is |
| Trainer | De smart trainer die zwaarder of lichter kan worden gemaakt | Houdt de weerstand vast en ontvangt FTMS-opdrachten |
| Powermeter | De meter die je echte vermogen in watt meet | Kan intern in de trainer zitten of extern op fiets, pedalen of crank |
| Cadence | Je trapfrequentie in omwentelingen per minuut | Wordt gebruikt voor live-weergave en als gate voor PowerMatch |
| ERG | Een modus waarin de trainer een doelvermogen probeert vast te houden | WattFlow stuurt dan target watts naar de trainer |
| Resistance | Een modus waarin je een vaste weerstand instelt in plaats van vaste watts | Gebruikt in FreeRide; WattFlow stuurt dan een FTMS resistance level |
| PowerMatch | De app vergelijkt trainer-power met externe powermeter-power en corrigeert de trainer | Maakt ERG nauwkeuriger vanuit het perspectief van je externe powermeter |
| L/R-balans | Hoeveel van je vermogen van links komt en hoeveel van rechts | Wordt gelogd en opgeslagen, maar stuurt de trainer niet aan |
| RR-interval | De tijd tussen twee hartslagen | Nodig voor HRV-berekeningen zoals RMSSD |
| RMSSD | Een bekende HRV-maat, afgeleid uit RR-intervallen | WattFlow berekent die alleen bij voldoende stabiele RR-data |
| Kalibratie | Een sensor opnieuw ijken of nulstellen | Bij trainers via spin-down, bij powermeters via zero offset |
1. Kort samengevat
- WattFlow houdt een centrale apparaatregistratie bij per
CBPeripheral.identifier. Vanuit die ene registry worden rollen afgeleid: trainer, powermeter, cadanssensor en hartslagmeter. - Een apparaat met FTMS-capability geldt in deze code als trainer. Zo'n device mag niet tegelijk als externe powermeter of cadanssensor worden gebruikt.
- Live cadans en live vermogen worden niet hard aan een enkel device gekoppeld, maar telkens opnieuw gerouteerd op basis van "recente" samples. De recency window is 1,5 seconde.
- PowerMatch stuurt nooit direct op een externe powermeter. Het gebruikt de externe CPS-power als feedbackbron, maar de trainer krijgt nog steeds FTMS ERG-targets.
- L/R-balans is metadata. Die wordt gelogd, opgenomen in workout-samples en meegenomen in FIT-export, maar niet gebruikt om de trainer aan te sturen.
- Trainer-kalibratie is FTMS spin-down; externe powermeter-kalibratie is CPS zero offset. De trainer-spin-down wordt in de app afgerond op basis van FTMS-snelheid en een timer, niet op basis van een aparte geparste eindwaarde.
2. Hoofdarchitectuur
De Bluetooth-stack bestaat functioneel uit twee lagen:
BluetoothManager: scan, connect, capability-detectie, characteristic-subscriptions, parsing van BLE-pakketten, live-routing, FTMS-control, kalibratie en logging.WorkoutSessionEngine: workout-timer, 1 Hz sample-opname, trainer-aansturing per blok en de PowerMatch-regelaar bovenop de BLE-data.
CoreBluetooth (CBCentralManager / CBPeripheral)
-> BluetoothManager
-> deviceRegistry[UUID]
-> rolpointers: trainer / power / cadence / heart rate
-> raw streams per device (FTMS, CPS, CSC, HR)
-> live routing (power, cadence, HR)
-> FTMS control / CPS calibration / logs
-> WorkoutSessionEngine
-> 1 Hz workout tick
-> PowerMatch controller
-> ActivitySample recording
-> FIT / activiteit export
3. Rollenmodel per apparaat
Rollen worden niet alleen uit advertising gehaald, maar ook later verrijkt met services en characteristics die na connect bekend worden.
| Rol | Wanneer geldig | Belangrijk gevolg |
|---|---|---|
| Trainer | Device heeft FTMS-advertising, FTMS-service of FTMS control/data characteristics | Mag ERG en weerstand ontvangen. Wordt uitgesloten als externe power- of cadansbron. |
| Externe powermeter | Device heeft Cycling Power, maar geen FTMS | Mag externe powerfeedback leveren voor live power en PowerMatch. |
| Cadanssensor | Device heeft CSC of CPS, maar geen FTMS | Mag live cadans leveren. Wiel-only speed sensors worden actief geweerd. |
| Hartslagmeter | Device heeft Heart Rate service | Levert BPM en eventueel RR-intervallen voor HRV. |
Extra guards die in de code zitten:
- Als een gekozen powermeter later toch FTMS-capability blijkt te hebben, wordt die direct losgekoppeld en wordt de voorkeur gewist.
- Hetzelfde geldt voor een gekozen cadanssensor die later FTMS blijkt te hebben.
- Een geselecteerde trainer moet na service-discovery echt FTMS hebben; anders verbreekt WattFlow die connectie meteen.
- Een wheel-only CSC-sensor zonder crank-data telt niet als cadanssensor. Ook een naam die op een pure snelheidssensor duidt (
speedofspd, zondercad) wordt geweerd.
4. BLE services en characteristics die WattFlow gebruikt
FTMS is de map, Indoor Bike Data en Control Point zijn twee onderdelen daarin.
| Service | UUID | Characteristics | Gebruik in WattFlow |
|---|---|---|---|
| Fitness Machine Service (FTMS) | 1826 |
2AD2 Indoor Bike Data, 2AD9 Control Point, 2ACC Feature, 2AD8 Supported Power Range |
Trainerdetectie, live speed/cadans/power, HR-forwarding, ERG, weerstand en trainer spin-down. |
| Cycling Power Service (CPS) | 1818 |
2A63 Measurement, 2A65 Feature, 2A66 Control Point |
Externe powermeterdata, trainer-CPS fallback, pedal balance, cadans uit crank-data, zero-offset kalibratie. |
| Cycling Speed and Cadence (CSC) | 1816 |
2A5B Measurement |
Cadans uit crank-revoluties, plus detectie van wheel-only speed sensors. |
| Heart Rate | 180D |
2A37 Measurement |
BPM, RR-intervallen en HRV/RMSSD. |
| Battery | 180F |
2A19 Battery Level |
Batterijniveau per verbonden sensor voor UI en status. |
De manager subscribe't waar mogelijk op notify/indicate en leest kenmerken die readbaar zijn. Voor FTMS en CPS betekent dit dat control points niet alleen voor schrijven worden gebruikt, maar ook voor het ontvangen van responses.
5. Scannen, detecteren en verbinden
5.1 Scanmodes
| Scanfunctie | Services | Timeout | Opmerking |
|---|---|---|---|
startScanTrainer() |
FTMS | 30 s | Trainer is expliciet FTMS; alleen power of cadans is niet genoeg. |
startScanPowerSource() |
CPS + FTMS | 30 s | FTMS wordt mee gescand om trainers te herkennen en later te blokkeren. |
startScanCadence() |
CSC + FTMS + CPS | 30 s | CPS telt ook mee, omdat sommige meters cadans uit CPS leveren. |
startScanHeartRate() |
Heart Rate | 30 s | Alleen directe HR-sensoren. |
scanAllSources() |
FTMS + CPS + CSC + Heart Rate | 30 s | Settings gebruikt dit voor een brede scan. |
5.2 Device discovery
- Bij
didDiscoverslaat WattFlow het periferalobject op, bewaart RSSI, onthoudt geadverteerde services en verrijkt het apparaatrecord. - De lijsten in Settings worden gesorteerd op hoogste RSSI eerst, daarna alfabetisch op naam.
- Advertised services zijn niet de hele waarheid. Het capability-model wordt verder aangevuld na service- en characteristic-discovery.
5.3 Auto-connect gedrag
- Na een volledige Settings-scan verbindt de huidige UI alleen de trainer automatisch, en dan alleen na 6 seconden scan-time.
- Powermeter, cadanssensor en HR-sensor vragen in de huidige Settings-flow een expliciete gebruikerskeuze.
- Een handmatige trainer-disconnect zet een cooldown van 180 seconden om autoconnect-loops te vermijden.
6. Live datastromen en prioriteitsregels
BluetoothManager.recomputeLiveMetrics() is het knooppunt waar alle live-metrics opnieuw worden bepaald.
Die functie draait niet alleen na nieuwe BLE-notificaties, maar ook via een watchdog-task elke 400 ms.
6.1 Cadans-routing
De prioriteitsvolgorde voor live cadans is exact als volgt:
- Geselecteerde externe cadanssensor via CSC
- Geselecteerde externe cadanssensor via CPS
- Trainer via CPS
- Trainer via CSC
- Trainer via FTMS
- Geselecteerde powermeter via CPS
Alleen de eerste recente bron wint. Daardoor kan een externe cadanssensor een trainer-cadans volledig overrulen zolang hij verse data blijft leveren.
6.2 Vermogens-routing
De vermogensprioriteit hangt af van twee dingen:
- is er een externe powermeter geselecteerd?
- staat de speciale modus "external balance from powermeter" runtime-matig aan?
Standaard zonder externe powermeter
- Trainer via CPS
- Trainer via FTMS
Met externe powermeter geselecteerd, normale power-routing
- Externe powermeter via CPS
- Trainer via CPS
- Trainer via FTMS
Met externe powermeter geselecteerd, maar "external balance routing" actief
- Trainer via CPS
- Trainer via FTMS
- Externe powermeter via CPS, alleen als tijdelijke fallback wanneer trainer-power niet vers genoeg is
6.3 Idle filter en pedaling-detectie
- De app beschouwt iemand als "pedaling" wanneer er een recente cadansbron is en de cadans minimaal 5 rpm is.
- Als er niet wordt getrapt en het ruwe vermogen lager of gelijk is aan 25 W, zet WattFlow de getoonde live power op 0 W.
- Bij hogere ruwe power blijft de waarde zichtbaar, ook zonder geldige pedaling-state.
6.4 Hartslag-routing
- Een directe HR-sensor via Heart Rate service levert BPM en eventueel RR-intervallen.
- Een trainer mag via FTMS ook hartslag forwarden. Dat levert alleen BPM, geen RR-intervallen en dus geen HRV.
- Trainer-forwarded HR wordt alleen gebruikt als er geen directe HR-sensor actief is, of als de gebruiker expliciet
viaTrainerheeft gekozen. - Voor HRV/RMSSD is dus een directe HR-sensor nodig die RR-intervallen uitzendt.
7. Parsing per protocol
Met "parsing" bedoelen we hier simpelweg: een ontvangen BLE-pakket uitlezen en omzetten naar bruikbare app-waarden zoals watts, rpm of bpm.
7.1 FTMS Indoor Bike Data
- Instantaneous speed wordt alleen gelezen als FTMS-flag bit 0 aangeeft dat speed wel in het pakket zit.
- Instantaneous cadence komt uit FTMS bit 2 en wordt omgerekend van 0,5 rpm-units naar hele rpm.
- Instantaneous power komt uit FTMS bit 6 en voedt de trainer-FTMS powerstream.
- Heart rate komt uit FTMS bit 9 en kan live BPM voeden, maar niet de RR/HRV-keten.
- Supported Power Range wordt gelezen om de maximale trainer-target power op te slaan. Alleen de maximumwaarde wordt gebruikt; min en step worden niet verder verwerkt.
7.2 Cycling Power Measurement
- Instantaneous power komt uit de eerste signed 16-bit powerwaarde en wordt negatief-clamped naar minimaal 0 W.
- Crank revolution data wordt gebruikt om cadans te berekenen, mits CPS-flag bit 5 aanwezig is.
- De code loopt netjes langs optionele velden heen (pedal balance, torque, wheel data) voordat crank-data gelezen wordt.
- Cadans uit CPS wordt alleen geaccepteerd als de uitkomst tussen 0 en 250 rpm ligt.
7.3 Pedal balance en afgeleide links/rechts power
- Als CPS-flag bit 0 aangeeft dat pedal power balance aanwezig is, leest de app byte 4 en deelt die door 2. Dat geeft een percentage.
- De huidige code interpreteert CPS-flag bit 1 als "left referenced".
- Alleen wanneer die referentie expliciet links is, leidt WattFlow links/rechts wattages af uit het totale vermogen.
- Als de referentie niet expliciet links is, bewaart de app wel het percentage maar markeert de referentie als
onbekenden vult geen afgeleide links/rechts watts in. - Dit geldt zowel voor trainer-CPS als voor externe CPS-data.
7.4 CSC Measurement
- CSC met crank-data levert cadans op basis van cumulatieve crankrevoluties en event time in stappen van 1/1024 seconde.
- CSC zonder crank-data geldt in runtime als wheel-only. Als de gebruiker zo'n sensor toch als cadence selecteert, verbreekt de app die connectie weer.
7.5 Heart Rate Measurement en HRV
- RR-intervallen worden uit Heart Rate Measurement gehaald als de RR-flag aanwezig is.
- De app accepteert alleen RR's tussen 300 en 2000 ms.
- Daarnaast wordt een sprongdetector gebruikt: een nieuwe RR mag niet te ver afwijken van de mediaan van recente waarden. De drempel is de maximum van 250 ms en 30% van de mediaan.
- HRV/RMSSD wordt pas "stabiel" nadat voldoende geaccepteerde RR's zijn verzameld: minimaal 10 accepted intervallen of minimaal 15 seconden warmup.
- RMSSD wordt berekend over een rolling window van 60 seconden.
- Alleen geaccepteerde RR-intervallen gaan naar HRV en export; ruwe BLE-RR's blijven alleen in de logregel zichtbaar.
8. PowerMatch: hoe de control loop precies werkt
PowerMatch is ondergebracht in een aparte PowermatchController. Die controller draait los van de UI-smoothing.
De workout-engine tikt hem op 1 Hz om oscillatie op BLE-jitter te beperken.
8.1 Wanneer PowerMatch actief kan zijn
- De gebruiker heeft PowerMatch aangezet in Settings.
- Er is een trainer geselecteerd.
- Er is een externe powermeter geselecteerd; een trainer mag niet als externe powermeter tellen.
- De workout draait in ERG-modus.
Bij een daadwerkelijke tick moet daarna ook nog aan runtime-voorwaarden worden voldaan:
- workout draait en is niet gepauzeerd;
- trainer-control is beschikbaar: trainer connected, FTMS control point gevonden en control verkregen;
- externe powermeter-power is vers genoeg;
- de gebruikte cadansbron is vers genoeg;
- cadans is minimaal 5 rpm.
8.2 Welke samples PowerMatch gebruikt
| Input | Bronregel | Opmerking |
|---|---|---|
| Trainer power | Nieuwste van trainer-CPS en trainer-FTMS | Voor afwijkingsstatistiek en vergelijking met powermeter. |
| Powermeter power | Alleen externe CPS | Dit is de feedbackbron voor de control loop. |
| Cadans | De al gerouteerde live cadans | Dus ook een aparte cadanssensor kan de gating van PowerMatch bepalen. |
8.3 Smoothing en sampleverwerking
- Powermeter-power krijgt een EWMA-smoothing met een tijdconstante van 2,0 seconden.
- Als de gap tussen twee powermeter-samples groter is dan 2,0 seconden, reset de smoothwaarde direct naar de ruwe sample.
- Naast die smoothing houdt de controller afwijkingsstatistiek bij:
delta = powermeter - trainer, plus gemiddelde, gemiddeld absolute afwijking en gemiddelde over de laatste 10 seconden.
8.4 Regelparameters
| Parameter | Waarde | Betekenis |
|---|---|---|
| Smoothing tau | 2,0 s | EWMA voor powermeter feedback |
| Stale threshold | 2,0 s | Sample ouder dan dit telt niet meer mee |
| Deadband | 3 W | Geen regelactie rond target |
| Kp | 0,22 | Proportionele versterking |
| Ki | 0,07 per seconde | Integrator |
| Integrator clamp | +/-35 W | Begrenst ophoping |
| Max correction | 110 W | Bovengrens op opwaartse correctie |
| Rate up | 160 W/s | Hoe snel target omhoog mag |
| Rate down | 45 W/s | Hoe snel target omlaag mag |
| Target jump reset | 20 W | Vanaf hier reset de regelaar bij blokwissel |
| Integrator hold na jump | 2,0 s | Voorkomt na-ijlen van vorige interval |
| Large positive error boost start | 30 W | Extra hulp bij duidelijke undershoot |
8.5 Formule in woorden
- Error:
error = intervalTarget - smoothedPowermeter. - Deadband: als
|error| <= 3 W, wordt de effectieve error op 0 gezet. - Integrator:
integrator += Ki * error * dt, daarna clamp op +/-35 W. - Extra boost bij grote positieve error: boven 30 W positieve fout komt er nog een extra boost bovenop.
- Correctie:
Kp * error + integrator + eventuele boost. - Downward guard: neerwaartse correctie wordt extra beperkt zodat herstelblokken niet te ver onder doel wegzakken. In code is de negatieve limiet
max(12 W, min(110 W, 18% van target)). - Rate limiting: het nieuw toe te passen trainer-target mag per seconde sneller omhoog dan omlaag.
8.6 Gedrag bij target-jumps
- Als een nieuw intervaltarget meer dan 20 W verschilt van het vorige, reset de controller zijn integrator, laatste error en correctie.
- De smooth powermeterwaarde wordt dan hard gereset naar de laatste ruwe powermeter-sample, zodat restenergie uit het vorige blok niet blijft doorwerken.
- Bij grote positieve sprongen krijgt het target een korte extra boost. Die boost is proportioneel aan de grootte van de sprong, met een schaalfactor van 0,32 en een maximum van 110 W.
- Bij neerwaartse sprongen is er geen vergelijkbare negatieve jump-boost.
8.7 Fallback-gedrag
- Als PowerMatch niet kan regelen op het moment dat een blok start of een target opnieuw toegepast wordt, valt de engine terug op het basis-ERG-target van het interval.
- Tijdens een lopend, stabiel interval zonder blokwissel doet de 1 Hz tick die fallback niet telkens opnieuw. In dat geval blijft het laatst verzonden trainer-target staan totdat er weer een geldige regelactie komt of een nieuw blok begint.
- Alle FTMS target-writes worden in
BluetoothManagernog eens 200 ms gede-bounced en worden niet opnieuw verstuurd als het target onveranderd is.
9. FTMS control en trainer-aansturing
9.1 FTMS control acquisition
Voordat ERG, weerstand of trainer-kalibratie kan werken, probeert WattFlow trainer-control te claimen via FTMS control point opcode 0x00.
- Pas na een succesvolle response wordt
hasFtmsControlwaar. - Als er op dat moment al een pending ERG-target of pending resistance is, stuurt de app eerst
Start/Resumemet opcode0x07. - Daarna worden pending ERG, pending resistance en pending calibration geflusht.
9.2 Welke FTMS opcodes WattFlow verstuurt
| Actie | Bytes | Uitleg |
|---|---|---|
| Request control | 00 |
FTMS-control claimen |
| Start / resume | 07 |
Alleen wanneer nodig na control-acquisitie |
| Set target power | 05 lo hi |
Signed 16-bit watt target, little-endian |
| Set target resistance level | 04 raw |
Eerste UI-mapping naar 0-100%, daarna naar FTMS level x 10 |
| Start trainer spin-down | 13 01 |
Kalibratie-opdracht voor FTMS trainer |
9.3 Weerstandsmodus (FreeRide)
- De UI werkt met 0-100% weerstand.
- Intern rekent WattFlow dat om naar een FTMS resistance level tussen 0,0 en
maxLevel, met 0,1 resolutie. - Default
maxLevelis 10,0, maar dit komt uit Settings. - De ruwe FTMS-byte wordt berekend als
(percent / 100) * maxLevel * 10, afgerond en geclamped tussen 0 en 255.
Voor ERG wordt geen expliciet "disable ERG" FTMS-commando gestuurd wanneer een workout stopt. De code kiest daar bewust voor, omdat trainers daar niet uniform op reageren.
10. L/R-balans: bronkeuze en opslag
L/R-balans leeft in WattFlow los van de vermogensregeling. Dat is belangrijk: de bron van live power en de bron van L/R-balans hoeven dus niet per se hetzelfde apparaat te zijn.
Simpel gezegd: L/R-balans vertelt hoeveel procent van het vermogen van links komt en hoeveel van rechts.
10.1 Bronkeuze voor balance snapshots
- Als de runtime-flag
preferExternalBalanceFromPowermeteraan staat en de externe powermeter heeft recente balansdata, wint de externe powermeter altijd. - Anders volgt de balancebron eerst de actuele live power source:
powermeterCps-> gebruik externe powermeterbalancetrainerCps-> gebruik trainer-CPS-balance
- Als de live power source iets anders is, bijvoorbeeld
trainerFtms, valt de code terug op recente externe balance als die er is, anders op trainer-CPS balance.
10.2 Relatie met PowerMatch en de Settings-toggle
- Als PowerMatch aangezet wordt, forceert de Settings-logica ook
externalBalanceFromPowermeterEnabled = true. - De feitelijke runtime-flag
preferExternalBalanceFromPowermeterstaat echter alleen aan wanneer PowerMatch uit staat. Bij PowerMatch is live power toch al extern, dus extra balance-routing is dan niet nodig. - Resultaat: tijdens PowerMatch komt balance in de praktijk vanzelf van dezelfde externe powermeter die ook de powerfeedback levert.
10.3 Vastlegging in workout en export
- Elke workout-tick (1 Hz) maakt een
ActivitySampleaan metbalancePercent,balanceReference,leftPowerWenrightPowerW. - De engine logt balance maximaal een keer per seconde en forceert elke 5 seconden een heartbeat-regel als de tekst niet verandert.
- In FIT-export schrijft WattFlow:
- het standaard FIT-veld
left_right_balance; - developer fields
left_power_wenright_power_w.
- het standaard FIT-veld
- Als alleen een balance-percentage aanwezig is en geen expliciete links/rechts watts, probeert de FIT-export die achteraf te reconstrueren uit totaal vermogen plus de bewaarde referentie.
11. Kalibratie
Kalibratie betekent hier: de sensor of trainer eerst netjes "rechtzetten" zodat de meting of weerstand daarna beter klopt. Bij een trainer heet dat vaak spin-down, bij een powermeter vaak zero offset.
11.1 Trainer spin-down via FTMS
Voorwaarden om te starten:
- er is een verbonden trainer;
- het FTMS control point is ontdekt;
- er loopt nog geen trainer-kalibratie.
Fasen in de app:
- requestingControl: app wil trainer-control claimen.
- pedalUp: spin-down command is verstuurd; gebruiker moet optrekken tot doeltempo.
- coastDown: zodra FTMS-speed minimaal 35 km/u is, moet de gebruiker stoppen met trappen.
- waitingForResult: zodra FTMS-speed tot 2 km/u of lager gedaald is, wacht de app nog 1 seconde.
- finished: daarna markeert de app de spin-down als afgerond.
coastDown.
Mislukkingen die expliciet worden afgevangen:
- geen verbonden trainer;
- trainer heeft geen FTMS control point;
- FTMS control request wordt geweigerd, bijvoorbeeld omdat een andere app de trainer nog bestuurt;
- FTMS spin-down opcode retourneert een foutresultaat.
11.2 Externe powermeter zero offset via CPS
Voorwaarden om te starten:
- de verbonden powerbron is een echte externe powermeter;
- de CPS control point characteristic is ontdekt;
- er loopt nog geen zero-offset proces.
Werking:
- De app stuurt CPS control point opcode
0x0C(start offset compensation). - De gebruiker moet de cranks stilhouden.
- De response moet beginnen met
0x20, daarna requestcode0x0Cen resultaat0x01voor succes. - Als er nog twee bytes volgen, bewaart WattFlow die als gerapporteerde zero-offset waarde.
Deze kalibratie geldt alleen voor de externe powermeter. De trainer-interne powermeter wordt via deze route niet gekalibreerd.
12. Wat wordt er tijdens een workout vastgelegd?
- De workout-engine tikt elke seconde.
- Per tick wordt een
ActivitySamplegemaakt met power, heart rate, HRV/RMSSD, RR-intervallen, cadence, block-index, target-percent en L/R-balansvelden. - Powermatch houdt daarnaast sessiestatistiek bij: gemiddelde afwijking, gemiddelde absolute afwijking en aantal samples.
- De uiteindelijke
ActivityRecordbewaart die PowerMatch-samenvatting naast de ruwe samples.
13. Praktische gevolgen en randgevallen
- Een trainer met CPS en FTMS kan prima zowel trainer-power als trainer-cadans leveren, maar geldt nooit als externe powermeter in de app.
- Als je een externe powermeter kiest en alleen L/R-balans van buiten wilt, dan blijft live power normaal van de trainer komen zolang trainer-power vers is.
- Als trainer-power tijdelijk wegvalt in die modus, kan live power kort terugvallen op de externe powermeter.
- Als live power uit FTMS komt maar de trainer geen CPS pedal balance heeft, kan de app toch externe L/R-balans tonen of vastleggen zolang die externe balans recent is.
- Voor betrouwbare HRV heb je een directe HR-sensor met RR-intervalsupport nodig; trainer-forwarded HR is daarvoor niet genoeg.
- De trainer max target power uit FTMS Supported Power Range wordt gebruikt om workout-targets te clampen. Daardoor zal de app geen doelvermogen boven die bekende bovengrens sturen.
14. Relevante bronbestanden
| Bestand | Rol in de architectuur |
|---|---|
WattFlow/Services/BluetoothManager.swift |
CoreBluetooth, device registry, routing, FTMS/CPS-control, kalibratie, logs |
WattFlow/Services/WorkoutSessionEngine.swift |
Workout-timer, sample-opname, trainer-aansturing, PowerMatch-regelaar |
WattFlow/Services/SettingsStore.swift |
Persistente settings voor powermatch, externe balance en device-keuzes |
WattFlow/Views/SettingsTabView.swift |
UI-logica rond scan, selectie, PowerMatch-toggle en externe balance-toggle |
WattFlow/Views/ContentView.swift |
Synchroniseert runtime-flag preferExternalBalanceFromPowermeter |
WattFlow/Services/FitExporter.swift |
Schrijft L/R-balans, left/right power en RR-data naar FIT |
Einde hoofddocument. Gebruik deze pagina voor de volledige uitleg, het diagramdocument voor volgorde en interacties, en de operator handleiding voor dagelijkse praktijk.