Need Interrupt Help

Ok. By the way, do you have an oscilloscope to monitor the continuous voltage on this pin?

Also, just to check if it is not the Arduino module, can you replace it just to check if has not gone bad. If you have one, it is a quick test.

Yes, I have one, and it shows less than 5 mV noise. Which should not trigger the interrupt.

Can you now tie the resistor to the 5V instead of ground. Just to see if it is level triggered (if by ground and not by a HIGH). I just want to make sure it is not HW related first is all before chasing a SW problem that may or may not exist.

The logic controller (Itsy Bitsy 32u4 3v) does not see the push button, it’s only the Raspberry PI 3+ that the push button is connected (GPIO pin 33) to.
Using a 10K pull-up resistor, still getting false triggers!!!

This:

is a function and not a HW interrupt. In other words, it needs to be explicitly called. Is there code (a conditional statement) that prompts this function to be called? If so, can you provide it.

I think that you are going about your Python interrupt script the wrong way. HW interrupts are automatic events that you should not have to call explicitly. That is, interrupts are called automatically based on pin events.

Here is a link that should help you setting up the interrupt for this pin:

https://docs.arduino.cc/language-reference/en/functions/external-interrupts/attachInterrupt/

Why are you thinking it is an Arduino problem? I have disconnected it from Raspberry PI. The Arduino has nothing to with this interrupt problem, It is handled by the Python code only.

The interrupt code you pasted can’t be your actual interrupt code because it has errors:

    while x <= 10:
     if not (GPIO.input(pushButton_Pin)):
#            x = +1
      else:
             x = 0
       sleep(0.01)

There’s only a comment, not a statement, after the if, and indentation of the else and sleep(...) don’t line up with any of the preceding lines.

The commented-out line of code is a paste mistake. That while loop is to ensure the the push button is not pressed for about 0.5 seconds.

I reviewed your original post. Here is my feedback:
-------------------------------------------------------------------

Reviewing your SmackBottomSelected() function code from your original post (you state that it “is the main function of the app”), you have the following configuration set-up code defined twice:

    GPIO.add_event_detect(pushButton_Pin , GPIO.RISING,
                        callback = SmackAbort_Function,
                        bouncetime = 100) 

Configuration code should only be stated once and not multiple times. In the first instance, you define the callback function as SmackAbort_Function and in the second instance, you define the callback function as Push_Button_Function.

Then, in the body of the Push_Button_Function, you once again call the configuration set-up code. This time, however, the callback function is itself:

GPIO.add_event_detect(pushButton_Pin,GPIO.RISING,
                    callback = Push_Button_Function,
                    bouncetime = 100) 

Recommendations:

  1. You should only define and call the configuration code once - for the entire script.
  2. You should not be redefining the callback function.

Can you provide the code where the Push_Button_Function function is called? You have only provided the function (and its body) that is called in the event that an edge is detected on Pin 33 but you have not provided the conditional code that prompts this function to be called.

The print statements inside the body of this function only work to notify the user that the function is being called but it does not provide the reason why. For this, we need to move up one level and understand the set-up code or condition for which this function is called.

What is: :smiley:? Is this even valid?

Additional Review:
I was reviewing one of your earlier posts:

Note how you have the push button pin set up as pin 37 in your previous set-up. But in this post, you have it as:

Note that Pin 37 -> GPIO26 and Pin 33 -> GPIO6. Can you verify your connections and that the SW pin setup configuration matches the physical HW connections. I am making the assumption that this is the same project and set-up. If not, then disregard.

The push button has 2 different operations. The user can select to run multiple tasks. When not running multiple tasks, and the push button is pressed, the code does a single task. Now, when the user selects multiple tasks, I change the push button interrupt to call SmackAbort_Function instead of Push_Button_Function by using the remove event statement.

GPIO.remove_event_detect(pushButton_Pin)

Then I reenable the push button interrupt to call the SmackAbort_Function>>

GPIO.add_event_detect(pushButton_Pin , GPIO.RISING,
                        callback = SmackAbort_Function,
                        bouncetime = 450) 

When the multiple functions have finished, I change the interrupt back to the push button function.

I do this in the SmackBottomSelected function>>>

def SmackAbort_Function(channel):
    global Abort_Flag;
    x = 1   
#Debut Point
    print("In Smack Abort Function");
    
    if Abort_Flag:
 
 #Debug Point
        print("Abort Flag already set to true returning")
        
        return
#Debug Point
         #Debug Point
    print("Seting Abort Flag to true");
        
    Abort_Flag = True
    ClickBuzzer()
    GPIO.remove_event_detect(pushButton_Pin)
   
