Blog

Convertir un Sigma RECO 80 en un RECO 81

José San JuanCiclismoDispositivosSigmaRECO 80RECO 81Reverse Engineering
Sigma RECO 81

El SIGMA RECO 80 LINK y el RECO 81 LINK son, al menos externamente, dos luces traseras con radar muy parecidas. Tan parecidas que te puede pasar como a mí y equivocarte a la hora de comprarlas en la web de Sigma. La diferencia más evidente está en los modos de iluminación. El RECO 80, destinado al mercado alemán que debe cumplir con la StVZO, solo permite seleccionar HIGH y RADAR ONLY. El RECO 81 internacional añade cuatro modos más: PELOTON, DAY FLASH, NIGHT FLASH y ECO FLASH.

Después de asumir mi torpeza a la hora de hacer el pedido y tratar de contactar con Sigma para ver si existía un método para convertir un modelo en el otro, decidí usarlo tal cual. El soporte de Sigma jamás me contesto a ninguno de los emails que les envié pero eso es otra historia.

Pero después de un tiempo dándole vueltas me pregunté ¿SIGMA fabrica realmente dos dispositivos diferentes, o el RECO 80 lleva dentro los modos del RECO 81 y simplemente los tiene bloqueados? Esa fue la pregunta con la que empecé. Después de analizar la aplicación, inspeccionar BLE, desmontar el firmware y finalmente seguir la señal hasta la PCB, pude confirmar que ambos modelos utilizan firmware común y que los seis modos están presentes en el RECO 80. La diferencia está en un selector físico. Durante el arranque, el firmware lee una entrada que determina si debe comportarse como RECO 80 o como RECO 81. En mi unidad, abrir ese contacto de la PCB marcado como S1 hace que el dispositivo pase de Product ID 0x39 a 0x42 y arranque con el comportamiento completo del RECO 81. No fue necesario parchear el firmware, modificar el bootloader ni instalar software del RECO 81.

Lo primero que hice fue leer el perfil GATT (Generic Attribute Profile) original de mi unidad, que mostraba lo siguiente:

CampoValor original
Nombre BLERECO 80 LINK
Modelo115300
Hardware1.20
Firmware1.04
Product ID0x39
Serie0039-00000058-000000-00-D9B387F1D6CA
Modos declarados06

El último campo fue la primera cosa que me llamó realmente la atención. El dispositivo se identificaba como RECO 80 y su botón físico solo permitía elegir dos modos, pero a través de GATT declaraba tener seis. No demostraba todavía que los otros cuatro estuviesen ahí, pero era una pista.

Empezando por la App

Analicé SIGMA RIDE para Android, paquete com.sigmasport.link2, versión 3.0.41, utilizando JADX, apktool y ripgrep. La aplicación distingue los dos dispositivos mediante identificadores diferentes:

ModeloProduct IDTipo de firmware
RECO 80 LINK57 / 0x39reco80
RECO 81 LINK66 / 0x42reco81

También localicé las características BLE relacionadas con los modos de iluminación:

FunciónUUIDFormato
Número de modosD6E8C70B-E33A-4533-A08C-15DA13C04B57Un byte, solo lectura
Estado/evento de modo165E3F0D-5824-4FDD-B386-220C5CA15899Un byte, lectura/escritura/indicación

El protocolo resultó ser bastante simple. El modo se indica como un único byte. Tampoco apareció en la aplicación una tabla que relacionase explícitamente PELOTON, DAY FLASH, NIGHT FLASH y ECO FLASH con valores numéricos, ni encontré una ruta de producción desde la interfaz que enviase directamente esos modos. La limitación no parecia venir por de App.

Probando la vía BLE

El siguiente paso fue observar qué hacía el dispositivo sin modificar nada. En la configuración original, cada pulsación del botón generaba únicamente estos valores:

00 = HIGH
05 = RADAR ONLY

Eso dejaba 01, 02, 03 y 04 como candidatos bastante evidentes. Probé esos cuatro valores. Las escrituras funcionaban, podía escribir 01, 02, 03 o 04, el dispositivo respondía y, al volver a leer, devolvía exactamente el valor que acababa de enviar. Pero físicamente no ocurría nada, los LED no cambiaban. La conclusión es que el estado comunicado por BLE y el estado físico de los LED no estaban necesariamente ligados. Había otra capa dentro del firmware decidiendo si ese modo debía ejecutarse realmente. Había que bajar un nivel más.

El firmware resultó ser el mismo

El servicio oficial de actualizaciones de SIGMA devolvía el mismo paquete 2.02 tanto para reco80 como para reco81:

Radar_RL_260528_OTA_v2.02-rc-for-mp.zip

El zip contiene una única aplicación Nordic Secure DFU. Al inspeccionar radar.bin apareció una evidencia clara: el mismo binario contiene las cadenas

RECO 80 LINK
RECO 81 LINK

Todo apuntaba a una única aplicación capaz de comportarse como ambos productos. Pero entonces ¿cómo decide elf firmware si es RECO 80 o un RECO 81?

Rebuscando en el firmware

Cargué radar.bin como ARM Thumb con dirección base 0x28000 y empecé a seguir cadenas, referencias y flujo de control con Rizin. En el binario no hay nombres de funciones que faciliten el trabajo. Las funciones tuve que identificarlas por comportamiento y algo de suerte.

Una pequeña función que acabó siendo importante

Una de las primeras piezas interesantes apareció en 0x3ff70. La función era ridículamente pequeña:

0x3ff70  cargar dirección 0x20003e34
0x3ff72  leer un byte
0x3ff74  retornar

