Adding attachInterrupt to my device causes it to go unresponsive

Hi all, I have a sort of multi-function application I wrote for one of my Photons which basically drives several relays that perform different functions in my garage. This includes turning on and off my outside lights at sunrise and sunset via Weather Underground webhook, several IFTTT button webhooks to trigger relays on and off, and most importantly in this case, a “sequencer” to ring an alarm bell (for fun, not actual security purposes). The bell is technically a fire bell, and it’s triggered by a fire alarm pull I have, so the functions in the code are named as such.

I finally got to test the digital input (my first ever!) last night, but I found that the performance was unreliable when I used a basic check in void loop() to check the state of the input, so I figured it might make sense to use interrupts instead. I read the documentation on interrupts, and implemented what I think is correct, but now my Photon just randomly hangs up on its own, usually shortly after boot. The breathing cyan light will freeze “in place”, and the device goes unresponsive.

It’s possible that I’ve written the worst code in the world, but I think it’s more likely that I’m either misusing interrupts, or I’m locking up the processor via some other function I wrote inappropriately. I did notice that with this code, the device goes unresponsive typically right after a webhook (any webhook) is triggered. For example, I triggered the garageBellControl function, which is supposed to turn on an output for 200 msec, then turn it back off, but all it did was turn the output on; it never got to the next step.

Here’s a link to my code: https://go.particle.io/shared_apps/5b4f39c4d487c461050010f1

I’ve already commented out the attachInterrupt statements for now, but they can be found on lines 50 and 51. All of the functions are below void loop(). The functions triggered by the interrupts start at line 133 and 139, respectively. The basic design I had in mind for most of the functions like garageControl was to use the webhooks to modify an associated bool variable, then each time I loop through void loop(), I check that variable, and if it changed, I change the state of the associated relay output.

Let me know if you have suggestions on any part of the code, or any questions. Thanks!

@dbsoundman, firstly, you can’t put a Particle.publish() within an interrupt routine. This is most likely why your Photon goes unresponsive. I suggest you set a flag which you read in loop() where you do the publish.

Second is that you are attaching twice to the same pin. Only the second attach will “take”. What you want is a single attach, specifying “CHANGE” for the condition. In your single interrupt service routine you would then read the interrupt pin state to decide if it was a RISING interrupt (the pin is HIGH) or a FALLING interrupt (the pin is LOW).

Finally, what is the purpose of delay(1) at the end of loop()?

2 Likes

Good to know, I was wondering if I could specifically monitor for rising and falling, that way I don’t have to worry about the state of the switch when the device is booted, but I can live with making sure the switch is correct first.

I will remove that Publish() statement, I use those mainly for debugging so I can see what the Photon is doing as I’m writing code.

The delay(1) was something I forgot about actually! I put it in there at one point to see if I could mitigate some issue I was having, and just never thought to remove it.

With a CHANGE interrupt you can call pinReadFast from the interrupt service routine to find the state.

https://docs.particle.io/reference/firmware/photon/#pinreadfast-

The state you get back is the current state, so if it’s HIGH you just got an interrupt because of a LOW->HIGH transition.

3 Likes

Thanks to @peekay123 and @rickkas7, I got it working; at least to the point where it no longer goes totally unresponsive! I just have one other possibly related problem:

When the Photon loses WiFi (goes to the blinking green status), it acts sort of oddly. If I trigger the interrupt while it’s not connected to WiFi, it seems like it only interates once in my fireBell function’s loop. If you’ll notice in the code, the interrupt triggers a function which toggles a relay 3 times, waits 1100 msec, then does so again. When it’s not connected, it will only trigger the relay once, and at random intervals. It will continue this random triggering even after I turn the switch back off (meaning it should have stopped triggering the relay entirely), until it regains WiFi/cloud connectivity, at which point it goes back to normal. Additionally, I’ve noticed that the Particle apps will report the device is online even though it’s actually blinking green.

Here’s the latest code: https://go.particle.io/shared_apps/5b5627621996fc72610001f7

Note that I removed the delay(1) at the bottom of the loop, I monitor the input as a CHANGE interrupt, and I use pinReadFast to set the variable I use to trigger the fireBell function.

You may want to consider SYSTEM_THREAD(ENABLED) and maybe also SYSTEM_MODE(SEMI_AUTOMATIC).
Also when your code realises loss of connection - potentially without chance to reconnect - then it might be good actively disconnect.

I also don’t really see the reason for the use of SparkTime.
Unless you have very specific reasons for using it, I’d go with the native Time object.

Additionally I’d also rather do second-based time arithmetic on rtc.now() (or Time.local() when using the native Time object) instead of first plucking the time apart the and then fumbling it back together via logical compares and combinatorics.

Avoid using String wherever possible
e.g. change

    String str = String(data);
    char strBuffer[50] = "";
    str.toCharArray(strBuffer, 50);

to the more suitable

    char strBuffer[strlen(data)+1];
    strcpy(strBuffer, data); // example: "\"21~99~75~0~22~98~77~20~23~97~74~10~24~94~72~10~\""

And since you are only using integers, you can simplify your parsing by means of sscanf()

Particle.publish() should be marked as PRIVATE

Thanks for the insights. I will give SYSTEM_THREAD a shot, but I’m not sure about setting the mode to SEMI_AUTOMATIC. I want the local functions (this single switch input) to work whether or not it’s cloud-connected, but I do want it to connect to the cloud normally. It would seem in that case that I want it to stay in AUTOMATIC mode, correct?

Regarding SparkTime: I wrote that code back on my Spark Core and “ported” it over to a couple Photons I’m currently using. I wanted an NTP-based time source, as I’m using the code to synchronize some analog clocks.

I do agree about the second-based arithmetic, which I could do even with SparkTime, I just haven’t gotten around to revising that code. For now, “it works”, so I haven’t messed with it. (This is my hobby, not my vocation after all!)

Finally, regarding String: I got that code from someone else’s example long ago when I was designing that interface to Weather Underground, and I still only have so much of an idea as to how it works. Again, since this is a hobby, that was more or less a convenient means to an end so I just took it at face value. Why is using String bad in this case? I can see how sscanf could be more efficient for sure, but everything “works” with the existing code.

Note that I’m not opposed to revising my code and making it more efficient; I’m just hesitant to go break things that I’ve built that already work and I’ve come to take for granted. At the moment I don’t have another spare Photon to play around with so I don’t have a test platform.

With SEMI_AUTOMATIC all you need to do in setup() is call Particle.connect() and then it behaves just like AUTOMATIC.

What do you consider the advantage of NTP based time sync over Particle cloud synced - Particle's time base is just as reliable and IIRC the Time object also takes typical network latency into account?

String objects tend to cause heap fragmentation especially on long running projects. That heap fragmentation may lead to system crashes or in worse cases to the inability to reconnect after a loss of connection without the convenience of clearing the heap as would happen with a proper crash :wink:

1 Like