¿Leer de un pin digital o Vcc usando interrupciones?

Hola de nuevo, me gustaría saber cual es la mejor forma, en cuanto a ahorro, de leer el estado de un botón, si conectándolo a Vcc o a un pin digital usando interrupciones. Me explico:

Actualmente el sistema de interrupciones que tengo es algo chapuza. Me baso en variables booleanas para saber si se ha interrumpido el Arduino o no y, si se ha interrumpido el pin que lee el estado del botón pasa a ser OUTPUT. Siendo sincero, no sé como pero hace lo que quiero. Esto es así porque lo que quiero es desactivar las interrupciones durante el intervalo de tiempo en el que el Arduino está en modo sleep para evitar múltiples pulsaciones.

Os preguntaréis, ¿que tiene que ver esto con leer de Vcc o de un pin digital? Mucho. Mucho porque si leo el estado de un botón usando un pin digital como 5V, entonces cuando interrumpa al Arduino, lo primero que hago es poner a nivel bajo el pin digital y por tanto simplifico el sistema de interrupciones. Además lo bueno que tiene es que cuando lo ponga a nivel bajo, por mucho que pulse algún botón no pasará corriente y no consumirá energía, mientras que si los 5V los proporciona la alimentación del circuito, aunque las interrupciones estén deshabilitadas, cierro el circuito, pasa corriente y eso implica gasto.

Sin embargo, desconozco que es la mejor solución en cuanto a reducción de consumo, si leer desde Vcc con el engorroso sistema de interrupciones que tengo montado o utilizar un pin auxiliar que proporcione cuando yo quiera los 5V.

PD: Es crucial el consumo energético en el proyecto.

Un saludo.

Esto es así porque lo que quiero es desactivar las interrupciones durante el intervalo de tiempo en el que el Arduino está en modo sleep para evitar múltiples pulsaciones.

A menos que le indiques que se despierte via interrupciones no lo hará. Asi que de mas está lo que hayas hecho.

Respecto de tu análisis de si consume o no corriente, estoy sorprendido. Jamás le he prestado atención a eso que dices pero claro yo siempre uso los pines de entrada como pull-down, si alguien puede drenar corriente será del pin hacia el resistor.

Bueno sin querer he respondido tu segunda inquietud. En lugar de usar PULL-UP como sugieres, usa PULL-DOWN o bien configura tu sensor (siempre que no te genere problemas) como INPUT_PULLUP o sea el pulsador directamente entre gnd y el pin.

Prueba estas sugerencias.

surbyte:
A menos que le indiques que se despierte via interrupciones no lo hará. Asi que de mas está lo que hayas hecho.

Actualmente se despierta con interrupciones, lo que quiero es que deje de funcionar la interrupción controlada por el botón una vez disparada durante un intervalo de tiempo corto. Actualmente mi código es el siguiente:

void setup(){
    PCintPort::attachInterrupt(PIN_INTERRUPT, interruptFunction,RISING);
}

