Interrupt fires repeatedly when it should only once...?

Hi,
I am still relatively new, and I still do not fully understand capacitors, but I'm thinking that is what I need in this circuit. This circuit is for a project in my car and I have seen this behavior there, but the easiest way to reproduce it is by touching the circuit with my finger.

I am pulling up input pin D2 and using a transistor to drain it to ground to trigger a falling interrupt. This seems like the safest way to shield the Arduino from potential voltage spikes from the car.

In the diagram below, you would touch the circuit with your finger to the left of the resistor, triggering the ISR.

The minimum code to reproduce is simple enough:

void setup() {
  pinMode(2, INPUT_PULLUP);    // pin D2
  attachInterrupt(0, theISR, FALLING);

  while (!Serial);
  Serial.begin(9600);
}

void loop() {
  Serial.println("loop");
  delay(500);
}

void theISR() {
  Serial.println("ISR");
}

The output, you can see that "loop" is being printed every .5s, but when you touch the resistor leading to the base of the transistor, it starts printing out "ISR" as quickly as it can. In fact it prints much more rapidly if you pinch the wire between 2 fingers than if you just barely touch the wire with one finger.

00:02:29.541 -> loop
00:02:30.019 -> loop
00:02:30.545 -> loop
00:02:31.009 -> loop
00:02:31.534 -> loop
00:02:32.025 -> loop
00:02:32.523 -> loop
00:02:33.012 -> loop
00:02:33.459 -> ISR
00:02:33.459 -> ISR
00:02:33.459 -> ISR
00:02:33.459 -> ISR
00:02:33.459 -> ISR
00:02:33.459 -> ISR
00:02:33.459 -> ISR
00:02:33.493 -> ISR
00:02:33.493 -> ISR
00:02:33.493 -> ISR
00:02:33.493 -> ISR
00:02:33.493 -> ISR
00:02:33.493 -> ISR
00:02:33.531 -> ISR
00:02:33.531 -> ISR
00:02:33.531 -> ISR
00:02:33.531 -> ISR
00:02:33.531 -> ISR
00:02:33.531 -> ISR
00:02:33.531 -> ISR
00:02:33.569 -> ISR
00:02:33.569 -> ISR
...

Try connecting the base resistor to Vcc to see if it is stable.
By touching it, you are probably introducing some sinusoidal wave (mains hum etc.) which is constantly triggering the interrupt.
Serial.print() etc. is not reliable in an ISR.
Are you attempting to design a touch operated switch ?

Ugh, your mains hum comment does remind me of something someone said to me months ago, I had forgotten that. I actually tried to see the frequency of the ISR while thinking I was crazy because I'm working on a DC circuit. I do know print() is not reliable and I would not do this in a real project, this was just used as proof that the ISR was being triggered.

My real program is around 2k lines and is a kill switch for my car. The above is the minimum reproduction. This was happening earlier today in my car when instead of my finger, it was the starter circuit connected to the base of the resistor. In that case, when using a multimeter, it did appear the input to the base of the resistor was stable around a few mV, yet I saw the same behavior - the ISR being triggered repeatedly, and I don't believe I was touching any part of the circuit or near mains.

Thank you for your comment - I need to revisit some things in the light of day with a clearer head, I hope I can find that I'm doing something wrong.

Hello jwallis,
Almost the same question came up a day or 2 ago, see D1 Mini repeated interrupt I think (but I hoping someone who knows will confirm) that the problem is using print inside an ISR. Certainly when I tried it on a D1 Mini it repeated endlessly. I would guess it's the same for other boards but I have not tried. Look at the code I posted in that other thread and adapt it to your Nano and try it.

Why are you using an interrupt anyway? It is a common mistake to use interrupts for inputs when they are not the correct tool to use.

You might need to think a bit about the application. You can make a kill switch with a latching push button wired direct into the car wiring.

Using the Arduino in this environment what happens if it falsely triggers a “kill” signal when you are driving ? Could be dangerous and could well invalidate insurance after the resulting accident.

In the diagram below, you would touch the circuit with your finger to the left of the resistor, triggering the ISR.