#Wait to make sure the button is release
    while x <= 25:
        if not (GPIO.input(pushButton_Pin)):
            x += 1
        else:
            x = 0;
        sleep(0.025);
        
#Debut Point
    print("Existimg Smack ABORT Function");

def SmackBottomSelected():

    global hitNum
    global Abort_Flag
#    global IntensitySelect_Pins
	
    if SmackBottom_Button.cget("text") == "Stop":
        print("Smack Bottom Stop Selected")
        Abort_Flag = True
        GPIO.output( Abort_Out_Pin, True )
        return;
    else:
        print("Smack Bottom Start Selected")	
 
# Update GUI Elements
    SmackBottom_Button.configure(text = "Stop")
    SmackBottom_Button.configure(bg = "red")

# Set LED off
    Set_Ready_LED_Off()	

    Disable_Buttons()

#Debug point
    print("Disabling the push button interupt")
    
#Next disable the button interrup so we do not have a interrupt while runing the function code
    GPIO.remove_event_detect(pushButton_Pin)

    ClickBuzzer()

# Update Spank Delay
#	SmackDelay_EntryBox.get is in Seconds floating number so we need to convert the floating number to
#	INT becuase the Sleep function paramter is an int number of milimSeconds (1000mS = 1 Second) 
    if SmackDelay_EntryBox.get() == "":
        SmackDelay_Val = 0.5 #Delay half a second
    else:		
        SmackDelay_Val = float(SmackDelay_EntryBox.get())
        
    if SmackDelay_Val <= 0:
        SmackDelay_Val = 1.0

#Debug Point
    print(f"	Smack Delay Val = {SmackDelay_Val}mS")


    # Update Number of impacts
    if NumberOfSmack_EntryBox.get() == "":
        NumberOfSmack_Val = 1
    else:	
        NumberOfSmack_Val = int(NumberOfSmack_EntryBox.get())

#Debug Point
    print(f"NumberOfSmack_Val = {NumberOfSmack_Val}")

#	if button_override != None:
#		NumberOfSmack_Val = 1
#		SmackDelay_Val = 1

    intensity = intensity_options.get() # Get Currently Selected Intensity string

#Debug Point
    print('Intensity >> ' , intensity)
    
    #OK now set up the stepper motor driver/controler with proper swig intensity 
    GPIO.output(IntensitySelect_Pins,Task_Num[intensity_options.get()]) 

#Debug Point
#	print('	Smack Bottom Selected -->> IntensitySelect_Pins >> ' , (GPIO.input(IntensitySelect_Pins)))
    
    Tk.update(root)
    
#Debug Point
    print("Enableing the abort interupt")
    GPIO.add_event_detect(pushButton_Pin , GPIO.RISING,
                        callback = SmackAbort_Function,
                        bouncetime = 450) 
    
    for hitnum in range(1,NumberOfSmack_Val+1):
        if Abort_Flag:
            break

        # Debug Point
        print(f'Smack Bottom Selected ==>> Hit Number= {hitnum}')
        
        statusbar.config(text=f"Status: Smack Number > {hitnum} || {NumberOfSmack_Val}")

        Run_Task()

        root.update()

        sleep(SmackDelay_Val)

    if Abort_Flag:
        statusbar.config(text="Spanking Aborted after %s! >> Ready..." %(hitNum - 1))
        while GPIO.input(pushButton_Pin): # Wait for user to release the Abort button
            pass
    else:
        statusbar.config(text="Spanking Concludedd || Smacks %d" % (hitNum - 1))

    Set_Ready_LED_On()
    SmackBottom_Button.configure(text = "Smack Bottom")  	
    hitNum = 1
    GPIO.output(Run_Pin, False)
    GPIO.output( Abort_Out_Pin, False)
    Abort_Flag = False
    
#Debug Point
    print('')
    print(' Spanking Completed')
    GPIO.output( Abort_Out_Pin, False)

    Restore_Buttons()


# Spanking Complete
    Set_Ready_LED_On()
    hitNum = 1
    Restore_Buttons()

    statusbar.config(text="Status: Spanking Complete >> Ready...")
    SmackBottom_Button.configure(text = "Smack Bottom")
    
#Next disable the all interrupt so we do not have a interrupt while runing the function code
    GPIO.remove_event_detect(pushButton_Pin)

#Debuug Point
    print(' Renabling the push button interrupt')
    
    GPIO.add_event_detect(pushButton_Pin , 
                          GPIO.RISING , 
                          callback = Push_Button_Function, 
                          bouncetime = 450) 	

#Debug Point
    print('Exiting Smack Bottom Selected')
    print('==============================')
    print('')
        
#********************************************

I set up the push button interrupt in the Finish_Alignment function >>>

