1
Máquina
de Estados Finitos – Sistema de Puertas automáticas
1.1 Definición
1.2 Estados
1. Estado Cerrado (totalmente Cerrado)
2. Estado Abriendo
3. Estado Abierto (totalmente Abierto)
4. Estado Cerrando
1.3
Transiciones
a. De
Cerrado a Abriendo: Se solicitó abrir. Puede ser por un botón,
por detectar cercanía, por detectar movimiento, o por mensaje de otro
dispositivo (serial)
b. De
Abriendo a Abierto: Se llegó hasta el fin, accionamiento del
botón “final de carrera”
c. De
Abierto a Cerrando: Se solicita cerrar. Puede ser por un botón, por
no detectar cercanía o por no detectar movimiento (y por ende, un tiempo ha
transcurrido), o por mensaje de otro dispositivo. Sugiero que deba haber un temporizador.
1.4 Componentes y conexiones
1. Sensor de Apertura (x1, x2) à Sensor de movimiento (PIR) o de distancia
(Ultrasonidos HC-SR04).
2. Sensor de límite totalmente Abierto
(x1) à Botón
3. Sensor de Cierre (x0) à Temporizador
4. Sensor de límite totalmente Cerrado
(x1) à Botón
5. Relé para el motor de apertura (x1) à Relé, alternativamente un LED
6. Relé para el motor de cierre (x1) à Relé, alternativamente un LED
1.5 Implementación
1.5.1
Sensor
de un sólo lado
1. Primero, preparar trucos para
programar más fácilmente: |
//
Trucos de C++ #define
ENTER "\n" template<class
CualquierTipo> inline Print &operator
<< (Print &izq, CualquierTipo der ) {
izq.print( der ) ;
return izq ; } |
2. Inicializar librerías. Creo que no
necesitamos ninguna, así que saltearemos esta parte. |
3. Generar los pines. |
#define PIN_DETECTOR 3 #define PIN_FINAL_ABIERTO 4 #define PIN_FINAL_CERRADO 5 #define PIN_RELE_ABRIR 6 #define PIN_RELE_CERRAR 7 |
4. Creación de variables para
dispositivos complejos. Como no hay, salteamos. |
5. Creación de variables de medición
de sensores |
float _medicionPersonaCerca
= 0 , _medicionFinalAbierto
= 0 ,
_medicionFinalCerrado = 0 ; |
6. Creación de las variables de los
estados |
float _tiempoEstado = 0.0f ; int _estado = 0 ; String _pcDice = "" ; |
7. Definición de los Estados para la
máquina |
#define ESTADO_CERRADO 0 #define ESTADO_ABRIENDO 1 #define ESTADO_ABIERTO 2 #define ESTADO_CERRANDO 3 |
8. Establecemos el setup() básico: |
void setup() {
Serial.begin( 9600 ) ; Serial
<< "arduino:conectado" << ENTER ; Serial.setTimeout(
10 ) ; |
9. Definimos modos de los pines e
inicializamos dispositivos complejos, si los hubiere. |
pinMode( PIN_DETECTOR,
INPUT ) ; pinMode(
PIN_FINAL_CERRADO, INPUT ) ; pinMode(
PIN_FINAL_ABIERTO, INPUT ) ; pinMode(
PIN_RELE_ABRIR, OUTPUT ) ; pinMode(
PIN_RELE_CERRAR, OUTPUT ) ; |
10. Habilitamos el primer estado,
cerramos la función setup() y comenzamos la loop(). |
fnCambiarAEstadoCerrado() ; } void loop() { _pcDice =
"" ; if( Serial.available()
) { _pcDice =
Serial.readString() ;
_pcDice.trim() ; } |
11. Armamos la máquina de estados
finitos. |
if( _estado
== ESTADO_CERRADO ) fnEstadoCerrado()
; else if(
_estado == ESTADO_ABRIENDO ) fnEstadoAbriendo()
; else if(
_estado == ESTADO_ABIERTO ) fnEstadoAbierto()
; else if(
_estado == ESTADO_CERRANDO ) fnEstadoCerrando()
; ; delay( 100 )
;
_tiempoEstado = _tiempoEstado + 100.00f ; } |
12. Pensemos en cada estado. En el
estado cerrado, estaremos esperando a que un usuario se acerque a un lado de
la puerta (y/o al otro), o de la computadora venga algún mensaje adecuado. |
void fnEstadoCerrado() { _medicionPersonaCerca
= digitalRead( PIN_DETECTOR ) ; if(
_medicionPersonaCerca == HIGH ) { return
fnCambiarAEstadoAbriendo() ; } if( _pcDice
== "abrir" ) { return
fnCambiarAEstadoAbriendo() ; } } |
13. Estado abriendo. Tenemos que esperar
a que la puerte llegue a su destino. |
void fnEstadoAbriendo() {
_medicionFinalAbierto = digitalRead(PIN_FINAL_ABIERTO); if(
_medicionFinalAbierto == HIGH ) { return
fnCambiarAEstadoAbierto() ; } } |
14. Estado abierto. Esperamos a que
pase el tiempo, o a que de la computadora venga un mensaje. |
void fnEstadoAbierto() { if(
_tiempoEstado >= 3000 ) { return
fnCambiarAEstadoCerrando() ; } if( _pcDice
== "cerrar" ) { return
fnCambiarAEstadoCerrando() ; } } |
15. Estado cerrando, como el estado
abriendo, deberemos esperar a que llegue a su destino. |
void fnEstadoCerrando() {
_medicionFinalCerrado = digitalRead(PIN_FINAL_CERRADO); if(
_medicionFinalCerrado == HIGH ) { return
fnCambiarAEstadoCerrado() ; } } |
16. Ahora pensemos en las
transiciones. Son todas parecidas, al menos en este proyecto. En las
transiciones deberemos: a. Cambiar el estado. b. Reinicializar el tiempo. c. Avisar el cambio a la PC. d. Activar o desactivar actuadores |
void fnCambiarAEstadoAbriendo()
{ _estado = ESTADO_ABRIENDO ; _tiempoEstado = 0.00f ; Serial << "estado:"
<< ESTADO_ABRIENDO << ENTER ; Serial << "Abriendo
puertas" << ENTER ; digitalWrite( PIN_RELE_ABRIR, HIGH ) ; } void fnCambiarAEstadoAbierto()
{ _estado = ESTADO_ABIERTO ; _tiempoEstado = 0.00f ; Serial << "estado:"
<< ESTADO_ABIERTO << ENTER ; Serial << "Puertas abiertas"
<< ENTER ; digitalWrite( PIN_RELE_ABRIR, LOW ) ; } void fnCambiarAEstadoCerrando()
{ _estado = ESTADO_CERRANDO ; _tiempoEstado = 0.00f ; Serial << "estado:"
<< ESTADO_CERRANDO << ENTER ; Serial << "Cerrando puertas"
<< ENTER ; digitalWrite( PIN_RELE_CERRAR, HIGH ) ; } void fnCambiarAEstadoCerrado()
{ _estado = ESTADO_CERRADO ; _tiempoEstado = 0.00f ; Serial << "estado:"
<< ESTADO_CERRADO << ENTER ; Serial << "Puertas cerradas"
<< ENTER ; digitalWrite( PIN_RELE_CERRAR, LOW ) ; } |
1.5.2
Sensor
de 2 lados
Variamos al
proyecto para tener 2 detectores de presencia o movimiento.
Del punto 3, agregamos la
definición del PIN_DETECTOR2 |
#define PIN_DETECTOR2 8 |
Del punto 5, agregamos la variable
de su medición, antes del punto y coma. |
float // … , _medicionDetector2
= 0 ; |
Redefinimos al
estado cerrado para detectar a la persona con el segundo detector. |
void fnEstadoCerrado() { _medicionPersonaCerca
= digitalRead(
PIN_DETECTOR ) + digitalRead(
PIN_DETECTOR2 ) ; if(
_medicionPersonaCerca ) { return
fnCambiarAEstadoAbriendo() ; } if( _pcDice
== "abrir" ) { return
fnCambiarAEstadoAbriendo() ; } } |
1.6 Mejoras
Como en muchos estados las condiciones dispararían
al mismo cambio de estado, podríamos juntarlas todas en mi mismo IF() con el
operador lógico || (or), cuestión que si ocurre una posibilidad o la otra, igual
se cumpliría. Incluso, ni hace falta utilizar la expresión return; esto se utilizaría
en contextos en que no queremos que continuara otra regla.
void fnEstadoCerrado() { _medicionPersonaCerca
= digitalRead(
PIN_DETECTOR ) + digitalRead(
PIN_DETECTOR2 ) ; if( _medicionPersonaCerca
|| _pcDice
== "abrir" ) { fnCambiarAEstadoAbriendo()
; } } |
n
Comentarios
Publicar un comentario