Sleep/ Interrupts and Tone

Hello all,

My project is about a PIR sensor, an LED and a buzzer. I have a barebone ATMega328p running this code. I am stuck, please see the code below. I am not able to make Tone() work. When the PIR detects nothing the uC sleeps. When movement is detected the uC lights the LED but does not sound the alarm. I am probably missing an elephant here.. suggestions?

Must I take the Tone part out of the ISR? Put it where?
And also: Thank you Nick Gammon!

/* PIR sensor * Sleepy * Interrupt * Buzzer */
#include "Arduino.h"
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>

const byte ledPin   = 13;                 // choose pin for the LED
const byte inputPin = 2;                  // choose input pin (for PIR)

const int pitchTone  = 3000;              // siren frequency
const int speakerPin = 8;                 // siren pin

void switchPressed() {                    // Interrupt Service Routine (ISR)
  if (digitalRead (inputPin) == HIGH) {
    digitalWrite  (ledPin, HIGH);
    tone(speakerPin, pitchTone);}
  else {
    digitalWrite (ledPin, LOW);
    noTone(speakerPin);}}                 // end of switchPressed
 
void setup() {
  pinMode(ledPin, OUTPUT);                // declare LED as output
  pinMode(inputPin, INPUT_PULLUP);}       // declare sensor as input
 
void loop() {
  ADCSRA = 0;                             // disable ADC
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);   // select sleep mode
  noInterrupts ();                        // timed sequence follows
  sleep_enable();                         // 
  attachInterrupt (digitalPinToInterrupt (inputPin), switchPressed, CHANGE);  // attach interrupt handler
  MCUCR = bit (BODS) | bit (BODSE);       // turn on brown-out enable select
  MCUCR = bit (BODS);                     // this must be done within 4 clock cycles of above
  interrupts();                           // guarantees next instruction executed
  sleep_cpu();}                           // sleep within 3 clock cycles of above

When movement is detected the uC lights the LED but does not sound the alarm. I am probably missing an elephant here.. suggestions?
Must I take the Tone part out of the ISR?

Yes. Tone() uses timer compare match interrupts, and interrupts are disabled within an ISR.

If you want tone() you will have to have set a flag in the ISR, and respond to it by calling tone() in loop(). Loop will have to do more than just go back to sleep. :slight_smile:

This is one more way that interrupts complicate things that ought to be easy. It should be unnecessary to use interrupts to handle a switch operated by humans, even when the switch is actually a PIR sensing movement. Humans are slow compared to any Arduino.

The loop() function can be looking for the PIR to sense movement and can turn on the LED and start the tone. No interrupts are needed unless this is a homework assignment.

I see that the CPU is being put to sleep but I do not understand why. The power used by almost any PIR is greater than that saved by putting the Arduino processor to sleep.

Thank you all, I will try the flag advice.

vaj4088,

when ATMega lights up everything it consumes about 25mA (for 3 seconds). When it goes to sleep it consumes about 120μA on its own and 170μA total sum ATMega and the PIR (which is in "idle" mode). That's what my MMeter says at least.

Thank you for the information. You seem to have an excellent PIR.

Done! Thanks guys!

vaj4088, I don't really have such a good PIR. It is the standard chinese little board but in idle mode it (really) consumes 50μA (see attached).