Need Interrupt Help

Working on a Raspberry Python app that has a interrupt that not working as expected.
When I start the app (before calling the main function) the push button interrupt works great and as expected. When the main function is called and returns the push button interrupt stops working. In the main function, I change the interrupt callback from the push button function to the smack abort function (not working). Then, just before the main function returns, the callback changed back to the push button function
The main function works as expected.
I think I am not handling the callback function change correctly.

Callback change code

    GPIO.remove_event_detect(pushButton_Pin)

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

.
The following code is the main function of the app.

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")	
 
#    if Abort_Flag == True;
#        GPIO.output( Abort_Out_Pin, True )
#        return

# 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()) # Change from seconds to mil-seconds
        
    if SmackDelay_Val == 0:
        SmackDelay = 1

#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")
    
#Next disable the all interrupt so we do not have a interrupt while runing the function code
    GPIO.remove_event_detect(pushButton_Pin)

    GPIO.add_event_detect(pushButton_Pin , GPIO.RISING,
                        callback = SmackAbort_Function,
                        bouncetime = 100) 
    
    for hitnum in range(1,NumberOfSmack_Val+1):
        if Abort_Flag:
            break

        # Debug Point
        print(f'Smack Bottom Selected ==>> Hit Number= {hitnum}')

        Run_Task()

        statusbar.config(text=f"Status: Smack Number > {hitnum}")

        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 = 100) 	

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

This is the push button interrupt function

def Push_Button_Function(channel):

#	global override
#	override = 1
    x=1

#Debug Point
    print('>>>PUSH BOTTON DETECTED <<<')

    #Next disable the button interrup so we do not have a interrupt while runing the function code
    GPIO.remove_event_detect(pushButton_Pin)
    statusbar.config(text="Push Button Detected")
    Disable_Buttons()
    
#First turn off the Ready LED
    Set_Ready_LED_Off()

    print("Telling controler the intensity value")
    
    GPIO.output(IntensitySelect_Pins,Task_Num[intensity_options.get()])
    
    print("Calling Run Task")
    
    Run_Task()
    
    print(" Back in Push Button Function")

#Wait for push button to be realsed
    while GPIO.input(pushButton_Pin):
        pass
        
#    while x <= 10:
#        if not (GPIO.input(pushButton_Pin)):
#            x = +1
#        else:
#            x = 0
#        sleep(0.01)
        
    Set_Ready_LED_On()        

# Renabling the push botton interuot
    print(" Reinabling Push Buttom interupt")
    
# Renable push botton interrup so we do not have a interrupt while runing the function code
    GPIO.add_event_detect(pushButton_Pin,GPIO.RISING ,callback = Push_Button_Function,bouncetime = 100) 

    statusbar.config(text="Ready")
    
    Restore_Buttons()
    
    print("Push Botton function exiting")
    print("^^^^^^^^^^^^^^^^^^^^")

This is the abort interrupt 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
    else:

#Debug Point
         #Debug Point
        print("Seting Abort Flag to true");
        
        Abort_Flag = True;
        ClickBuzzer()
        GPIO.remove_event_detect(pushButton_Pin)
        GPIO.output( Abort_Out_Pin, True) # Tell logic controler to abort current task
        
# Wait for push botton to be realsed        
#        while GPIO.input(pushButton_Pin):
#           pass;

        while GPIO.input(pushButton_Pin):
            pass
#Wait to make sure the button is release
#        while x <= 20:
#            if not (GPIO.input(pushButton_Pin)):
#                x = +1;
#            else:
#                x = 0;
#            sleep(0.050);
	
# Smack         	
# Renable push botton interrup so we do not have a interrupt while runing the function code
#        GPIO.add_event_detect(pushButton_Pin ,
#                          GPIO.RISING ,
#                          callback = Push_Button_Function,
#                          bouncetime = 100) 
        
#Debut Point
    print("Existimg Smack ABORT Function");

Thank you for sharing your wisdom :smiley: :smiley:

Interrupts should be fast. They shouldn’t do a lot of work themselves.

What does Run_Task() do? If it starts a task, that might be OK, but if it takes a while, that’s not OK.

I have doubts about whether you should be removing and adding interrupt handlers inside an interrupt handler, and also having a busy wait loop in there.

Have a handler for when the button is depressed and a handler for when the button is released, and don’t keep adding and removing them.

