Asynchronous signal isn't detected by interrupt

Hello, I'm having a problem with interrupts (I always had it when I try to used it in the past).

My system has an arduino UNO, three peripherals (SD, RTC and thermocouple(MAX 675) ) and a Flyport WIFI.

The main program (or loop) read temperatures and write them with the date and time in the SD. This part works fine.

But I've to integrate the openpicus Wifi module (Or FLYPORT) to send the data stored at the SD to my server via HTTP. To do this I'm using the UART so I've made a simple protocol to communicate them.

The flyport module turns on a signal which is wired to Arduino's digital Pin 2 (interrupt 0) and the interrupt sets "true" a (volatile) variable.
When this variable is high at the beginning of Loop() the program should enter in the first if (you can see it in the code) but, I don't have any idea why, the arduino enter in the interrupt when he wants... Some times he does, another doesn't.

Here is my code (without the funcions I'm using cause they work and there are a lot of them):

#include <SPI.h>
#include <stdlib.h>
#include <SD.h>


//Variables generales

float temperatura_1;
float suma_temp;
float media_temp;

volatile boolean Peticion_flyport=false;//Se utiliza en la interrupcion


File archivo;

//Declaración de puertos
const byte CS_temp=A0; //cs para el max675
const byte CS_SD=10;   //cs para la SD
const byte CS_RTC=9; //cs para el reloj
const byte CS_temp2=A1;
const byte CS_temp3=A2;
const byte RL_ON=3; //Bobina de rele que da tensión a los perifericos
const byte flyport_signal=2; //Señal que nos envia el flyport para pedir dato


/*******************************************************************************
                                   SETUP
*******************************************************************************/
void setup()
{
  
  Serial.begin(57600);//Inicio el puerto serie (RX TX)
   Serial.println(F("Iniciando..."));
   //Inicio pines de cs
  pinMode(CS_temp,OUTPUT);//Selecciono el cs de la temperatura como salida
  pinMode(CS_RTC,OUTPUT); //Selecciono el cs del RTC como salida
  pinMode(CS_SD,OUTPUT);  //Selecciono el cs de la SD
  pinMode(CS_temp2,OUTPUT); 
  pinMode(CS_temp3,OUTPUT);
  pinMode(flyport_signal,INPUT);//Selecciono como entrada el pin de señal de peticion de dato del flyport
  pinMode(RL_ON,OUTPUT);
  digitalWrite(RL_ON,HIGH);
  
  //Inicio SPI-------------------
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);  
  //------------------------------
  
 
  
  /************************************************
                    DEBUGGING
  **************************************************
  //Serial.println("Reiniciando...");
  delay(1000);
//Serial.println("Iniciado");
****************************************************/

IniciarReloj();
Serial.println(F("Reloj iniciado"));
//Iniciar_hora_RTC();
SetTimeDate(06,05,14,11,32,40);
delay(1000);
Serial.println(F("Hora actual del reloj"));
Serial.println(ReadTimeDate());
delay(1000);


Serial.println(F("Iniciamos la SD"));
IniciarSD();
Serial.println(F("Tarjeta iniciada"));

if (!SD.exists("Conf.txt")) //Si el archivo de configuracion no existe lo creamos y rellenamos
 {
   archivo =SD.open("Conf.txt", FILE_WRITE);
   archivo.println(F("Firmware V 1.0"));   //<--------------------------------CONFIGURACION
   archivo.print(F("Fecha de ultima carga: "));
   archivo.print(__DATE__);
   archivo.print(F(" "));
   archivo.println(__TIME__);
   archivo.close();
   Serial.println(F("Creado archivo de configuracion"));
   
   archivo =SD.open("Conf.txt",FILE_READ);
   Serial.println(F("Archivo de Configuracion: "));
   while (archivo.available())
   {
     Serial.write(archivo.read());
   }
   archivo.close();
 }
 else //Si el archivo de configuracion existe lo leemos
   { archivo =SD.open("Conf.txt",FILE_READ);
     Serial.println(F("Configuracion:"));
     while (archivo.available())
     {
       Serial.write(archivo.read());
     }
     archivo.close();
   }
   
   
if (!SD.exists("Medidas.txt")) //Si el archivo de Medidas no existe lo creamos
  {
    archivo =SD.open("Medidas.txt",FILE_WRITE);
    archivo.close();
    Serial.println(F("Creado archivo de medidas"));
  }
  
  
Serial.println(F("Setup finalizado con exito"));


attachInterrupt(0,peticion_dato,RISING);//Cuando hay un flanco ascendente en la señal debemos enviar un OK\r\n
//attachInterrupt(0,,FALLING);//Cuando hay un flanco descendente en la señal debemos enviar el siguiente dato o un FIN\r\n
Peticion_flyport=false; //Si no hago esto entra siempre en el bucle :S 

}


