Detect which Interrupt fired

I have a bare board ATTiny1634 board.
I am running the following sketch.
The sketch uses 2 hardware interrupts to wake from Sleep:
PCINT5 - D3
PCINT6 - D2
The interrupts fire correctly when the pins are pulled LOW.

Code:

#include <avr/sleep.h>
#include <avr/power.h> 
#include <avr/interrupt.h>

const int pinMagnet  = 3;
const int pinVibrate = 2;

volatile boolean gotKnock = false;
volatile boolean gotMagnet = false;

void setup() {
  Serial.begin(9600);
  Serial.println("BOOT");

  pinMode(pinMagnet, INPUT);
  digitalWrite(pinMagnet, HIGH);

  pinMode(pinVibrate, INPUT);
  digitalWrite(pinVibrate, HIGH);
  sleep();
} // setup

void sleep() {
  Serial.println("Sleeping....");
  delay(100);

  GIMSK |= _BV(PCIE0);                     // Enable Pin Change Interrupts
  PCMSK0 = bit (PCINT5) | bit (PCINT6);  // pins 3 and 2
  ADCSRA &= ~_BV(ADEN);                   // ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement

  sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
  sei();                                  // Enable interrupts
  sleep_cpu();                            // sleep

  cli();                                  // Disable interrupts
  PCMSK0 &= ~_BV(PCINT5);                  // Turn off 3 as interrupt pin
  PCMSK0 &= ~_BV(PCINT6);                  // Turn off 2 as interrupt pin

  sleep_disable();                        // Clear SE bit
//  ADCSRA |= _BV(ADEN);                    // ADC on

  sei();                                  // Enable interrupts
} // sleep

ISR(PCINT0_vect) {
  // This is called when the interrupt occurs
}

//ISR(PCINT5_vect) {
//  gotMagnet = true;
//  gotKnock = false;
//}

//ISR(PCINT6_vect) {
//  gotMagnet = false;
//  gotKnock = true;
//}

void loop() {
  Serial.println("Awake....");
  delay(100);

  if ((gotKnock) && (!gotMagnet)) {
    Serial.print("Got Knock: ");
  }

  if ((gotMagnet) && (!gotKnock)) {
    Serial.println("Got Magnet");
  }
  sleep();
}

However, I do not know which interrupt woke the ATTiny1634.
I have tried both the following IRS, but they do not work:

ISR(PCINT5_vect) {
  gotMagnet = true;
  gotKnock = false;
}

ISR(PCINT6_vect) {
  gotMagnet = false;
  gotKnock = true;
}

It seems that the only ISR that is used when either interrupt is fired is:

ISR(PCINT0_vect) {
  // This is called when the interrupt occurs
}

How do I either:

  1. read which interrupt was fired
    or
  2. use ISR(PCINT5_vect) or ISR(PCINT6_vect)

On each interrupt, read both pins. The pin that changed is the one that caused the interrupt. Record the new state so you can tell when it changes again.

Thanks.
Something like:

  Serial.print("stateMagnetOLD:");
  Serial.println(stateMagnetOLD);
  delay(100);
  stateMagnetNEW = digitalRead(pinMagnet);
  Serial.print("stateMagnetNEW:");
  Serial.println(stateMagnetNEW);
  delay(100);
  stateMagnetOLD = stateMagnetNEW;

    Serial.print("stateKnockOLD:");
  Serial.println(stateKnockOLD);
  delay(100);
  stateKnockNEW = digitalRead(pinVibrate);
  Serial.print("stateKnockNEW:");
  Serial.println(stateKnockNEW);
  delay(100);
  stateKnockOLD = stateKnockNEW;

I have this in Loop, or should it be in ISR(PCINT0_vect)
Is there a better way to do this - my code is scrappy
Serial.print is for debug

It most certainly should not

Probably something like this.

I haven't tested it.
Because I don't have an ATtiny1634 board.

Edited a line with ********** in the comment.
There is probably redundant volatile, please optimize yourself.

#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/interrupt.h>

const int pinMagnet  = 13;
const int pinVibrate = 12;

volatile boolean gotKnock = false;
volatile boolean gotMagnet = false;

volatile byte oldPort; // **********
volatile byte nowPort; // **********

void setup() {
  Serial.begin(9600);
  Serial.println("BOOT");

  pinMode(pinMagnet, INPUT_PULLUP);
  pinMode(pinVibrate, INPUT_PULLUP); // More better **********

  sleep();
}