I am working on a stepper motor project. For proper stepper motor operation I need uS timing which Python can not provide. So I have a driver board with a logic controller ( Adafruit ItsyBitsy 32u4 - 3V) to provide the required timing. The Python app sets up the required info by the GPIO pins. Once all the pins are set, it then sets the run pin high telling the logic controller to run the task (Run_Task function). When the logic controller sees the run pin is high it turns on the busy pin, telling the Python code it is busy running a task. Then it decodes the other pins into stepper motor commands. When the logic control has finished, it returns the busy pin low. I had to make the busy on time at least 100mS, so Python has enough time to react. Shorter than 100mS, Python does not see the busy signal, causing Python to freeze.

How would you do the required interrupts using a single GPIO pin?

def Run_Task():

   global DIR_Pin
   global Run_Pin
   global DIR_Value
  
# Debug Statement
#    print("Telling controller to run the task")
#    print("Run Task || Toy Sel >> " , Toy_Sel, "||| Dir >> ", GPIO.input(DIR_Pin)," // ", DIR_Value)
# Tell controller to execute the task
    GPIO.output(Run_Pin, True) 

#    print("Wait for Busy Pin to go true")
# Wait until stepper controller has started the task
    while(not GPIO.input(Busy_Pin)):
        pass
    
#    print("Wait for Busy Pin to go false")
# Now wait until stepper controller has finished the task	
    while(GPIO.input(Busy_Pin)):
        pass

# Control has finished so return Run pin to false to tell the controller the PI has 	
    GPIO.output(Run_Pin, False)	
    Stop_LED_Flashing = True

# Debug Statement
    print("Run Task function >>> Controller comleted task Retuning")

Hello,

