Gadget para desconectar un ascensor

Esto es lo que he echo con lo me has dicho de los eventos, no lo acabo de ver claro, me parece que lo programo demasiado enrevesado:

#define E_INICIO 1


#define E_IDLE 1
#define E_MENU 2
#define E_CTIME 3
#define E_CCALENDAR 4
#define E_EXTRAERSD 5

void setup(){}
void loop(){
	leer_hora_fecha();
	key = leer_tecla();
	
	switch (desconexion){
		case E_INICIO:
			/* aqui quiero poner:
				si el menu esta en en el estado de espera (E_IDLE) y aprieto la tecla # entonces
				se limpia la pantalla y se cambia el estado del menu a estado menu (E_MENU)<--es decir que entro en el menu
				
				tambien tengo que tener en cuenta que la pantalla de inicio solo se muestre cuando el menu esta en estado de espera (E_IDLE)
			*/	
			if(menu == E_IDLE){
				if(ev_teclaHash89){
					lcd.clearDisplay();
					menu = E_MENU;
				}
				pantalla_inicio(); //Pantalla que se muestra siempre (menos cuando se esta dentro del menu) que muestra la hora y la fecha
			}
			break;
	}
	switch (menu){
		case E_IDLE:
			//Aqui no hago nada
			break;
		case E_MENU:
			//Se muestra el menu y se pasa a los siguientes estados segun la tecla apretada
			//No se si he entendido bien lo que me has dicho sobre los eventos, pero esto es lo que se me ocurre de como ponerlo
			//También he pensado que en vez de los if se podria poner un switch y dependiendo del ev_tecla se envia un estado o a otro
			
			if(ev_tecla1()){
				lcd.clearDisplay();
				menu = E_CTIME;
			}
			if(ev_tecla2()){
				lcd.clearDisplay();
				menu = E_CCALENDAR;
			}
			if(ev_tecla3()){
				lcd.clearDisplay();
				menu = E_EXTRAERSD;
			}
			break;
		case E_CTIME:
			//Se configura la hora y la fecha del RTC
			break;
		case E_CCALENDAR:
			//Se configura los dias festivos
			break;
		case E_EXTRAERSD
			//Se extrae la SD
			break;
	}
}

Si me pudieras dar algun consejo te lo agradeceria muchismo

Un saludo

Hola,

Hay muchas maneras de hacerlo. La manera que yo elegí (que no es para nada como te pongo aquí abajo), la puedes ver en el código de mi librería y ejemplos....SECUduino/FSM Library at master · IgorReal/SECUduino · GitHub

Lo hagas como lo hagas, quieres que en tu loop se este haciendo todo el rato (hablando de una máquina sencilla):

  1. Leer eventos (transiciones)
  2. Actualizar estados
  3. Realizar la función que corresponda a dicho estado.

Por ejemplo, una muy básica pero que tiene el problema de gastar mucha RAM por las variables utilizadas(ya que tienes una variable por cada evento), podría ser:

//EVENTOS
if (Serial.available()>0) myReceivedByte=Serial.read();
ev_botonOK=digitalRead(5) || myReceivedByte=="O";
ev_botonUP=digitalRead(1) || myReceivedByte=="U";
if (RTC.Hora==13 && RTC.Minutos>=30)
{
ev_alarma1=1;
}else
ev_alarma1=0;
}

//MAQUINA DE ESTADOS
switch(estado)
{
case st_Menu:
//Muestro por pantalla cosas, activo salidas, etc
if (ev_botonOK) estado=st_Pantalla1;
break;
case st_Pantalla1;
//muestro por pantalla cosas
if (ev_botonUP)
{
estado=st_MENU;
}else if (ev_alarma1)
{
estado=st_Alarma;
}
break;
case st_Alarma:
....
break;
etc.
}

