El sistema Linux de una pasarela Matter: RDK X5 SDK y Buildroot en WSL

Compilar el SDK de D-Robotics RDK X5 en WSL e integrar una pasarela en Rust en una imagen Buildroot: compilación cruzada, overlays, servicios, arranque automático y el kernel sin IPv6.

En el post anterior conté cómo escribimos el programa del gateway en Rust con rs-matter. Por ahora corre en un PC. Para convertirlo en un gateway que se pueda vender todavía falta una capa: el propio sistema Linux del gateway.

Las placas de desarrollo suelen venir con una imagen de Ubuntu que funciona nada más enchufarlas. La primera reacción de mucha gente es: ¿por qué no instalar el programa del gateway en Ubuntu y listo?

Para desarrollo, vale. Para producción, no. Este post recoge cómo compilamos el SDK de la RDK X5 de D-Robotics dentro de WSL y generamos la imagen del sistema del gateway con Buildroot, además de varios problemas que solo descubres cuando te pones manos a la obra.

Por qué no usar Ubuntu directamente

El propio SDK de la X5 de D-Robotics ofrece dos sistemas de archivos raíz: una imagen de Ubuntu 22.04 para desarrollo y Buildroot para producción. En el caso de un gateway, la diferencia salta a la vista:

Imagen de Ubuntu Sistema Buildroot a medida
Tamaño Varios GB Unos cientos de MB; la partición system tiene solo 250MB
Qué lleva dentro Un conjunto completo de paquetes de escritorio Solo lo que tú decidas incluir
Servicios de arranque Muchos, y no sabes bien cuáles están corriendo Todos a la vista y se pueden quitar
Actualizaciones apt, paquete a paquete Se sustituye la imagen completa del sistema; con particiones A/B se puede hacer rollback
Revisión de seguridad Hay que justificar un montón de software que no se usa Superficie de ataque pequeña, fácil de explicar

Un gateway es un equipo que se queda en casa del usuario y en el que nadie inicia sesión durante años. Lo que necesita es: arrancar el programa del gateway al encender, un sistema de solo lectura que no se corrompa por escrituras y volver automáticamente a la versión anterior si falla una actualización. En Buildroot todo eso es lo habitual; en Ubuntu, en cambio, cuesta bastante recortar.

Por qué empezar con la RDK X5

En el post sobre la elección del procesador principal dejamos la RDK X5 en «sirve, pero no encaja como gateway»: 10 TOPS de capacidad de IA que un gateway no aprovecha y un coste algo elevado.

Pero tiene una ventaja muy práctica: el SDK se puede descargar públicamente, con documentación oficial de compilación, recorte del sistema, arranque seguro, OTA A/B y almacenamiento seguro con OP-TEE. El SDK de Rockchip también se basa en Buildroot y el planteamiento es muy parecido. Así que, para la fase de personalización del sistema, usamos primero el SDK mejor documentado, el de la X5, para recorrer todo el flujo de principio a fin.

Una aclaración previa: en este post la imagen ya está compilada, pero todavía no la hemos grabado ni probado en una placa. La grabación y las pruebas reales vendrán en un próximo artículo, cuando tengamos la placa.

Entorno: una distro de WSL aparte

El SDK de la X5 LNX6.1.176_PL5.1_V2.0.0 requiere Ubuntu 22.04 y ocupa unos 9.5GB descomprimido. Yo uso Ubuntu 24.04 a diario y no quería mezclar las dependencias del SDK, así que instalé una distro aparte con el disco virtual en la unidad D:

wsl --install -d Ubuntu-22.04 --name x5build --location 'D:\wsl\x5build' --no-launch

Al ejecutar comandos wsl desde Git Bash hay que añadir MSYS_NO_PATHCONV=1; si no, Git Bash convierte las rutas que empiezan por / en rutas de Windows.

Componentes principales del SDK:

Componente Versión
Kernel de Linux 6.1.176
U-Boot 2022.10
ATF 2.8
Buildroot 2025.02
Sistema de archivos raíz glibc, SysV init
Toolchain cruzado ARM GNU Toolchain 11.3 (aarch64), incluido en el SDK

Descargar el código, instalar dependencias y hacer la primera compilación completa:

B=https://archive.d-robotics.cc/downloads/sdk/LNX6.1.176_PL5.1_V2.0.0/board_support_package
curl -O $B/platform_source_code.tar.gz.md5sum
curl -C - --retry 5 -O $B/platform_source_code.tar.gz   # 约 4.1GB,断了能续传
md5sum -c platform_source_code.tar.gz.md5sum
tar -I pigz -xf platform_source_code.tar.gz && cd platform_source_code

sudo build/install_host_deps.sh
sudo tar -xf toolchain/arm-gnu-toolchain-11.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz -C /opt/

