Curso Arduino y robótica: Eventos discretos y máquinas de estados

Material para realizar los ejercicios

  • 3 x Leds
  • 3 x Pulsadores
  • 6xResistencias 220 ohm

 

En esta sesión vamos a realizar un ejercicio relacionado con eventos discretos y con las máquinas de estados, de manera que vamos a implementar el modelo de programación asociado a este concepto.

Si nos hemos fijado en los ejercicios anteriores, al realizar una acción en bucle con un programa simple, estabamos mandando ejecutar una acción en cada iteración, aunque ésta fuera la misma. En este modelo de programación vamos a evitar ejecutar instrucciones dentro de un bucle que se ejecuta indefinidamente y supone una dependencia temporal.

 

Eventos discretos

Un problema que podremos observar en uno de nuestros programas anteriores es la lectura de entrada de un botón.

Tan simple como leer la entrada de un botón, si tuvieramos que ejecutar la acción de sumar una unidad a una variable creada en el programa, podremos comprobar que al pulsar el botón, nuestro dedo permanece durante un tiempo presionando aunque nosotros identifiquemos que ha sido instantáneo, pero obviamente no es así.

 



int LEDpin;
int buttonPin;
int sum;

void setup() {
  Serial.begin(9600);
  pinMode(buttonPin, INPUT);
  pinMode(LEDpin, OUTPUT);

  LEDpin = (int)(3);
  buttonPin = (int)(4);
  sum = (int)(0);

}

void loop() {
  if (digitalRead(buttonPin) == true) {
    digitalWrite(LEDpin, HIGH);
    sum = sum + 1;
    Serial.println(sum);
  } else {
    digitalWrite(LEDpin, LOW);
  }

}


Por lo que el hecho de apretar y soltar el botón rápidamente no es una solución a nuestro problema. Es decir, no estamos ejecutando un evento puntual discreto, sino que éste evento se extiende por el hecho de ejecutarse en bucle con una dependencia temporal.

Para resolver esta cuestión debemos hacer uso de variables auxiliares que definan un estado a nuestro programa. Vamos a utilizar la memoria de nuestro programa para definir cuando debería sumar puntualmente.



int LEDpin;
int buttonPin;
int sum;
boolean state;

void setup() {
  Serial.begin(9600);
  pinMode(buttonPin, INPUT);
  pinMode(LEDpin, OUTPUT);

  LEDpin = (int)(3);
  buttonPin = (int)(4);
  sum = (int)(0);
  state = (boolean)(false);

}

void loop() {
  if (state != digitalRead(buttonPin)) {
    state = digitalRead(buttonPin);
    if (state == true) {
      digitalWrite(LEDpin, HIGH);
      sum = sum + 1;
      Serial.println(sum);
    } else {
      digitalWrite(LEDpin, LOW);
    }
  }

}

Como vemos en la solución, haremos que nuestro estado del botón se almacene en una variable «state». Si apretamos el botón, al pasar el estado a un estado distinto del anterior, entonces ejecutaremos la acción y dentro de esta acción guardaremos la variable de estado antigua a la actual.

Si mantenemos el botón presionado, no hará nada, ya que habiamos modificado la variable de estado y al ser la misma no aplicaría ningún cambio. Hasta que finalmente soltemos el botón que si ejecutará el apagado del LED, pero nada más.

Para facilitar el modelo de eventos discretos hemos añadido un nuevo bloque y el mismo  sistema se podría diseñar de la siguiente manera.

 

Maquinas de estados

Una máquina de estados es aquella que evoluciona de un estado a otro, cuando detecta una acción que puede venir determinada por un sensor. También se le puede denominar como autómata finito, en el que nuestra maquina produce una salida en función de unas entradas.

Estos modelos son de gran interés en circuitos electrónicos digitales con entradas y salidas y deben cumplir las siguientes reglas.

  • La máquina debe tener definidos un número finito de estados.
  • El estado de la máquina debe quedar determinado por su estado actual y sus entradas actuales.
  • La máquina de estados es independiente del tiempo.

Antes hemos realizado un ejercicio en el que solo disponíamos de dos estados determinados por un botón.

Ahora vamos a realizar un ejercicio en el que vamos a aumentar el número de estados con 3 botones y 3 LEDs con la siguiente configuración electrónica.