Y a partir de esto básico, pues seguro que ves como puedes mejorar y hacerla más efectiva (en cuanto a código).
Para botones, al leer tus entradas, puedes crearte un byte (o del tamaño que necesites) que cada bit sea una de ellas. Es decir, el bit0 es botonUP, el bit1 es botonOK, etc...Incluso si te lo montas bien en tu código, puedes conectarlo al mismo puerto(hardware), y leer el puerto de una vez.Cosa que será más rápida que digitalRead() de Arduino.
Bueno, en realidad podrías tener una variable evento, y que cada bit tiene un significado (según su posición, indica el estado de los eventos).

Luego usas máscaras para saber si está activo o no.

Pero poco a poco!! :wink:

Saludos,

Igor R.

[1] Manejo de puertos => http://arduino.cc/es/Reference/PortManipulation
[2] Máscaras y operaciones con bits => http://arduino.cc/es/Tutorial/BitMask

En estas lineas, hace mucho tiempo diseñé una pequeña librería de co-rutinas o proto-threads la mar de maja para un proyecto con PICs que debería ser portable a Arduino sin muchas historias.

Si la encuentro, abro un hilo con ella.

Muy útil para algunas cosas.

Muchas gracias fm XD

Igor, gracias a ti tambien XD. Estoy haciendo otro proyecto (un cronometro cuenta atras, que cuando llega a 0 activa un rele) que tambien lo he hecho por maquina de estados. Como es mucho mas corto que el del ascensor, intentare programarlo segun dices y segun vea en el codigo del link. De momento no me entero mucho, he entendido el usar solo codigo no bloqueante para poder hacer "multi-tarea", pero aun me queda para escribir un codigo decente.

Lo dicho, seguire mirando sobre fsm y el codigo que has posteado, muchas gracias

Edit: Acabo de leerme el pdf de la FSM Library y me ha gustado. Muy sencillo todo, con un par de sentencias creas una maquina de estado y la manipulas. Ahora me miraré el código a ver si entiendo algo :stuck_out_tongue:

Hola erikcrane.

Oye, una cosilla que no me ha quedado clara. ¿Qué pasa si llega el momento de la desconexión y el ascensor está siendo utilizado por un pasajero? ¿Y si alguien lo está esperando en planta?

Creo que es un punto conflictivo de este montaje.

BoTuR:
Hola erikcrane.

Oye, una cosilla que no me ha quedado clara. ¿Qué pasa si llega el momento de la desconexión y el ascensor está siendo utilizado por un pasajero? ¿Y si alguien lo está esperando en planta?

Creo que es un punto conflictivo de este montaje.

Muy buenas y gracias por tu comentario.

Es un montacargas donde las personas no pueden subir, es solo para material. Y se para cuando se acaba la jornada laboral. Por tanto nadie lo estará usando.

Igualmente para situaciones puntuales en que se necesite fuera de un horario laboral voy a implementar un interruptor para desconectar este sistema y que pueda funcionar normalmente. Luego cuando se acabe de usar se vuelve a conectar y el sistema ya se encarga de comprobar que el montacargas este en la 4 planta y el boton de stop este apretado.

@erikcrane, para eso agrega una excepcion por medio de una clave, y se transforma en un estado nuevo, cuando llega arriba pierde el estado y si quieres que baje vuelves a acargar la clave.

@Igor R, es excelente tu trabajo voy a usarlo en mi proyecto domotico y si me permite lo voy a mencionar en arduino playground de linkedin donde hay una persona que esta desarrollando un plc con arduino mega

Gracias @maxid.

He subido un video del invento...Así ves como se puede cambiar menus, mientras se están haciendo otras cosas... :wink:

Eso por dentro tiene un Arduino core... ]:smiley:
Y usando la librería que he comentado en anteriores post (FSM).

Saludos,

Igor R.

Pero esto no son pruebas caseras, no? esto son pruebas de rendimiento de motores en algun curro. Me mola mucho :D, no solo la maquina de estados sino el centro de trabajo :smiley:

Le voy pillando el tranquillo ha hacer una maquina de estados sencillita a traves de switch e ifs. Antes de empezar con la del ascensor he hecho una para un cronometro cuenta atras, que cuando empieza la cuenta atras se activa un rele y cuando acaba se desactiva. Creo que he seguido la filosofia de la maquina de estados y no hay codigo bloqueante, pero no se si lo he hecho todo bien (el progama hac elo que tiene que hacer)

