Tengo un pequeño circuito donde controlo una tira led mediante control remoto, que de momento sólo utilizo los botones On y Off para encenderla y apagarla.
El circuito está alimentado con 4 pilas AAA, es decir, 6.5 V. El caso es que cuando está en "Off", el circuito sigue consumiendo energía y para reducir este consumo me gustaría utilizar las funciones de la librería "LowPower.h" para que se ponga en modo de mínimo consumo de energía cuando pulso el botón Off (me interesa que solo sea cuando pulso el botón Off, porque mi idea es repetir en un futuro el programa utilizando una tira led RGB donde utilice otros botones para iluminar con otros colores). La idea es que la interrupción del sensor IR al pulsar otro botón lo haga despertar.
He seguido algunos tutoriales y he mirado dudas similares en el foro para añadir las líneas que me faltaban al código que ya tenía, pero hay algo que se me sigue escapando porque con el código siguiente la parte de "LowPower" no funciona (el resto, lectura de la señal y encendido del led sí):
En principio intPin0 tiene que ser entrada, no salida, y obviamente no puedes poner ese pin en HIGH ni en LOW.
Una rutina de servicio de interrupción no puede usar el puerto Serial.
Tampoco puedes ponerlo a dormir desde la interrupción.
Mucho menos llamar la decodificación desde dentro de la interrupción.
Y por cierto, debiste postear en el foro en español.
Gracias por tu respuesta, y disculpas por lo de postear fuera del foro en español. Di a crear el post desde el foro en español, pero pensé que el desplegable para seleccionar la categoría de cuestiones de programación se encontraba dentro de la parte de español, y veo que no es así. Lo tendré en cuenta la próxima vez.
Gracias por tus comentarios. Revisaré lo que me dices para retrabajar el código.
Mientras tanto, ¿alguna orientación sobre cómo poner a dormir el arduino solamente cuando pulse el botón de "off"? Mi idea era utilizar la interrupción para todo, es decir, para posibles bucles que pudiera tener con una tira led RGB, pero como dices, es raro poner a dormir y despertar al arduino con la misma interrupción. No se me ocurre cómo plantearlo.
Las interrupciones tienen que ser lo más cortas posible y rápidas, piensa que interrumpen otros procesos, además las interrupciones se bloquean así que nada que use interrupciones se podría ejecutar.
Te sugiero que revises los ejemplos de la librería para orientarte mejor.
Igual, te paso algunas ideas.
Ponlo a dormir desde el loop(), en la interrupción solo haz el dettach de la misma, haz el attach cuando procesas el "off" y justo antes de dormirlo. Agrego: Si por alguna razón necesitas saber que se produjo la interrupción usa una variable (recuerda que debes declararla volatile) que incrementarás o cambiarás de estado según convenga desde la misma rutina de interrupción.
Una cosa es donde pones el sensor IR que debe estar con el micro activo para poder leer y otra es ponerlo a dormir y despertarlo supuestamente usando de nuevo el IR.
No lo veo factible.
Veamos por qué y tal vez al explicarte vea una solución:
Hay dos pines reservados para interrupciones en un UNO o NANO. Pin 2 y pin 3 que son INT0 e INT1.
En ambos puedes hacer que vuelva de LowPower y despierte.
Por eso esos pines deben ser entradas o mejor dicho dejarlos si usar porque los usará la interrupción debidamente inicializada.
Por otro lado, un sensor en cualquier otro pin del arduino leerá tus comandos IR. Eso ya lo has hecho y funciona.
Pero el tema es que decirle ON u OFF devuelve un código, y con ese código haces tareas. Hasta ahi bien, una de ellas será ponerse a dormir.
Con lo que no te queda nada para despertarlo salvo... compartir el pin del sensor con INT0 o INT1 y que la trama enviada sin validar o sea sin que responda a ON por ejemplo, con lo que puedes usar VOLUMEN UP , lo va a despertar
Si eso te sirve, intenta.
Solo comparte el pin 2 por ejemplo con el pin que usas para el sensor IR que es pin 4
Exactamente, es tal y como lo describes.
¿Por qué no sería factible?
Edito:
Revisando la librería, ni siquiera habría que compartir ningún pin si se usan los que define por defecto (suponiendo que hablamos de un UNO/Nano y otras con ATmega328)
/*
* Pin mapping table for different platforms
*
* Platform IR input IR output Tone Core/Pin schema
* --------------------------------------------------------------
* DEFAULT/AVR 2 3 4 Arduino
...
*/
y luego
# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc.
#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here.
#define IR_SEND_PIN 3
#define TONE_PIN 4
...
Gracias Surbyte. Durante el día revisaré todo lo que me estáis indicando.
Hay una cosa que todavía no he entendido, y es ¿por qué con mi código inicial, el arduino no se va a dormir?
Es decir, a falta de hacer las correcciones que me habéis dicho, con este código la interrupción funciona, se pasa a la función "led()", se decodifica la señal de ir, y si corresponde a la de "off" apaga el led, pero ¿por qué no se ejecuta la línea posterior de LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);?
Mi temor era no poder despertarlo de nuevo con la interrupción, pero no comprendo por qué no se pone en modo bajo de energía, si sí que se ejecutan las líneas de código anteriores.
Uno el que dice @Surbyte aunque creo que es obvio que ese LED debe volar (hay que eliminar todos los consumos superfluos).
El otro detalle es que no encontré (ni se me ocurre que sea posible) la forma de evitar que una nueva pulsación de "Off" lo despierte porque la interrupción se genera con cualquier señal que llegue del sensor. Como sea, para decodificar la tecla el micro tiene que estar despierto así que filtrarla por soft es imposible.
Saludos
Agrego:
Hice unas pruebas más con las siguientes modificaciones:
El LED "Power" desconectado (corté la pista que lo conecta a masa).
Modifiqué el código para que con una tecla encienda el LED "L" y con otra lo apague. O sea, eliminé el "blink".
Los resultados de consumo con el Nano alimentado con 5V:
Con LED "L" encendido, 17.5 mA.
Con LED "L" apagado, 14.4 mA.
En modo sleep (LED se apaga), 1.7 mA.
Como el Nano de prueba es un clon, imagino que parte del consumo es del CH340 y algo que drenará por el regulador.
Las mediciones no incluyen el consumo del receptor IR (HX1838) pero es prácticamente despreciable cuando está en reposo.
Hola Javi, de qué circuito estamos hablando? ¿has mirado otros con menor consumo?
Mira este link donde controla RGB con un ATTiny 85 que seguro que el consumo es mucho menor que un ATmega328 que trae el Arduino Uno... https://github.com/Jannled/ATTiny-RGB-IR
Saludos.
He probado el programa adaptando a los códigos de mi control remoto y funciona
En cuanto a que se despierte pulsando también el Off, quizá podría poner alguna línea para que el LED quede encendido o que de alguna manera avise de que no está dormido, para volver a pulsar Off y ponerlo a dormir.
Tengo otra duda sobre el código, y es cómo podría poner, por ejemplo dentro de case Boton_1 , un bucle para que el led haga algún efecto, no solo sea quedarse totalmente encendido o apagado. Por ejemplo un encendido o apagado progresivo:
analogWrite(ledR, i);
i++;
Con i variando entre 0 y 255. Lo he probado con un for, pero solo lo realiza una vez, y con un while, donde se repite continuamente pero no sale del bucle aunque pulses cualquier botón del control remoto. Entiendo que aquí debería haber una nueva interrupción, pero no me ha funcionado. ¿Alguna idea?
Para controlar el tiempo deberás de usar un "flag" por ejemplo int ledActivo=2000; . Lo inicializas ahí y después fuera de los IF, Case etc (dentro del Loop principal) vas restando y haciendo "cosas" como incrementar o disminuir la intensidad, etc.
Saludos.
Parte es el CH340 y tmb el relulador por eso si lo quieta y lo alimentas con batería LIPO bajas también eso. Hay una nota de Nick Gammon de low power donde te da todos los trucos a seguir.
Antes de quitar tantas cosas de la placa, directamente armaría una con solo el 328P DIL (y su cristal) y listo, bajo consumo asegurado.
Pongo el link al tutorial de @nickgammon ya que me parece que debería ser de lectura obligatoria. Power saving techniques for microprocessors
Copio el código basado en el ejemplo que envió @MaximoEsfuerzo con el bucle para disminuir la intensidad del led, tal como he entendido que decías que debía hacerlo: si el arduino recibe la señal del Botón 1 activa el flag "bucleOn" y comienza a sumar valores a la variable "i" a un tiempo determinado:
#include <IRremote.h>
#include "LowPower.h"
#define Boton_Off 0xFD02EF00
#define Boton_On 0xFC03EF00
#define Boton_1 0xF807EF00
const byte SENSOR = 2;
unsigned long time;
bool pwrOff = false;
bool bucleOn = false;
int ledW = 9;
int i = 0;
IRrecv irrecv(SENSOR);
decode_results codigo;
void IR_ISR() { // INT0
detachInterrupt(digitalPinToInterrupt(SENSOR));
}
void setup() {
Serial.begin(115200);
pinMode(SENSOR, INPUT);
irrecv.enableIRIn();
}
void loop() {
if (irrecv.decode()) {
Serial.println(irrecv.decodedIRData.decodedRawData, HEX);
switch (irrecv.decodedIRData.decodedRawData) {
case Boton_On:
analogWrite(ledW, 0);
bucleOn = false;
break;
case Boton_1:
i = 0;
time = millis();
bucleOn = true;
break;
case Boton_Off:
analogWrite(ledW, 255);
bucleOn = false;
pwrOff = true;
break;
}
}
if (pwrOff) {
delay(500); // para evitar detección de nuevos pulsos del C/R
noInterrupts();
EIFR = bit(INTF0); // limpia interrupciones que pudiesen quedar pendientes
attachInterrupt(digitalPinToInterrupt(SENSOR), IR_ISR, LOW);
interrupts();
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); // *** A dormir! ***
pwrOff = false;
}
if (bucleOn) {
noInterrupts();
EIFR = bit(INTF0); // limpia interrupciones que pudiesen quedar pendientes
attachInterrupt(digitalPinToInterrupt(SENSOR), IR_ISR, LOW);
interrupts();
if (i<=255){
analogWrite(ledW, i);
if (millis() - time > 200UL){
i++;
time = millis();
}
}
}
irrecv.resume();
}
El problema es que el bucle no se interrumpe de inmediato cuando vuelvo a pulsar los botones "On" y "Off". Entiendo que habría que poner una nueva interrupción similar a lo que hay cuando se pulsa Off para despertar al arduino, pero tal como lo tengo no está funcionando, el bucle no se interrumpe inmediatamente.
if (bucleOn) {
// esto acá no va
/*
noInterrupts();
EIFR = bit(INTF0); // limpia interrupciones que pudiesen quedar pendientes
attachInterrupt(digitalPinToInterrupt(SENSOR), IR_ISR, LOW);
interrupts();
*/
if (i <= 255){
if (millis() - time >= 200UL){
analogWrite(ledW, i);
i++;
time = millis();
}
} else {
bucleOn = false;
}
}
Tené en cuenta que ese "bucle" demora 51 segundos en completarse.