En este ejercicio vamos a crear una máquina de estados similar a la de una máquina de bebidas que va a realizar lo siguiente.

  • Solamente podemos meter dos monedas a nuestra máquina, 1€ o 2€.
  • El botón «COIN_1€» indicará que hemos insertado una moneda de 1 euro  y «COIN_2€» una moneda de 2 euros.
  • El tercer botón será de «RESET» que interpretará que nos devuelva las monedas.
  • Los LEDs indicarán cuanto dinero llevamos acumulado en modo binario hasta un máximo de 7 euros.
  • Cuando sobrepasemos el valor de 7, representará que hemos llegado a obtener nuestra bebida y el contador volverá a 0€.
  • Solamente se puede pulsar un botón. Es decir, apretar los dos a la vez no significa otra entrada diferente a las definidas.

Entonces vamos a definir el número de estados diferentes que define nuestro sistema.

Y el comportamiento de cómo va a evolucionar el sistema se encuentra representada en la siguiente figura.

Para realizar este programa, tendremos el siguiente código.



int LED_1;
int LED_2;
int LED_3;
int COIN_1;
int COIN_2;
int RESET;
int state_BTN1;
int state_BTN2;
int state_RESET;
int sum;
int status;

// Describe this function...
void LED_Status(int status) {
  if (status % 2 == 1) {
    digitalWrite(LED_1, HIGH);
  } else {
    digitalWrite(LED_1, LOW);
  }
  status = status / 2;
  if (status % 2 == 1) {
    digitalWrite(LED_2, HIGH);
  } else {
    digitalWrite(LED_2, LOW);
  }
  status = status / 2;
  if (status % 2 == 1) {
    digitalWrite(LED_3, HIGH);
  } else {
    digitalWrite(LED_3, LOW);
  }
  status = status / 2;
  if (status > 0) {
    digitalWrite(LED_1, LOW);
    digitalWrite(LED_2, LOW);
    digitalWrite(LED_3, LOW);
  }
}


void setup() {
  pinMode(COIN_1, INPUT);
  pinMode(COIN_2, INPUT);
  pinMode(RESET, INPUT);
  pinMode(LED_1, OUTPUT);
  pinMode(LED_2, OUTPUT);
  pinMode(LED_3, OUTPUT);

  LED_1 = (int)(9);
  LED_2 = (int)(10);
  LED_3 = (int)(11);
  COIN_1 = (int)(2);
  COIN_2 = (int)(3);
  RESET = (int)(4);
  state_BTN1 = (int)(0);
  state_BTN2 = (int)(0);
  state_RESET = (int)(0);
  sum = (int)(0);

}

void loop() {
  if(digitalRead(COIN_1) != state_BTN1){
  	state_BTN1 = digitalRead(COIN_1);
  	  if (digitalRead(COIN_1) == true) {
      sum = sum + 1;
    }
    LED_Status(sum);

  }if(digitalRead(COIN_2) != state_BTN2){
  	state_BTN2 = digitalRead(COIN_2);
  	  if (digitalRead(COIN_2) == true) {
      sum = sum + 2;
    }
    LED_Status(sum);

  }if(digitalRead(RESET) != state_RESET){
  	state_RESET = digitalRead(RESET);
  	  sum = 0;
    digitalWrite(LED_1, LOW);
    digitalWrite(LED_2, LOW);
    digitalWrite(LED_3, LOW);

  }
}


Funciones

Como se puede ver hemos usado una función para reducir un poco el coste de nuestros programas. Podemos hacer uso de las funciones para no tener que programar demasiadas veces lo mismo y reutilizar partes de nuestro código si nos acostumbramos a el manejo de variables en todo el programa.

Recomendamos encarecidamente usar funciones con parámetros de entrada. Para desarrollar el uso de funciones con Ardublockly podéis acceder al siguiente enlace.

 

Las máquinas de estado son muy útiles para definir un esquema de cómo debe evolucionar nuestra máquina. El único problema es que en el caso de aumentar el número de entradas o salidas del sistema, nuestro espacio de estados aumenta exponencialmente y suele ser muy complicado desarrollar una solución simple.

Uno de los objetivos a la hora de programar es encontrar soluciones simples a problemas complejos. En el momento que nuestra solución de programación se convierte en un quebradero de cabeza, quizás es que no estamos planteando bien el problema en cuestión.

 

Una vez completado este tutorial, puedes acceder al siguiente nivel.

Puedes acceder además a los ejemplos de código que desarrollaremos durante el curso a través de nuestro Github.

Documentacion ZaragozaMakerSpace Github

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

1 × 5 =

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.