./bd.sh lunch 8   # board_x5_soc_debug_config.mk
./bd.sh           # 第一次完整编译,约 12 分钟

lunch 8 es la configuración debug, con adb y herramientas de depuración; para producción hay que pasar a la configuración release de lunch 9.

Ojo también con la memoria: mi límite global de WSL es de 8GB y el SDK recomienda 16GB. De momento solo compilo el gateway y reempaqueto, y con 8GB basta; si más adelante hay que compilar todo Buildroot desde el código fuente y falta memoria, se cambia memory= en C:\Users\<用户名>\.wslconfig y luego se ejecuta wsl --shutdown. Ten en cuenta que esto apaga todas las distros de WSL.

Meter el programa del gateway en el sistema

Metí todo el flujo en un script, así que después de cambiar el código basta con una línea en Windows:

wsl -d x5build -- bash /mnt/d/<项目目录>/x5/build.sh

El script hace lo siguiente: sincroniza el código, compila en cruzado el programa del gateway, lo coloca en el sistema de archivos raíz y reempaqueta la imagen. Por el camino hay algunos detalles que merece la pena contar.

Sincronizar el código al sistema de archivos de Linux antes de compilar

El código del proyecto está en la unidad D de Windows, que en WSL corresponde a /mnt/d. Compilar Rust directamente en /mnt/d es mucho más lento, porque cada acceso a un archivo tiene que cruzar al sistema de archivos de Windows. Por eso el script primero usa rsync para copiar el código a un directorio del propio WSL (excluyendo target y .git) y compila allí.

Target glibc, no musl

Cuando elegimos el procesador principal, la idea era compilar con aarch64-unknown-linux-musl un binario enlazado estáticamente que sirviera tanto para Rockchip como para D-Robotics.

En la X5 lo cambiamos a aarch64-unknown-linux-gnu, con el linker apuntando al GCC 11.3 que trae el SDK:

CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/opt/arm-gnu-toolchain-11.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc \
  cargo build --release --target aarch64-unknown-linux-gnu

El motivo es que el sistema de archivos raíz de la X5 ya usa glibc. Al enlazar con el toolchain del SDK, cuya glibc es más antigua que la del firmware, el programa resultante funciona seguro en la placa, sin tener que lidiar con problemas de compatibilidad de musl.

Directorio overlay: restaurar primero y luego superponer

El SDK deja un directorio overlay para el sistema de archivos raíz, system/buildroot/prebuilt/boot-utils-runtime; lo que pongas ahí entra tal cual en la imagen. Nuestros propios archivos están en x5/overlay/ dentro del proyecto.

La primera vez que se ejecuta, el script guarda una instantánea del directorio overlay original del SDK. En cada compilación posterior, primero restaura la instantánea y después superpone nuestros archivos.

La ventaja es que es reversible: si borras un archivo de x5/overlay/, la imagen vuelve a quedar como en el SDK original, sin restos de la modificación anterior.

Desactivar los servicios de arranque que no hacen falta

Para desactivar servicios también se usa el overlay: se pone un script con el mismo nombre cuyo único contenido es exit 0, que sustituye al script de arranque original del SDK. De momento hemos desactivado estos:

Servicio Motivo
S50telnet Acceso remoto en texto plano, incompatible con normativas de ciberseguridad como la EN 18031 de la UE o la PSTI del Reino Unido
S40bluetoothd No usamos el Bluetooth integrado del módulo; Bluetooth Mesh va por un módulo aparte de Silicon Labs
S65ptp4l, S66phc2sys Sincronización horaria de precisión PTP, que un gateway no necesita
S90cam-service Servicio de cámara
S99iiod Servicio de sensores IIO

sshd y adb se quedan durante el desarrollo y se desactivarán para producción.

Arranque automático: el problema que trae un simple &

El programa del gateway tiene que arrancar solo al encender y volver a levantarse si se cae. La X5 usa SysV init, así que basta con escribir un /etc/init.d/S95gateway.

Lo primero a tener en cuenta: en la placa no hay bash. Desde la V2.0.0, el SDK de la X5 ya no incluye bash, así que los scripts del sistema tienen que ser POSIX sh.

El segundo problema es más sutil. Lo más directo es mandar el bucle supervisor a segundo plano en el script de arranque:

while true; do /usr/bin/gateway; sleep 2; done &

Parece correcto, pero durante el arranque, cuando rcS termina de ejecutar todos los scripts y sale, envía la señal de hangup a los procesos en segundo plano que lanzó, y el bucle muere con ellos. El programa del gateway arranca un instante al encender y luego desaparece.

Lo correcto es usar start-stop-daemon -b, que separa el demonio de la sesión de rcS:

