Logo du projet fl3xbl0w

Contrôleur du Moteur du Tapis de Course - fl3xbl0w

Lancé le 28 mai 2022

Projet d'ingénierie inverse. Il a commencé avec le tapis de course Bowflex Treadmill 22 mais s'est généralisé pour toute machine Android vendue par Nautilus Inc. (Nautilus, Bowflex, Schwinn).

Cela s’applique principalement au Treadmill 22 & Treadmill 56.

La carte de contrôle du moteur est fabriquée par Electronics Way Industry.

Carte contrôleur du Moteur B017D

D’après le manuel de service fourni par Nautilus Inc. (sauvegarde sur archive.org):

Schéma électrique du tapis de course

Et en se concentrant spécifiquement sur cette partie:

Chemin de communication du tapis de course

Nous pouvons identifier le “câble de communication” qui relie le contrôleur du moteur comme étant à 5 broches. Il n’y a qu’un seul connecteur à 5 broches. J’ai étiqueté les câbles avec leurs couleurs correspondantes (les données & l’interrupteur sont opto-isolés):

Couleur du câbleÉtiquette
rougeGND
blancRXD
noirTXD
jaune+12
vertSW

La carte n’est pas directement connectée à la console Android.

Le seul connecteur à 5 broches est de marque Molex. Une recherche Google de “petits connecteurs Molex” m’a conduit à une image de ce qu’ils appellent Molex Micro-Fit 3.0 Single Row (5-Pin), qui est utilisé pour connecter la carte contrôleur du moteur:

Connecteur Molex Micro-Fit 3.0

Liens AliExpress

En jetant un œil dans NautilusLauncher.apk via jadx-gui, je peux voir qu’ils communiquent la tablette Android avec leur “Console Universelle” en utilisant Serial à 230400 Baud (en utilisant /dev/ttyS4). CE N’EST PAS ce que nous analysons ici. Cela fait référence à la communication entre Android et la “Console Universelle”. Nous examinons la communication entre la “Carte contrôleur du bouton” et la “Carte contrôleur du moteur”, excluant ainsi trois cartes comme points de défaillance possibles.

Tenter de connecter un ESP32 ou un pont Serial basé sur CH340 directement aux câbles entre la base du tapis de course et la carte contrôleur Bowflex a fait que la base du tapis de course ne s’initialisait pas correctement, après quoi j’ai acquis un analyseur logique pour investiguer plus en profondeur.

Mise à jour 2025

Ces dernières semaines, et presque trois ans après avoir commencé ce projet, plusieurs personnes m’ont contacté pour demander des avancées à ce sujet, confirmant mon hypothèse initiale que le système du tapis de course est horrible et qu’il était seulement question de temps pour que les machines commencent à tomber en panne. Il m’a semblé être le bon moment pour mettre en service mon analyseur logique, qui jusqu’à présent ne servait qu’à prendre la poussière.

En connectant l’analyseur logique aux lignes TXD et RXD (et GND, bien sûr), j’ai immédiatement pu commencer à intercepter les messages entre les deux parties sans interrompre la communication. Je suppose qu’initialement, je n’ai pas pu avec un ESP32 en raison de problèmes d’impédance. Après quelques minutes d’essais et d’erreurs, j’ai abouti à la configuration Serial suivante:

- 2400 Baud
- 8 Bits par trame
- 1 Bit d'arrêt
- Pas de bit de parité
- Bit de poids faible envoyé en premier
- TXD: Signal inversé
- RXD: Signal non inversé

Avec ces configurations, j’ai pu voir des messages clairement définis.

Interception des messages UART

Interception des messages UART lors du processus de démarrage

Quelques observations immédiates:

  • Tous les messages envoyés par le panneau de boutons commencent par 0x68
  • Tous les messages envoyés par la carte contrôleur du moteur commencent par 0x73
  • Les messages des deux parties se terminent par 0x43
  • En général, les messages du panneau de boutons sont envoyés 100ms après avoir reçu un message de la carte contrôleur du moteur
    • À l’exception du processus de démarrage, où à un instant donné, il y a une différence de 300ms
  • Le bruit sur les lignes de communication est incroyable, rendant la lecture des messages difficile

Avec cela comme base, commence le processus de déchiffrage des messages et de compréhension de ce qui est communiqué entre les deux parties, en effectuant des modifications contrôlées dans une routine d’exercices.

