I need to put my arduino into the sleep mode in order to consume less power. My arduino receives IR data and do some work with it, but I need to make it sleep until there is no data. If the data has appeared it should wake up and do something with data and go back to sleep mode.
Is there any way to do this.
I would be grateful for any help.
-
Is this the IRLib library from playground.arduino.cc/Main/IRLib ?– Nick Gammon ♦Commented Sep 22, 2015 at 1:49
-
Actually I didn't know it that one. I use an adaptation of Ken Shirriff library made by Chris Young. Is very generic I found it in a lot of examples.– Leandro BardelliCommented Sep 22, 2015 at 1:58
-
More info can be found at this stack exchange arduino.stackexchange.com/questions/6753/…– Leandro BardelliCommented Sep 22, 2015 at 2:20
2 Answers
This is what I have managed to achieve ...
Library
Version 1.51 March 2015 Copyright 2013-2015 by Chris Young http://tech.cyborg5.com/irlib/
This library is a major rewrite of IRemote by Ken Shirriff which was covered by GNU LESSER GENERAL PUBLIC LICENSE ...
Code
Modified code (from the demo IRrecord example):
/* Example program for from IRLib – an Arduino library for infrared encoding and decoding
* Version 1.5 June 2014
* Copyright 2014 by Chris Young http://cyborg5.com
* Based on original example sketch for IRremote library
* Version 0.11 September, 2009
* Copyright 2009 Ken Shirriff
* http://www.righto.com/
*/
/*
* IRrecord: records IR signals
* An IR detector/demodulator must be connected to the input RECV_PIN.
* Record a value by pointing your remote at the device
*/
#include <IRLib.h>
#include <avr/sleep.h>
#include <avr/power.h>
const byte LED_PIN = 8;
const byte RECV_PIN = 11;
const unsigned long BAUD_RATE = 115200;
const unsigned long NO_ACTIVITY_TIMEOUT = 2000; // milliseconds
IRrecv My_Receiver(RECV_PIN);
IRdecode My_Decoder;
IRsend My_Sender;
/*
* Because this version of the library separated the receiver from the decoder,
* technically you would not need to "store" the code outside the decoder object
* for this overly simple example. All of the details would remain in the object.
* However we are going to go ahead and store them just to show you how.
*/
// Storage for the recorded code
IRTYPES codeType; // The type of code
unsigned long codeValue; // The data bits if type is not raw
int codeBits; // The length of the code in bits
// These values are only stored if it's an unknown type and we are going to use
// raw codes to resend the information.
unsigned int rawCodes[RAWBUF]; // The durations if raw
int rawCount; //The number of interval samples
bool GotOne, GotNew;
// added by Nick Gammon
EMPTY_INTERRUPT (PCINT0_vect);
void setup()
{
GotOne=false; GotNew=false;
codeType=UNKNOWN;
codeValue=0;
Serial.begin(BAUD_RATE);
while(!Serial);//delay for Leonardo
Serial.println(F("Send a code from your remote and we will record it."));
Serial.println(F("Type any character and press enter. We will send the recorded code."));
My_Receiver.enableIRIn(); // Start the receiver
}
// Stores the code for later playback
void storeCode(void) {
// added by Nick Gammon
pinMode (LED_PIN, OUTPUT);
digitalWrite (LED_PIN, HIGH);
GotNew=true;
codeType = My_Decoder.decode_type;
if (codeType == UNKNOWN) {
Serial.println("Received unknown code, saving as raw");
// To store raw codes:
// Drop first value (gap)
// As of v1.3 of IRLib global values are already in microseconds rather than ticks
// They have also been adjusted for overreporting/underreporting of marks and spaces
rawCount = My_Decoder.rawlen-1;
for (int i = 1; i <=rawCount; i++) {
rawCodes[i - 1] = My_Decoder.rawbuf[i];
};
My_Decoder.DumpResults();
codeType=UNKNOWN;
}
else {
Serial.print(F("Received "));
Serial.print(Pnames(codeType));
if (My_Decoder.value == REPEAT) {
// Don't record a NEC repeat value as that's useless.
Serial.println(F("repeat; ignoring."));
}
else {
codeValue = My_Decoder.value;
codeBits = My_Decoder.bits;
}
Serial.print(F(" Value:0x"));
Serial.println(My_Decoder.value, HEX);
}
// added by Nick Gammon
pinMode (LED_PIN, INPUT);
digitalWrite (LED_PIN, LOW);
}
void loop() {
// added by Nick Gammon
static unsigned long lastActivity; // when we last woke
// sleep if no activity for a while
if (millis () - lastActivity >= NO_ACTIVITY_TIMEOUT)
{
Serial.flush (); // wait for Serial to finish outputting
Serial.end (); // shut down Serial
noInterrupts (); // timed sequence coming up
// pin change interrupt for D11
PCMSK0 |= bit (PCINT3); // want pin 11
PCIFR |= bit (PCIF0); // clear any outstanding interrupts
PCICR |= bit (PCIE0); // enable pin change interrupts for D8 to D13
set_sleep_mode (SLEEP_MODE_STANDBY);
sleep_enable();
byte old_ADCSRA = ADCSRA;
// disable ADC to save power
ADCSRA = 0;
power_all_disable (); // turn off various modules
interrupts ();
sleep_cpu ();
sleep_disable();
power_all_enable (); // enable modules again
ADCSRA = old_ADCSRA; // re-enable ADC conversion
Serial.begin(BAUD_RATE);
lastActivity = millis ();
} // end of no activity for a couple of seconds
if (My_Receiver.GetResults(&My_Decoder)) {
My_Decoder.decode();
GotOne=true;
storeCode();
delay(500);
My_Receiver.resume();
}
}
Explanation
Main changes are in loop
(near the bottom) where it sleeps if there is no activity for two seconds. This "no activity" test gives it time to catch an incoming pulse stream.
Before sleeping it turns off Serial (to save power), and sets up to detect the pin-change interrupt. It also turns off the ADC, and calls power_all_disable
to disable various internal modules.
Note that it uses pin-change interrupts on Arduino pin D11 (pin 17 on the chip). For a different processor than the Atmega328P, or a different input pin, you would have to change which pin-change interrupt is used.
I found that I had to use SLEEP_MODE_STANDBY rather than SLEEP_MODE_PWR_DOWN, otherwise it seemed to take too long to wake up and handle the incoming pulse.
For testing, I flashed D8 pin to make sure it was working when hooked up in low-power configuration.
Power consumption
I tested with an AX-1838T IR-receiver that I had to hand (same pin-outs as in the Adafruit article).
Measured stand-alone (ie. not connected to the Arduino) it consumed 630 µA, so therefore we could not expect lower power consumption than that.
Connected up to a "bare-bones" board (ie. just the Atmega328P chip, decoupling capacitors, etc.) I measured 1.158 mA when idle.
My tests on my page about power indicate that SLEEP_MODE_STANDBY uses 480 µA more power than SLEEP_MODE_PWR_DOWN, so this sounds about right.
630 + 480 = 1110 µA
You might be able to save more power by operating at a lower voltage. This particular receiver can operate at a lower voltage. As an indication, I tried the setup at 3.3 V (which is out of spec for the processor, but whatever, I was just testing) and it consumed 978 µA when idle. This is a modest saving over the 5 V test.
References
-
oh my god, this is an answer! I need time to understand and test it ! Thanks a lot Nick ! Commented Sep 22, 2015 at 11:25
-
1Great page. About the low power mode: the low frequency clock is useful in scenarios where one has to do relatively frequent operations, like debouncing: the debouncing can happen at low frequency and then the clock gets cranked up only if the wakeup was real. So that a noisy environment doesn't kill the battery. Commented Sep 22, 2015 at 20:02
-
LOL !!! Nick, you are the author of that page, is really great !!! I didn't notice it. Commented Sep 22, 2015 at 20:14
-
1@IgorStoppa - indeed, however in this particular case the IR receiver uses the bulk of the power. Still, it may well be possible to get a lower powered one.– Nick Gammon ♦Commented Sep 22, 2015 at 21:21
-
1
I didn't get why you sleep and the next command is power all enabled
- the sleep call is the one where it stops, maybe for hours.– Nick Gammon ♦Commented Sep 23, 2015 at 21:14
Connect the IR receiver to INT0 or INT1 (pins 2 or 3 respectively). You can use the attachInterrupt function. E.g. attachInterrupt(0, yourfunctionname, LOW). The function being called can be empty, if you only use it to wake up the MCU.
To wake from sleep you need to use the LOW trigger mode. Once waken up you probably want to remove the interrupt handler (detachInterrupt
), or it will swamp the cpu with interrupts.
(Note that most IR receiver will still use something like 3mA.)
-
It has been confirmed by Atmel that any level change will wake the processor from sleep. See Interrupts for a quote of their email.– Nick Gammon ♦Commented Jul 5, 2015 at 3:00
-
-
1Most of my examples were written before someone on the Arduino forum raised the issue. Until then I had believed the datasheet. I raised a ticket at Atmel and got this response: "Our design team has confirmed that “Note-3 mentioned under Table 10-1” is a datasheet bug. So you can use any type of interrupt (Rising edge/ Falling edge / Low level / Any logical change) to wake up from sleep mode. Sorry for the inconvenience caused."– Nick Gammon ♦Commented Jul 5, 2015 at 20:39
-
For example see this thread External RTC to wake Arduino up on interrupt. The poster there said, after I recommended changing from LOW to FALLING: "Wow. Nick you were right. Tried it with FALLING and it works like a charm. Thanks!" As you noted @Gerben, using LOW has its own problems.– Nick Gammon ♦Commented Jul 5, 2015 at 20:46
-
I guess I need to give it another try on my next project. Thanks– GerbenCommented Jul 6, 2015 at 12:25