arduino-simulator-unit-testing

Cómo realizar test unitarios en Arduino con Arduino Simulator

Hoy vamos a ver como hacer test automatizados en Arduino, y para ello comparto una de mis “armas secretas”, la librería Arduino Simulator

Supongamos que tienes una máquina que quieres poner en producción. Esta máquina tiene una botonera, un menú de usuario, un botón de parada de emergencia, un altavoz de alarma, sensores … y vete tu a saber que más.

Lógicamente, vas a querer probar que funciona correctamente. Cada vez que hagas un cambio, no vas a subir el código a la máquina real y ponerte a pulsar botones y comprobar que todo funciona ¿verdad? ¿!¿verdad?!?

arduino-simulation-meme

No, a ti lo que te gustaría es hacer una serie de pruebas automáticas que comprueben que el código funciona correctamente. Ahí es donde entra en juego Arduino Simulator.

Schedule

Imaginemos que tu máquina tiene que hacer ciertas cosas, cuando pasan una serie de eventos. Por ejemplo, si pulsamos el botón de emergencia, tiene que pararse y encender la alarma.

A esto lo llamaremos un caso de uso. Con Arduino Simulator podemos crear un caso de uso mediante un Schedule, que contiene las acciones que van a ocurrir en cada momento.

TEST_METHOD(MY_COOL_EXAMPLE_TEST)
{
   SIMULATION_STEP_US = 50;
   AT_MILLIS(2000, []() { digitalWrite(0, HIGH); });
   AT_MILLIS(5000, []() { digitalWrite(0, LOW); });
   AT_MILLIS(8000, []() { SIMULATE_INTERRUPT(0, HIGH); });
   AT_MILLIS(12000, []() { SAVE_STATE(); });

   EACH_MILLIS(1000, []() { digitalWrite(1, digitalRead(1) == LOW ? HIGH : LOW); });

   THEN([]() { analogWrite(0, 250); });

   for (size_t pwm = 250; pwm > 50; pwm -= 50)
   {
      THEN_AFTER_MILLIS(500, [pwm]() { ADC_Input[0] = pwm; });
   }

   simulate_seconds(50);

   Assert::IsTrue(Gpio_Status[0] == LOW);
   Assert::IsTrue(Gpio_Status[1] == LOW);
   Assert::IsTrue(Gpio_Status[2] == HIGH);
   Assert::IsTrue(PWM_Output[0] > 200);
   Assert::IsTrue(ADC_Input[0] <= 100);
}

Para ello podemos usar

  • AT_MILLIS, AT_MICROS
  • THEN
  • THEN_AFTER_MILLIS, THEN_AFTER_MICROS
  • EACH_MILLIS, EACH_MICROS

Testing

Puedes usar el entorno de Test de tu IDE para generar varios test unitarios, o casos de uso completos.

arduino-simulation-tests

Cuando tú, o alguien del equipo, modifique el código, lanzar todos los Test es tan sencillo como dar a un botón y comprobar que el comportamiento de la máquina sigue siendo el correcto.

Debug

Otra ventaja de simular tu máquina es que puedes usar el debug de tu IDE para comprobar el estado de la máquina.

arduino-simulation-debug

¡Se acabo usar Serial.println por todos lados para debuggear! Ahora puedes buscar los errores de forma sencilla, añadir breakpoints, y debuggear en condiciones.

Arduino Simulator

Arduino Simulator es una librería que nos permite testear el código en C++ de Arduino desde tu IDE favorito, como Visual Studio o Visual Studio Code.

No es, ni pretende ser, ni será nunca, una simulación del hardware de Arduino. Está pensado para simular tu código, no tu hardware.

Para ello, la “máquina” que simula tiene 16 GPIO, 8 salidas PWM, 8 entradas ADC, y 2 interrupciones. Aunque podéis ampliarlo fácilmente cambiando el código.

Es decir, que si tu máquina se pone a escribir registros y hacer cosas como PORTy = 0x00 Arduino Simulator te va a mandar a escaparriar ¡Y bien que hace!

En definitiva, para poder testear tu código, este debe ser testeable. Si no sabes como hacer tu código testeable, pásate por el resto del blog que es un tema que me gusta hablar recurrentemente.

Usando Arduino Simulator

Usar Arduino Simulator es muy sencillo:

  • Clonas el repo de la solución
  • En el fichero Sketch pones el código de tu máquina
  • Creas uno o varios Schedule
  • Ejecutas la simulación

El Schedule lo puedes crear en la función main() del fichero main.cpp aunque lo normal, si tu IDE te lo permite, es que crees varios Test, cada uno con sus acciones.

Para ejecutar la simulación tienes las funciones,

  • simulate_seconds()
  • simulate_milliseconds()
  • simulate_microseconds()

Por defecto la simulación se realiza cada microsegundo. En muchos casos este paso es innecesariamente pequeño. Si por ejemplo, tu código ‘hace cosas’ cada segundo, simularlo cada microsegundo es gastar tiempo para nada.

Para ello, puedes ajustar el paso de la simulación con la variable SIMULATION_STEP_US. Por ejemplo, puedes ponerlo a 100 para que la simulación se ejecute cada 100us.

Puedes acceder a las variables del hardware simulado con las colecciones

  • Gpio_Mode[] y Gpio_Status
  • ADC_Input[]
  • PWM_Output[]
  • Interrupt[]

Tambien puedes simular la ocurrencia de una interrupción con la función SIMULATE_INTERRUPT(numInterrupt, status). Ejecutarán el código que hayas definido en tu Sketch con atachInterrupt(...).

Finalmente, también puedes guardar estados de la máquina con la función SAVE_STATE, que crea un ‘snapshot’ con el estado de la máquina en un determinado momento.

Arduino Simulator es Open Source, y todo el código y documentación están disponibles en el repo https://github.com/luisllamasbinaburo/Arduino_Simulator