Interception des changements de vitesse

En effectuant des changements contrôlés à des vitesses spécifiques, on peut observer les valeurs suivantes envoyées à la carte contrôleur du moteur:

Vitesse à l’écranMessage envoyé
0,0 km/h (en attente ou pause)0x68 0x08 0x80 0x50 0x00 0x0A 0x00 0x00 0xE2 0x43
2,0 km/h0x68 0x08 0x80 0x50 0x14 0x0A 0x00 0x00 0xF6 0x43
3,0 km/h0x68 0x08 0x80 0x50 0x1D 0x0A 0x00 0x00 0xFF 0x43
5,0 km/h0x68 0x08 0x80 0x50 0x31 0x0A 0x00 0x00 0x13 0x43

On peut observer que le byte 5 et le byte 9 changent. Le byte 5 semble représenter la vitesse en hexadécimal, et le byte 9 semble être une somme de contrôle.

En convertissant les valeurs du byte 5 en décimal:

Vitesse à l’écranHexadécimalDécimal
0,0 km/h (en attente ou pause)0x000
2,0 km/h0x1420
3,0 km/h0x1D29
5,0 km/h0x3149

Ayant décompilé certaines parties du système Android des années auparavant, je me suis souvenu que lors de la configuration de la machine en système métrique, l’application de Bowflex effectue en interne la conversion du système métrique à impérial pour communiquer avec l‘“UCB”. La carte contrôleur du moteur semble utiliser le système métrique, et il semble y avoir une perte de précision dans la conversion du système métrique à impérial et ensuite de retour au métrique (ce que la carte contrôleur du moteur attend), car tout est géré avec une précision d’un décimal. Était-ce si difficile de bien faire, Nautilus ?

En tenant compte de cela, et si un facteur d’échelle de 10 est appliqué, cela correspond parfaitement aux valeurs envoyées à la carte contrôleur du moteur, donc la formule serait:

Valeur en décimal = Vitesse en km/h × 10

Interception des changements d’inclinaison

En suivant le même processus qu’avec la vitesse, on peut observer les valeurs suivantes envoyées à la carte contrôleur du moteur:

Inclinaison à l’écranMessage envoyé
-5°0x68 0x08 0x80 0x50 0x1D 0x00 0x00 0x00 0xF5 0x43
0x68 0x08 0x80 0x50 0x1D 0x32 0x00 0x00 0x27 0x43
0x68 0x08 0x80 0x50 0x1D 0x8C 0x00 0x00 0x81 0x43

Dans ce cas, le byte 6 semble représenter l’inclinaison en hexadécimal, et cela confirme que le byte 9 est une somme de contrôle.

En convertissant les valeurs du byte 6 en décimal:

Inclinaison à l’écranHexadécimalDécimal
-5°0x000
0x3250
0x8C140

La formule qui correspond parfaitement aux valeurs envoyées à la carte contrôleur du moteur est:

Valeur en décimal = (Angle + 5) × 10

Somme de contrôle

Cela semble être une somme de contrôle simple et standard sur les microcontrôleurs, additionnant tous les bytes du message et effectuant un débordement arrivé à 256. Une représentation simple serait quelque chose comme:

uint8_t calculateChecksum(uint8_t *msg) {
  return msg[1] + msg[2] + msg[3] + msg[4] + msg[5] + msg[6] + msg[7];
}

En utilisant uint8_t comme type de retour, le débordement se produit naturellement. On pourrait utiliser une boucle for pour additionner les valeurs et retourner sum % 256, mais ce serait plus lent pour les microcontrôleurs sans aucun bénéfice réel.

Prochaines étapes

  • Comprendre logiquement le processus de démarrage, ou au moins le reproduire
  • Capturer les interactions de la clé de sécurité (la chose rouge qui se place dans les vêtements)
  • Interpréter les messages envoyés par la carte contrôleur du moteur, qui ne devraient pas différer beaucoup des messages envoyés par le panneau de boutons

Avec cela, on peut déjà reproduire le fonctionnement du panneau de boutons, et ainsi contrôler le tapis de course depuis un microcontrôleur.

— À suivre —

Contenu traduit par o1-mini

©2022-2025 Sebastián Barrenechea. Tous droits réservés.

Construit avec Astro v5.6.1.