Multiple interrupts


  • Hero Member

    Hi guys,

    I previously posted a question regarding how to use multiple interrupts on my Arduino together with my RF 69 radio. The suggestion was to use pinChangeInt, but I couldn't exactly get it to work.i decided to pick this up again today, and I found this new library which appears to supersede it, called EnableInterrupt.

    https://github.com/GreyGnome/EnableInterrupt

    At first it did not work since it apparently had some kind of conflict with the radio library which relies on interrupt 0, and possibly also something in the mysensors framework related to sleeping, I guess? Anyway, since I didn't needed to handle the external interrupts, just the pinChangeInt, I was able to modify the header file to remove the clashing definitions.

    I finally have a working sensor that can cover multiple buttons and reed switches through interrupts. This means it can sleep most of the time, but still wake and act immediately if something changes :-)


  • Hero Member

    Can you elaborate a little on the changes to the header file? Maybe post your sketch?



  • Yes, please post your sketch and share more info.
    Or even more - edit the sketch available at mysensors repository, so 'water pulse sensor' is supported properly by default, for two values. :-)


  • Hero Member

    I have included my sketch here. It uses the RF69 radio on a moteino platform. Since I'm lazy I have added the weather shield also from lowpowerlabs. In addition to the sensors on this I use a soil moisture sensor, and I have added support for three interrupt driven reed switches (for one door and two Windows).

    I downloaded the latest version of the EnableInterrupts library from the link in my first post and made the following changes to EnableInterrupts.h. I cannot guarantee that these changes will work for everyone, but I suspect it at least should work on the 328p versions of the process that used on the moteino and other similar arduinos

    Comment out lines 956 to 999:

    /*
    ISR(INT0_vect) {
    #ifndef NEEDFORSPEED
      (*functionPointerArrayEXTERNAL[0])();
    #else
    #if defined ARDUINO_MEGA
    #ifdef INTERRUPT_FLAG_PIN21
      INTERRUPT_FLAG_PIN21++;
    #endif
    #endif
    #if defined ARDUINO_LEONARDO
    #ifdef INTERRUPT_FLAG_PIN3
      INTERRUPT_FLAG_PIN3++;
    #endif
    #endif
    #if defined ARDUINO_328
    #ifdef INTERRUPT_FLAG_PIN2
      INTERRUPT_FLAG_PIN2++;
    #endif
    #endif
    #endif // NEEDFORSPEED
    }
    
    ISR(INT1_vect) {
    #ifndef NEEDFORSPEED
      (*functionPointerArrayEXTERNAL[1])();
    #else
    #if defined ARDUINO_MEGA
    #ifdef INTERRUPT_FLAG_PIN20
      INTERRUPT_FLAG_PIN20++;
    #endif
    #endif
    #if defined ARDUINO_LEONARDO
    #ifdef INTERRUPT_FLAG_PIN2
      INTERRUPT_FLAG_PIN2++;
    #endif
    #endif
    #if defined ARDUINO_328
    #ifdef INTERRUPT_FLAG_PIN3
      INTERRUPT_FLAG_PIN3++;
    #endif
    #endif
    #endif // NEEDFORSPEED
    }
    */
    

    Comment out the last part of the line 480:

    arduinoPin=interruptDesignator;// & ~PINCHANGEINTERRUPT;
    

    Here is the sketch. I have commented out the code related to the environmental sensors since I have not yet attached these to the board.

    #include <EnableInterrupt.h>
    
    #include <MySigningNone.h>
    #include <MyTransportRFM69.h>
    #include <MyTransportNRF24.h>
    #include <MyHwATMega328.h>
    #include <MySigningAtsha204Soft.h>
    #include <MySigningAtsha204.h>
    
    #include <MySensor.h>
    #include <SPI.h>
    #include <DHT.h>
    #include <Bounce2.h>
    #include <SFE_BMP180.h>    //get it here: https://github.com/LowPowerLab/SFE_BMP180
    #include <SI7021.h>        //get it here: https://github.com/LowPowerLab/SI7021
    #include <Wire.h>
    
    #define REPORT_INTERVAL 9000
    #define ALTITUDE 220
    #define BUTTON 3 //cannot change because of interrupt
    #define MOISTURE A4
    #define DOOR 5
    #define WINDOW_ONE 6
    #define WINDOW_TWO 7
    
    #define CHILD_TEMPERATURE 1
    #define CHILD_HUMIDITY 2
    #define CHILD_PRESSURE 3
    #define CHILD_MOISTURE 4
    #define CHILD_DOOR 5
    #define CHILD_WINDOW_ONE 6
    #define CHILD_WINDOW_TWO 7
    #define CHILD_POWER 9
    
    SI7021 sensor;
    SFE_BMP180 pressure;
    int BATTERY_SENSE_PIN  = A7;  // select the input pin for the battery sense point
    
    MyTransportRFM69 transport;
    // Hardware profile
    MyHwATMega328 hw;
    
    MySensor gw(transport, hw /*, signer*/);
    
    // Change to V_LIGHT if you use S_LIGHT in presentation below
    MyMessage temperatureMessage(CHILD_TEMPERATURE, V_TEMP);
    MyMessage humidityMessage(CHILD_HUMIDITY, V_HUM);
    MyMessage pressureMessage(CHILD_PRESSURE, V_PRESSURE);
    MyMessage moistureMessage(CHILD_MOISTURE, V_HUM);
    MyMessage doorMessage(CHILD_DOOR, V_TRIPPED);
    MyMessage windowOneMessage(CHILD_WINDOW_ONE, V_TRIPPED);
    MyMessage windowTwoMessage(CHILD_WINDOW_TWO, V_TRIPPED);
    MyMessage powerMessage(CHILD_POWER, V_VOLTAGE);
    
    unsigned long lastReport = 0, time, buttonStart = 0, buttonFinish = 0, lastCheck = 0, lastReduce = 0;
    
    void setup()
    {
      pinMode(BATTERY_SENSE_PIN, INPUT);
      pinMode(DOOR, INPUT_PULLUP);
      pinMode(WINDOW_ONE, INPUT_PULLUP);
      pinMode(WINDOW_TWO, INPUT_PULLUP);
      sensor.begin();
      pressure.begin();
      gw.begin(NULL, 7, false);
      gw.sendSketchInfo("Water control", "1.0");
      delay(250);
      gw.present(CHILD_TEMPERATURE, S_TEMP);
      delay(250);
      gw.present(CHILD_HUMIDITY, S_HUM);
      delay(250);
      gw.present(CHILD_MOISTURE, S_HUM);
      delay(250);
      gw.present(CHILD_PRESSURE, S_BARO);
      delay(250);
      gw.present(CHILD_DOOR, S_DOOR);
      delay(250);
      gw.present(CHILD_WINDOW_ONE, S_DOOR);
      delay(250);
      gw.present(CHILD_WINDOW_TWO, S_DOOR);
      delay(250);
      gw.present(CHILD_POWER, S_POWER);
      enableInterrupt(DOOR, notifyDoor, CHANGE);
      enableInterrupt(WINDOW_ONE, notifyWindowOne, CHANGE);
      enableInterrupt(WINDOW_TWO, notifyWindowTwo, CHANGE);
    }
    volatile int doorInterrupt = 0, windowOneInterrupt = 0, windowToInterrupt = 0;
    
    void notifyDoor() {
      //  gw.send(doorMessage.set(digitalRead(DOOR)));
      doorInterrupt++;
    }
    
    void notifyWindowOne() {
      //  gw.send(windowOneMessage.set(digitalRead(WINDOW_ONE)));
      windowOneInterrupt++;
    }
    
    void notifyWindowTwo() {
      //  gw.send(windowTwoMessage.set(digitalRead(WINDOW_TWO)));
      windowToInterrupt++;
    }
    //  Check if digital input has changed and send in new value
    void loop()
    {
      // Get the update value
      gw.process();
      if (doorInterrupt > 0) {
        gw.send(doorMessage.set(digitalRead(DOOR)));
        doorInterrupt = 0;
      }
      if (windowOneInterrupt > 0) {
        gw.send(windowOneMessage.set(digitalRead(WINDOW_ONE)));
        windowOneInterrupt = 0;
      }
      if (windowToInterrupt > 0) {
        gw.send(windowTwoMessage.set(digitalRead(WINDOW_TWO)));
        windowToInterrupt = 0;
      }
    
      //*************** READING BATTERY VOLTAGE *********************
      //turn MOSFET ON and read voltage, should give a valid reading
      pinMode(A3, OUTPUT);
      digitalWrite(A3, LOW);
      Serial.print("  BATT: ");
      int sensorValue = analogRead(A7);
      Serial.println(sensorValue);
      float batteryV  = sensorValue / 10 * 0.00520430107;
      int batteryPcnt = sensorValue / 100;
      gw.send(powerMessage.set(batteryV, 1));
      gw.sendBatteryLevel(batteryPcnt);
      pinMode(A3, INPUT); //put A3 in HI-Z mode (to allow mosfet gate pullup to turn it OFF)
      //*************** READING BATTERY VOLTAGE *********************
      float moistureReading = analogRead(MOISTURE);
      Serial.println(moistureReading);
      gw.send(moistureMessage.set(100 * (1024 - moistureReading) / 1024.0, 1));
    
      Serial.println("************ BMP180 *********************************");
      Serial.print("provided altitude: ");
      Serial.print(ALTITUDE, 0);
      Serial.print(" meters, ");
      Serial.print(ALTITUDE * 3.28084, 0);
      Serial.println(" feet");
      /*
        float temperature = sensor.getCelsiusHundredths() / 100;
        Serial.println("************ Si7021 *********************************");
        Serial.print("C: "); Serial.print(temperature);
        int humidity = sensor.getHumidityPercent();
        Serial.print("   H: "); Serial.print(humidity); Serial.print("%   ");
        gw.send(temperatureMessage.set(temperature, 1));
        gw.send(humidityMessage.set(humidity, 1));
        char status;
        double T, P, p0, a;
        status = pressure.startTemperature();
        if (status != 0)
        {
          // Wait for the measurement to complete:
          delay(status);
    
          // Retrieve the completed temperature measurement:
          // Note that the measurement is stored in the variable T.
          // Function returns 1 if successful, 0 if failure.
    
          status = pressure.getTemperature(T);
          if (status != 0) {
            Serial.print("C: ");
            Serial.print(T, 2);
            Serial.print("    F:");
            Serial.print((9.0 / 5.0)*T + 32.0, 2);
            Serial.println("");
            status = pressure.startPressure(3);
    
            if (status != 0)
            {
              delay(status);
              status = pressure.getPressure(P, T);
              if (status != 0)
              {
                // Print out the measurement:
                Serial.print("abs pressure: ");
                Serial.print(P, 2);
                Serial.print(" mb, ");
                Serial.print(P * 0.0295333727, 2);
                Serial.println(" inHg");
    
                // The pressure sensor returns abolute pressure, which varies with altitude.
                // To remove the effects of altitude, use the sealevel function and your current altitude.
                // This number is commonly used in weather reports.
                // Parameters: P = absolute pressure in mb, ALTITUDE = current altitude in m.
                // Result: p0 = sea-level compensated pressure in mb
    
                p0 = pressure.sealevel(P, ALTITUDE); // we're at 1655 meters (Boulder, CO)
                Serial.print("relative (sea-level) pressure: ");
                Serial.print(p0, 2);
                Serial.print(" mb, ");
                Serial.print(p0 * 0.0295333727, 2);
                Serial.println(" inHg");
                gw.send(pressureMessage.set(p0, 1));
              }
            }
          }
        }
        */
      gw.sleep(REPORT_INTERVAL);
    }```

  • Hero Member

    I took a quick look at the water pulse sketch. I believe that the only change required is to replace attachInterrupt with enableInterrupt and choose a pin other than 2 or 3 which are connected to the external interrupts.

    Adding multiple sensors simply requires the sensor to be connected to different pins and enabled using enableInterrupt. Make sure not to use anything else that uses interrupts inside the handler such as serial.print or anything related to the radio. It probably can't use anything related to the time, either, since sleeping breaks timing.



  • It would be great if those pin change interrupts could be combined with a timer interrupt. This way we could build a sensor which can collect temp / hum data and react on motion, window open etc, too. A modified version of the library could be included in the mysensors package. Should be possible, right?


  • Hero Member

    As far as I can tell my sketch works with timer interrupts as well as the pin interrupts.

    I have not tested this thoroughly, though.


  • Hero Member

    I just tested this, and both the pinChange interrupt and the timer interrupts work great.



  • Hello, GreyGnome here. Glad to see the library is helping you. Sorry to see you had to edit code.

    In the next upcoming release I will include compiler directives that allow you to comment out conflicting interrupts.

    Also, as someone else mentioned- don't use attachInterrupt() together with EnableInterrupt(). It just makes the code fatter, and EnableInterrupt() can already cover both interrupt types; eg on an Arduino Uno:

    enableInterrupt(2, myFunkyExternalInterruptFunction, LOW);
    enableInterrupt(10, myExcellentPinChangeInterruptFunction, CHANGE);

    ...note that enableInterrupt will use External interrupts on pin 2 by default. You can force it to use pin change interrupts:

    enableInterrupt(2 | PINCHANGEINTERRUPT, myFunkyExternalInterruptFunction, FALLING);

    ...in which case, "myFunkyExternalInterruptFunction" is something of a misnomer. ALSO NOTE: Pin Change Interrupts do not support the LOW state!


Log in to reply
 

Looks like your connection to MySensors Forum was lost, please wait while we try to reconnect.