The comment above states: SmackDelay_EntryBox.get is in Seconds and that you want to convert it to milliseconds. How could it be in “seconds” if it is being compared to the value of an empty string? Did you mean to have it compared to zero (“0”)? I think that this conditional statement needs clarification. Btw, that is not what it is doing. If it is a value other than an empty string, then it would convert it to type float (example, type int or type string str as in '5.6`, 3, 2, etc.).

If you do want to convert a seconds value to milliseconds, multiply by 1000 and then convert the result to type int to have whole numbers without trailing numbers after the period. Here is an example:

# Convert seconds to milliseconds (mult by 1000 then convert the result to int)
num_second = 5.025255     # value in seconds
mill_s = num_second * 1000
print(mill_s)
mill_int = int(mill_s)    # value in milliseconds
print(mill_int)

>>> 5025.254999999999
>>> 5025

Next,

This isn’t “C” so semicolons are not needed (check elsewhere in script as well).

Both of the following functions have a parameter named channel but I could not find where it is being referenced within their body statements (check):

def Push_Button_Function(channel):
def SmackAbort_Function(channel):

In the SmackAbort_Function function, you have a return statement in the if conditional at entry if it is True. This would make sense if other if statements were present or additional code below the corresponding else conditional. However, you only have one else conditional statement present. If the if conditional statement was true, then the else would not be executed (ignored) and thus no need for the return statement since the else would be skipped anyway and exit the function immediately.

Please review your script carefully and see where it may be cleaned up.

The comment at the end of the SmackDelay_Val = float(SmackDelay_EntryBox.get()) is misleading ( brain fart). I have deleted that comment. SmackDelay needs to be in seconds.

Abort_Flag = True; is a typo. simecollon removed.

Your right, I do not need the else statement(corrected and redid code). I do not know what I thinking at the time. :flushed: :flushed:

As for the channel parameter. I ask in the forum about it, here is their response.

Is your question about the Raspberry Pi and its RPi.GPIO library?

If yes, the channel argument refers to the pin number, using either physical or BCM numbering depending on how you called setmode().

I almost forgot about this one:

Here, it appears that you are creating a new variable. Is this intentional? I did a search for this variable in all of the functions that you provided but could not find where it is being used. Did you mean:?

    if SmackDelay_Val == 0:
        SmackDelay_Val = 1

Thank you for pointing out my bad typing. :face_with_peeking_eye: Yes, it should be SmackDelay_Val

Back to the interrupt problem. How would you do them?

If you were expecting a value of 1 somewhere in your script, but it was still a 0, then it could keep your code from working as expected (a bug for sure). So, bad typing is not just bad typing as it can have adverse consequences for the expected behavior of your script - not something to be taken lightly.

For this, you need feedback loop(s) for closed-loop motor control - PID, 2-nd order filter, etc., to have dependable and accurate motor control (this way the output follows the reference for tight tracking). Without them, you have open-loop control which is not too great.

Just an fyi, this is related to Control Theory. If you think that you will be working with projects where you need to track a control variable (i.e., PWM duty cycle - voltage/current), I would brush up on Control Theory in general as the concepts will come in handy. Then apply those concepts into careful code design to properly control the speed of your motors.

What does the manual say for this project? Is their related documentation or reference material for the Arduino that you’re using? Are there examples that you can share?

The operation of the motor is done by a DRV8825 controlled by the logic controller (step and direction) on the driver board. I designed the driver board and it works great.

Yes, the driver board is the interface between your microcontroller and the MOSFETs / IGBTs FETs switching devices that you’re using (since the microcontroller is unable to do so). That is, they drive the high voltage signals at the gates of the solid state switching devices.

If you have no load (no stress applied to the motor), then yes, it would work as expected when operating at a constant speed. It is when you make changes to the speed or when you apply a load when the performance is most noticeable.

Btw, I am referring to a SW designed feedback loop. There are also HW designed feedback loops. Perhaps the boards come with a HW designed feedback loop?

It works with a load and no load.
The driver chip has all that inside the chip. If you need more info, please take a look at its data sheet.

… referring to the documentation for interrupts. Are there any examples provided that you can reference for proper set-up?

I do not understand. What examples are you talking about.

Generally, reference designs come with both schematics and reference SW to get the user started and up to speed.

Check Youtube.

I have been running the code since my last post, 24/7, with no errors until today. Then the push button interrupt gets false triggers. I removed all the connections, and I’m still getting false triggers. Which tells me these false triggers is something in the Python code.

Here is the interrupt code>>

pushButton_Pin = 33

GPIO.setup(pushButton_Pin, GPIO.IN)


def Push_Button_Function(channel):

#	global override
#	override = 1
    x=1

#Debug Point
    print('>>>PUSH BOTTON DETECTED <<<')

    #Next disable the button interrup so we do not have a interrupt while runing the function code
    GPIO.remove_event_detect(pushButton_Pin)
    statusbar.config(text="Push Button Detected")
    Disable_Buttons()
    
#First turn off the Ready LED
    Set_Ready_LED_Off()

    print("Telling controler the intensity value")
    
    GPIO.output(IntensitySelect_Pins,Task_Num[intensity_options.get()])
    
    print("Calling Run Task")
    
    Run_Task()
    
    print(" Back in Push Button Function")

#Wait for push button to be realsed
    while GPIO.input(pushButton_Pin):
        pass
        
    while x <= 10:
     if not (GPIO.input(pushButton_Pin)):
           x = +1
     else:
             x = 0
       sleep(0.01)
        
    Set_Ready_LED_On()        

# Reinabling the push button interrupt
    print(" Reinabling Push Buttom interupt")
    
# Reenable push button interrupt so we do not have a interrupt while runing the function code
    GPIO.add_event_detect(pushButton_Pin,GPIO.RISING ,callback = Push_Button_Function,bouncetime = 450) 

    statusbar.config(text="Ready")
    Restore_Buttons()
    
    print("Push Botton function exiting")
    print("^^^^^^^^^^^^^^^^^^^^")
#==================================

def Run_Task():
    
    global DIR_Value
    
# First start the LED flashing thread
    Stop_LED_Flashing = False
#	Ready_LED_Flashing = Thread(Set_Ready_LED_Flashing())
#	Ready_LED_Flashing.daemon = True
#	Ready_LED_Flashing.start()
# Debug Statement
#    print("Telling controller to run the task")
#    print("Run Task || Toy Sel >> " , Toy_Sel, "||| Dir >> ", GPIO.input(DIR_Pin)," // ", DIR_Value)
# Tell controler to excute the task
    GPIO.output(Run_Pin, True) 

#    print("Wait for Busy Pin to go true")
# Wait until stepper controler has started the task
    while(not GPIO.input(Busy_Pin)):
        pass
    
#    print("Wait for Busy Pin to go false")
# Now wait until stepper controler has finish the task	
    while(GPIO.input(Busy_Pin)):
        pass

# Control has finshed so return Run pin to false to telling the controller the PI has 	
    GPIO.output(Run_Pin, False)	
    Stop_LED_Flashing = True

# Debug Statement
    print("Run Task function >>> Controller comleted task Retuning")

#**********************************************


So, how would you approach tracking down what the problem is?

Thank you for sharing your wisdom. :smiley: :smiley:

SInce your SW is set-up to read an input on this pin, leaving it floating may have unexpected results. First try tying a 10k resistor from this pin to ground and try again for testing purposes to see if you still observe false triggering.

OK, I did as you suggested, and still getting false triggers.

How do you know that it is generating a false trigger? Is there an indicator (i.e., an LED) or if you are running in debug mode that prints a statement to the console via a print statement inside the interrupt function? How do you know? I need to catch up and understand what you are observing.

If you look at the code in my last post, you will see there are several debugging print statements.
That is the way I know that I am getting false triggers.