/***********************************************************************************
***********************************************************************************/






/***********************************************************************************
                                BUCLE CONTINUO DE PROGRAMA
***********************************************************************************/
                                
void loop()
{
  
  if (Peticion_flyport==true)
  {
   
     if(!SD.exists("Medidas.txt"))
     {
        Serial.println("FIN");
        archivo.close();
        Peticion_flyport=false;
        loop();
     }
     else //Si el archivo existe lo abrimos y empezamos a leer.
     {
       archivo=SD.open("Medidas.txt",FILE_READ);
     
     //Empiezo a leer necesitaré un bucle aqui
       while (archivo.available())
       {
         Serial.println("OK");//Añade \r\n al final 
         delay(500);//Le doy tiempo a borrar su buffer RX
        //Para leer haré:
         char dato_leido=' ';
      
         while (dato_leido!=0x0A)
         {
           dato_leido=archivo.read();
           Serial.write(dato_leido);//le envio el dato
         }
      
      
         Peticion_flyport=false;
         delay(100);//Espero 100 ms
         int espera=0;//Esta variable me permite salir si hay algun problema de este bucle<----------------------***********************
         while(Peticion_flyport==false)//Espero a que me haga otra petición o a que pasen 15 seg
         {
           delay(200);//espero 200 ms
           espera++;
   //CODIGO EN CASO DE NO RESPUESTA DEL FLYPORT
           if (espera==75)
           {
             Serial.println("Se ha pasado del tiempo");
             archivo.close();
             loop();//Si tarda mas de 15 seg sale del bucle y vuelve al inicio del bucle
           }
  //--------------------------------------------
         }
      
      }
      
      //Cuando ya acabo enviamos un FIN
      Serial.println("FIN");
      archivo.close();
      Peticion_flyport=false;
      
      }
    
  }
  
  
  
  else
  {
  
    suma_temp=0;
//Leo una temperatura cada segundo durante 10 segundos y le hago la media aritmetica
// SPI_Temp(); //Funcion que configura el SPI para el MAX
    for (int i=0; i<10; i++)
    {
      temperatura_1=LeerTemp(CS_temp);//Leo la temperatura proporcionada por el sensor
      suma_temp +=temperatura_1;
      delay(1000);
    }
    media_temp=suma_temp/10;
    Serial.println(F("Media de ultimas 10 temperaturas"));
  
  /**Convierto la temperatura en string  
  char media_string[5];
 dtostrf(media_temp,4,2,media_string);
 */
 
    Serial.print(media_temp);
    Serial.print(" ");
  
   
    String tiempo = ReadTimeDate();
    Serial.println(tiempo);
  
    SPI_SD(); //Funcion que configura el SPI para la SD
  
    archivo = SD.open("Medidas.txt",FILE_WRITE);
  // -----------------------------------------------------------------------------------NECESITO CARD DETECT PARA SABER SI PUEDO O NO ESCRIBIR 
    if (archivo) //Compruebo si se abrio el archivo
    {
      Serial.println(F("Ecribiendo en la SD"));
      archivo.print(media_temp);
      archivo.print(" ");
      archivo.println(tiempo);
      archivo.close();
      Serial.println(F("Dato escrito correctamente"));
    }
    else
    {
      Serial.println(F("Error al abrir el archivo Medidas.txt"));
    }
  
  
  }
}



/**********************************************************************************
                                    INTERRUPCIONES
**********************************************************************************/
void peticion_dato(void)
{
  Peticion_flyport=true;//El sistema flyport me pide un dato
 
  
}

I hope you can help me cause it's my first Arduino program and I've a lot of doubts which I'm solving in this community.

