#!/usr/bin/env bash # TASK-2: does a control write actually change what the pedals feel like? # Every write comes back acknowledged; nobody has confirmed it does anything. # # Four connect/hold/disconnect cycles against one adapter, which is where this # script's own hazard lives — see `settle` and the EXIT trap below. A run that # leaves the trainer connected at the BlueZ level poisons the *next* run with # `le-connection-abort-by-local`, and the failure looks like a trainer fault # rather than the script's. set -uo pipefail P=./target/debug/probe NAME=VANRYSEL D="--name $NAME" # Seconds to let BlueZ tear a link down before asking it to build another. A # connect issued straight after a disconnect is the reliable way to provoke # `le-connection-abort-by-local`. SETTLE=4 if [[ ! -x $P ]]; then echo "Build the probe first: cargo build -p bikecontrol-probe" >&2 exit 1 fi # The address of the trainer, if BlueZ currently knows one. Used only for # cleanup — the probe finds its own device by name. trainer_addr () { bluetoothctl devices 2>/dev/null | awk -v n="$NAME" '$3 ~ n {print $2; exit}' } # Drop any GATT link BlueZ still holds. # # This is the whole point of the trap: the probe disconnects cleanly on its own # exit path, but it never gets there if the run is interrupted — Ctrl-C reaches # every process in the foreground group, so bash can die while the probe is # mid-hold and leave the link open with nothing owning it. The next run then # fails at connect for reasons that have nothing to do with the trainer. cleanup () { local addr addr=$(trainer_addr) [[ -z $addr ]] && return 0 if bluetoothctl info "$addr" 2>/dev/null | grep -q "Connected: yes"; then echo echo " (releasing GATT link to $addr)" bluetoothctl disconnect "$addr" >/dev/null 2>&1 sleep 2 fi } trap cleanup EXIT INT TERM step () { local target=$1 title=$2 echo echo "════════════════════════════════════════════" echo " $title" echo "════════════════════════════════════════════" echo " connecting (~8s), then holding for 20s — PEDAL THROUGHOUT" # Output is NOT piped. `| tail -3` swallowed the live telemetry this test # exists to produce, and hid connect errors until the run was over. It also # put the probe in a pipeline, so its exit status vanished behind tail's and # `set -e` never fired on a failed step. if ! $P set $D "$target" --secs 20; then echo echo " !! step failed: $target" >&2 cleanup return 1 fi # Let the link fully drop before the next step reconnects. sleep "$SETTLE" } # Start from a clean slate: a link left over from a previous run is the most # common cause of the first step failing. cleanup echo "Wake the trainer first: spin the cranks for a few seconds." echo "Press Enter when it's awake and you're ready to ride." read -r step resistance=15 "TEST 1 of 4 — resistance 15 of 100 (should feel EASY)" || exit 1 step resistance=75 "TEST 2 of 4 — resistance 75 of 100 (should feel MUCH HARDER)" || exit 1 step sim=0.0 "TEST 3 of 4 — simulated gradient 0% (flat)" || exit 1 step sim=8.0 "TEST 4 of 4 — simulated gradient +8% (a climb)" || exit 1 echo echo "Done — trainer reset to zero load." echo echo "Report back:" echo " a) Did 2 feel harder than 1? (resistance mode works)" echo " b) Did 4 feel harder than 3? (SIM MODE works — this is the one that matters)" echo " c) Roughly how much harder, and did it change instantly or drift in?"