def Finish_Alignment():
    global Stop_LED_Flashing

# Debug Point
    print("************************")
    print("Finish_Alignment Function")

    FinishAlignment_Button.configure(bg = "#f0f0f0")
    FinishAlignment_Button.configure(text = "Returning Home...")	
    FinishAlignment_Button.config(state="disabled")
    
    statusbar.config(text="Status: Please Wait...")	
    
# Update window size
    FinishAlignment_Button.update()
    app_width = FinishAlignment_Button.winfo_width() + 25
    root.geometry(f'{app_width}x{app_height}+{int(x)}+{int(y)}')	
    
    Tk.update(root)	

    print(" Finish_Alignment >> Telling controler the return back to home postion")
# Tell controler to excute the Finish Alignment task
    GPIO.output(IntensitySelect_Pins,Task_Num ['Finish_Alignment']) 

    Run_Task() #Tell logic controler to excute task

# Hide Screen Elements
    FinishAlignment_Button.grid_forget()
    toytype_Label.grid_forget()
    toytype_drop.grid_forget()
    orientation_Label.grid_forget()
    Orientation_drop.grid_forget()	

    SmackIntensity_Label.grid(column=0, row = 0, sticky=W)
    intensity_drop.grid(column = 1, row = 0, padx = 
        10, pady = 10, sticky='w')

    NumberOfSmack_Label.grid(column = 0, row = 1, sticky=W)	
    NumberOfSmack_EntryBox.grid(column = 1, row = 1, padx = 10, pady = 10)	
    
    SmackDelay_Label.grid(column=0, row = 2, sticky=W)
    SmackDelay_EntryBox.grid(column = 1, row = 2, padx = 10, pady = 10)	
    
    SmackBottom_Button.grid(column = 0, row = 3, columnspan=2, padx=10, pady=10, sticky = "WE")	

    statusbar.config(text="Status: Ready...")

    # Update window size
    SmackBottom_Button.update()
    app_width = SmackBottom_Button.winfo_width() + 25
    root.geometry(f'{app_width}x{app_height}+{int(x)}+{int(y)}')
    
#>>>Setup the Push_Botton_Pin to dected rising
#>>>There was a change on pushButton_Pin so call the Push_Button_Function 
#>>Set deboune to 0.075 seconds the signal so we do not have a false triggering of the function
    GPIO.add_event_detect(pushButton_Pin , 
                          GPIO.RISING , 
                          callback = Push_Button_Function, 
                          bouncetime = 450) 	
# Stop RDY LED fashing	
    Stop_LED_Flashing = True

# Now set RDY LED on
    Set_Ready_LED_On()

    #Debug Point
    print("Finish_Alignment Completed")
    print()
    print('*********************************')

Like I said, I only changed the interrupt call function in the smack bottom function