Then don't touch it, or make the 10K resistor into a 1K and also add another 1K resistor from the base of the transistor to ground.

I do not think the internal pull up resistors are strong enough for this environment. Make it an external pull up about 510R.

But I would also strong agree with what hammy and peter said. You shouldn't be using interrupts unless the rest of the code is poorly written and has lots of delays in it.

The term "interrupt" in the subject title strongly implies a mistake! :cold_sweat:

PerryBebbington:
Hello jwallis,
Almost the same question came up a day or 2 ago

Why are you using an interrupt anyway? It is a common mistake to use interrupts for inputs when they are not the correct tool to use.

I have convinced myself that it is not the printing that is my problem. As noted above, my issue was the AC mains.

For my actual project, I am simply sampling the D2 pin, and I am seeing the value of the pin generally be TRUE (INPUT_PULLUP is enabled), but randomly, avergage of every .2 sec, it goes to FALSE

I am using an interrupt because I am doing a lot of work in my main loop, it will take between 2s and 60s to complete each loop, depending on if it needs to process incoming messages or enable GPS. For this reason I cannot sample the input fast enough to detect when D2 changes.

All I am doing in my ISR is setting 1 var to TRUE so that the main loop will know to take action.

hammy:
Using the Arduino in this environment what happens if it falsely triggers a “kill” signal when you are driving ? Could be dangerous and could well invalidate insurance after the resulting accident.

Absolutely true, and thank you for the concern. I was originally using software to control the relay that controls the car wiring but have switched to using hardware for this reason. The relay can ONLY open when both the arduino has a pin set to TRUE and the signal from the starter wire (directly off the steering column) is energized. The latter part of the circuit is not related to the arduino at all, it is directly connected to the relay. The user would have to try to start their car while moving for the relay to open. I do intend to sell this project, to a Very niche classic car market, like 1 specific model.

Grumpy_Mike:
make the 10K resistor into a 1K and also add another 1K resistor from the base of the transistor to ground.

I do not think the internal pull up resistors are strong enough for this environment. Make it an external pull up about 510R.

But I would also strong agree with what hammy and peter said. You shouldn't be using interrupts unless the rest of the code is poorly written and has lots of delays in it.

This sounds like the right track. In my second message above I admitted the issue was the mains, 60Hz, all that good stuff, so my example was very poor compared to what I'm really after. I hoped no one would respond after that. Unit A of my project, in the car, when all the components are wired together and a few inches apart from each other works great. Unit B of my project, soldered to the custom PCB I had made, where all the components are mm from each other, has this issue of the interrupt firing over and over. I think it's due to the proximity, so I think this is the right track. I just changed the pullup resistor to a 5k, and that didn't do it. I will try changing the values and adding the resistor from base to ground as you advised. I genuinely abhor this part of this discipline. Software seems so much cut and dried in a lot of ways. I don't know how you all do it, it makes me want to pull my hair out.