LOOP="cd /userdata/gateway && while true; do /usr/bin/gateway </dev/null 2>&1 | logger -t gateway; sleep 2; done"
start-stop-daemon -S -b -m -p /var/run/gateway.pid -x /bin/sh -- -c "$LOOP"

Si el programa termina de forma anómala, se reinicia a los 2 segundos, y su salida va al log del sistema a través de logger. En la X5 no hay logread; los logs están en /userdata/log/kernel/message y se consultan con grep gateway. Los datos del gateway van en /userdata/gateway/data/: esa partición es escribible, mientras que la partición del sistema es de solo lectura.

Dos configuraciones por defecto del SDK que hay que cambiar

El kernel viene sin IPv6

Este fue, con diferencia, el problema más grave de todo el proceso.

La configuración por defecto del kernel en el SDK de la X5 dice # CONFIG_IPV6 is not set, tanto en hobot_x5_soc_defconfig como en hobot_x5_soc_perf_defconfig.

Y Matter se apoya en IPv6: el mDNS para descubrir dispositivos y la comunicación entre Apple Home y el gateway van todos por IPv6. Con el kernel por defecto del SDK, el programa del gateway arranca, pero Apple Home nunca lo encuentra.

El script revisa ambas configuraciones; si IPv6 no está activado, lo cambia a CONFIG_IPV6=y y recompila el kernel con ./bd.sh boot. Cada vez que se cambie a una versión nueva del SDK, hay que volver a comprobarlo.

Tabla de particiones: fuera la partición app de 700MB

La tabla de particiones por defecto del SDK incluye una partición app de 700MB que solo contiene los programas de ejemplo de D-Robotics, que el gateway no necesita. El script la elimina de x5-soc-debug-gpt.json y x5-soc-release-gpt.json con un pequeño fragmento de Python.

En cuanto cambia la tabla de particiones, hay que recompilar todo para generar una GPT nueva. El script comprueba si el cambio ya está hecho y, si es así, se lo salta, para no hacer una compilación completa cada vez.

Sin ella, la partición system queda en 250MB y userdata en solo 50MB. Para la tabla de dispositivos del gateway y los datos de Matter, 50MB bastan por ahora, pero antes de producción hay que replanificarlo según las necesidades reales.

Lo que sale de la compilación

Archivo Uso
out/product/emmc_disk.img Imagen eMMC completa; se puede grabar por fastboot, DFU o TFTP
out/product/emmc_disk.simg Imagen sparse, más pequeña, pero no sirve con DFU
out/product/uart_usb/ Firmware de arranque para grabar chips vírgenes
out/product/*.img Imágenes individuales de cada partición

La herramienta de grabación xburn está en software_tools/download_tools/ dentro del directorio de descarga del SDK.

Lo que falta

Compilar la imagen es solo el primer paso. Hasta llegar a producción quedan estas tareas:

  1. Grabarla en una placa y probarla: arranque automático, IPv6, commissioning de Matter y logs, todo hay que repasarlo en hardware real.
  2. Particiones A/B y OTA: el SDK trae actualizaciones A/B sin interrupciones. ./bd.sh otapackage genera paquetes de actualización completos y diferenciales, y en el dispositivo hay una interfaz C en libupdate.so; nuestro programa se encarga de descargar y llamarla. Pero por defecto hay una sola partición, así que hay que modificar la tabla de particiones para activarlo. Así no hace falta integrar RAUC ni SWUpdate por separado.
  3. Pasar a la configuración release: quitar adb y las herramientas de depuración, y desactivar sshd.
  4. Arranque seguro: las verificaciones de cada etapa posterior a U-Boot tienen que usar nuestras propias claves de firma, grabadas en eFuse. Grabar eFuse es irreversible, así que primero hay que validarlo en unas pocas muestras.

Para terminar

Mirando atrás, en esta fase lo que llevó tiempo no fueron los comandos de compilación en sí; el bd.sh del SDK ya los encapsula muy bien. Lo que de verdad llevó tiempo fueron los valores por defecto: el kernel sin IPv6, la placa sin bash, los procesos en segundo plano que rcS mata y una partición de ejemplos de 700MB metida en la tabla de particiones. Ninguno da error; simplemente hacen que el gateway deje de funcionar en silencio en algún punto.

El siguiente paso: en cuanto llegue la placa, grabar la imagen y conectar el módulo Bluetooth de Silicon Labs, para que el gateway controle por primera vez una luz Bluetooth Mesh sobre hardware real.

Si tú también estás haciendo gateways o dispositivos IoT con la X5 de D-Robotics, Rockchip u otro SDK basado en Buildroot, me encantará intercambiar experiencias: linktr.ee/weiyii

Leer y compartir con WeChat

Código QR del artículo

Escanea para abrir y comparte desde el menú de WeChat.