Es decir, simplemente devuelve el byte almacenado en 0x20003e34. Por sí sola, esta función no dice demasiado. Lo importante eran sus referencias cruzadas. Aparecía utilizada desde suficientes zonas distintas del firmware como para que resultase poco probable que fuese un estado temporal. Parecía un flag global.

El flag se inicializa leyendo hardware

Seguí el valor hacia atrás hasta la secuencia de inicialización. Entre 0x40032 y 0x40048, el firmware hace aproximadamente lo siguiente:

configurar entrada 0x1b con parámetro 3
leer entrada 0x1b
normalizar el resultado a 0 o 1
guardar el byte en 0x20003e34

0x1b es 27 en decimal. La placa utiliza un nRF52832, así que la entrada corresponde a P0.27. El programa configura P0.27 como entrada, lo lee y transforma el resultado en el flag almacenado en 0x20003e34.

El flag selecciona exactamente 0x39 o 0x42

La siguiente referencia fue mucho más concluyente. La función situada en 0x413fe consulta el flag y selecciona uno de dos Product ID:

if (model_flag != 0) {
    product_id = 0x39;  // 57, RECO 80
} else {
    product_id = 0x42;  // 66, RECO 81
}

Aquí la coincidencia era demasiado obvia para ser accidental. 0x39 y 0x42 eran exactamente los identificadores que ya había recuperado previamente la App. La aplicación distinguía RECO 80 y RECO 81 mediante esos valores, y ahora el firmware estaba utilizando un flag derivado directamente de un GPIO para decidir cuál de los dos anunciar. Pero había más. Otra rutina situada aproximadamente en 0x4446a consulta el mismo flag para elegir entre las cadenas embebidas:

RECO 80 LINK
RECO 81 LINK

que luego son utilizadas por Device Information. No estaba controlando una capacidad aislada, estaba seleccionando la identidad del dispositivo durante el arranque.

El mismo flag aparece delante de los patrones de iluminación

Faltaba conectar esa identidad con los modos que estaba intentando desbloquear.

El manejador que comienza en 0x3f750 procesa eventos internos relacionados con la iluminación. Dentro de él aparecen caminos separados para los valores internos 3, 4, 5 y 8.

Las ramas localizadas en:

0x3f7cc
0x3f7e8
0x3f804
0x3f826

siguen esencialmente el mismo patrón:

if (is_reco80()) {
    return;
}

configure_pattern(...);
apply_pwm(...);

Es decir: si el flag identifica al dispositivo como RECO 80, la función abandona el flujo antes de llegar a la configuración física del patrón. Si se identifica como RECO 81, continúa hacia distintas rutinas de configuración de iluminación. En esas rutas aparecen constantes como 0x0b, 0x0c y 0x0f, relacionadas con configuraciones diferentes de los patrones. También localicé una máquina de seis estados alrededor de 0x3f9a4. Acepta valores entre 0 y 5, pero consulta el mismo flag para redirigir una transición restringida cuando el modelo es RECO 80. Esto encajaba exactamente con lo que había observado externamente. Los seis estados existen. El firmware sabe trabajar con ellos. Lo que cambia es si el dispositivo tiene permiso para alcanzar las rutinas que terminan aplicándolos sobre el hardware.

Del firmware a la PCB

Llegados a este punto, el análisis parecía claro: si conseguía cambiar el estado que el firmware lee en P0.27 durante el arranque, deberían cambiar simultáneamente la identidad del dispositivo y el acceso a los modos.

En la placa encontré dos procesadores principales:

  • Un ARTERY AT32F403A, asociado al radar.
  • Un Nordic N52832/nRF52832 QFN48, encargado de la aplicación BLE.

Utilizando el pinout oficial del nRF52832 localicé P0.27 en el pin físico 39. Después seguí visualmente las pistas. Después seguí visualmente las pistas hasta un pad marcado en la PCB como:

S1
Foto de la placa

Abrí el contacto y ahí apareció magia. El identificador BLE cambió de:

390000390000005800000000d9b387f1d6ca

a:

420000420000005800000000d9b387f1d6ca

Los dos campos de producto habían pasado de 0x39 a 0x42. El dispositivo había cambiado de identidad.

Un nuevo inventario GATT confirmó el resultado:

CampoAntesCon S1 abierto
Product ID0x390x42
Modelo115300115301
Prefijo de serie00390042
Hardware1.201.20
Firmware1.041.04
Modos declarados0606

El hardware y el firmware seguían siendo exactamente los mismos. Lo que había cambiado era la identidad seleccionada durante el arranque.La comprobación final fue extremadamente sencilla: pulsar el botón.

Antes de abrir S1, el ciclo era:

00 <-> 05

Después de abrirlo:

00 -> 01 -> 02 -> 03 -> 04 -> 05 -> 00

Los seis estados estaban ahora accesibles desde el propio botón físico.

Conclusión

Los modos adicionales del RECO 81 no están ausentes del firmware del RECO 80. Están implementados en el mismo binario, pero permanecen inaccesibles cuando el dispositivo arranca con la identidad RECO 80. La selección se realiza mediante una señal de hardware leída durante el arranque:

S1 cerrado -> RECO 80 -> Product ID 0x39 -> HIGH y RADAR ONLY

S1 abierto -> RECO 81 -> Product ID 0x42 -> los seis modos

Por tanto, no fue necesario modificar, parchear ni intercambiar firmware. Abrir S1 fue suficiente para que el firmware común arrancase utilizando la configuración del RECO 81. Eso no significa que sea una modificación inocua: es una intervención física que puede afectar a la homologación, la garantía, la estanqueidad y la legalidad del dispositivo. Restaurar eléctricamente S1 debería hacer que el firmware vuelva a detectar la configuración RECO 80 después de un reinicio.