The code works without any errors ( including the interrupts) except for these false interrupts triggering, even when the push button is disconnected (pin with a 10K to GND.

If you want to see the entire app code, here is a Dropbox link Smacker Code

I still think that you should not be changing the configuration and designated callback function interrupt set-up code. This should only be done once, at the very beginning during the initialization of the script and just prior to the system loop. Once you configure your HW code, it should stay fixed. The only thing that you should be clearing related to the HW is the interrupt flag.

There must be a flag variable that keeps track of which type of selection the user has selected, right? If so, you can rewrite your script such that you pass the flag variable to the callback function. Based on its value, the corresponding function will be called:

You can do something like the following script (modify it to your specific requirements but understand the general jist):

SINGLE_TASK = 0
MULTIPLE_TASKS = 1

def smack_abort_function():
    print('Inside smack_abort_function.')

def other_function_etc():
    print('Inside other_function_etc.')


def button_function_call(tasks):

    if tasks == SINGLE_TASK:
        other_function_etc()   # Run the single task here

    elif tasks == MULTIPLE_TASKS:
        smack_abort_function() # Run multiple tasks here

# test the script - passing values to the callback function
button_function_call(0)
button_function_call(1)
button_function_call(1)
button_function_call(0)

When I run this script, I get the following results:

Inside other_function_etc.
Inside smack_abort_function.
Inside smack_abort_function.
Inside other_function_etc.

Doing it this way not only reduces the probability of introducing errors into your script, it simplifies it, and makes it easier to maintain.

Note that inside the callback function, the correct function will be executed based on the value of the variable flag. This way, you do not have to keep reconfiguring the set-up code for every rising edge detection event.

Just one additional note:
Ideally, this would work better if you programmed it in C as you would have direct access to the HW interrupt and thus you would not have to call the button interrupt function explicitly via polling since the interrupt would be called automatically as soon as it detects a valid rising edge as per your configuration.

Hope this helps.

I did not think of using a flag to tell the interrupt function which interrupt to use. Thank you, that’s a great suggestion.
I have a question, tho. How do I ensure I do not have an interrupt while in the interrupt function?
I do not know the C programming language. A friend did the GUI part of this app, and I do not know if he could change over to C.

Again, thanks for sharing your wisdom. :heart_eyes:

You could use the same solution: a flag.

Set a global flag at the start of the interrupt function and clear it at the end. If it’s already set when you enter the function, then you’re already running the interrupt function, so return immediately.

A race condition could still occur because if there were 2 rising edges very close together, the interrupt function could be triggered again between the time you check the flag and see that it’s clear and the time you set it, but that problem could occur already with your existing code - a second rising edge could occur between the time the function is triggered and the time the function removes itself from the event.

Maybe a little play on semantics here, but button_function_call IS your interrupt and the functions would be the desired tasks to execute … they are NOT interrupts.

You can’t (that’s why they’re called interrupts :wink:). However, you can mitigate this by assigning priority levels to the interrupts. If you read the documentation for your device, it should include a section that provides information regarding interrupt priority levels. There should be specific registers assigned for configuring the priority levels for each interrupt. You should audit your interrupts and assign each interrupt a priority level based on their significance (priority). Generally a 0 is the highest priority level with ascending integer values having lower priority levels. Read your device’s documentation for specifics and additional information.

The way it works is that if you are, say in a priority 5 interrupt, and an interrupt with level 2 comes online, then the program flow should automatically jump to the priority 2 interrupt (nested interrupt). It should stay in the level 2 interrupt unless a higher level interrupt is generated (say a 1 or a 0). Once it has completed its tasks, the program flow will return to the priority 5 interrupt and resume its task. If you are in an interrupt with priority level 1 and an interrupt with priority level 4 comes online, the level 1 priority interrupt will not branch out to the priority level 4 interrupt. An interrupt ONLY branches out for higher priority level interrupts but NOT the other way around.

It’s ok if you don’t do it in C. If you can do it in Python, then do it in Python.

Good luck!

OK, I have rewritten the code to use flags to tell the interrupt what task to do. It has been running for the past 20 hours without a false trigger. I will let it run 24/7 for at least a week. I will keep you all informed.

I cannot find any info about the interrupt level of the Raspberry PI / Python that you mentioned.

Awesome! It could have been that changing its configuration was either implemented incorrectly (what I meant by potentially introducing errors in a previous post) or the device behaves unexpectedly when doing so while its actively running. In any case, this is why it should only be performed once, at the very beginning. Thereafter, you can design your SW to flow according to design specifications / requirements. That’s the beauty of SW. It’s super malleable.

Ok, I just reviewed the datasheet for the Broadcom BCM2837 microcontroller (the core that the Rasberry Pi 3B board utilizes). It does not provide the option for the user to specify the priority levels for individual interrupts as other microcontrollers do. For example, the dsPIC33EP does allow you to do this as per the screenshot of its datasheet.

I just found a webpage that explains interrupts using the sys library. Here is a link to it Handling and confirming (interrupt) signals in Python

I have a problem, tho. It does not show how to assign a GPIO pin to the interrupt.

Maybe someone knows there how to assign the GPIO pin.

The interrupt problem is back.
Here is a picture of the test jig and installed in the enclosure. I do not get any false triggering with the test jig. Installed the problem reappears
.

What puzzles me the most is that when I connect my DVM to the interrupt test point, I do not get any false triggers! Everythings works as designed. Remove the DMV false triggering returns. :unamused_face: :unamused_face:

I hope someone has a suggestion on what the problem could be.

Ok, so stand alone the board does not generate an interrupt. Good! It is working as expected. If you connect the external board, then this causes an interrupt. This might also be good as this is perhaps what you want. That is the purpose after all of interrrupts … to be triggered by some external event or a synchronized event (i.e., timer, etc.). What I would then suggest is, if you have an oscilloscope, inside the interrupt, toggle an I/O pin (toggle high on entering and toggle LOW on exit). Connect a probe to the pins that are of interest that are connected between your main board and the external board. Set the probe connected to the I/O pin in the interrupt to trigger on rising edge. You’ll have to relatively slow down the timing setting to catch it properly (you will have to play around with the timing setting as it is dependend on the general frequency that you are working with). This will detect when it is triggered (when the pin goes HIGH). This will catch which pin is triggering your board.

If you have multiple pins, you will have to test this multiple times to see which pin is triggering your board.

An example of what the interupt might look like (the monitoring event pin might stay HIGH instead of immediately toggling LOW as shown here):

Notice how the I/O pin event happens AFTER the external pin event. Thus it is being triggered by the external event.