void sleep() {
  Serial.println("Sleeping....");
  delay(100);

  GIMSK |= _BV(PCIE0);                    // Enable Pin Change Interrupts
  PCMSK0 = _BV(PCINT5) | _BV(PCINT6);     // pins 3 and 2
  ADCSRA &= ~_BV(ADEN);                   // ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement
  sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)

  oldPort = PINA;                         // Save PIN status before sleep **********

  sei();                                  // Enable interrupts
  sleep_cpu();                            // sleep

  cli();                                  // Disable interrupts
  PCMSK0 &= ~(_BV(PCINT5) | _BV(PCINT6)); // Turn off interrupt pin **********

  sleep_disable();                        // Clear SE bit
  //  ADCSRA |= _BV(ADEN);                // ADC on

  sei();                                  // Enable interrupts

  // Check reason for wake-up **********
  gotMagnet = (boolean)((oldPort & _BV(PCINT5)) != (nowPort & _BV(PCINT5)));
  gotKnock  = (boolean)((oldPort & _BV(PCINT6)) != (nowPort & _BV(PCINT6)));
}

ISR(PCINT0_vect) {
  nowPort = PINA; // Store PIN status at time of wake **********
}

void loop() {
  Serial.println("Awake....");
  delay(100);

  if ((gotKnock) && (!gotMagnet)) {
    Serial.println("Got Knock");
  }

  if ((gotMagnet) && (!gotKnock)) {
    Serial.println("Got Magnet");
  }
  sleep();
}

@chrisknightley
Many thanks - the above example works.
I do get the "Got Magnet" but not the "Got Knock"

Just another question:
Why are pins designated
const int pinMagnet = 13; (Was 3 )
const int pinVibrate = 12; (Was 2)

Oh sorry, maybe I made a mistake. :woozy_face:
Maybe when I compile it myself, I thought try to test it on another Arduino, but I probably gave it up. :sleeping:
I forgot to fix it.
Please ignore and restore the pin number.

Did it fix the pin number and test it?

Please test this.

Fixed an incorrect pin number. (my mistake)
The ISR cycle has been shortened as much as possible to check the pin state faster.
Bit better pin check routine.

#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/interrupt.h>

const int pinMagnet  = 3; // PA5
const int pinVibrate = 2; // PA6

boolean gotKnock, gotMagnet;

void setup() {
  Serial.begin(9600);
  Serial.println("BOOT");

  pinMode(pinMagnet, INPUT_PULLUP);
  pinMode(pinVibrate, INPUT_PULLUP);

  sleep();
}

void sleep() {
  Serial.println("Sleeping....");
  delay(100);

  GIMSK |= _BV(PCIE0);                    // Enable Pin Change Interrupts
  PCMSK0 = _BV(PCINT5) | _BV(PCINT6);     // pins 3(PA5) and 2(PA6)
  ADCSRA &= ~_BV(ADEN);                   // ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // Set Sleep mode "Power Down"
  sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register

  byte storePin = PINA;                    // Save PIN status before sleep

  sei();                                  // Enable interrupts
  sleep_cpu();                            // sleep
  /***** SLEEP *****/
  cli();                                  // Disable interrupts
  PCMSK0 &= ~(_BV(PCINT5) | _BV(PCINT6)); // Turn off interrupt pin
  sleep_disable();                        // Clear SE bit
  //  ADCSRA |= _BV(ADEN);                // ADC on
  sei();                                  // Enable interrupts

  // Check reason for wake-up
  storePin ^= GPIOR2;
  gotMagnet = (boolean)(storePin & _BV(PCINT5));
  gotKnock  = (boolean)(storePin & _BV(PCINT6));
}

ISR(PCINT0_vect, ISR_NAKED) {
  GPIOR2 = PINA;
  reti();
}

void loop() {
  Serial.println("Awake....");
  delay(100);
  if (gotKnock) {
    Serial.println("Got Knock");
  }
  if (gotMagnet) {
    Serial.println("Got Magnet");
  }
  sleep();
}

Thanks,
Now I do not get either "Got Magnet" or "Got Knock"

Log:

BOOT
Sleeping....
Awake....
Sleeping....
Awake....
Sleeping....
Awake....
Sleeping....
Awake....
Sleeping....

I'm sorry, I forgot to save the register with ISR!

#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/interrupt.h>

const int pinMagnet  = 3; // PA5
const int pinVibrate = 2; // PA6

boolean gotKnock, gotMagnet;

void setup() {
  Serial.begin(9600);
  Serial.println("BOOT");

  pinMode(pinMagnet, INPUT_PULLUP);
  pinMode(pinVibrate, INPUT_PULLUP);

  sleep();
}