I am surprised that you say (it sounds like you're saying this is true in general) that one shouldn't be using interrupts. As I said above, my main loop can take on the order of minutes to complete, so without multi-threading, how can I sample a pin that will be signaled arbitrarily? Maybe I could build something like a flip flop in hardware, but my interrupt solution seems to be the most obvious. As I said above, my ISR is very short and simply sets a var to true so that the main loop will know to take action.

Paul__B:
The term "interrupt" in the subject title strongly implies a mistake! :cold_sweat:

I also find this an interesting comment that if you were someone else I'd ignore, but I am really curious what you mean by that. Is there a different word that would have more clearly described my problem? To me, in my ignorance, interrupt is a neutral word that is simply part of the system just like register, pin, etc...?

Oh... maybe you, like some others, generally consider using interrupts to be bad practice...? I'm so surprised to hear this from all of you. I will do some research on why this might be.

Ok, after a very long day, there were 2 solutions that worked independently, I will use the second. To be clear, this is for my actual project in the car.

1.add another 1K resistor from the base of the transistor to ground
2.change the resistor going into the base of the transistor from 10k to 100k. I believe this works because when the voltage is "floating" while the vehicle is running, it's floating between 0.1 and -0.08V, but when the starter is energized, the resistor sees close to 12V. This is the only time I want the interrupt to be signaled. BJTs are current-controlled, so the bigger resistor filters out the tiny currents that .1V will create through a 100K resistor.

I really hope my understanding of the situation is sound. I still don't really grok a lot of this stuff and in the past I have completely convinced myself I understand what's going on, it all makes sense.... and turned out to be wrong. That's more scary than simply not understanding it. I'm a professional software engineer and this EE stuff is impressively difficult, I give you all a lot of credit. Thanks everyone-

I am using an interrupt because I am doing a lot of work in my main loop, it will take between 2s and 60s to complete each loop, depending on if it needs to process incoming messages or enable GPS. For this reason I cannot sample the input fast enough to detect when D2 changes.

I am surprised that you say (it sounds like you're saying this is true in general) that one shouldn't be using interrupts. As I said above, my main loop can take on the order of minutes to complete, so without multi-threading

.

Yes, it's true in general. Your main loop should take no more than about 10ms to go round once, if it is taking as long at even a second then the code is not written to multi task. These 2 tutorials tell you how to write non-blocking code that will be a lot more responsive and allow inputs to be polled.
Using millis for timing
Demonstration for several things at the same time

As an example, I have a program that occupies over 60kB of memory and does the following:

  • Reads 4 temperature sensors over I2C
  • Reads another temperature sensor, an anemometer, a wind direction indicator, a rain gauge and data from my UPS and gets NTP time all over WiFi.
  • It controls 4 heating zones
  • Maintains a clock synced to NTP
  • Logs power interruptions
  • Logs and displays the outside temperature over the previous week
  • Manages a Nextion display with 6 pages

The typical loop time is about 20μs.

True it's probably running on a faster processor than you are using but that's not really the point, all this can be achieved using the techniques in the 2 tutorials above.

With a traditional, higher-level programming background, I think a lot of people would consider this a hack in place of multi-threading. I'm not putting a value on the idea, saying it's good or bad, I just think that's what most people would say. It works, it's relatively straightforward, it may be the only option available, cool, I'm all for it. I have actually done very little multi-threading programming in my life and I'm fine with that.

My project does the following each loop

  • use a SimCom module to check incoming SMS messages
  • parse SMSs, potentially take action such as setting vars or turning on SimCom GPS chip (takes up to 60s)
  • respond to SMSs via TCP socket connection and delete incoming SMS (takes up to 30s)
  • check if the GPS module has been on but not accessed in the last 20 minutes, potentially turn off GPS (takes 1-2s)
  • check SimCom's clock and turn on/off variables accordingly (takes < 1s)
  • check GPS location, compare to "home" location and send SMS message if distance > 500 feet (takes around 5s)
  • check variable set by interrupt routine, potentially send SMS to user warning that someone attempted to start the vehicle (takes up to 20s)
  • check time of last reset of SimCom module, potentially reset the module (takes around 15s)

I think in theory these tasks could be accomplished using the "at the same time" strategy, but you'd have to save the complex states of each of these tasks as globals which would eat up memory (a big problem for me), and the interdependencies of the half-completed, in-progress tasks would become overwhelming. I already have a lot of interdependencies between these tasks and use more globals than I'd like. I imagine a pi would be more suited to what I'm doing, but size, power consumption, and price are all priorities in my application.

All that said, the "at the same time" strategy is cool, I appreciate your introducing it to me, I will certainly use it in the future : )

How long does it take to write to the Nextion display and to read those values over WiFi? Those both seem like they might take a while. Do you determine a power interruption has occurred simply by reading something that has sensed it itself and it tells you, or is your arduino sensing the power interruption itself? The latter seems like a good for an interrupt since a power interruption could be an arbitrarily small window of time.

As has already been pointed out, your main loop should be completing in milliseconds, regardless of what it is doing. I haven't looked at your code, but I'll bet there are delay() and while(), possibly some "for" blocking functions?

jwallis:
How long does it take to write to the Nextion display and to read those values over WiFi? Those both seem like they might take a while. Do you determine a power interruption has occurred simply by reading something that has sensed it itself and it tells you, or is your arduino sensing the power interruption itself? The latter seems like a good for an interrupt since a power interruption could be an arbitrarily small window of time.

The question indicates that you have yet to understand how to write non blocking code. It does not matter how long it takes to write to a Nextion or receive the data from WiFi because the code only bothers itself with these things when they can be done immediately. Data is only written to the serial transmit buffer if there is room in it to take a byte of data, if not the program goes off to do something else, then comes back about 20μs later, checks the buffer and moves on. It carries on like that until all the data has been deposited in the buffer. Similarly for the data received over WiFi, which is actually received on an ESP8266 and send to to my main processor over serial. Again, my code checks to see if there is a byte waiting, if there is it reads it and saves it, it not it moves on and checks if the next thing needs doing. When enough bytes have been received it parses them and does with them whatever needs to be done. For Nextion displays you can see how I do this by looking at my code in the tutorial Using Nextion Displays with Arduino. No where in my code does it wait for things to be ready, it just checks if something needs to be done, does it if it's ready, moves on to the next thing if not.

This analogy is intended to illustrate the point:
You wouldn’t expect this when you go into a restaurant:
A waiter meets you at the door, takes you to a table, gives you a menu then waits by your table while you decide what to order. The waiter takes your order, goes to the kitchen and waits there while the chef cooks your food. However, as the staff in this restaurant only ever deal with one customer at a time your waiter has to wait with other waiters while the chef cooks 5 other meals before starting on yours. When the food is eventually ready the waiter brings it to your table then waits by your table while you eat it. When you’ve finished eating the waiter takes your plates away and returns to ask if you want anything else. This continues until you leave. No one else gets served. This is how your code is working at the moment.
I’m not going to describe what really happens in a restaurant as you already know. A waiter uses exactly the same system as a state machine to serve people when they need serving and check to see who needs serving next between dealing with customers. You can build functions for the different tasks a waiter does such as:

void takeOrder();
void bringFoodToTable();

You call these from loop(); While in loop the waiter checks to see if any tables need attention, and if they do s/he goes to find out what they need. If not, then s/he keeps checking until someone needs something. Computer code should be written along the same principals.

PerryBebbington:
The question indicates that you have yet to understand how to write non blocking code.

Took the words right out of my fingers! :sunglasses:

+1!

PerryBebbington:
The question indicates that you have yet to understand how to write non blocking code.

SeveralThingsAtTheSameTime is clear. Beyond that, maybe I am missing something, but this is how I see it: there are things that require either blocking or added complexity to achieve, for example

PerryBebbington:
Data is only written to the serial transmit buffer if there is room in it to take a byte of data

What if functionA() and functionB() both write to a transmit buffer. Sometimes the buffer is full. The output from functionA() must always immediately precede the output from functionB(). Either we can call functionA(), have it block until it's able to write successfully, then call functionB(), or we must add variables that maintain state, i.e. has functionA() successfully written to the buffer, or we can organize the program structurally to ensure order, i.e.
if (functionA())
then functionB();

No problem, but when functionA(), functionB() .. functionZ() all write to the same buffer in a specific order, the complexity of maintaining state gets overwhelming quickly.

A separate potential issue with the strategy of attempting to write to a buffer (or doing some other task) every Xμs is that one or more of the functions could be starved. It's possible that every time functionX() is called, the resource it's trying to use is busy (due to functionY() having used the same resource) and it never achieve its purpose. This could be fixed by... the added complexity of maintaining state using variables or structuring your program.

PerryBebbington:
Similarly for the data received over WiFi, which is actually received on an ESP8266 and send to to my main processor over serial.

If you did not use an ESP8266 and your arduino was receiving data directly from some source over wifi, how long would that take? I assume > 20us, otherwise why use the ESP? I assume you have solved the problem by adding hardware complexity instead of software complexity. (I'm clearly making assumptions here, and we know that's often not a winning idea)

My program does a lot of user i/o over TCP, but to my program it looks like a SoftwareSerial. I send something out and yes, I absolutely block and wait for a response. I could return and come back and check for responses, but I may have sent 4 things out in 4 other function calls. Even if I added the complexity of remembering what messages were sent out in what order, I'm not sure the order of the responses (over an LTE network) are deterministic, so how do I know which functions succeeded, failed, or simply gave no response? Blocking is the only strategy I can think of.

The modified analogy of my user i/o over LTE may be something like a food-service window.
Blocking version: You walk up, choose your meal, make your order and pay, and they give you what you ordered.
Non-blocking version: You walk up, choose your meal, make your order and pay, walk away. The next user does the same, next user does the same. The users, in turn, get to listen to the server for the announcement of food.
At some point, the server shouts to everyone "I have food." Whichever user is listening gets the food. The solution is to add the complexity of an order number.

In my scenario, there is no way I know of to add an order number.

Writing a loop that executes quickly and checks each resource on the order of microseconds is a solution, apparently one preferred by a lot of people in this thread, but I'm not convinced it is "always the best solution." Again, it may be possible that my code could be written this way if sufficient complexity was added to maintain state and avoid issues of interdependencies, but I value understandability very highly and I expect that this strategy would lead to a significant decrease in understandability of my code.

Sorry jwallis,
I honestly think you are inventing problems that don't exist. I guess if I think back to when I first became aware of state machines your thinking must be like mine was then; it looks more complex than it is. My first real encounter with state machines was a piece of equipment in an electronic telephone exchange that used core memory (look it up, or better still visit the museum of computing at Bletchley Park) to hold states and random logic to calculate what to do next.

Writing good non-blocking code comes with experience, you have to get into the right mindset. Honestly, the problems you are imagining are just things you are imagining, these things are easy to deal with with practice. I can't teach you that, you just have to start simple and work up to something more complicated.

I use the additional hardware of an ESP8266 because it has the WiFi capability my main processor does not have. The ESP8266 does not have other functionality that I need, hence the use of 2 different processors to do 2 different jobs, the choice was dictated by hardware requirements.

Download and study the code in this Nextion additional features. The code is fully multi taking and does many of the things you express concerns about. I don't claim it as being an award winning example of excellent programming, I am sure it could be improved, but it does the job, uses state machines and flags to track what needs doing next and illustrates ways to achieve non-blocking code that returns to the start every few milliseconds.

With a traditional, higher-level programming background, I think a lot of people would consider this a hack in place of multi-threading.

So how do you think multi-threading is achieved? It is not some magic it is simply the application of this hack in the operating system or compiler.
It can be done in two ways, preemptive multitasking where an interrupt wipps the ground from under you, or cooperative where your thread yields.

Or by having more than one processor core. Most smaller microcontrollers have one. Also the dust seems to have settled on RISC mostly. It's about economics - if you're running a washing machine or something, how much processing power do you really need? So you can play with micros up and down the range.

The other issue, though, which I may have been seen touting as an advantage, it seems that round-robin multitasking does not have direct support in the language, only help from library functions. Those work but lack the directness of say, a 'for(...)' statement (which always gums up the works for beginners). So I wonder, is one path any better than another as a beginner, to reach a level of understanding necessary to properly use a real time multitasking system? Or, to build a clean and functional round robin system in the end?

I did program for an RTOS on an 8 bit system, the tasks were very straightforward to write and the flag system worked for interprocess communication. These flags or semaphores are a common feature between RTOS and RR.

I have mused about some way to automatically translate an RTOS idea to an RR idea but I don't think I have the time because I think it would take a lot of it.

Way way back in 1989 I made a multitasking system on an 8 bit processor called a 6800. Basically this was an interrupt driven system that returned to a different task every time it was fired. I did this by having the ISR manipulate the return address on the stack to be the other task and then return. As I understand it you can’t do that in C because you can’t manipulate the ISR stack.
I made up this technique and I haven’t read it anywhere since, but it did work quite well. I was controlling two model trains running on two tracks with a common section.

Grumpy_Mike:
I did this by having the ISR manipulate the return address on the stack to be the other task and then return.

But you manipulated not only the return address, but all the saved registers (A, B, X, SR) and had to store them also in the process array. :grinning: