Construyendo máquinas de estado con Desarrollo Dirigido por Pruebas en C embebido
Las máquinas de estado son fundamentales para muchos sistemas embebidos, sin embargo su desarrollo puede ser complejo. Este artículo describe estrategias probadas para construir software de máquina de estado (SM) usando Desarrollo Dirigido por Pruebas (TDD), con foco en implementaciones C adecuadas para entornos con recursos limitados.
En su núcleo, un modelo SM consiste en estados, transiciones y acciones. Los estados representan condiciones, las transiciones definen el camino de un estado a otro —generalmente disparado por un evento— y las acciones capturan el comportamiento ejecutado durante entrada, salida o mientras se transiciona.
En máquinas de estado UML, las acciones pueden adjuntarse a entrada/salida de estado, la transición misma, o reacciones internas. Todos los formalismos de máquina de estado asumen un modelo de ejecución Run-To-Completion (RTC): cada evento se procesa completamente —incluyendo acciones de salida, acciones de transición y acciones de entrada— antes de manejar el siguiente evento.
Antes de sumergirnos en estrategias TDD, es esencial entender por qué TDD importa. TDD es un enfoque de desarrollo incremental que exige escribir un unit test que falle antes de cualquier código de producción. Los tests son pequeños, automatizados y sirven como especificación del comportamiento deseado. El ciclo TDD —derivado de "Test-Driven Development for Embedded C" de James Grenning— incluye:
- Escribir un test pequeño que falle.
- Ejecutar todos los tests; el nuevo test debe fallar.
- Escribir el código mínimo para pasar el test.
- Ejecutar todos los tests de nuevo para confirmar que el nuevo test pasa.
- Refactorizar para eliminar duplicación y mejorar claridad.
Considere el diagrama SM en Figura 1. Comienza en EstadoA y, al recibir el evento Alpha, transiciona a EstadoB, ejecutando xStateA(), effect() y nStateB() en secuencia. ¿Cómo validamos que este comportamiento es correcto?
Figura 1. Máquina de estado básica (Fuente: VortexMakes)
El enfoque de prueba más directo implica verificar la tabla de transiciones de la SM. Esto crea un caso de test por estado, estimulando la SM con eventos relevantes y asertiendo el estado resultante y acciones ejecutadas. Acciones complejas merecen tests dedicados. (Ver artículo "Testing state machines using unit test" para discusión en profundidad.)
Cada test sigue el patrón de cuatro fases xUnit:
- Setup – establecer precondiciones (ej. estado actual, evento, estado objetivo esperado, y secuencia de acciones).
- Exercise – disparar la SM con el evento.
- Verify – asertar que los resultados coinciden con expectativas.
- Cleanup – resetear la SM a su estado inicial (opcional).
Aunque esta estrategia funciona bien para SMs simples, comportamiento más complejo —como una cadena de transiciones donde un efecto solo es observable tras varios pasos— requiere testing de escenarios funcionales. En vez de checks aislados de estado, probamos una secuencia completa que ejerce los estados, eventos y acciones necesarios.
Figura 2 ilustra esto con una SM DoWhile que imita un bucle do-while tradicional. La máquina inicializa con valores por defecto vía x = 0 y output = 0, establece el contador de bucle con x++ o x = (x > 0) ? x-: x, e itera con i++ hasta que la guarda i == x falla, tras lo cual emite output = i.
Figura 2. La máquina de estado DoWhile (Fuente: VortexMakes)
Antes de codificar, creamos una lista de tests derivada de la especificación. Para DoWhile, la lista inicial incluye:
- Todos los datos establecidos a defaults al inicializar.
- Incrementar el atributo X.
- Decrementar el atributo X.
- Ejecutar bucle de una iteración.
- Ejecutar múltiples iteraciones.
- Manejar escenario sin iteración.
- Verificar condiciones out-of-bounds.
Empleamos Ceedling como sistema de build y Unity como harness de test ligero para código C. La SM se define en DoWhile.h y DoWhile.c, y la suite de tests reside en test_DoWhile.c. A continuación, un extracto conciso ilustrando el caso de test "iteración única".
Code Listing 1: Test de iteración única (Fuente: VortexMakes)
El test inicializa la SM, establece el conteo de iteración a cero, despacha evento Up para incrementarlo, dispara evento Start, y finalmente envía Alpha para ejecutar una iteración de bucle. Aserciones verifican que el atributo out iguala el número de iteraciones y que la SM permanece en EstadoC.
Extender el mismo test demuestra múltiples iteraciones (ver Code Listing 2). Un test separado (Listing 3) confirma que la SM no realiza iteraciones cuando Alpha se recibe sin un previo evento Up.
Code Listing 2: Test de múltiples iteraciones (Fuente: VortexMakes)
Code Listing 3: Test de no-iteración (Fuente: VortexMakes)
Los detalles de implementación no son el foco de este artículo, pero extractos de DoWhile.c y DoWhile.h ilustran un enfoque limpio switch-case para manejo de estados.
Code Listing 4: Fragmento de implementación DoWhile (Fuente: VortexMakes)
Code Listing 5: Fragmento de especificación DoWhile (Fuente: VortexMakes)
En resumen, TDD proporciona dos estrategias complementarias para desarrollo de máquina de estado: (1) prueba de transiciones estado-por-estado, y (2) prueba de escenarios funcionales que cubren múltiples estados y acciones. Estos métodos son agnósticos a lenguaje, agnósticos a herramientas, y particularmente adecuados para sistemas embebidos donde la lógica basada en estados domina.
Como C sigue siendo la lingua franca del software embebido, emparejarlo con Ceedling y Unity produce un flujo de trabajo TDD robusto. Adoptar estas prácticas conduce a software más flexible, mantenible y reutilizable que enfoques convencionales.
Todo el código y modelos están disponibles en el repositorio GitHub https://github.com/leanfrancucci/sm-tdd.git.
Incrustado
- Fiabilidad CNC mejorada: Herramientas de fresado modernas entregan velocidad, precisión y menores costes
- Tendencias de Manufactura 2021: Parte 2 – Perspectivas de Make UK
- Boy Machines: Avanzando producción pequeña escala con tecnología punta
- Automatización de Pruebas Impulsada por IA: Revolucionando QA para Sistemas ERP Modernos
- Proyecto Acelerar se lanza con externalización estratégica
- Problemas Comunes de Máquinas CNC y Soluciones Expertas
- Dominar las fresadoras verticales: ventajas, operaciones y por qué superan a las horizontales
- Evitando Errores Comunes al Comprar Máquinas CNC Usadas
- Explore Posibilidades Infinitas: Qué Puede Fabricar una Máquina CNC
- Logre acabados perfectos con las rectificadoras de precisión Okamoto
- Actualice CNC Okuma: sustitución de disquetes por USB y Ethernet