void sleep() {
  Serial.println("Sleeping....");
  delay(100);

  GIMSK |= _BV(PCIE0);                    // Enable Pin Change Interrupts
  PCMSK0 = _BV(PCINT5) | _BV(PCINT6);     // pins 3(PA5) and 2(PA6)
  ADCSRA &= ~_BV(ADEN);                   // ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // Set Sleep mode "Power Down"
  sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register

  byte storePin = PINA;                    // Save PIN status before sleep

  sei();                                  // Enable interrupts
  sleep_cpu();                            // sleep
  /***** SLEEP *****/
  cli();                                  // Disable interrupts
  PCMSK0 &= ~(_BV(PCINT5) | _BV(PCINT6)); // Turn off interrupt pin
  sleep_disable();                        // Clear SE bit
  //  ADCSRA |= _BV(ADEN);                // ADC on

  // Check reason for wake-up
  storePin ^= GPIOR2;
  gotMagnet = (boolean)(storePin & _BV(PCINT5));
  gotKnock  = (boolean)(storePin & _BV(PCINT6));

  sei();                                  // Enable interrupts
}

ISR(PCINT0_vect, ISR_NAKED) {
  __asm__ __volatile__(
    "\n out 0x15,r16"
    "\n in  r16,0x0f"
    "\n out 0x16,r16"
    "\n in  r16,0x15"
    "\n reti"
  );
}

void loop() {
  Serial.println("Awake....");
  delay(100);
  if (gotKnock) {
    Serial.println("Got Knock");
  }
  if (gotMagnet) {
    Serial.println("Got Magnet");
  }
  sleep();
}

@Declan
Sorry :woozy_face:
ISR was wrong, so I fixed the above code.

Cool, thanks for your help :smiley:
Now I get "Got Magnet" but not "Got Knock"

Log:
BOOT
Sleeping....
Awake....
Got Magnet
Sleeping....
Awake....
Got Magnet
Sleeping....
Awake....
Sleeping....
Awake....
Sleeping....

What happens if I remove the sensor from the PIN (D2) to which the knock sensor is connected and tap it to GND using a jumper wire with your hands?

I get the same result "Awake...."

Really? :thinking:

In this code, the logical state of PINA is stored to the variable "storePin" at just before sleep, and the new logical state of PINA is also stored to the "GPIOR2 (general-purpose register 2)" when it wakes up by an interrupt.
After that, do XOR with "storePin" and "GPIOR2" to detect which pin has changed from the saved state before sleep.

Your result is bit strange because the routines for detect to changed pin are same for both pins...

I'll test it with another Arduino if I can afford it, but I think it works and hope it. :hushed:

Hi, I have done some tests with the following code:

void loop() {
  Serial.println("Awake....");
  delay(100);

  Serial.print("gotKnock: ");
  Serial.println(gotKnock);
  delay(100);

  Serial.print("gotMagnet: ");
  Serial.println(gotMagnet);
  delay(100);

  sleep();
}

With this I get Log:

BOOT
Sleeping....

--> NOW I PLACE MAGNET
Awake....
gotKnock: 0
gotMagnet: 32
Sleeping....

--> NOW I REMOVE MAGNET
Awake....
gotKnock: 0
gotMagnet: 32
Sleeping....

--> NOW I CREATE KNOCK - WITH MAGNET REMOVED
Awake....
gotKnock: 0
gotMagnet: 0
Sleeping....

--> NOW I PLACE MAGNET
Awake....
gotKnock: 0
gotMagnet: 32
Sleeping....

--> NOW I CREATE KNOCK - WITH MAGNET IN PLACE
Awake....
gotKnock: 0
gotMagnet: 0
Sleeping....

So, my thinking is:

  1. I do not have to worry about the Magnet sensor as I can read the state of the pin to ascertain if the magnet is in place or not. Whenever the pinMagnet (PA5) goes LOW or HIGH, I always get "gotMagnet: 32".

  2. When a Knock is detected on pinVibrate (PA6), I always get "gotKnock: 0" AND "gotMagnet: 0".
    Having said that, every Interrupt that generates "gotKnock: 0" AND "gotMagnet: 0" WILL be a Knock Interrupt.
    So, on every interrupt I check the state of gotKnock and gotMagnet - if both == 0, then I have received a Knock.

Your thoughts......

It's a good test.
Can you try the same test with this code (include debug serial output) and report the results?

#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/interrupt.h>

const int pinMagnet  = 3; // PA5
const int pinVibrate = 2; // PA6

boolean gotKnock, gotMagnet;

void setup() {
  Serial.begin(9600);
  Serial.println("BOOT");

  pinMode(pinMagnet, INPUT_PULLUP);
  pinMode(pinVibrate, INPUT_PULLUP);

  sleep();
}

