Go Down

Topic: Prevenir que un Switch case se interrumpa antes de completar su funcion?  (Read 3414 times) previous topic - next topic

surbyte

Post#4 
Quote
Normalmente se agrega una histeresis, o banda de cambio.

noter

Hola que tal!

Incialmente lo tenía.. lo he buscado creo que te refieres a esto;

IF ((temp>=21) | (temp<=28)){
output_low(RELE);
}
IF ((temp<21) | (temp>28)){
output_high (RELE);

Cuidado con estos pequeños detalles, y no me refiero al IF en mayúsculas (supongo que es código hecho "al vuelo"), sino al | , que es operador OR a nivel de bit. Entiendo que lo correcto es ||, osea OR lógico.

AlexLPD

Hola noter... de hecho una vez que me dijeron lo de la histerisis fui y busque.. me encontre con ese codigo lo copie y lo pegue... tenía esa duda yo usaba || en arduino, creo que el codigo no es de arduino, si no de alguna otra plataforma.

De cualquier modo...no me funcionaba por que a veces por cualquier error la camara se salia de la temperatura y entonces ya no funcionaba el termostato

Code: [Select]
If( temC > hightemp || tempC < hightemp + 2.00)

esto sencillamente no me funicionaba... digamos que la temperatura set era de 10C... si por algo se subia a 12... el control ya no operaba...

despues agregue:

Code: [Select]
if( temC > hightemp) pero habia confilctos, habia que hacerlo tanto para arriba como para abajo...

ahora uso solamente algo asi;

Code: [Select]
if(tempC >= highTemp - 0.5 )
     {
       if( tempC >= highTemp )
       {
       refrigerationBegin = true; 
       turnOnCompressor();
       }
     }
     
     if(tempC <= lowTemp + 0.5 )
     {
       if(tempC <= lowTemp)
       {
       refrigerattionEnd  = true;
       turnOffCompressor();
       }     
     }
     


En fin... ahora lo que tengo es un problemin... tengo dos estados bien definidos... encender o apagar refrigeracion... pero tengo un tercer estado... cuando el control se inicia y la temperatura esta "en medio" de los extremos... deberia hacer inciar un ciclo de ventilacion... pero hace cosas raras ahora que todo el codigo es "non blocking"

Es que para iniciar la subrutina de ventilacion tengo esto:

Code: [Select]

if( cycleFansvar == true &&  refOff == false )       // to cycle only once the set of the instructions per cycle.)
     {
       if(tempC < highTemp )  cycleFans();
     } 


Ambas variables son false, y true respectivamente, cuando el control recien arranca... intente poner otr if aparte pero... eso solo hace que se conjuguen ambas acciones...

Como puedo hacer mi termostato tome varias desiciones en este caso?

Estaba pensando en varios Ifs... y despues un switch case que lanze! la subrutina correcta

Code: [Select]

if(temC < hightem)    state = refrigerationON
if(temC > lowtemp)    state= refrigerationOFF
if(temC >lowtmp && temC < hightem)  state= ciclyeFans

switch ( state)

case refrigerattionON :
refrigerationON();
break;

case refrigerationOFF:
refrigerationOFF();
break;

case cycleFans
cycleFans();
break;



Ahora en este aspecto...
como puedo hacer que el switch case me acepte el texto, para que sea mas explicativo?

como puedo "reiciar" las variables de una subrutina en caso de sea interrumpida por algun otra cosa?

Lo que hago en las que tengo;
Code: [Select]

case 6:
  lcd.setCursor(0,1);  lcd.print(F("                    "));
  lcd.setCursor(0,2);  lcd.print(F("                    "));
  refrigerattion = false;
  refOff = true     ;       // to cycle only once the set of the instructions per cycle.
  compOn = 0;
  cycleFansvar = false;        //bool to chek the refrigerattion has stop
  break;


Muchas gracias.
-Alex.

noter

Hola, Alex.
¿Podrías explicarme qué acciones debe realizar tu máquina? Es decir, los posibles estados. Pon un ejemplo del "guión de la película" y lo pasamos a un switch/case, que es lo que yo entiendo como máquina de estados. Me parece que tu error es querer tomar todas las decisiones en un solo lugar.

AlexLPD

Hola Noter, muchas gracias... Mira la cuestion es esta;

tempC;  es la variable de temperatura.

Tengo tres estados bien definidos;
1.- refON - temC es mas alto que el HighPoint (la temperatura esta en el limite superior o es mas alta)
2.- refOFF-     temC es mas bajo que el lowPoint (la temperatura esta o es menor que el lim inferior)
3.- cyclefans - temC no "toca" ni el highPoint ni el lowPoitn... esta "en medio" por lo que llamamos la subrutina de ventilacion.

Estos son los 3 estados que deseo controlar en la función termostato, ya tengo hecha cada maquina de estado finito para cada uno de ellos, y por si solas funcionan bien, el detalle que he tenido es;

1.-A veces el "termostato" interrumpe la maquina que se esta ejecutando... y con ello.. impide que se ejecute nuevamente de manera correcta. (creo que he encontrado la solucion a esto)

2.-El termostato tal como se describe arriba puede tener dos o mas condiciones a la vez que sean ciertas, con lo que la subrutina que varia la ventilación me explico;

if(temC <= lowPoint) refrigerationOFF()

if(temC >= lowPoint && temC <= highPoint) cycleFans();

como puedes apreciar, se pueden y de hecho se cumple tanto la funcion superior como la inferior.

Creo que eso se resolveria usando un Switch case, como el comentado en el de arriba
el detalle que quiero que la variable sea explicativa;  Var = refrigerattionON, refrigerattionOFF, cycleFans, en lugar de 0,1,2...

Y creo que puedo evitar que la maquina incie donde no se quedo haciendo esto;

Actualmente cada maquina tiene una variable que me dice si se completo, en este caso es; cycleFansVar, por poner algo...

Code: [Select]
case 6:
  lcd.setCursor(0,1);  lcd.print(F("                    "));
  lcd.setCursor(0,2);  lcd.print(F("                    "));
  refrigerattion = false;
  refOff = true     ;       // to cycle only once the set of the instructions per cycle.
  compOn = 0;
  cycleFansvar = false;        //bool to chek the refrigerattion has stop
  break;


Si agrego un if, en la maquina que diga;
If(cycleFansVar =! false){ //limpia todas las variales}

Puedo reiniciar la maquina de estado finito tantas veces como sea interrumpida. Creo yo.

Gracias, por la ayuda.


noter

Bueno. No me has explicado finalmente el "guión de la película de estados". Te propongo un esqueleto de aplicación, aunque no tiene por qué ajustarse a tu guión. Simplemente intento explicarte cómo organizaría yo las transiciones entre estados, creo que evitando eso que llamas interrupción de la función, y de paso implementando un enum, que creo que resuelve eso que dices de "variable explicativa".

Code: [Select]

enum {
freezingOffFanOff,
freezingOffFanOn,
freezingOnFanOff,
freezingOnFanOn,
} status;


void setup() {
fanOff();
freezingOff();
status = freezingOffFanOff;
}


void loop() {
temperature = tempRead();
switch(status) {
case freezingOffFanOff:
if (temperature > downLimit) {
if (temperature < upLimit) {
fanOn();
status = freezingOffFanOn;
} else {
freezingOn();
status = freezingOnFanOff();
}
} // else continuamos, osea apagado fan y compresor
break;
case freezingOffFanOn:
if (temperature <= downLimit) {
fanOff();
status = freezingOffFanOff();
} else if (temperature >= upLimit) {
fanOff();
freezingOn();
status = freezingOnFanOff;
}
break;
case freezingOnFanOff:
if (temperature < uplimit) {
if (temperature > downLimit) {
fanOn();
status = freezingOnFanOn;
} else { // Aunque no tiene lógica que pase directamente a este estado, pero por si acaso
freezingOff();
status = freezingOffFanOff;
}
} // else continuamos sólo refrigerando
break;
case freezingOnFanOn:
if (temperature <= downLimit) {
freezingOff();
fanOff();
status = freezingOffFanOff();
} else if (temperature >= upLimit) {
fanOff();
status = freezingOnFanOff;
}
break;

}
}


Como te he dicho es un esqueleto de aplicación. Me he inventado unas funciones fanOn, fanOff, freezingOn, freezingOff que se supone encienden/apagan ventilador o compresor, y probablemente las transiciones que tengas tú pensadas serán diferentes (por ejemplo, tal vez quieras añadir una histéresis o "colchón" en alguna de las condiciones de transición. Lo importante es que entiendas el código y si sería posible adaptarlo a tu "guión".
Saludos.

AlexLPD

Gracias noter..
Bueno, viendo tu codigo puedo ver algo nuevo, el uso de enum..

es mas o menos lo que buscaba una variable que pueda ser nombrada de manera correcta para poder leer el codigo;

Algo como; http://ralphniels.nl/arduino/using-enums-to-make-your-code-more-readable/


Bueno, quizas sea mas claro asi;

Code: [Select]
//PSEUDOCODIGO
 
enum termostato{
refrigeracionON
refrigeracionOFF
ventilacion 
}


void setup() {
}

void loop() {
 
  termostato();
}



void termostato(){
// Aqui leemos la temperatura y asignamos una tarea
 
  temC = get.temp(INSIDEBOX);

  if(temC <= highSet )                                termostato = refrigeracionON
  if(temC >= lowSet  )                                termostato = refrigeracionOFF
  if(temC > lowSet && temC < highSet)       termostato = ventilacion

switch (termostato){
    case refrigeracionON:
         refrigerationON();
    break;

    case refrigeracionOFF:
         refrigerationOFF();
    break;

    case ventilacion:
         cycleFans();
    break;
 
}
}


void refrigerationON(){ 

if( limpia variables =! true)
{
limpiar todas las variables de incializacion
}

enciende vent 1
espera un segundo
enciende ventilador 2
espera un segundo
enciende compresor
(termina maquina //limpia variables)
}


void refrigerationOFF(){

if( limpia variables =! true)
{
limpiar todas las variables de incializacion
}

apaga compresor
espera un segundo
apaga ventilador dos
espera un segundo
apaga ventilador uno
(termina maquina //limpia variables)
}

void cycleFans(){
enciende ventilador 1 x 1min
apaga ventilador 1 x 5 min
enciende ventilador 2 x 1 min
apaga ventilador 2 x 5min   
(vuele a comenzar)
}



Como lo ves asi?
Lo que pasa es que todo el sistema depende de la temperatura, por eso la leo constantemente para saber que hacer... agregue un if a cada maquina para incializarla en caso de ser necesario, sin importar si ha sido interrumpida o no.


Gracias.

noter

Bien. Ahora parece que al menos entiendo un poco más lo que quieres hacer, así que al respecto hemos avanzado :).
Te digo cosillas que puntualizaría sobre tu pseudocódigo.

  • El nombre del enum antes de la declaración es la declaración de un tipo, al igual que en las estructuras. De hacerlo así, luego tendrías que utilizar el tipo para declarar variables de ese tipo: termostato miVariableTermostato. Como sólo vas a declarar una variable de ese tipo, puedes prescindir de declarar tipo y declarar directamente una variable, pero el nombre va tras las llaves, no antes.
  • Cuidado con usar el mismo nombre para la función y para el enum. También veo que llamas igual a los estados que a las funciones que llamas. Mejor evitar líos y llamar a cada cosa con un nombre exclusivo.
  • Creo que no es lo mismo el estado de la máquina que las acciones que se toman en cada estado. Yo definiría en el enum y en el switch un listado de posibles estados de la máquina más parecidos a lo que te proponía yo, que visto tu código ahora serían algo así como apagada (estado inicial al arrancar), encendiendoCompresor, apagandoCompresor, ventilando (Por cierto, ¿ventilando puede estar tanto con el compresor encendido como apagado?). Dentro de cada case de estado actual pondría los if de los posibles cambios de estado, en lugar de tomar una decisión global basada sólo en la temperatura. De esa manera podemos tomar decisiones distintas dependiendo no sólo de la temperatura, sino del estado actual. Por ejemplo, tras saltar la refrigeración, puedes decidir no pasar a ventilación, aunque estés en umbral de temperaturas, hasta que no haya finalizado el arranque de la refrigeración, o incluso hasta no alcanzar una temperatura umbral, sin embargo mientras estás en estado ventilación puedes decidir que el umbral para el apagado de esa ventilación sea distinto.
  • Sería cuestión de conocer un poco más los pormenores del mundo de la refrigeración, y si sería posible (y conveniente) encender o apagar el compresor durante el propio ciclo de ventilación, o usar algún tipo de PID.



AlexLPD

Gracias Noter por los puntos que me recalcas.

Si vi lo de las funciones iguales a la variable enum, esto ultimo no me queda muy claro, en los ejemplo que vi hacen algo como

Esta quedando mucho mejor organizado, ahora bien... la razon en la que insisto en dejarlo como
subrutina es por lo que pasa en la misma... dejame mostrate las subrutinas...

primero el esqueleto  nuevo que estoy armando:

Code: [Select]
enum {altaTemp, bajaTemp,enRango }  temperatura;

int temC = 0;
float highTemp = 7.00;
float lowTemp  = 3.00;

void setup() {
  // put your setup code here, to run once:

}

void loop() {
 termostato();
}


void termostato()
{
  if(temC >= highTemp )                  temperatura = altaTemp;
  if(temC <= lowTemp  )                  temperatura = bajaTemp;
  if(temC > lowTemp && temC < highTemp)  temperatura = enRango;

switch ( temperatura )
{
        case altaTemp:
            refrigerationON();
        break;

        case bajaTemp:
            refrigerationOFF();
        break;

        case enRango:
            cycleFans();
        break;
}
}//fin de termostato


void refrigerationON()
{
 
}

void refrigerationOFF()
{
}

void cycleFans()
{

}


Ventilador... si puede estar encendido en la refrigeración como sin la refrigeración;
Cuando la refrigeración esta encendida los dos ventiladores están encendidos... cuando la refrigeración esta apagada, enciende uno por un tiempo, luego lo apaga por otro tiempo, después enciende el otro por un tiempo y lo apaga por cierto tiempo y así mientras esto este encendido.

En cuanto el PID quizas se pueda jugar un poco con la incercia termica, pero el sistema al menos este solo tiene a plena carga y apagado.

Bueno... sigo avanzando a ver que mas le notas a el codigo. Gracias.


AlexLPD

Y aqui, por poner una... la subrutina de ventilacion;

Bueno no cupo pegada aqui, te la dejo en un archivo.

-Alex.

AlexLPD

Bueno, justo como lo sospechaba... algo tiene el sketch que no funciona de manera adecuada...

Miren me arme este pequeño sketch de prueba... donde puedo mover la temperatura con el serial;

(solo el principal)

Code: [Select]
void setup() {
Serial.begin(9600);
}

void loop() {
  
  // send data only when you receive data:
        if (Serial.available() > 0) {
                // read the incoming byte:
                incomingByte = Serial.parseInt();

          if(incomingByte == 8 )
             {
             temC = temC + 0.25 ;
             }

          if(incomingByte == 2 )
             {
             temC = temC - 0.25 ;
             }      

                // say what you got:
                Serial.print("Current Temp :  ");
                Serial.println(temC);
        }
  
  
  termostat();
}



Code: [Select]

void termostat()
{
  /*
  Take the temperature of the probe and use it to take
  a choice of what subrutine will call.
  */
  
  if(temC >= highTemp )                  temperatura = altaTemp;
  if(temC <= lowTemp  )                  temperatura = bajaTemp;
  if(temC > lowTemp && temC < highTemp)  temperatura = enRango;



switch ( temperatura )
{
        case altaTemp:
             refrigerationON();
             Serial.println("termostato en refrigeracion");
        break;

        case bajaTemp:
             refrigerationOFF();
             Serial.println("termostato en refrigeracion Apagada");
        break;

        case enRango:
             Serial.println("termostato en ventilacion");
             cycleFans();
        break;
}
}//fin de termostato



Bueno como se pueden imaginar... subo y bajo la temperatura... aqui los primeros resultados;

Inicio con una temperatura de 5.00;

Code: [Select]

Current Temp :  5.00
termostato en refrigeracion Apagada
Current Temp :  5.25
termostato en refrigeracion Apagada
Current Temp :  5.50
termostato en refrigeracion Apagada
Current Temp :  5.75
termostato en refrigeracion Apagada
Current Temp :  6.25
termostato en ventilacion


Precisamente hice la prueba en 5.00 por que esta justo en el centro del rango que estoy utilizando (para prueba) :

Code: [Select]
float temC = 5.00;
float highTemp = 7.00;
float lowTemp  = 6.00;


Hasta aqui, la primera prueba de el termostato... algo tan sencillo y que no funciona.

Seguire informando.

-Alex.

AlexLPD

Bueno, me acorde de que a veces los float, no son muy exactos e hice la siguiente prueba;

 
Code: [Select]
if(temC >= highTemp )                  temperatura = altaTemp;
  if(temC <= lowTemp  )                  temperatura = bajaTemp;
  if(temC + 0.2 > lowTemp && temC < highTemp - 0.2 )  temperatura = enRango;


Los resultados;

Code: [Select]
Current Temp :  5.00
termostato en refrigeracion Apagada
Current Temp :  5.25
termostato en refrigeracion Apagada
Current Temp :  5.50
termostato en refrigeracion Apagada
Current Temp :  5.75
termostato en refrigeracion Apagada
Current Temp :  6.00
termostato en ventilacion


Parece que sigue sin funcionar adecuadamente.
Alguna idea?

Creo que este es uno de los glitch que hay en este programa, el otro es que las maquinas de estado no tienen algo que les permita "inciar de cero" si fueron interrumpidas.

Gracias.
-Alex.

AlexLPD

Bueno, curiosamente... esto si funciona:

Code: [Select]

  if(temC >= highTemp )                  temperatura = altaTemp;
  if(temC <= lowTemp  )                  temperatura = bajaTemp;
  if(temC > 3.1 && temC < 6.99 )         temperatura = enRango;


Resultados:

Code: [Select]
Current Temp :  5.25
termostato en ventilacion

Current Temp :  6.25
termostato en ventilacion

termostato en ventilacion
Current Temp :  6.50

termostato en ventilacion
Current Temp :  7.00
INICIANDO REFRIGERACION
termostato en refrigeracion


Aparentemente necesita ser mas diferenciado.

Tambien hice esta prueba (QUE NO FUNCIONO)

Code: [Select]
float temC = 5.00;
float highTemp = 7.00;
float lowTemp  = 6.00;
float venLowtem = lowTemp + 0.1;
float venHightem = highTemp -0.1;


  if(temC >= highTemp )                      temperatura = altaTemp;
  if(temC <= lowTemp  )                      temperatura = bajaTemp;
  if(temC > venLowtem && temC < venHightem ) temperatura = enRango;


Current Temp :  6.00
termostato en refrigeracion Apagada
Current Temp :  7.00
INICIANDO REFRIGERACION
Current Temp :  6.00
termostato en refrigeracion Apagada


Asi que ya lo saben ...al menos en este caso hay que poner las variables en el If, no declararlas antes.

-Alex.

noter

Sigo pensando que en lugar de poner los condicionales de temperatura para cambiar el estado agrupados en un solo punto, los pasaría de una forma lógica a cada uno de los propios estados. De esa forma, por ejemplo:

- Al saltar estado refrigeración puedo evitar hacer caso a ninguna lectura hasta haber completado el ciclo de encendido, y después sólo estar pendiente de la condición lowtemp de apagado de refrigeración (entiendo que hasta que se produce ese apagado no se debería poder entrar en estado de ventilación).
- Al saltar estado de apagado de refrigeración no hacer caso a ninguna lectura hasta haber completado el ciclo de apagado, y después sólo estar pendiente de la condición de superar lowtemp para saltar a estado ventilación.

Porque entiendo que una vez salte la refrigeración no debería pasar a estado ventilación sin pasar antes por apagado de refrigeracion, ¿No?

AlexLPD

Bueno precisamente es algo que hay q ver. Si por ejemplo la cámara está funcionando y se la apagan digamos en 5C para cargar... y cuando han terminado la encienden... a pesar de que el control.. no ha iniciado una refrigeracion... debería incitar la ventilacion.

Como sea este tablero me esta volviendo loco... primero q la pantalla se borraba... después de ponerle los snubbers se congelaba o se quedaba pegado un contactor. (El subversión conducia) y ahora q cambiamos de topologia de snubber resulta que toda la electronica... se apaga nada mas por que si....

Y todavía estos errores en el codigo...

Gracias por el consejo pensare como replantear las maquinas.

Alex.

Go Up