Arquitectura
Con un solo árbol de fuentes se compila el firmware de nueve placas de dos familias de chip. Lo que lo hace posible no es ingenio, sino una forma que se comprueba en cada commit: un módulo puede usar los que están por debajo y ninguno por encima, y la compilación falla cuando eso deja de ser verdad.
Igual que la página de seguridad, esta describe un repositorio que todavía no puedes abrir. La comprobación se ejecuta en cada commit; que puedas ejecutarla tú es un paso de la hoja de ruta.
El orden que nada puede subir
Veintidós módulos en un único orden, el consumidor primero. Un módulo incluye sus propias cabeceras y las de los módulos que tiene debajo; un include que apunte hacia arriba no pasa la comprobación, en el gancho del commit y otra vez en integración continua.
El remedio para un include hacia arriba nunca es una excepción. Es bajar la cabecera, o invertir la llamada con una interfaz que el módulo de abajo declare, que es como el panel de administración llega a la aplicación en marcha sin que la aplicación sepa que existe un panel. Hay un mecanismo para fijar excepciones, una por una. Está vacío, y lo importante es precisamente que esté vacío: en el firmware no hay dos módulos que dependan el uno del otro.
Los includes se escriben desde la raíz del árbol de fuentes, así que una ruta relativa que se salga de su propio directorio no pasa la misma comprobación. Dentro de un módulo la comprobación va más allá y pone vallas entre unos grupos de ficheros y otros, y rechaza un ciclo entre cabeceras. El dibujo de la sección siguiente se genera con esas mismas líneas de include, y la comprobación falla mientras la copia que se guarda en el repositorio esté desfasada.
El grafo
El grafo de módulos se genera a partir de los propios includes en vez de dibujarse a mano, y eso es lo que evita que el diagrama y el código que describe se contradigan: la herramienta que lo dibuja es la que impone el orden.
Montaje y control
Los archivos que nombran cada pieza de una placa y la gobiernan. Están arriba porque alcanzan todo lo de abajo; nada los alcanza a ellos.
- El cableado de la placa
boards/Un archivo por dispositivo: qué pantalla, qué controlador de energía, qué botones tiene de verdad esta placa. usaui/diag/hal/config/ - El compañero de banco
tools/Una segunda placa que hace de ordenador, para probar la radio y la página de administración contra algo real. usaplatform/No en todas las compilaciones - La máquina de estados
main.cppArranque y un solo bucle: dormido, bloqueado, abierto, escribiendo, administración, y cada paso entre ellos. usadebug/states/web/ui/reset/net/ble/diag/vault/totp/i18n/crypto/hal/drivers/time/platform/ota/config/ - La consola del desarrollador
debug/Una línea de comandos por serie que conduce cada pantalla. No se compila en una versión de uso, y cada compilación se comprueba. usastates/web/ui/reset/net/ble/diag/vault/totp/i18n/crypto/hal/drivers/time/platform/config/No en todas las compilaciones Probado en un PC
Pantallas y superficies
Todo lo que una persona mira o donde escribe, en el dispositivo o en un navegador.
- Las pantallas
states/Qué muestra cada estado y qué hace una pulsación ahí: el dial del PIN, la bóveda, el árbol de ajustes, el primer arranque. usaweb/ui/reset/net/ble/diag/vault/totp/i18n/crypto/hal/drivers/time/platform/config/Probado en un PC - La página de administración
web/Una página que el dispositivo sirve desde su propio Wi-Fi, para editar en lote y actualizar. Apagada hasta que la enciendes, y nunca junto a la radio. usaui/reset/net/ble/diag/vault/totp/i18n/crypto/hal/drivers/time/platform/config/Probado en un PC - La biblioteca de dibujo
ui/Cada píxel: listas, diálogos, iconos, marcas. Un juego de piezas que encaja en nueve pantallas, del cuadrado de tinta a la táctil en color. usable/vault/brands/totp/i18n/crypto/hal/time/platform/config/Probado en un PC
Servicios del dispositivo
Lo que hace el dispositivo una vez abierto: escribir, conectar, borrar, medirse.
- El borrado
reset/Un único sitio que borra todo (bóveda, emparejamientos, ajustes) para que cada botón y cada página que borra haga exactamente lo mismo. usanet/ble/vault/platform/Probado en un PC - Los recados de red
net/Unirse a un Wi-Fi lo justo para poner el reloj, y recordar el nombre con el que responde la página de administración. usable/vault/crypto/time/platform/Probado en un PC - El teclado en que se convierte
ble/El teclado Bluetooth que ve tu ordenador. Solo envía pulsaciones: no hay camino por el que pudiera leer lo que escribes. usatime/platform/Probado en un PC - Los instrumentos
diag/Lo que el dispositivo mide de sí mismo: batería, memoria libre, cuánto tardó en dibujarse un fotograma. usahal/drivers/time/platform/Probado en un PC
Datos y secretos
Lo que el dispositivo guarda y lo que lo cierra. Por debajo de cada pantalla y cada radio: la bóveda no depende de lo que la dibuja ni de lo que la envía.
- La bóveda
vault/Tus credenciales en la memoria, cada una en su propio registro sellado, y el único código que las abre. usabrands/totp/crypto/hal/time/platform/config/Probado en un PC - La búsqueda de logotipos
brands/Convertir una dirección web en el logotipo que reconoces en la fila, desde una tabla incrustada en el firmware. No se descarga nada. usaplatform/config/Probado en un PC - Las seis cifras
totp/El código de doble factor de una cuenta, y la regla de reloj que se niega a mostrarlo cuando la hora no es de fiar. usatime/platform/config/Probado en un PC - Las palabras
i18n/Cada frase que el dispositivo puede mostrar, en cada idioma que habla, en una sola tabla. usaplatform/Probado en un PC - La cerradura
crypto/Convertir tu PIN en una clave, y la clave en bytes sellados. La aritmética, y nada más: no sabe nada de pantallas ni de registros. usaplatform/Probado en un PC
Hardware y cimiento
Los chips, y la única puerta a los servicios del propio chip. Aquí es donde nueve placas distintas se encuentran con el resto del firmware.
- El traductor
hal/Un solo vocabulario para nueve placas (dibujar, leer un botón, medir la batería), sea cual sea el chip que haya debajo. usadrivers/time/platform/config/Probado en un PC - Los controladores de chip
drivers/Un controlador por chip real: pantalla, controlador de energía, reloj, sensor de movimiento, teclado. Escritos aquí en lugar de prestados. usatime/platform/config/Probado en un PC - El reloj y su confianza
time/Qué hora cree el dispositivo que es, de dónde viene, y si eso basta para mostrar un código de doble factor. usaplatform/config/Probado en un PC - El cimiento
platform/La única puerta a los servicios del propio chip (tareas, tiempo, registro, almacenamiento, sueño) para que nada de arriba los llame directamente. usaota/Probado en un PC - La regla de actualización
ota/Si un firmware ofrecido puede instalarse. Aritmética sobre números de versión; no depende absolutamente de nada. no usa ningún otro módulo Probado en un PC - Qué es esta placa
config/Los hechos de la placa, fijados al compilar: tamaño de pantalla, tamaño de memoria, qué piezas existen siquiera. no usa ningún otro módulo
Apunta a un bloque, o tócalo, y se encienden los bloques de los que depende. Todos quedan por debajo.
ble/, que dependen entre sí por diseño y están fijadas por su nombre. Dos núcleos, y qué no se permite en cada uno
El chip tiene dos núcleos, y el reparto entre ellos es una regla y no una tendencia.
Núcleo 1
Recoge la última cifra del PIN
Dibuja el progreso, sin esperar a nada
Dibuja la bóveda
Núcleo 0
En reposo
Deriva la clave: PBKDF2-SHA256, 175 000 iteraciones, alrededor de 1,5 s
Abre el índice de la bóveda
Reloj
80 MHz
240 MHz
80 MHz
-
Núcleo 1: lo que miras
El bucle principal, la máquina de estados, la pantalla y los botones. No se bloquea nunca esperando a una radio ni al almacenamiento, porque cada milisegundo que pasa esperando es un milisegundo en que notas lento el dispositivo en la mano.
-
Núcleo 0: lo que esperas
El host de Bluetooth, el Wi-Fi y el servidor web, el sistema de ficheros y la criptografía. Derivar la clave tarda alrededor de segundo y medio: ocurre aquí, con un indicador de progreso dibujado en el otro núcleo, y al reloj más rápido del chip, mientras que todas las pantallas funcionan al más lento.
Una cola o un cerrojo al que cualquiera de dos tareas pueda llegar primero se construye antes de que exista la tarea que lo consume, o dentro de una llamada que ocurre una sola vez. Mirar si es nulo y crearlo en ese caso es el fallo que parece correcto: los dos núcleos pasan esa comprobación, los dos construyen uno, y a uno de ellos no lo espera nadie.
Un árbol, nueve placas
Una placa es una configuración, no una copia del firmware.
Cada placa tiene un directorio con su mapa de pines, su pantalla, su controlador de alimentación, su tabla de particiones y sus propias cifras, entre ellas la estimación de sueño que imprime la página de mediciones. El firmware se compila sobre ESP-IDF 6.0.3 sin ningún core de Arduino dentro, en C++26, y con cada aviso del compilador tratado como un error. Un guardián lee las nueve placas y falla ante una incoherencia entre ellas: treinta y tantos invariantes, entre ellos que cada compilación lleve exactamente un factor de trabajo para la derivación de clave, y que la consola serie no pueda compilarse dentro de una imagen de producción.
Cada placa tiene hasta cuatro compilaciones: una abierta de desarrollo, una de depuración que no se publica nunca, una abierta de producción y una endurecida que en su primer arranque quema fusibles de un solo uso, algo que no se puede deshacer. Tres placas no tienen compilación endurecida, porque 4 MB de flash no dan para dos ranuras de actualización y un gestor de arranque firmado.