Arduino Mega trava ao atingir setpoint com interrupção

Estou desenvolvendo um sistema de controle de temperatura com essa biblioteca PID (http://playground.arduino.cc/Code/PIDLibrary). Tenho um display TFT touch onde defino o meu setpoint e acompanho o valor da variável manipulada. Interrupções ocorrem a cada passagem da onda senoidal da tensão por 0, então através do PID controlo o ângulo de disparo do pulso de controle. O sistema funciona perfeitamente até que quando ele chega no setpoint, quando ele chega nesse valor o sistema simplesmente fica travando e o sistema fica muito lento. Alguém poderia me ajudar a solucionar esse erro e explicar exatamente pq isso está acontecendo? Segue link demonstrativo:

https://www.youtube.com/watch?v=5G6dI1evavM&feature=youtu.be

Observe apenas as Variáveis "Seringa". A que tem as setinhas do lado são onde eu a defino, ou seja, é meu Setpoint. A de baixo é a varíavel medida diretamente na minha resistência, esse valor medido abaixo da seringa é o PWM, que coloquei apenas para ver o PID atuando sobre ele. Observe a contagem do relógio, até atingir o Setpoint ele conta normal, depois, conta lentamente. Note tbm como fica a atualização da temperatura, antes ele fica atualizando a cada segundo, depois, atualiza lentamente.

E colocares aqui o codigo? Custa muito???

Existe quem nao possa ver o youtube... e mesmo podendo nao consiga adivinhar o codigo atraves dum video.

Possivelmente é um problema com a rotina de interrupção. Ela deve ficar constantemente a ser chamada o que não deixa tempo para o Arduino fazer o resto das coisas (nomeadamente, escrever no ecrã). Mas como diz o outro utilizador, a questão devia ter sido acompanhada pelo código porque assim é difícil adivinhar.

Eu já descobri qual era o problema, realmente era na interrupção, pois a mesma contém um delay, vejam abaixo:

void zeroCrossInt()
{
      delayMicroseconds(map(powerS, 10, 240, 8000, 2000));  
      digitalWrite(CtrlPulsosSeringa, HIGH); 
      delayMicroseconds(70);     
      digitalWrite(CtrlPulsosSeringa, LOW);  
  }

A variável "powerS", é controlada por um controlador PID, que quando o sistema atinge o setpoint, o primeiro delay faz com que o sistema se trave por meio ciclo de onda senoidal, ou seja, no setpoint, o sistema ficava metade do tempo ligado e metade desligado ocasioanando o travamento parcial como mostrado no vídeo. Agora, tenho que ver como faço para dar esse pulso como o que há quando ocorre quando da interrupção, só que sem usar o delay, no caso, sei que eu teria que usar algum outro dos timers do Arduino mega2560, alguém sabe como eu faço isso para essas interrupções externas? Desde já agradeço pela atenção.

Mostrares o codigo seria bastante mais simples para ajudarmos...

Eu esta-me a parecer que pegaste no boi pelos cornos... mas teria de ver o codigo todo para ter a certeza.

Para essa aplicacao, eu fazia assim:

  • Desactivava o Timer 0 (ficavas sem a possibilidade de usar o millis() )
  • Configurava o Timer0 para uma dada frequencia. Se vais comutar 50Hz, escolheria de forma a ter 10ms quando o timer fizesse overflow de 8 bits.
  • Quando houvesse a passagem por zero, essa interrupcao limpava o contador, actuava a saida do TRIAC e definia onde e que o Timer0 ia ter a sua interrupcao.
  • quando o timer0 fosse interrompido, desactivava a saida e colocava o valor da interrupcao no maximo.

Isto certamente que tem problemas... mas e uma possibilidade.

Nalguns chips os timers sao possiveis de utilizar ligados com interrupcoes externas, mas nao sei se e esse o caso com o ATmega. Ja leste alguma coisa sobre os timers?

Segue o código, tive que apagar umas configurações de mostragem do display e do PID para poder caber aqui:

volatile int powerS = 100;
volatile int powerV = 100;

int TMin = 0;
int TSeg = 60;
String stringTMin;
String stringTSeg;
//Variáveis Monitoradas
int tSeringaAmb = 0;
int tVialAmb = 0;
String StringtSeringaAmb;
String StringtVialAmb;
//Variáveis setadas
int tSeringa = 0;
int tVial = 0;
String stringtSeringa;
String stringtVial;

//Pino que receberá as insterrupções externas será o pin 18 - interruptor 5
//Pinos que controlarão o sistema de aquecimento da Seringa e farão a leitura de sua temperatura
const int CtrlPulsosSeringa = 8;
const int analogInPinSeringa = A0;
//Pinos que controlarão o sistema de aquecimento do Vial e farão a leitura de sua temperatura
const int CtrlPulsosVial = 9;
const int analogInPinVial = A1;

//Variáveis para contagen do tempo:
long previousMillis = 0;
long interval = 1000;
short flag = false;

void setup()
{

 InicializadaHS();
  
  attachInterrupt(2, zeroCrossInt, RISING);
  pidS.SetOutputLimits(10, 240);
  pidS.SetMode(AUTOMATIC); 
  pidS.SetSampleTime(10);  
  SetPointS=tSeringa;
 
}

void InicializadaHS()
{ 
                //Lendo os valores salvos na EEPROM
                tSeringa = EEPROM.read(1);
                tVial    = EEPROM.read(2);
                TMin     = EEPROM.read(3); 
              
                //Mostrando os valores da EEPROM na tela ao inicializar o aparelho
                stringtSeringa = String(tSeringa);
                myGLCD.print(stringtSeringa, 133, 54);
              
                stringtVial = String(tVial);
                myGLCD.print(stringtVial, 133, 83);
              
                stringTMin = String(TMin);
                MostraMin();
}

void SalvaEEPROM()
{
                EEPROM.write(1, tSeringa);
                EEPROM.write(2, tVial);
                EEPROM.write(3, TMin); 
}

void Relogio()
{

             unsigned long currentMillis = millis();
             
             if(currentMillis - previousMillis > interval)
             {
                StringtSeringaAmb = String(tSeringaAmb);
                myGLCD.print("   ", 133, 142);
                myGLCD.print(StringtSeringaAmb, 133, 142);
                
                //String spower = PresentValue*100;
                String spower = String(powerS);
                myGLCD.print("   ", 133, 162);
                myGLCD.print(spower, 133, 162);
                Serial.println(tSeringaAmb);
                delay(30);
                
               previousMillis = currentMillis;
  if(flag)
  {
               TSeg--;
                 if(TSeg ==0 && TMin>=1)
                 {
                   
                   stringTMin = String(TMin);
                   MostraMin();
                   TSeg = 59;
                   TMin--;
                 }
               stringTSeg = String(TSeg);
               MostraSeg();
   }
     
             if(pressed_button==but9 || (TSeg ==0 && TMin ==0))
             {
                 flag = false;
                 TMin = EEPROM.read(3); 
                 TSeg = 59;
                 stringTMin = String(TMin);
                 MostraMin();
                 myButtons.enableButton(but8);
                 
            
             }
  }
}

void MostraMin()
{
               if(TMin < 10)
               {
                 myGLCD.print("  ",       101, 117); 
                 myGLCD.print("0",        101, 117);
                 myGLCD.print(stringTMin, 117, 117);
               }
               else
               {
                 myGLCD.print("  ",       101, 117);
                 myGLCD.print(stringTMin, 101, 117);
               }
}

void MostraSeg()
{
               if(TSeg < 10)
               {
                  myGLCD.print("  ",       149, 117);
                  myGLCD.print("0" ,       149, 117);
                  myGLCD.print(stringTSeg, 165, 117);
               }
               else
               {
                  myGLCD.print("  ",       149, 117);
                  myGLCD.print(stringTSeg, 149, 117);
               }
}
void PIDCtrlS()
{
              tSeringaAmb  = GetTempS(analogInPinSeringa);
              PresentValueS = tSeringaAmb;
              pidS.Compute();
              powerS        = ManipulatedValueS;
              
//              if(tSeringaAmb >= tSeringa)
//                 detachInterrupt(5);
//              if(tSeringaAmb < tSeringa)
//                 attachInterrupt(5, zeroCrossInt, RISING);  
}

void PIDCtrlV()
{
              tVialAmb  = GetTempS(analogInPinVial);
              PresentValueV = tVialAmb;
              pidV.Compute();
              powerV        = ManipulatedValueV;
              
//              if(tSeringaAmb >= tSeringa)
//                 detachInterrupt(5);
//              if(tSeringaAmb < tSeringa)
//                 attachInterrupt(5, zeroCrossInt, RISING);  
}

void loop()
{
  while(1)
  {
    if (myTouch.dataAvailable() == true)
    {
      pressed_button = myButtons.checkButtons();
      VerificaBotaoPress(pressed_button);
    }
    Relogio();
    PIDCtrlS();
    PIDCtrlV();
   
  }
}

void VerificaBotaoPress(int pressed_button)
{
  if(pressed_button==but7)
          {
           TMin++; 
             if(TMin >=0 && TMin<= 99)
               {
               stringTMin = String(TMin);
               MostraMin();
               }
          }
          if (pressed_button==but6)
          {
           TMin--; 
               if(TMin >=0 && TMin<= 99)
               {
               stringTMin = String(TMin);
               MostraMin();
               }
          }
           
           // Temperatura da Seringa
           if(pressed_button==but3)
          {
           tSeringa++; 
               if(tSeringa>=0 && tSeringa<=999)
               {
               stringtSeringa = String(tSeringa);
               myGLCD.print("   ", 133, 54);
               myGLCD.print(stringtSeringa, 133, 54);
               }
               SetPointS=tSeringa;
          }
           if (pressed_button==but2)
          {
           tSeringa--; 
               if(tSeringa>=0 && tSeringa<=999)
               {
               stringtSeringa = String(tSeringa);
               myGLCD.print("   ", 133, 54);
               myGLCD.print(stringtSeringa, 133, 54);
               }
               SetPointS=tSeringa;
          }
          
          // Temperatura do Vial
          if(pressed_button==but5)
          {
           tVial++; 
               if(tVial >=0 && tVial<= 999)
               {
               stringtVial = String(tVial);
               myGLCD.print("   ", 133, 83);
               myGLCD.print(stringtVial, 133, 83);
               }
               SetPointV=tVial;
          }
          if (pressed_button==but4)
          {
           tVial--; 
               if(tVial >=0 && tVial<= 999)
               {
               stringtVial = String(tVial);
               myGLCD.print("   ", 133, 83);
               myGLCD.print(stringtVial, 133, 83);
               }
               SetPointV=tVial;
          }
                         
        //Botão Start
         if (pressed_button==but8)
          {
           TMin--;
           stringTMin = String(TMin);
           MostraMin();  
           flag = true;
           myButtons.disableButton(but8);

        }
    SalvaEEPROM();
}

int GetTempS(int sensor)
{
  float temp = 0;
  for(int i=0; i< 5;i++)
  {
    temp += analogRead(A0);
    //delay(20);
  }
  temp = (temp * 0.48875855)/5;
  return (int)temp;
}

int GetTempV(int sensor)
{
  float temp = 0;
  for(int i=0; i< 5;i++)
  {
    temp += analogRead(A1);
    //delay(20);
  }
  temp = (temp * 0.48875855)/5;
  return (int)temp;
}

void zeroCrossInt()
{
  if(tSeringaAmb < tSeringa)
  {      
      delayMicroseconds(map(powerS, 10, 240, 8000, 2000));  
      //delayMicroseconds(5000);  
      digitalWrite(CtrlPulsosSeringa, HIGH); 
      delayMicroseconds(70);     
      digitalWrite(CtrlPulsosSeringa, LOW);  
  }
  
    if(tVialAmb < tVial)
  {
      delayMicroseconds(map(powerV, 10, 240, 8000, 2000));  
      //delayMicroseconds(5000);  
      digitalWrite(CtrlPulsosSeringa, HIGH); 
      delayMicroseconds(70);     
      digitalWrite(CtrlPulsosSeringa, LOW);  
  }

}

Terei que controlar duas zonas de temperatura, por isso existe dois grupos de delay na interrupção, estava testando a possibilidade de passar esse delay quando atingir o setpoint, mas isso só funciona com uma zona de temperatura, para duas, não deve existir esse delay, pois se não vai interferir na outra zona, muito do código acima é da interface com o display, qualquer dúvida sobre ele é só questionar. Estou estudando sim sobre timers.

O delay nao e solucao.

Por isso acho melhor recomecares e esqueceres que o delay existe.

Como referi... pegaste nisto pelos cornos. Tenta recomecar de novo. Esquece o LCD e teclado e foca-te em fazer um programa que realiza o controlo como deve ser sem nenhum interface pipi. Nota tambem que esse interface pipi vai muito provavelmente dar-te dores de cabeca, mas o importante do sistema e o controlo, logo o foco deve ser colocado ai e so depois no resto.

Comeca por experimentar aquilo que referi e ve como funciona. Existem imensos tutoriais na net sobre como configurar os timers de todas as formas e feitios. Nota tambem que ao mexeres nos timers, concretamente o Timer0, vais ficar sem algumas funcoes e bibliotecas do Arduino...

Certo, irei estudar mais sobre os timers no Arduino para adquirir o conhecimento necessário para não usar o delay, irei seguir seus conselhos. Quando eu terminar de implementar volto aqui para dar um feedback. Muito obrigado pela ajuda. Att.

//Variáveis setadas int tSeringa = 0; int tVial = 0; .... void SalvaEEPROM() { EEPROM.write(1, tSeringa); EEPROM.write(2, tVial); EEPROM.write(3, TMin); }

Isto esconde um potencial problema! O que pode acontecer se setares o tSeringa a 256 por exemplo.Iras destruir o conteudo do endereço 2! Se nao preves ultrapassar os setpoints a algo superior a um byte nao tens problemas, mas se o fizeres iras ter sobreposiçao de valores na eeprom! No teu codigo vejo tambem que usas a class String com abundancia. Livra-te delas e usa char arrays

Obrigado pelas dicas HugoPT. Não terei temperaturas maiores que 150 graus Celsius, então não terei problemas com isso a princípio, mas posso aumentar a segurança do código. Vou ver como faço a mudança para arrays.