Using the attachInterrupt() function


#1

I have three digital pins, each sensing whether a particular appliance is on or off. I wanted to publish an event to the cloud, showing the on/off status of each of those appliances, whenever any one of the appliances switched from on to off. The amount of code necessary to store the status of each of the pins, read each of the pins, see if any had changed, and to publish an event if any had changed, while doable appeared excessive. I came up with an elegant solution which I’d like to share with the community.

I map the pins D1, D2, D3 to appliance1, appliance2, and appliance3 because, apparently, the attachInterrupt function doesn’t work with D0

the syntax for the attachInterrupt() function is:

attachInterrupt(pin_name, function_to_call, MODE), where MODE is something like UP (the pin goes from low to high), DOWN (the pin goes from high to low), or CHANGED (the pin changes). I used CHANGED because I want to publish when any one of the three pins changes.

I put this in setup:

  attachInterrupt(appliance1, STAT_CHANGE, CHANGE) ; 
  attachInterrupt(appliance2, STAT_CHANGE, CHANGE) ;
  attachInterrupt(appliance3, STAT_CHANGE, CHANGE) ;

What this is saying is when the photon detects a change in one of the pins, call stat_change. Stat change sets the boolean variable changed to true, this triggers the if statement in the loop, and the status of all three pins is published.

The STAT_CHANGE function is defined below the loop (and declared at the top of the program) and changed is defined as volatile boolean changed = 0 ;

There are limitations on the types of functions attachInterrupt() can call… the function can’t take any arguments and it can’t return any value. You also want the function to be fast. I use I use it to set indicate changed as true.

void STAT_CHANGE()
{
  changed=TRUE; 
}

I define three strings appliance_status1 through 3, and a string to hold my published results “sout” in the header, and In the body of the loop I do this:

if (changed) 
{
changed=false
clear the strings: appliance_status1 through three = "" and clear sout (the output string)

        appliance_status1  = (digitalRead(appliance1)) ? "ON|||" : "OFF|||"               ;
        appliance_status2  = (digitalRead(appliance2)) ? "ON|||" : "OFF|||"               ;
        appliance_status3  = (digitalRead(appliance3)) ? "ON"    : "OFF"                  ;
        sout            =  sout+=appliance_status1+=appliance_status2+=appliance_status3     ;
        Particle.publish("appliance_is",sout)                                          ;
    }
   delay(5000)                              
}

When the “If this then that” website sees the “appliance_is” event it adds a line to my spreadsheet in Google Docs. The tripple pipe symbol ||| which you see at the end of “ON||” and “OFF|||” above puts each of the results in a separate column on the spreadsheet… that’s another trick good for another post.

The attachInterrupt() function has some limitations… you should read the documentation before using it. But this works great… and as I said… the resulting code is elegant (in my opinion).

I know I’ve sort of glossed over this… but with some tinkering you can figure it out. Happy programming!


#2

One thing to mention:

Any variable altered in an interrupt (or other out of order instruction) and used some other place too should be declared volatile.

In your case

volatile boolean changed;

About elegance, this instruction relies on the optimizer doing away with a lot of extra work

  sout            =  sout+=appliance_status1+=appliance_status2+=appliance_status3     ;

Which would end up as

appliance_status2+=appliance_status3;
appliance_status1+=appliance_status2;
sout += appliance_status1;
sout = sout;

I guess this should rather be

  sout            =  appliance_status1 + appliance_status2 + appliance_status3;

Or if has to be

  sout           +=  appliance_status1 + appliance_status2 + appliance_status3;

Or even simpler

    sout = (digitalRead(appliance1) ? "ON|||" : "OFF|||")
         + (digitalRead(appliance2) ? "ON|||" : "OFF|||")
         + (digitalRead(appliance3) ? "ON"    : "OFF"   );

#3

I declare changed as volatile in the header… so I caught that one. I was wondering why I was using += last night… changing it to + will probably solve one of the problems I was having… the string it was publishing was appending the current events to a string of prior events… resulting in an progressively longer string… I fixed the problem by setting all of the strings to “” before loading in the content of the current event. I’m new to C programming… (or Wiring) I appreciate your input.


#4

For some reason your code isn’t working. (I added the semicolons where needed) What does work is

sout = (digitalRead(appliance1) ? “ON|||” : “OFF|||”) ;
sout = sout + (digitalRead(appliance2) ? “ON|||” : … etc.

Not sure why… it looks like it should work (and I’d like it if it did)…


#5

Yea, that’s an order of execution thing I think. The compiler can’t decide for the correct overload for operator+=()

But this should work

sout = String(digitalRead(appliance1) ? "ON|||" : "OFF|||")
     + String(digitalRead(appliance2) ? "ON|||" : "OFF|||")
     + String(digitalRead(appliance3) ? "ON"    : "OFF"   );

But to be honest I’d rather avoid String and use char[] and snprintf() anyway.