Te dejo el setup y el loop por si quieres hecharle un ojo, sino ningún problema porque queria ponerme a leer algun libro sobre maquinas de estado ahora que ya he ido asimilando mejor en que consisten

#include "Wire.h"
#include "lcd_i2c.h"
#include "Bounce.h"

#define RELE 12
#define LED 10
#define START 4
#define RESET 5
#define SEG_UP 6
#define SEG_DOWN 7
#define MIN_UP 8
#define MIN_DOWN 9

#define INTERVAL_MILLIS 5

#define E_INICIAL 0
#define E_CONFIG_TIME 1
#define E_CUENTA_ATRAS 2
#define E_RELE_ACTIVADO 3

#define REBOUNCE_TIME 250

LCD_I2C lcd(0x38);

uint8_t state = E_CONFIG_TIME;

Bounce buttonStart = Bounce(START, INTERVAL_MILLIS);
Bounce buttonReset = Bounce(RESET, INTERVAL_MILLIS);
Bounce buttonSegUp = Bounce(SEG_UP, INTERVAL_MILLIS);
Bounce buttonSegDown = Bounce(SEG_DOWN, INTERVAL_MILLIS);
Bounce buttonMinUp = Bounce(MIN_UP, INTERVAL_MILLIS);
Bounce buttonMinDown = Bounce(MIN_DOWN, INTERVAL_MILLIS);

struct tiempo{
  uint8_t horas;
  uint8_t minutos;
  uint8_t segundos;
  uint8_t decimas;
}cuenta = {0,0,0,0};

long milisegundos;

void setup(){
  pinMode(RELE, OUTPUT);
  pinMode(LED, OUTPUT);
  digitalWrite(RELE, LOW);
  digitalWrite(LED, LOW);
  
  for(int i=4; i<10; i++){
    pinMode(i, INPUT_PULLUP);
  }

  lcd.init(0x38);
}
void loop(){
  switch(state){
    case E_INICIAL:{
      imprimirTiempo();    //Escribir en pantalla el tiempo que en este caso como es el inicial es 0. Este estado se podria eliminar porque la   
                                    //funcion configTiempo() tambien llama a imprimirTiempo()
      state = E_CONFIG_TIME;
    }break;
    case E_CONFIG_TIME:{
      configTiempo();     //con unos botones configuo los segundos y minutos
      if(ev_reset()){         //ev_reset comprueba si se ha apretado el boton reset
        resetTiempo();        //Pongo minutos, segundos y decimas a 0
        state = E_INICIAL;
      }
      if(ev_start()){     //ev_start comprueba si se ha apretado el boton start
        activarRele();    //activa el rele y un led indicador
        state = E_CUENTA_ATRAS;
      }
    }break;
    case E_CUENTA_ATRAS:{
      if(!ev_fin_cuenta_atras() && !ev_reset()){   //ev_fin_cuenta_atras devuelve true cuando los minutos, segundos y decimas son igual a 
                                                                   //0
        cuentaAtras();
      }else{
        desactivarRele();
        resetTiempo();
        state = E_INICIAL;
      }
    }break;
  }
}

Muy bueno Igor ! Gracias por compartirlo.

El problema que yo tengo en uno de mis proyectos es que una de las tareas requiere hacer un bucle que tarda unos 200 a 250 ms, por lo que aunque para la pantalla no es problema, si que afecta cuando intentas navegar por el menu. No quiero usar interrupciones para tener control total del programa. Tu no utilizas interrupciones,no? el loop es muy rapido para permitir moverte así por los menus.

¿Qué tipo de bucle es para durar 250-300 ms?

Calcula el consumo electrico de una linea, para ello tiene que realizar muchos samples, y esperar a que la frecuencia de la linea pase por 0 en cada caso...

emon1.calcVI(20,2000);         // Calculate all. No.of half wavelengths (crossings), time-out

