# ATtiny85 Input/Pin Change Interrupt problem

**URL:** <https://forum.arduino.cc/t/attiny85-input-pin-change-interrupt-problem/976971>\
**Category:** 3rd Party Boards\
**Created:** [April 4, 2022, 7:13am UTC](https://forum.arduino.cc/t/attiny85-input-pin-change-interrupt-problem/976971 "2022-04-04T07:13:50Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![X0R](https://avatars.discourse-cdn.com/v4/letter/x/7ba0ec/32.png) [@X0R](https://forum.arduino.cc/u/X0R)\
**Post date:** [April 4, 2022, 7:13am UTC](https://forum.arduino.cc/t/attiny85-input-pin-change-interrupt-problem/976971/1 "2022-04-04T07:13:50Z")

</div>

I originally had this code for an ESP8266 project and I've adapted it to my current project. Originally I had 3 Pin Change Interrupt inputs for 3 sensors that would send a code to the ESP8266 and they worked as expected.

Now, the only pin responding to any input is pin 7 (PB2). My output data goes to PB3 when pin 7 (PB2) goes high and it should do the same for the other input pins 3,5,6, but nothing happens (verified with Oscope).

The interrupt library I'm using is  
[GitHub - GreyGnome/EnableInterrupt: New Arduino interrupt library, designed for Arduino Uno/Mega 2560/Leonardo/Due](https://github.com/GreyGnome/EnableInterrupt) which doesn't require an ISR routine.

I'm using the Sparkfun [Tiny AVR Programmer](https://cdn.sparkfun.com//assets/parts/8/1/1/1/11801-01.jpg)

I've gotten to the point where I've confused myself about bit shifting  
assignments to registers as you'll see in comments to myself, however even using pinmode() hasn't helped. The enableINTs() function sets the register bits. setup() is where I attempt to set Inputs.

Currently there's no circuit, just the chip in the programmer and a jumper from Vcc I touch to the pins while watching the output on pin 3 with my scope.

What am I missing/doing wrong? (other than forgetting to attach my code)

```cpp
/**
   ATtiny85 (2MHz) (APR 2022)
**/

// TIP -- Enable CKOUT and then use the scope to view the clock frequency on pin 5/6
// Use AVRDudess 2.13 GUI to set/chg these.
// Default Fuses L=0x62 H=0xDF E=0xFF
// CKOUT Enabled L=0x22 H=0xDF E=0xFF

// ATMEL ATTINY 25/45/85
//
// +-\/-+
// <> RESET 1| |8 Vcc
// DATA OUT <> Ain3 (D3) PB3 2| |7 PB2 (D2) Ain1 <> pirSensorA
// pirSensorD <> Ain2 (D4) PB4 3| |6 PB1 (D1) pwm1 <> pirSensorB
// GND 4| |5 PB0 (D0) pwm0 <> pirSensorC
// +----+

#include <avr/io.h>
#include <avr/sleep.h> // Sleep Modes
#include <avr/power.h> // Power management
#include <avr/delay.h> // Delay for < 8MHz _delay_us / _delay_ms
#include <avr/interrupt.h> // Conflict with EnableInterrupt.h??
#include <EnableInterrupt.h> // Easy to use interrupts for ATtiny (GreyGnome)
#include <avr/wdt.h>

#define BODS 7 // BOD Sleep bit in MCUCR
#define BODSE 2 // BOD Sleep enable bit in MCUCR

#define PIRsensorD 3 // pin 3 / PCINT4 / PB4
#define PIRsensorC 5 // pin 5 / PCINT0 / PB0
#define PIRsensorB 6 // pin 6 / PCINT1 / PB1
#define PIRsensorA 7 // pin 7 / PCINT2 / PB2
#define DATA PB3 // pin 2 Output pulses to TX module

#define F_CPU 2000000UL // 2 MHz (Set in code below to 2Mhz)

volatile int state = LOW; // Used in PinChgInt ISR
volatile int Awakened = LOW;
volatile uint8_t seconds = 0; // Used in Timer ISR
volatile uint8_t DelayBeforeSleeping = 45; // Time in seconds to delay before turning off ESP01

uint8_t mcucr1, mcucr2;
int delaycnt = 0; // Interval counter
int bit = 0;
int runonce = 0;

// code = 0001 0001 0101 0000 0101 0101 0;
bool code[25]={0,0,0,1, 0,0,0,1, 0,1,0,1, 0,0,0,0, 0,1,0,1, 0,1,0,1, 0}; // Harborfreight motion sensor code

//###########################################################################################################################################//
void setup () {

  // Change CPU Freq. Page 33, Table 6-15
  cli();
  CLKPR = (1<<CLKPCE);
  //CLKPR = ((0<<CLKPS3) |(0<<CLKPS2) |(1<<CLKPS1) | (1<<CLKPS0)); // 1Mhz
  //CLKPR = ((0<<CLKPS3) |(0<<CLKPS2) |(0<<CLKPS1) | (1<<CLKPS0)); // 4Mhz
  CLKPR = ((0<<CLKPS3) |(0<<CLKPS2) |(1<<CLKPS1) | (0<<CLKPS0)); // 2Mhz
  sei();

  DDRB |= (1<<DATA); // set PB1 as Output for data pulses

  // Set pins as inputs
  DDRB |= ((0 << PIRsensorA) | (0 << PIRsensorB) | (0 << PIRsensorC) | (0 << PIRsensorD));

  // Which is correct? Want Input/should be ZERO.
  /*  
  DDRB &= ~(1 << PIRsensorA); //_INPUT PB2 port (pin 7)
  DDRB &= ~(1 << PIRsensorB); //_INPUT PB1 port (pin 6)
  DDRB &= ~(1 << PIRsensorC); //_INPUT PB0 port (pin 5)
  DDRB &= ~(1 << PIRsensorD); //_INPUT PB4 port (pin 3)

  DDRB |= (1 << PIRsensorA); //_INPUT PB2 port (pin 7)
  DDRB |= (1 << PIRsensorB); //_INPUT PB1 port (pin 6)
  DDRB |= (1 << PIRsensorC); //_INPUT PB0 port (pin 5)
  DDRB |= (1 << PIRsensorD); //_INPUT PB4 port (pin 3)
  */

  goToSleep();

}

//###########################################################################################################################################//
void loop() {
  
  // This condition is for repeat triggers while awake.
  if ((state == HIGH) && (Awakened == HIGH)) {

    TIMSK &= ~(1 << OCIE1A); // DISABLE 1-second TIMER

    // Disable interrupts to avoid a loop
    // Interrupts re-enabled below if condition goes LOW.
    disableInterrupt(PIRsensorA);
    disableInterrupt(PIRsensorB);
    disableInterrupt(PIRsensorC);
    disableInterrupt(PIRsensorD);

    if(PINB & ((1 << PB4) | (1 << PB2) | (1 << PB1) | (1 << PB0))) {
      while(PINB & ((1 << PB4) | (1 << PB2) | (1 << PB1) | (1 << PB0))) {
        sendcode();
      }

      state = LOW;
      seconds = 0;
    }
  }
  
  // This is condition after waking from Sleep.
  if ((state == HIGH) && (Awakened == LOW)){

    TIMSK &= ~(1 << OCIE1A); // DISABLE 1-second TIMER, sensors could be on longer than 30 seconds

    Awakened = HIGH;
    seconds = 0;
  }

  // This condition happens when all sensors have gone LOW
  if(state == LOW){

    enableInterrupt(PIRsensorA|PINCHANGEINTERRUPT, trigger, RISING);
    enableInterrupt(PIRsensorB|PINCHANGEINTERRUPT, trigger, RISING);
    enableInterrupt(PIRsensorC|PINCHANGEINTERRUPT, trigger, RISING);
    enableInterrupt(PIRsensorD|PINCHANGEINTERRUPT, trigger, RISING);
    
    TIMSK |= (1 << OCIE1A); //Re-enable 1 second timer interrupt.
    sei();
  }
}

//###########################################################################################################################################//
void sendcode(void){

  if (runonce == 0) { // Was for testing in main loop
    runonce = 0;

    // Send bitsream 6 times
    for(int cnt=1;cnt<=6;cnt++){
      for(bit=0;bit<=24;bit++) {
        if (code[bit] == 1) {
            PORTB |= (1 << DATA); //_HIGH_
            _delay_us(4790);
            PORTB &= ~(1 << DATA); //_LOW_
            _delay_us(1600);
        }
        else {
            PORTB |= (1 << DATA); //_HIGH_
            _delay_us(1600);
            PORTB &= ~(1 << DATA); //_LOW_
            _delay_us(4790);
        }
      }
      _delay_us(24000); // Delay between pulse streams, if too short fails to get decoded.
    }
  }
}

//###########################################################################################################################################//
void enableINTs(void){

  PCMSK |= (1 << PCINT0); //Set pin 5(PB0/D0) to be interrupt pin
  PCMSK |= (1 << PCINT1); //Set pin 6(PB1/D1) to be interrupt pin
  PCMSK |= (1 << PCINT2); //Set pin 7(PB2/D2) to be interrupt pin
  PCMSK |= (1 << PCINT4); //Set pin 3(PB4/D4) to be interrupt pin
  
  GIFR |= (1 << PCIF); //Clear any outstanding interrupts
  GIMSK |= (1 << PCIE); //Enable Pin Change interrupts
  
  enableInterrupt(PIRsensorA|PINCHANGEINTERRUPT, trigger, RISING);
  enableInterrupt(PIRsensorB|PINCHANGEINTERRUPT, trigger, RISING);
  enableInterrupt(PIRsensorC|PINCHANGEINTERRUPT, trigger, RISING);
  enableInterrupt(PIRsensorD|PINCHANGEINTERRUPT, trigger, RISING);
}

//###########################################################################################################################################//
void trigger(void) {
  
  //state = !state; // Toggle "state"
  state = HIGH; // For Clarity
  seconds = 0;
}

//###########################################################################################################################################//
void goToSleep()
{
  
  cli(); //Disable Interrupts
  TIMSK &= ~(1 << OCIE1A); //Disable Timer

  GIFR |= (1 << PCIF); //Clear any outstanding interrupts
  
  enableINTs(); //Set Pin Change interrupts before we sleep, or coma!

  ACSR |= _BV(ACD); //disable the analog comparator
  ADCSRA &= ~_BV(ADEN); //disable ADC

  Awakened = LOW;
  
  mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE); //turn off the brown-out detector
  mcucr2 = mcucr1 & ~_BV(BODSE);
  MCUCR = mcucr1; // WTF?
  MCUCR = mcucr2; // WTF? This overwrites previous line!

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  power_all_disable(); // power off ADC, Timer 0 and 1, serial interface
  
  GIFR |= (1 << PCIF); //Clear any outstanding interrupts
  
  sleep_enable();
  sei(); //Enable Interrupts
  sleep_cpu(); //Interrupt during sleep runs trigger() from here FIRST, then returns here.

  GIMSK = 0x00; //disable INT0
  sleep_disable();
  power_all_enable(); //Power everything back on

  state = HIGH; 
  Awakened = LOW;
  seconds = 0;
}

//###########################################################################################################################################//
ISR(TIM1_COMPA_vect)
{
  delaycnt++;
  
  if(seconds <= DelayBeforeSleeping) seconds++;
  else {

    TIMSK &= ~(1 << OCIE1A); // DISABLE 1-second TIMER
  }
}

```

---

<div class="post-metadata">

**Author:** ![pylon](https://avatars.discourse-cdn.com/v4/letter/p/59ef9b/32.png) [@pylon](https://forum.arduino.cc/u/pylon)\
**Post date:** [April 4, 2022, 3:54pm UTC](https://forum.arduino.cc/t/attiny85-input-pin-change-interrupt-problem/976971/2 "2022-04-04T15:54:45Z")

</div>

> [@X0R](#):
>
> The interrupt library I'm using is

That library isn't compatible with the ATtiny.

---

<div class="post-metadata">

**Author:** ![X0R](https://avatars.discourse-cdn.com/v4/letter/x/7ba0ec/32.png) [@X0R](https://forum.arduino.cc/u/X0R)\
**Post date:** [April 4, 2022, 6:02pm UTC](https://forum.arduino.cc/t/attiny85-input-pin-change-interrupt-problem/976971/3 "2022-04-04T18:02:36Z")

</div>

Not sure why you're saying that. The documentation in the link I provided says it is, and I used it in my previous project and it worked perfectly.

> **[EnableInterrupt - Arduino Reference](https://www.arduino.cc/reference/en/libraries/enableinterrupt/)**
>
> The Arduino programming language Reference, organized into Functions, Variable and Constant, and Structure keywords.

---

<div class="post-metadata">

**Author:** ![ZX80](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/zx80/32/643687_2.png) [@ZX80](https://forum.arduino.cc/u/ZX80)\
**Post date:** [April 4, 2022, 6:57pm UTC](https://forum.arduino.cc/t/attiny85-input-pin-change-interrupt-problem/976971/4 "2022-04-04T18:57:54Z")

</div>

Do you use pull down resistors?  
Which core do you use?  
You should probably make a simple demo sketch that would be easier to troubleshoot.  
You could also try this library  
[https://github.com/NicoHood/PinChangeInterrupt](https://github.com/NicoHood/PinChangeInterrupt)

---

<div class="post-metadata">

**Author:** ![X0R](https://avatars.discourse-cdn.com/v4/letter/x/7ba0ec/32.png) [@X0R](https://forum.arduino.cc/u/X0R)\
**Post date:** [April 4, 2022, 7:21pm UTC](https://forum.arduino.cc/t/attiny85-input-pin-change-interrupt-problem/976971/5 "2022-04-04T19:21:56Z")

</div>

Thanks for taking a look and replying. I actually just did a demo sketch by stripping a ton of stuff from the code I posted and it works, so its definitely not the library.

No core I'm aware of, I pop the chip in the programmer, hit upload and my program is loaded, no core has ever been loaded unless its automagically without my knowledge. I'm not using any pulldown resistors either.

I guess with the demo sketch I just did working, it points to some coding problem or register conflict.

---

<div class="post-metadata">

**Author:** ![X0R](https://avatars.discourse-cdn.com/v4/letter/x/7ba0ec/32.png) [@X0R](https://forum.arduino.cc/u/X0R)\
**Post date:** [April 5, 2022, 6:34am UTC](https://forum.arduino.cc/t/attiny85-input-pin-change-interrupt-problem/976971/6 "2022-04-05T06:34:14Z")

</div>

Ok, I can't point to any specific thing that caused this not to work, but the problem was in the main loop(). Let's just say I don't know what I was thinking when I wrote this, but there was definitely some 'excessive logic' and some duplicate/unnecessary code. I also want to make it clear for anyone in the future, the pin change interrupt library is definitely compatible with the ATtiny85 and works fine for me. I'll try to repost the new code when I'm finished polishing it up a little more.

---

<div class="post-metadata">

**Author:** ![pylon](https://avatars.discourse-cdn.com/v4/letter/p/59ef9b/32.png) [@pylon](https://forum.arduino.cc/u/pylon)\
**Post date:** [April 5, 2022, 7:31am UTC](https://forum.arduino.cc/t/attiny85-input-pin-change-interrupt-problem/976971/7 "2022-04-05T07:31:36Z")

</div>

> [@X0R](#):
>
> The documentation in the link I provided say

You're right, in the intro it shows support the the ATtinys but the documentation below doesn't have a section for the ATtinys.

My guess is that the first call of enableInterrupt() actually registers the pinchange mask and the rest is ignored but finding the actual code that is compiled for the ATtiny is like finding a needle in the haystack.

> [@X0R](#):
>
> ```arduino
> DDRB |= ((0 << PIRsensorA) | (0 << PIRsensorB) | (0 << PIRsensorC) | (0 << PIRsensorD));
> 
> ```

That code doesn't do what you expect it to do. Or-ing with 0 won't change the value. But as the default value of the register at startup is 0 this might work, nevertheless.  
The next version (in the comment) is the correct on to be used but you must use the port bit numbers and not the pin numbers. So the value for sensor D is 4, for sensor C it's 0, for sensor B it's 1 and for sensor A it's 2.

---

<div class="post-metadata">

**Author:** ![X0R](https://avatars.discourse-cdn.com/v4/letter/x/7ba0ec/32.png) [@X0R](https://forum.arduino.cc/u/X0R)\
**Post date:** [April 5, 2022, 8:20am UTC](https://forum.arduino.cc/t/attiny85-input-pin-change-interrupt-problem/976971/8 "2022-04-05T08:20:07Z")

</div>

> documentation below doesn't have a section for the ATtinys

I agree, the documentation isn't clear, if you look at the EnableInterrupt.h file though you can find a section for the ATtinys.

Thanks for clearing up the part about OR-ing. I was grasping at straws doubting what I had in my original code even though that worked. I changed my #defines from using pin numbers to port names defined in some other include.h

```arduino
#define PIRsensorD PB4 // pin 3 / PCINT4 / PB4
#define PIRsensorC PB0 // pin 5 / PCINT0 / PB0
#define PIRsensorB PB1 // pin 6 / PCINT1 / PB1
#define PIRsensorA PB2 // pin 7 / PCINT2 / PB2
#define DATA PB3 // pin 2 Data Output pulses to TX module

```

Fixed port initialization back to this:

```arduino
  DDRB &= ~(1 << PIRsensorA); //_INPUT PB2 port (pin 7)
  DDRB &= ~(1 << PIRsensorB); //_INPUT PB1 port (pin 6)
  DDRB &= ~(1 << PIRsensorC); //_INPUT PB0 port (pin 5)
  DDRB &= ~(1 << PIRsensorD); //_INPUT PB4 port (pin 3)

```

Appreciate the clarification, thank you.

---

<div class="post-metadata">

**Author:** ![system](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/system/32/1140315_2.png) [@system](https://forum.arduino.cc/u/system)\
**Post date:** [October 2, 2022, 8:21am UTC](https://forum.arduino.cc/t/attiny85-input-pin-change-interrupt-problem/976971/9 "2022-10-02T08:21:04Z")

</div>

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.