void loop(){ 
    
    pinMode(PIN_INTERRUPT);
    
    if(loops <=  loopsToSend ){
    
        if(pulsed){
            //doSomething
            PCintPort::detachInterrupt(PIN_INTERRUPT);
        }
    
        pulsed = false;
    
        LowPower.powerDown(SLEEP_TIME, ADC_OFF, BOD_OFF);
        PCintPort::attachInterrupt(PIN_INTERRUPT, interruptFunction, CHANGE);  
        loops++;
}

void interuptFunction(){
    if(pulsed){
        pinMode(PIN_INTERRUPT, OUTPUT);
        digitalWrite(PIN_INTERRUPT, LOW);
    }
    else{
        pulsed = true;
    }
}

En cuanto a la chapuza me refiero al if que tengo dentro de la interrupción para saber si ha sido pulsado o no, luego eso significa que ha saltado nuevamente la interrupción y eso no debería suceder. Por eso, si utilizase un pin digital auxiliar, con ponerlo a nivel bajo una vez entrase en la interrupción, hasta que no vuelva a despertarse no volveré a ponerlo a nivel alto.

El siguiente código lo he intentado probar y no funciona como yo quiero:

void interrupt(){
    opinions++;
    disableInterrupt(PIN_INTERRUPT);
}

//Debug function
void switchOnLED(int loops, int millis){
    int i;
    for(i = 0; i < loops ; i++){
        digitalWrite(PIN_LED, HIGH);
        delay(millis);
        digitalWrite(PIN_LED, LOW);
        delay(millis);
    }
}

void setup(){
    pinMode(PIN_LED, OUTPUT);
    switchOnLED(2,500);
    enableInterrupt(PIN_INTERRUPT, interrupt,RISING);
}

void loop(){
    if(opinions < 10){
        LowPower.powerDown(SLEEP_TIME, ADC_OFF, BOD_OFF);
        enableInterrupt(PIN_INTERRUPT, interrupt,RISING);
        switchOnLED(5,75);
    }
    else{
        switchOnLED(1,400);
    }
}

surbyte:
Respecto de tu análisis de si consume o no corriente, estoy sorprendido. Jamás le he prestado atención a eso que dices pero claro yo siempre uso los pines de entrada como pull-down, si alguien puede drenar corriente será del pin hacia el resistor.

Bueno sin querer he respondido tu segunda inquietud.
En lugar de usar PULL-UP como sugieres, usa PULL-DOWN o bien configura tu sensor (siempre que no te genere problemas) como INPUT_PULLUP o sea el pulsador directamente entre gnd y el pin.

No entiendo que tiene que ver usar un PULL-DOWN o un PULL-UP con el ahorro de energía que podría suponer o no alimentar los botones con un pin digital auxiliar en vez de con VCC, así como la simplificación del sistema de interrupciones. Disculpa mi torpeza surbyte.

Un saludo.

Actualmente se despierta con interrupciones, lo que quiero es que deje de funcionar la interrupción controlada por el botón una vez disparada durante un intervalo de tiempo corto.

Como va cambiando el eje del tema que iniciaste o al menos a mi me lo parece.

Entonces, despiertas el Arduino con la interrupción. Okay. Luego si ese tiempo es corto quieres que no presete mas atención a las interrupciones. Okay

Veo que tu interrupción (1er código) esta fijada a CHANGE. cualquier 0 a 1 o 1 a 0, la activa. Presionas el pulsador se activa y la bloqueas cuando sueltas, porque se vuelve a activar justo cuando liberas el pulsador. Por esa razón necesitas el if dentro de la interrupción pero se debe a tu elección de CHANGE.

El segundo código que es lo que no hace como quieres?

Buenas tardes, supongo que estaré metiendo la pata pues yo también soy novato en esto pero creo que tal vez este manual te ayude para lo que buscas http://www.prometec.net/modo-sleep-interrupciones/ pues creo que el código es este, atas la interrupción y cuando se despierta la desatas...

void loop()
   {   attachInterrupt( 0, ServicioBoton, FALLING);
       LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
       detachInterrupt(0);

       if (n != contador)
          {     Serial.println(contador);
                n = contador ;
                Serial.flush();
          }
   }

Disculpad si no he sido de ayuda, mi intención es participar.

Saludos,

surbyte:
Veo que tu interrupción (1er código) esta fijada a CHANGE. cualquier 0 a 1 o 1 a 0, la activa.
Presionas el pulsador se activa y la bloqueas cuando sueltas, porque se vuelve a activar justo cuando liberas el pulsador.
Por esa razón necesitas el if dentro de la interrupción pero se debe a tu elección de CHANGE.

El segundo código que es lo que no hace como quieres?

He modificado el momento en el que salta la interrupción de CHANGE a RISING, en todas las veces en las que activo la interrupción. Sin embargo no cumple lo que quiero que es una vez que pulso el botón y despierto al Arduino, modificar una variable y desactivarla al momento, que vuelva a dormir sus 8 segundos y, una vez que despierte, activar de nuevo la interrupción.

He visto que los registros que utiliza el ATmega328p para controlar las interrupciones externas son el EIMSK y el EIFR. Si mal no he entendido, para que una interrupción salte, tiene que estar el bit de interrupción del SREG a 1 (algo así como el interruptor general de las interrupciones) y los dos primeros bits del EIMSK también a 1 para que puedan funcionar las interrupciones externas. El EIFR es el que almacena el flag de que se ha producido una interrupción por dicho pin. He probado a tocar esos registros en vez de utilizar las funciones de attachInterrupt y disableInterrupt y tampoco funciona:

void interrupt(){
	opinions++;
	// disableInterrupt(PIN_INTERRUPT);
	EIMSK &= 0xFC;
	// EIFR &= 0xFF;
}

void switchOnLED(int loops, int millis){
  int i;
  for(i = 0; i < loops ; i++){
    digitalWrite(PIN_LED, HIGH);
    delay(millis);
    digitalWrite(PIN_LED, LOW);
    delay(millis);
  }
}

void setup(){
	pinMode(PIN_LED, OUTPUT);
	switchOnLED(2,500);
	enableInterrupt(PIN_INTERRUPT, interrupt,RISING);
}

void loop(){
	if(opinions < 10){
		LowPower.powerDown(SLEEP_TIME, ADC_OFF, BOD_OFF);
		switchOnLED(5,75);
		EIMSK |= 0xFF;
	}
	else{
		switchOnLED(1,400);
	}
}

Gracias también a alfredomrh por la ayuda que estás ofreciendo.

Fuentes de búsqueda: