Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
  1. Home
  2. General Discussion
  3. Pin Change Interrupt on "any" pin.

Pin Change Interrupt on "any" pin.

Scheduled Pinned Locked Moved General Discussion
12 Posts 8 Posters 8.5k Views 9 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    shabba
    wrote on last edited by
    #3

    I am about to use the EnableInterrupt library to catch interrupts on lots of pins on a mega. I had to comment out in the header file where it tried to take control of the external interrupts. Yet to test though (gate controller replacement sketch).

    1 Reply Last reply
    0
    • P Offline
      P Offline
      popunonkok
      wrote on last edited by
      #4

      Thanks for you answers!

      @DavidZH
      In this particular case I dont want to send data periodically. It is a Door/Lock node. I just want it to wake up when there is a change on one of the pins, run a Loop and then fall back to sleep.

      If I could use more pins I could have 1 node monitor 2 doors/locks.

      Will we see the result of your work (Multiple pin interrupts AND timed interrupts) her on mySensors later?

      Would be great!

      sundberg84S 1 Reply Last reply
      0
      • P popunonkok

        Thanks for you answers!

        @DavidZH
        In this particular case I dont want to send data periodically. It is a Door/Lock node. I just want it to wake up when there is a change on one of the pins, run a Loop and then fall back to sleep.

        If I could use more pins I could have 1 node monitor 2 doors/locks.

        Will we see the result of your work (Multiple pin interrupts AND timed interrupts) her on mySensors later?

        Would be great!

        sundberg84S Offline
        sundberg84S Offline
        sundberg84
        Hardware Contributor
        wrote on last edited by sundberg84
        #5

        @popunonkok sleep is implemented in the MySensors library, with or without interups: https://www.mysensors.org/download/sensor_api_20#sleeping.

        Int 2 + 3 can detect FALLING, CHANGE and RISING while the other pins can be used for interrupt but only detect CHANGE.

        Controller: Proxmox VM - Home Assistant
        MySensors GW: Arduino Uno - W5100 Ethernet, Gw Shield Nrf24l01+ 2,4Ghz
        MySensors GW: Arduino Uno - Gw Shield RFM69, 433mhz
        RFLink GW - Arduino Mega + RFLink Shield, 433mhz

        1 Reply Last reply
        0
        • F Offline
          F Offline
          flopp
          wrote on last edited by flopp
          #6

          I found a library for this, I have not tried myself
          https://github.com/GreyGnome/EnableInterrupt

          Also a post on Arduino forum
          http://playground.arduino.cc/Main/PinChangeInterrupt

          1 Reply Last reply
          0
          • rednaxiR Offline
            rednaxiR Offline
            rednaxi
            wrote on last edited by
            #7

            This library work great for me. You only need disable external interrupts before include header.

            #define EI_NOTEXTERNAL // Do not use external interrupts in EnableInterrupt lib
            #include <EnableInterrupt.h>

            C: CubieBoard2 - Fhem, SmartHome
            GW: Orange Pi Zero (Ethernet)

            1 Reply Last reply
            1
            • D Offline
              D Offline
              DavidZH
              wrote on last edited by
              #8

              @popunonkok

              Well here's the first example of some code that works for me. It's a house controller mounted on a pulse switch (for a standard NL/BE/FR/DE switch box). Own developed PCB that can handle 4 switches, so needed to find a way to be able to sens all our inputs.
              I used the "Button" library to de-clutter the code a bit, and I followed the MySensors way of setting up the program (with defines). That's because I want to deploy a whole bunch of these, so I can adjust some settings quick and then shoot the sketch in the MCU.
              At first I included a sensor function as well but as the standard sketch grew too big I axed that.
              I'll only give the relevant parts of my code now, as I'm not finished yet. Still working on the part where the connection with the GW is lost and reinstated. As this project can also be used as a light switch, it should ALWAYS work. So after the communication is restored the node has to send all the states to the GW again.

              #define PULLUP true        
              #define INVERT true        
              #define bounceTime 20             // A debounce time of 20 milliseconds usually works well for tactile button switches.
              
              #include <Button.h>
              #include <PinChangeInt.h>
              
              #define buttApin  14              // Arduino Digital I/O pin numbers for connected buttons 
              #define buttBpin  15              // when connected to '`official' PCB.
              #define buttCpin  8
              #define buttDpin  9
              
              bool battPower = true;
              int inputPin[5] = {0, 0, 0, 0, 0};          // State machine registers
              int actuatorPin[2] = {0, 0};
              int function[4] = {0, 0, 0, 0};
              bool safetyBttn[4] = {0, 0, 0, 0};
              bool reqAck[4] = {0, 0, 0, 0};
              bool inState[5] = {0, 0, 0, 0, 0};
              bool longPr[5] = {0, 0, 0, 0, 0};
              bool outState[4] = {0, 0, 0, 0};
              
              Button BtnA(buttApin, PULLUP, INVERT, bounceTime);    
              Button BtnB(buttBpin, PULLUP, INVERT, bounceTime);
              Button BtnC(buttCpin, PULLUP, INVERT, bounceTime);
              Button BtnD(buttDpin, PULLUP, INVERT, bounceTime);
              
              attachPinChangeInterrupt(inputPin[ChanA], ChanA_ISR, CHANGE);
              attachPinChangeInterrupt(inputPin[ChanB], ChanB_ISR, CHANGE);
              attachPinChangeInterrupt(inputPin[ChanC], ChanC_ISR, CHANGE);
              attachPinChangeInterrupt(inputPin[ChanD], ChanD_ISR, CHANGE);
              
              void ChanA_ISR()
              {
                 pinChanged = ChanA;
                 inState[ChanA] = BtnA.read();
              }
              
              
              void ChanB_ISR()
              {
                 pinChanged = ChanB;
                 inState[ChanB] = BtnB.read();
              }
              
              
              void ChanC_ISR()
              {
                pinChanged = ChanC;
                inState[ChanC] = BtnC.read();
              }
              
              
              void ChanD_ISR()
              {
                pinChanged = ChanD;
                inState[ChanD] = BtnC.read();
              }
              

              So when the node wakes up from sleep the variable pinChanged carries the number of the pin that has made it enter the ISR. That can be used to take action. After everything is handled pinChangedis set to the resting value (255 in this case).

              Depending on whether the circuit is powered by a battery or a 5V supply the node enters sleep state or simply loops without doing anything. When sleeping I use sleep(0xff,0x00, 0xff, 0x00, 0); When using any other call to sleep, it will not work (the node never wakes up). So I got a hint from one of the developers how to solve it, and I have yet to try that. But I'll keep you updated here (and the other thread).

              I hope this helps you a bit further.

              1 Reply Last reply
              1
              • D Offline
                D Offline
                DavidZH
                wrote on last edited by
                #9

                Well, that's embarrassing... I gave these tips last week, but it appears now that I have been surpassed by updates to the MyS library. The sleep command i issued here now leads to a loop, the node will not sleep. The changes to the library are intentional, as they make MySensors compliant with the designrules from Atmel/MicroChip.
                I have tried another library by the same author (EnableInterrupt) but to no avail.

                Back to the sketchboard...

                F 1 Reply Last reply
                1
                • D DavidZH

                  Well, that's embarrassing... I gave these tips last week, but it appears now that I have been surpassed by updates to the MyS library. The sleep command i issued here now leads to a loop, the node will not sleep. The changes to the library are intentional, as they make MySensors compliant with the designrules from Atmel/MicroChip.
                  I have tried another library by the same author (EnableInterrupt) but to no avail.

                  Back to the sketchboard...

                  F Offline
                  F Offline
                  freynder
                  wrote on last edited by
                  #10

                  @DavidZH

                  I have been working on a similar problem lately and just noticed your post. I also tried using libraries at first, but ran into issues due to bouncing signals from the buttons. The pin change interrupt service routine tried to detect which button was pressed, but since the signal was bouncing, button presses were missed. The system would wake up but not call the appropriate callback function associated with the pin.

                  I put together a sketch that works for me. It involves setting up the change pin interrupts, detecting which button is pressed and some provisions for debouncing the signal. It uses a small hack to leave the timed sleep function on pin change interrupts. I did not have trouble entering sleep like you did.

                  Please note that this sketch uses the current MySensors development branch.

                  The sketch is below, I hope it helps.

                  /**
                   * The MySensors Arduino library handles the wireless radio link and protocol
                   * between your home built sensors/actuators and HA controller of choice.
                   * The sensors forms a self healing radio network with optional repeaters. Each
                   * repeater and gateway builds a routing tables in EEPROM which keeps track of the
                   * network topology allowing messages to be routed to nodes.
                   *
                   * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
                   * Copyright (C) 2013-2015 Sensnology AB
                   * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
                   *
                   * Documentation: http://www.mysensors.org
                   * Support Forum: http://forum.mysensors.org
                   *
                   * This program is free software; you can redistribute it and/or
                   * modify it under the terms of the GNU General Public License
                   * version 2 as published by the Free Software Foundation.
                   *
                   *******************************
                   *
                   * DESCRIPTION
                   *
                   * Interrupt driven binary switch example with dual pin change interrupts
                   * Author: Francis Reynders
                   * MySensors does not support pin change interrupts currently. This sketch
                   * initializes pin change interrupts and combines it with MySensors sleep
                   * functions. Some hacking is required to get out of the hwInternalSleep loop.
                   * Tested with atmega328p standalone but should work with Arduino Nano/Pro MiniCore
                   * Uses RFM69 for transport.
                   *
                   * Based on original work by:
                   * Author: Patrick 'Anticimex' Fallberg
                   * Connect one button or door/window reed switch between
                   * digitial I/O pin 3 (BUTTON_PIN below) and GND and the other
                   * one in similar fashion on digital I/O pin 2.
                   * This example is designed to fit Arduino Nano/Pro Mini
                   *
                   */
                  
                  // Enable debug prints to serial monitor
                  #define MY_DEBUG
                  
                  // Enable and select radio type attached
                  //#define MY_RADIO_NRF24
                  #define MY_RADIO_RFM69
                  #define MY_RFM69_ENABLE_ENCRYPTION
                  #define MY_RFM69_FREQUENCY RF69_433MHZ // Set your frequency here
                  //#define MY_IS_RFM69HW // Omit if your RFM is not "H"
                  //#define MY_RFM69_NETWORKID 100  // Default is 100 in lib. Uncomment it and set your preferred network id if needed
                  #define MY_RF69_IRQ_PIN 2
                  #define MY_RF69_IRQ_NUM 0
                  #define MY_RF69_SPI_CS 10
                  //#define MY_NODE_ID 2
                  
                  #include <MySensors.h>
                  
                  #define SKETCH_NAME "Binary Sensor"
                  #define SKETCH_MAJOR_VER "2"
                  #define SKETCH_MINOR_VER "0"
                  
                  #define PRIMARY_CHILD_ID 3 // PD3
                  #define SECONDARY_CHILD_ID 4 // PD4
                  
                  #define PRIMARY_BUTTON_PIN 3   // Arduino Digital I/O pin for button/reed switch
                  #define SECONDARY_BUTTON_PIN 4 // Arduino Digital I/O pin for button/reed switch
                  
                  #define DEBOUNCE_INTERVAL 100
                  #define DEBOUNCE_COUNT_THRESHOLD 15 // required consecutive positive readings
                  #define PREVENT_DOUBLE_INTERVAL 400
                  
                  #define SLEEP_TIME (6 * 60 * 60 * 1000ul) // Check battery every 6 hours
                  
                  #define BATTERY_MAX_MVOLT 2900
                  #define BATTERY_MIN_MVOLT 2300
                  
                  // Change to V_LIGHT if you use S_LIGHT in presentation below
                  MyMessage msg(PRIMARY_CHILD_ID, V_LIGHT);
                  MyMessage msg2(SECONDARY_CHILD_ID, V_LIGHT);
                  
                  bool triggered = false;
                  uint32_t lastWakeup = 0;
                  uint16_t lastBatteryVoltage = 0u;
                  
                  enum wakeup_t {
                    WAKE_BY_TIMER,
                    WAKE_BY_PCINT0,
                    WAKE_BY_PCINT1,
                    WAKE_BY_PCINT2,
                    UNDEFINED
                  };
                  
                  volatile wakeup_t wakeupReason = UNDEFINED;
                  
                  // Pin change interrupt service routines
                  ISR (PCINT0_vect) // handle pin change interrupt for PCINT[7:0]
                  {
                    wakeupReason = WAKE_BY_PCINT0;
                    _wokeUpByInterrupt = 0xFE; // Dirty hack to get out of MySensors sleep loop
                  }
                  
                  ISR (PCINT1_vect) // handle pin change interrupt for PCINT[14:8]
                  {
                    wakeupReason = WAKE_BY_PCINT1;
                    _wokeUpByInterrupt = 0xFE; // Dirty hack to get out of MySensors sleep loop
                  }
                  
                  ISR (PCINT2_vect) // handle pin change interrupt for PCINT[23:16]
                  {
                    wakeupReason = WAKE_BY_PCINT2;
                    _wokeUpByInterrupt = 0xFE; // Dirty hack to get out of MySensors sleep loop
                  }
                  
                  void pciSetup(byte pin)
                  {
                    *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin));  // enable pin
                    PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
                    PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
                  }
                  
                  void setup()
                  {
                    CORE_DEBUG(PSTR("Started\n"));
                  
                    // Workaround to use center frequency
                    //_radio.setFrequency(RF69_EXACT_FREQ);
                    #ifdef MY_IS_RFM69HW
                      _radio.setPowerLevel(16); // 10dBm for RFM69HW
                    #else
                      _radio.setPowerLevel(28); // 10dBm for RFM69W
                    #endif
                  
                    pinMode(PRIMARY_BUTTON_PIN, INPUT);           // set pin to input
                    digitalWrite(PRIMARY_BUTTON_PIN, INPUT_PULLUP);       // turn on pullup resistors
                  
                    pinMode(SECONDARY_BUTTON_PIN, INPUT);           // set pin to input
                    digitalWrite(SECONDARY_BUTTON_PIN, INPUT_PULLUP);       // turn on pullup resistors
                  
                    // Set up Pin change interrupt
                    pciSetup(PRIMARY_BUTTON_PIN);
                    pciSetup(SECONDARY_BUTTON_PIN);
                  }
                  
                  void presentation()
                  {
                  	// Send the sketch version information to the gateway and Controller
                  	sendSketchInfo(SKETCH_NAME, SKETCH_MAJOR_VER "." SKETCH_MINOR_VER);
                  
                  	// Register binary input sensor to sensor_node (they will be created as child devices)
                  	// You can use S_DOOR, S_MOTION or S_LIGHT here depending on your usage.
                  	// If S_LIGHT is used, remember to update variable type you send in. See "msg" above.
                  	present(PRIMARY_CHILD_ID, S_LIGHT);
                  	present(SECONDARY_CHILD_ID, S_LIGHT);
                  }
                  
                  void loop()
                  {
                    // Unset value from dirty hack to get out of sleep loop (set in interrupt)
                    _wokeUpByInterrupt = INVALID_INTERRUPT_NUM;
                  
                    CORE_DEBUG(PSTR("Woken up\n"));
                    if(wakeupReason == WAKE_BY_PCINT2) {
                      wakeupReason = UNDEFINED;
                      handleButtons();
                    }
                    handleBatteryLevel();
                  
                    CORE_DEBUG(PSTR("Going to sleep...\n"));
                    sleep(SLEEP_TIME);
                  }
                  
                  void handleButtons()
                  {
                    static uint8_t button1Count;
                    static uint8_t button2Count;
                    static uint32_t started, ended, delta;
                  
                    CORE_DEBUG(PSTR("Detecting buttons START\n"));
                  
                    button1Count = 0;
                    button2Count = 0;
                  
                    // Try and detect which key during max DEBOUNCE_INTERVAL
                    started = millis();
                    while(millis() - started < DEBOUNCE_INTERVAL) {
                      if(digitalRead(PRIMARY_BUTTON_PIN) == LOW) {
                        button1Count++;
                      } else {
                        button1Count=0;
                      }
                      if(digitalRead(SECONDARY_CHILD_ID) == LOW) {
                        button2Count++;
                      } else {
                        button2Count=0;
                      }
                      if(button1Count > DEBOUNCE_COUNT_THRESHOLD) {
                        CORE_DEBUG(PSTR("Button 1 pressed\n"));
                        send(msg.set(1));
                        break;
                      }
                      if(button2Count > DEBOUNCE_COUNT_THRESHOLD) {
                        CORE_DEBUG(PSTR("Button 2 pressed\n"));
                        send(msg2.set(1));
                        break;
                      }
                    }
                    CORE_DEBUG(PSTR("Detecting buttons END\n"));
                  
                    // This section prevents detecting additional bounces
                    ended = millis();
                    if(ended > started) {
                      delta = ended - started;
                      if(delta < PREVENT_DOUBLE_INTERVAL) {
                        CORE_DEBUG(PSTR("Waiting: %d \n"), PREVENT_DOUBLE_INTERVAL - delta);
                        wait(PREVENT_DOUBLE_INTERVAL - delta); // In case the signal still is not stable after detection
                      }
                    }
                  }
                  
                  void handleBatteryLevel()
                  {
                    static uint16_t voltage;
                    static uint8_t batteryPct;
                  
                    CORE_DEBUG(PSTR("Checking Battery BEGIN\n"));
                    voltage  = hwCPUVoltage();
                    CORE_DEBUG(PSTR("Voltage: %d\n"), voltage);
                  
                    // Process change in battery level
                    if(lastBatteryVoltage == 0 || lastBatteryVoltage != voltage) {
                      lastBatteryVoltage = voltage;
                      if(voltage < BATTERY_MIN_MVOLT) {
                        batteryPct = 0;
                      } else {
                        batteryPct = 100 * (voltage - BATTERY_MIN_MVOLT) / (BATTERY_MAX_MVOLT - BATTERY_MIN_MVOLT);
                      }
                      sendBatteryLevel(batteryPct);
                    } else {
                      CORE_DEBUG(PSTR("No Change\n"));
                    }
                  
                    CORE_DEBUG(PSTR("Checking Battery END\n"));
                  }
                  
                  
                  1 Reply Last reply
                  2
                  • D Offline
                    D Offline
                    DavidZH
                    wrote on last edited by
                    #11

                    Well I've had some success on my own. There's another thread from January where I've discussed a little bit withe the MySensors team.
                    Your dirty hack is beautiful in it's simplicity! :) I have been roving a round in the library files and wound up in MyHWAVR.cpp to change a little bit. And I think I have found a way which is also simple. But I have to offer the change up for review to have it incorporated in a future version of MySensors. Only downside that it's tied to AVR for now. Needs testing to get it going on SAMD as well probably.
                    For my tests I have been using the enableInterrupt library by the way. Found no trouble using that. And I have a little RC network on my switches to suppress bounce. Checked that on a scope and that worked like a charm.

                    Keep you posted!

                    gerritvG 1 Reply Last reply
                    0
                    • D DavidZH

                      Well I've had some success on my own. There's another thread from January where I've discussed a little bit withe the MySensors team.
                      Your dirty hack is beautiful in it's simplicity! :) I have been roving a round in the library files and wound up in MyHWAVR.cpp to change a little bit. And I think I have found a way which is also simple. But I have to offer the change up for review to have it incorporated in a future version of MySensors. Only downside that it's tied to AVR for now. Needs testing to get it going on SAMD as well probably.
                      For my tests I have been using the enableInterrupt library by the way. Found no trouble using that. And I have a little RC network on my switches to suppress bounce. Checked that on a scope and that worked like a charm.

                      Keep you posted!

                      gerritvG Offline
                      gerritvG Offline
                      gerritv
                      wrote on last edited by
                      #12

                      @DavidZH Any place where I can see your proposed changes? I am using @freynder 's approach but would like something more portable for future use.

                      1 Reply Last reply
                      0
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      12

                      Online

                      11.7k

                      Users

                      11.2k

                      Topics

                      113.0k

                      Posts


                      Copyright 2019 TBD   |   Forum Guidelines   |   Privacy Policy   |   Terms of Service
                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • MySensors
                      • OpenHardware.io
                      • Categories
                      • Recent
                      • Tags
                      • Popular