void sleep() {
  Serial.println("Sleeping....");
  delay(100);

  GIMSK |= _BV(PCIE0);                    // Enable Pin Change Interrupts
  PCMSK0 = _BV(PCINT5) | _BV(PCINT6);     // pins 3(PA5) and 2(PA6)
  ADCSRA &= ~_BV(ADEN);                   // ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // Set Sleep mode "Power Down"
  sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register

  byte storePin = PINA;                    // Save PIN status before sleep

  sei();                                  // Enable interrupts
  sleep_cpu();                            // sleep
  /***** SLEEP *****/
  cli();                                  // Disable interrupts
  PCMSK0 &= ~(_BV(PCINT5) | _BV(PCINT6)); // Turn off interrupt pin
  sleep_disable();                        // Clear SE bit
  //  ADCSRA |= _BV(ADEN);                // ADC on

  // ********** DEBUG CODE **********
  byte oldPin = storePin;
  byte nowPin = GPIOR2;
  // ********** END DEBUG **********

  // Check reason for wake-up
  storePin ^= GPIOR2;
  gotMagnet = (bool)(storePin & _BV(PCINT5));
  gotKnock  = (bool)(storePin & _BV(PCINT6));

  sei();                                  // Enable interrupts

  // ********** DEBUG CODE **********
  Serial.print("1:");
  Serial.print(oldPin, HEX);
  Serial.print(",2:");
  Serial.print(nowPin, HEX);
  Serial.print(",K:");
  Serial.print(gotKnock);
  Serial.print(",M:");
  Serial.print(gotMagnet);
  Serial.println("");
  // ********** END DEBUG **********
}

ISR(PCINT0_vect, ISR_NAKED) {
  __asm__ __volatile__(
    "\n out 0x15,r16"
    "\n in  r16,0x0f"
    "\n out 0x16,r16"
    "\n in  r16,0x15"
    "\n reti"
  );
}

void loop() {
  Serial.println("Awake....");
  delay(100);
/*  
  if (gotKnock) {
    Serial.println("Got Knock");
  }
  if (gotMagnet) {
    Serial.println("Got Magnet");
  }
*/
  sleep();
}

Hi,
Log from above code:

BOOT
Sleeping....

--> NOW I PLACE MAGNET
1:E0,2:C0,K:0,M:1
Awake....
Sleeping....

--> NOW I REMOVE MAGNET
1:C0,2:E0,K:0,M:1
Awake....
Sleeping....

--> NOW I CREATE KNOCK - WITH MAGNET REMOVED
1:E0,2:E0,K:0,M:0
Awake....
Sleeping....

--> NOW I PLACE MAGNET
1:E0,2:C0,K:0,M:1
Awake....
Sleeping....

--> NOW I CREATE KNOCK - WITH MAGNET IN PLACE
1:C0,2:C0,K:0,M:0
Awake....
Sleeping....

Thank you.

Arduino's uC will wake-up when the pin state changes due to knocking, but it will take bit time to start program.
The pulse from the knock sensor is fairly short and seems to have returned to steady state before the ISR execute and reads the pin state.

Therefore, if there is no other interrupt for wake-up and the your project is enough with only these two pins, if pin change interrupt occurs and it isn't caused by a magnet, it can be determined that the knock sensor caused the interrupt.

Is this code a solution?

#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/interrupt.h>

const int pinMagnet  = 3; // PA5
const int pinVibrate = 2; // PA6

bool gotKnock, gotMagnet;

void setup() {
  Serial.begin(9600);
  Serial.println("BOOT");

  pinMode(pinMagnet, INPUT_PULLUP);
  pinMode(pinVibrate, INPUT_PULLUP);

  sleep();
}

void sleep() {
  Serial.println("Sleeping....");
  delay(100);

  GIMSK |= _BV(PCIE0);                    // Enable Pin Change Interrupts
  PCMSK0 = _BV(PCINT5) | _BV(PCINT6);     // pins 3(PA5) and 2(PA6)
  ADCSRA &= ~_BV(ADEN);                   // ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // Set Sleep mode "Power Down"
  sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register

  byte storePin = PINA;                    // Save PIN status before sleep

  sei();                                  // Enable interrupts
  sleep_cpu();                            // sleep
  /***** SLEEP *****/
  cli();                                  // Disable interrupts
  PCMSK0 &= ~(_BV(PCINT5) | _BV(PCINT6)); // Turn off interrupt pin
  sleep_disable();                        // Clear SE bit
  //  ADCSRA |= _BV(ADEN);                // ADC on

  // Check reason for wake-up
  storePin ^= GPIOR2;
  gotMagnet = (bool)(storePin & _BV(PCINT5));
  gotKnock  = (bool)(storePin & _BV(PCINT6)) || (!gotMagnet);

  sei();                                  // Enable interrupts
}

ISR(PCINT0_vect, ISR_NAKED) {
  __asm__ __volatile__(
    "\n out 0x15,r16"
    "\n in  r16,0x0f"
    "\n out 0x16,r16"
    "\n in  r16,0x15"
    "\n reti"
  );
}

void loop() {
  Serial.println("Awake....");
  delay(100);
  if (gotKnock) {
    Serial.println("Got Knock");
  }
  if (gotMagnet) {
    Serial.println("Got Magnet");
  }
  sleep();
}

Hi,
Yes, you are correct - the pulse from the knock sensor is very short.
The above code works great.
May I PM you?