Logo do projeto fl3xbl0w

Controlador do Motor da Esteira - fl3xbl0w

Lançado em 28 de mai. de 2022

Projeto de engenharia reversa. Começou com a esteira Bowflex Treadmill 22, mas acabou se generalizando para qualquer máquina com Android vendida pela Nautilus Inc. (Nautilus, Bowflex, Schwinn).

Esta aplicação se refere principalmente à Treadmill 22 & Treadmill 56.

A placa de controle do motor é fabricada pela Electronics Way Industry.

Placa controladora do Motor B017D

Considerando o manual de serviço fornecido pela Nautilus Inc. (backup no archive.org):

Diagrama elétrico da esteira

E focando especificamente nesta parte:

Caminho de comunicação da esteira

Podemos identificar o “cabo de comunicação” que conecta o controlador do motor como um de 5 pinos. Há apenas um conector de 5 pinos. Etiquetei os cabos com suas cores correspondentes (os dados & interruptor estão optoisolados):

Cor do caboEtiqueta
vermelhoGND
brancoRXD
pretoTXD
amarelo+12
verdeSW

A placa não está conectada diretamente ao console Android.

O único conector de 5 pinos é da marca Molex. Uma busca no Google por “conectores Molex pequenos” me levou a uma imagem do que chamam de Molex Micro-Fit 3.0 Single Row (5-Pin), que é utilizado para conectar a placa controladora do motor:

Conector Molex Micro-Fit 3.0

Link do AliExpress

Ao dar uma olhada no NautilusLauncher.apk através do jadx-gui, posso ver que eles comunicam o tablet Android com seu “Console Universal” usando Serial a 230400 Bauds (usando /dev/ttyS4). ISSO NÃO é o que estamos analisando aqui. Isso se refere à comunicação entre Android e o “Console Universal”. Investigamos a comunicação entre a “Placa controladora da botoneira” e a “Placa controladora do motor”, eliminando assim três placas como possíveis pontos de falha.

Tentar conectar um ESP32 ou uma ponte Serial baseada em CH340 diretamente aos cabos entre a base da esteira e a placa controladora Bowflex fez com que a base da esteira não inicializasse corretamente, após o que adquiri um analisador lógico para investigar mais a fundo.

Atualização 2025

Nas últimas semanas, e quase três anos depois de ter começado com isso, várias pessoas entraram em contato comigo para perguntar sobre avanços a respeito disso, confirmando minha suposição inicial de que o sistema da esteira é horrível e que apenas era uma questão de tempo para que as máquinas começassem a falhar. Achei que era um bom momento para colocar em uso meu analisador lógico, que até então estava guardando pó.

Conectando o analisador lógico às linhas TXD e RXD (e GND, é claro), pude imediatamente começar a interceptar mensagens entre ambas as partes sem interromper a comunicação. Presumo que inicialmente não consegui com um ESP32 por questões de impedância. Após alguns minutos de tentativa e erro, cheguei à seguinte configuração Serial:

- 2400 Bauds
- 8 Bits por Quadro
- 1 Bit de Parada
- Sem Bit de Paridade
- Menos Significativo Primeiro
- TXD: Sinal Invertido
- RXD: Sinal Não Invertido

Com essas configurações, pude ver mensagens claramente definidas.

Interceptando mensagens UART

Interceptando mensagens UART no processo de inicialização

Algumas coisas que pude perceber imediatamente:

  • Todas as mensagens enviadas pela botoneira começam com 0x68
  • Todas as mensagens enviadas pela placa controladora do motor começam com 0x73
  • As mensagens de ambas as partes terminam em 0x43
  • Em geral, as mensagens da botoneira são enviadas 100ms depois de receber uma mensagem da placa controladora do motor
    • Exceto no processo de inicialização, onde em uma instância há uma diferença de 300ms
  • O ruído nas linhas de comunicação é incrível, dificultando a leitura das mensagens

Com isso como base, já começa o processo de decifrar as mensagens e entender o que está sendo comunicado entre ambas as partes, realizando mudanças controladas em uma rotina de exercícios.

Interceptando mudanças na velocidade

Realizando mudanças controladas em velocidades específicas, é possível observar os seguintes valores enviados para a placa controladora do motor:

Velocidade na telaMensagem enviada
0,0 km/h (em espera ou pausa)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

Pode-se observar que muda o byte 5 e o byte 9. O byte 5 parece ser a velocidade em hexadecimal, e o byte 9 parece ser um checksum.

Convertendo os valores do byte 5 para decimal:

Velocidade na telaHexadecimalDecimal
0,0 km/h (em espera ou pausa)0x000
2,0 km/h0x1420
3,0 km/h0x1D29
5,0 km/h0x3149

Tendo decompilado algumas partes do sistema Android anos atrás, lembrei que ao configurar a máquina no sistema métrico, internamente o aplicativo da Bowflex realiza a conversão do sistema métrico para imperial para se comunicar com a “UCB”. A placa controladora do motor parece usar o sistema métrico, e aparentemente há perda de precisão na conversão do sistema métrico para imperial e depois de volta para métrico (que é o que a controladora do motor espera), já que tudo é tratado com 1 decimal de precisão. Foi tão difícil fazer isso direito, Nautilus?

Tendo isso em consideração, e se for aplicado um fator de escala de 10, coincide perfeitamente com os valores enviados para a placa controladora do motor, portanto a fórmula seria:

Valor em decimal = Velocidade em km/h × 10

Interceptando mudanças na inclinação

Seguindo o mesmo processo que com a velocidade, é possível observar os seguintes valores enviados para a placa controladora do motor:

Inclinação na telaMensagem enviada
-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

Neste caso, o byte 6 parece ser a inclinação em hexadecimal, e confirma que o byte 9 é um checksum.

Convertendo os valores do byte 6 para decimal:

Inclinação na telaHexadecimalDecimal
-5°0x000
0x3250
0x8C140

A fórmula que faz coincidir perfeitamente os valores enviados para a placa controladora do motor é:

Valor em decimal = (Ângulo + 5) × 10

Checksum

Este parece ser um checksum simples e padrão em microcontroladores, somando todos os bytes da mensagem e ocorrendo um overflow ao chegar a 256. Uma representação simples seria algo como:

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

Ao utilizar uint8_t como tipo de retorno, o overflow ocorre naturalmente. Poderia ser utilizado um for loop somando os valores e retornando sum % 256, mas seria mais lento para os microcontroladores sem nenhum benefício real.

Próximos passos

  • Obter um entendimento lógico do processo de inicialização, ou pelo menos replicá-lo
  • Capturar interações da chave de segurança (a coisa vermelha que é colocada na roupa)
  • Interpretar as mensagens que a placa controladora do motor envia, que não devem diferir muito das mensagens que a botoneira envia

Tendo isso, já é possível replicar o funcionamento da botoneira, e com isso controlar a esteira a partir de um microcontrolador.

— Continua —

Conteúdo traduzido por o1-mini

©2022-2025 Sebastián Barrenechea. Todos os direitos reservados.

Construído com Astro v5.6.1.