en la libraria...

 while(st==false)                                   //the while loop...
  {
     startV = analogRead(inPinV);                    //using the voltage waveform
     if ((startV < 550) && (startV > 440)) st=true;  //check its within range
     if ((millis()-start)>timeout) st = true;
  }

tengo que mirar exactamente que tarda pero requiere unos cuantos ms que bloquean el programa.

ahora acabo de acordarme de lo que realmente tambien bloquea mi programa, y es que cada 10 segundos envia unos datos a una web php con una llamada a un URL, esto si que recuerdo que tambien añadia bastante bloqueo al programa.

Bueno, tienes que cambiar la filosofía de hacer for, y cosas de esas que he visto rápidamente al abrir los links...

Si te aseguras que el tiempo de ciclo de tu soft es rápido para poder capturar el paso por cero podrías hacer "evento" podría ser:
volt=analogRead(inPinV);
bZeroCrossing=(startV < 550) && (startV > 440);

Y entonces cuando pase este evento, cambiarías a un estado de ir calculando las cosas (potencia activa, reactiva,etc) y esta ahi hasta que el numero de samples es igual a 500 (o los que sean), luego de ahi pasa a otro estado que es mostrar el resultado por pantalla... o lo que sea.

Tampoco me lo he mirado en profundidad, pero es para explicar la idea....es decir, hay que cambiar un poco la forma de pensar las cosas.
Ten en cuenta que te puedes encontrar muchas librerías (incluso el core de Arduino) tienen código bloqueante...

También puedes combinar cosas, dependiendo de tus necesidades... Yo por ejemplo, en esa aplicación tengo un timer que controla el envio de tramas CAN de forma periódica, aunque todo el flujo va controlado por la máquina de estados (incluso la actualización del contenido de dichos mensajes CAN).

Saludos,

Igor R.

@Sergegsx, ese cacho de código es solo parte del problema de la rutina. Lo que hace es detectar el paso por cero como punto de partida (20ms del total en el peor de los casos).

El mayor problema es que captura y hace los cálculos sobre una ventana de muestras (20 pasos por cero - 200ms). Esto se puede hacer a medida que hay datos disponibles para procesar. Por ejemplo, puedes modificar el codigo para que las adquisiciones se hagan de forma periodica o cuando termina una adquisición utilizando interrupciones. El procesamiento lo puedes hacer en contexto de interrupción (dependiendo del tiempo que tarde en procesar) o en el bucle principal de la máquina de estados en varios bloques.

En mi último proyecto, implementé un pequeño filtro sintonizado a una frecuencia para ver si esa frecuencia está o no en la señal que capturo. El filtro es recursivo (como los cálculos que hacen el codigo que pones); cuando adquiero una muestra (interrupción de final de captura) la proceso en contexto de interrupción. Lo puedo hacer porque tardo 31us en procesar cada muestra.

Como hago analisis de señal, utilizo 2 interrupciones, una para tener una frecuencia de muestreo estable y precisa y la otra para adquirir la forma de onda.

  1. Compare on match int (interrumpe cuando un contador alcanza un determinado valor) para hacer un muestreo periódico. En contexto de interrupción, programo la adquisición e interrupción de fin de captura.
  2. interrupción fin de captura, hace el procesamiento.

Entre interrupción e interrupción sigo ejecutando el lazo principal del programa.

El problema que veo con el código que has puesto es que se deja cosas en el tintero ya que las adquisiciones no son completamente periodicas, e.d. tienen cierta oscilación. Se podría hacer análisis del factor de calidad de tu instalación, factor de potencia, ...

Por cierto, todo el fregao' bajo el control de un autómata finito:

  • esperando señal
  • capturando
  • ejecutando

Son algunos de los estados que hay.

Fm mañana me leo tu explicación otra vez que estoh en el movil y no me he enterado de todo.
El problema es que como digo, cada diez segundos subo datos mediante una llamada a una url y esto si que deja el programa bloqueado un tiwmpo considerable.
Gracias a todos, mañana lo releo todo.
No creo que haya solucion facil para las llamadas a urls