PD: English isn't my first language so I'm sorry if there are some faults in my post :zipper_mouth_face:

agallego:
When this variable is high at the beginning of Loop() the program should enter in the first if (you can see it in the code) but, I don't have any idea why, the arduino enter in the interrupt when he wants... Some times he does, another doesn't.

I don't understand what that means, but there is a big problem here:

void loop()
{
  
  if (Peticion_flyport==true)
  {
   
     if(!SD.exists("Medidas.txt"))
     {
        Serial.println("FIN");
        archivo.close();
        Peticion_flyport=false;
        loop();

Having loop() call itself like this is a spectacularly bad idea. If this code ever executed you would promptly overflow the stack and after that the behaviour is undefined.

Have you confirmed that your interrupt handler is being called reliably when it is supposed to be? If necessary, you could test that just by incrementing a byte counter in the interrupt handler and have the main loop print and then zero the counter any time you see a non-zero value.

Unless you have some specific reason for wanting to use interrupts I suggest you don't. Interrupts are very useful but using them introduces significant design issues. You should only use interrupts when necessary, and only reluctantly. The only reason I can think of for needing them here is that the signal you're trying to monitor is very brief (relative to the Arduino's processing speed) but you haven't said anything to suggest that's the case.

I imagined that call Loop() was a bad idea, but I didn't test the "goto" instruction. Will this instruction fix the problem with the stack memory?
Thank you for the correction. :slight_smile:

PeterH:
Have you confirmed that your interrupt handler is being called reliably when it is supposed to be? If necessary, you could test that just by incrementing a byte counter in the interrupt handler and have the main loop print and then zero the counter any time you see a non-zero value.

I've tried what you say. I created a volatile int variable named "Prueba" with value 0 and in the interrupt code I introduced "Prueba++", finally I show the variable with Serial.println(Prueba) at the beggining of Loop() but the variable is 1 all time. (I'm doing the pulses in digital pin 2 with 5V wire)
I don't understand why the interrupt is called in the first time (cause initially pin 2 is free, I mean it's not connected) but this often happens.

Any idea?

I need the interrupt cause the flyport code is an "State machine" and I can't guarantee that the pin will be HIGH when arduino reads it. (time problems)

Anyway if I can't make it work this way I will think in another solution, probably changing the flyport code

I've programmed michochip PICs in the past and I use interrupts continuously so I use to think in them to solve a lot of problems, maybe it's my fault.
Why are there problems with interruptions? I can think in a lot of projects which will need them.

Another question, I tried to write this:

void loop()
{
  inicio:
  if (Peticion_flyport==true || digitalRead(2)==HIGH)
  {
   
     if(!SD.exists("Medidas.txt"))
     {
        Serial.println("FIN");
        archivo.close();
        Peticion_flyport=false;
        goto inicio;
...

to debug the code but I found that condition is always true... even if pin 2 is connected to ground ... Do you have any idea why this could be happening? (Maybe I can't use this pin like digital pin if I use it for interrupt?)

Thanks for your time

Put each bit of code in a separate function, then you can use "return" instead of
goto. Much more structured and easy to understand.

Using recursion on a process with 2k of RAM is only to be done with great care.

My approach to organizing an Arduino program that needs to do different things is to put each activity in its own function and call the functions from loop(). That way loop() is very simple and the code for each activity is separate making debugging and integration easier. If I was doing your project my loop() would probably look like this

void loop() {
  readTemperatures();
  saveToSdCard();
  callFlyport();
}

...R

agallego:
I've tried what you say. I created a volatile int variable named "Prueba" with value 0 and in the interrupt code I introduced "Prueba++", finally I show the variable with Serial.println(Prueba) at the beggining of Loop() but the variable is 1 all time. (I'm doing the pulses in digital pin 2 with 5V wire)

I don't understand why the interrupt is called in the first time (cause initially pin 2 is free, I mean it's not connected) but this often happens.

Any idea?

I suggest you post the code you used to test the interrupt, because it's not clear whether you have tested it correctly. How often do you expect the interrupt to be triggered?

agallego:
cause initially pin 2 is free, I mean it's not connected

If the pin is not connected then it is floating and may take arbitrary values. If you read that or use it to trigger interrupts, you'll be reading garbage. If the device that is supposed to be driving it leaves it floating in some situations then you could consider adding a pull-up or pull-down resistor to make sure it is pulled to a defined state. Make sure the value of the resistor is high enough that the device can override it when it is driving the input.

If you haven't got a pullup resistor on pin2 it would probably be wise to set the internal pullup:

  pinMode(flyport_signal,INPUT_PULLUP);

In the loop function, as soon as you detect that Peticion_flyport is true you should set it false. Setting it false later on in different if statements can lead to difficult to debug problems because you'll probably forget one instance where it needs to be cleared and then your code will get confused:

  if (Peticion_flyport==true)
  {
    Peticion_flyport = false;

Also in the loop function, if you want to exit, all you need to do is use the return statement. Instead of loop(); use return;
If, on the other hand, you want to stop the program completely, use while(1);

Pete

Thank you MarkT and Robin2 for your advice. My final program will look like you said but I'm testing things right now and divide the main program in functions it's a bit uncomfortable for me.

PeterH:
I suggest you post the code you used to test the interrupt, because it's not clear whether you have tested it correctly. How often do you expect the interrupt to be triggered?

I've changed some things right now and I'm coming to bed but the test I made looks like this:

volatile int prueba=0;

void setup()
{
   Serial.begin(57600);
   pinMode(2,INPUT);
   prueba=0;
   attachInterrupt(0,inter,RISING);
}
void loop()
{
Serial.println(prueba);
delay(500);
}

void inter(void)
{
prueba++;
}

I added this lines in these functions. I hope it's clearer now.

You're right, I'll add a pull-down resistor in order to guarantee LOW signal when it's floating.

el_supremo, I don't understand why should I add a pull-up resistor. The flyport turns on a signal which is wired to pin 2 and the interruption is triggered with rising edge of the signal.
I think a pull-down resistor is a better choice cause it doesn't force me to invert the signal. Am I wrong?

el_supremo:
In the loop function, as soon as you detect that Peticion_flyport is true you should set it false. Setting it false later on in different if statements can lead to difficult to debug problems because you'll probably forget one instance where it needs to be cleared and then your code will get confused:

  if (Peticion_flyport==true)

{
   Peticion_flyport = false;

Thank you for the improvement, I did it three times in the code and I can change it for your solution.

I've a question about return. This instruccion exits you from the function where it is or exits you from the "Control structure"?
I mean if I have this code:

void funcion1()
{

for ( int x=0; x<1000; x++)
{
   if (x=500)
   {
    return;
    }

}
}

Will Return take me to the main funcion (loop in this case) or exits me from the for?

Return will take you back to whatever piece of code called "funcion1()".

If you just want to exit a loop prematurely use "break".

...R

Yup, think of return as what every function does at the end anyway. You also use
"return" to return a value if the function isn't a void one.

As well as "return" and "break" there's also "continue", which takes you to the
end of the innermost enclosing loop, ready to go round again:

  for (int i = 0 ; i < 10 ; i++)
  {
    if (i % 3 == 0)
      continue ;
    Serial.println (i) ;
    Serial.println (i*i) ;
  }

is equivalent to

  for (int i = 0 ; i < 10 ; i++)
  {
    if (i % 3 != 0)
    {
      Serial.println (i) ;
      Serial.println (i*i) ;
    }
  }

but often will involve less nesting in complex loops, so it can be clearer.

I think a pull-down resistor is a better choice cause it doesn't force me to invert the signal

The advantage to a pullup is that there is one built into the Arduino which can be switched on very easily with software as I showed. If you are going to use a pull-down you have to add your own external resistor. Whether you use pullup or pulldown the signal is still going to have a rising edge and a falling edge and the attachInterrupt function allows you to define which edge to trigger on so you don't need to invert the signal.

Pete

I've changed my code to make it works without interruption so it's working well (although the start of communication takes more time).

Thank you all for your advice and assistance.

PD: el_supremo, I'll feed the system with a battery so I prefer to use a pull-down to save energy.

PD: el_supremo, I'll feed the system with a battery so I prefer to use a pull-down to save energy.

You care to explain how a pull-down resistor is going to save energy?