Debounce multimple switches

Goodmorning everyone,
I’m new to the mini maestro scripting language, but I think I’m progressing well…
My project has two servo that could be in oly two positions (let’s say full right, full left). Each servo could me moved by three buttons toggle, right, left.
I have therefore to debounce 6 switches by software.

My idea is to wait for all the switches to be “0 (not pressed )” and then check if some of the switch is pressed for at least 50 ms. If so, call the subroutine for the movement.
I copy the script below… when I trace the code executing one instruction at time, it’s fine. If I let the code run freely, the memory gets full in a while.
Does someone know the reason why? Some other better techniques for detecting/debouncing switches?
many thanks, Pietro

Main Loop:
The return after having checked the first button is for having to trace less instructions with the single line executin command

sub Wait_for_Some_Button_Pressed
		Check_All_Inputs_to_0

	#	Button1_Status		#00xxxxx1   1 Toggle 	 Clutch 1
			Channel_Button1 Input_Status
			if 
				Toggle_Clutch1
			endif
			drop
return
	#	Button2_Status		#00xxxx1x   4 Toggle 	 Clutch 1
			Channel_Button2 Input_Status
			if 
				Toggle_Clutch2
			endif

	#	RemoteIn1_Status		#00xxx1xx   8 Connect 	 Clutch 1
			Channel_RemoteIn1 Input_Status
			if 
				Connect_Clutch1
			endif

	#	RemoteIn2_Status		#00xx1xxx  16 Disconnect Clutch 1
			Channel_RemoteIn2 Input_Status
			if 
				Disconnect_Clutch1
			endif

	#	RemoteIn3_Status		#00x1xxxx  32 Connect 	 Clutch 2
			Channel_RemoteIn3 Input_Status
			if 
				Connect_Clutch2
			endif

	#	RemoteIn4_Status		#001xxxxx  64 Disconnect Clutch 1
			Channel_RemoteIn4 Input_Status
			if 
				Disconnect_Clutch2
			endif
return

Routine to check that the switches are not pressed:

sub Check_All_Inputs_to_0
		1802 # control constant to exit the "begin/repeat" loop below
		Channel_Button1 #Input switch 1
		Channel_Button2 #Input switch 2
		Channel_RemoteIn1 #Input switch 3
		Channel_RemoteIn2 #Input switch 4
		Channel_RemoteIn3 #Input switch 5
		Channel_RemoteIn4 #Input switch 6
		begin
			dup
			1802 not_equals
			if
			 	CheckAllZero
			else
				drop
				return
			endif
		repeat


sub CheckAllZero
			get_ms # put the current time on the stack
			begin
				# reset the time on the stack if it is pressed
				swap get_position 500 less_than
				if	
					drop get_ms
				else
					get_ms over minus 10 greater_than
					if 
						drop
						return 
					 endif
				endif

			repeat
return

Routine to check that the switch is kept pressd for at least 50 ms

sub Input_Status
        get_ms
			begin
				over get_position 500 less_than
				if
					get_ms over minus 50 greater_than	#The button has been kept pressed for 50 ms
					if
						drop
						1
						return
					endif
				else							#Button has been released
						drop
						0
						return
				endif
			repeat

return

Hello, Pietro.

I’m sorry you are having trouble. Please simplify your code as much as possible (does the problem happen with just one or two buttons?) Then please post your entire code here in one piece so I can see how the different pieces fit together and I can see how you defined your helper subroutines.

It sounds like your stack is overflowing or underflowing, so you should make sure that every value you add to the stack gets removed eventually.

–David

Actually I think I see the problem: I think what you want the “CheckAllZero” subroutine to do is take a channel number as an argument and wait until the button has been released for 10 milliseconds, then drop the channel number and return. When you do “swap get_position” you are accidentally consuming the channel number, so it won’t be available the next time the loop runs. Eventually this function will consume all the values on the stack and cause a stack underflow error. I think you need to do “over” instead of “swap”. It would help me understand your program better if you chose more appropriate names for your subroutines, wrote comments to document what they are supposed to do, and for the complex ones give an example of the stack before and after.

–David

Goodmorning David!
many thanks for your answer. [quote]I think what you want the “CheckAllZero” subroutine to do is take a channel number as an argument and wait until the button has been released for 10 milliseconds, then drop the channel number and return[/quote] That’s correct, it’s what I want to do.

[quote]When you do “swap get_position” you are accidentally consuming the channel number, so it won’t be available the next time the loop runs. Eventually this function will consume all the values on the stack and cause a stack underflow error[/quote]. Consuming the channel number is fine… very time I enter the function, I load the values on the stack. The problem is that if I let the maestro run, the stack goes overflow with zeroes after 8 “10”.
I’ll continue to check!
Have a nice one,
Pietro

#############################################################################################################
#																		#
#	REV 1.0 Two toggle push buttons												#
#																		#
#############################################################################################################
#
#
# Set the constants 
#		Const_Connect 
#		Const_Disconnect
#		Led1_Connect
#		Led1_Disconnect
# for defining the Connect and Disconnect position of the servo, and the LED ON/OFF status.

# Channel 00: Servo 1
# Channel 01: Button 1
# Channel 02: In 1
# Channel 03: In 2
# Channel 04: Out 1 LED1 Status
# Channel 05: Out 2 LED1 Remote
# Channel 06: Servo 2
# Channel 07: Button 2
# Channel 08: In 3 
# Channel 09: In 4
# Channel 10: Out 3 LED2 Status
# Channel 11: Out 4 LED2 Remote

goto Startup_Configuration


#-------------------------------------------------------------------------
#				CONSTANTS DECLARATION
#-------------------------------------------------------------------------
Sub Const_Version
		1
return

Sub Const_SubVersion
		0
return

sub Channel_Servo1
		0
return

sub Channel_Servo2
		6
return


Sub Const_Connect1_Position
		7550
return

Sub Const_Disconnect1_Position
		4850
return

sub Channel_LED1_Status
		4
return

sub Channel_LED1_Remote
		5
return

Sub Channel_Button1
		1
return

Sub Channel_Button2
		6
return

Sub Channel_RemoteIN1
		2
return

Sub Channel_RemoteIN2
		3
return

Sub Channel_RemoteIN3
		8
return

Sub Channel_RemoteIN4
		9
return

Sub Const_Connect2_Position
		7550
return

Sub Const_Disconnect2_Position
		4850
return

sub Channel_LED2_Status
		10
return

sub Channel_LED2_Remote
		11
return



##########################################################################
#------------------------------------------------------------------------#
#												 #
#	FUNCTIONs    FUNCTIONs    FUNCTIONS    FUNCTIONS    FUNCTIONs 	 #
#												 #
#------------------------------------------------------------------------#
##########################################################################

# LED LED LED LED LED LED LED LED
sub LED_Status1_ON
		8000 # LED ON
		Channel_LED1_Status servo
return

sub LED_Status1_OFF
		0000 # LED OFF
		Channel_LED1_Status servo
return

sub LED_Status2_ON
		8000 # LED ON
		Channel_LED2_Status servo
return

sub LED_Status2_OFF
		0000 # LED OFF
		Channel_LED2_Status servo
return

sub LED_Remote1_ON
		8000 # LED ON
		Channel_LED1_Remote servo
return

sub LED_Remote1_OFF
		0000 # LED OFF
		Channel_LED1_Remote servo
return

sub LED_Remote2_ON
		8000 # LED ON
		Channel_LED2_Remote servo
return

sub LED_Remote2_OFF
		0000 # LED OFF
		Channel_LED2_Remote servo
return

# CONNECT, DISCONNECT & TOGGLE
sub Connect_Clutch1
		Const_Connect1_Position Channel_Servo1 servo
		LED_Status1_ON
return

sub Disconnect_Clutch1
		Const_Disconnect1_Position Channel_Servo1 servo
		LED_Status1_OFF
return

sub Connect_Clutch2
		Const_Connect2_Position	Channel_Servo2 servo
		LED_Status2_ON
return

sub Disconnect_Clutch2
		Const_Disconnect2_Position Channel_Servo2 servo 
		LED_Status2_OFF
return

sub Toggle_Clutch1
		#This sub toggles the status of the servo 1	
		Channel_Servo1 get_position Const_Disconnect1_Position equals   	# check if it is equal to the disconnect position
		if
			Connect_Clutch1		
		else
			Disconnect_Clutch1
		endif

return

sub Toggle_Clutch2
		#This sub toggles the status of the servo 2
		Channel_Servo2 get_position Const_Disconnect2_Position equals 	# check if it is equal to the disconnect position
		if
			Connect_Clutch2		
		else
			Disconnect_Clutch2
		endif

return


#
##########################################################################
#------------------------------------------------------------------------#
#												 #
#      	BEGIN OF THE PROGRAM     ####     BEGIN OF THE PROGRAM       #
#												 #
#------------------------------------------------------------------------#
##########################################################################

#-------------------------------------------------------------------------
#				STARTUP CONFIGURATION
#-------------------------------------------------------------------------
Startup_Configuration:

# This is the start up configuration, both clutches connected. No remote commands
		Connect_Clutch1
		Connect_Clutch2
		LED_Remote1_OFF
		LED_Remote2_OFF

goto main_loop # Run the main loop when the script starts (see below).



sub Wait_for_Some_Button_Pressed
		Check_All_Inputs_to_0

	#	Button1_Status		1 Toggle 	 Clutch 1
			Channel_Button1 Input_Status
			if 
				Toggle_Clutch1
			endif

	#	Button2_Status		4 Toggle 	 Clutch 1
			Channel_Button2 Input_Status
			if 
				Toggle_Clutch2
			endif

	#	RemoteIn1_Status		8 Connect 	 Clutch 1
			Channel_RemoteIn1 Input_Status
			if 
				Connect_Clutch1
			endif

	#	RemoteIn2_Status		16 Disconnect Clutch 1
			Channel_RemoteIn2 Input_Status
			if 
				Disconnect_Clutch1
			endif

	#	RemoteIn3_Status		32 Connect 	 Clutch 2
			Channel_RemoteIn3 Input_Status
			if 
				Connect_Clutch2
			endif

#		RemoteIn4_Status	64 Disconnect Clutch 1
			Channel_RemoteIn4 Input_Status
			if 
				Disconnect_Clutch2
			endif
			drop
return


sub Check_All_Inputs_to_0
		#Load the stack with the numbers of the channeles to be checked. 1802 is a control constant to exit the routine
		1802 				#Control constant to exit the Begin/Repeat loop below
		Channel_Button1		#Channel for servo 1 toggle
		Channel_Button2		#Channel for servo 2 toggle
		Channel_RemoteIn1		#Channel for servo 1 Connect
		Channel_RemoteIn2		#Channel for servo 1 Disconnect
		Channel_RemoteIn3		#Channel for servo 2 Connect
		Channel_RemoteIn4		#Channel for servo 3 Disconnect

		begin				#Check all the inputs, when the stack value is 1802, exits the routine
			dup
			1802 not_equals
			if
			 	CheckButtonReleased # Routine to check that the button on the channel is released for 10 ms minimum
			else
				drop
				return
			endif
		repeat


sub CheckButtonReleased # This routine checks that the button is released for 10 ms minimum
			get_ms # put the current time on the stack
			begin
				# reset the time on the stack if it is pressed
				swap get_position 500 less_than
				if	
					drop get_ms
				else
					get_ms over minus 10 greater_than
					if 
						drop
						return 
					 endif
				endif

			repeat
return



sub Input_Status
        get_ms
			begin
				over get_position 500 less_than
				if
					get_ms over minus 50 greater_than	#The button has been kept pressed for 50 ms
					if
						drop
						1
						return
					endif
				else							#Button has been released
						drop
						0
						return
				endif
			repeat

return





Main_Loop:
begin
		Wait_for_Some_Button_Pressed
#		wait_for_button_press
#		Toggle_Clutch1
repeat

Hello, Pietro.

Consuming the channel number in your “CheckButtonReleased” (formerly known as CheckAllZero) function is fine, but your problem is that you consume the channel number in a loop so you really have no idea how many times the channel number will be consumed. Every time the loop runs, you consume another channel number from the stack. The loop could easily run 10 times and consume everything on the stack, leading to a stack underflow.

–David

Mmmm Thanks David.
I do not know how to do then…
What I have to do is simply read 6 switches (debouncing them) and call six functions…
Any smart advice?
Many many thanks!

Well how about you just fix that function by adding a few a few DUPs and/or DROPs in the right place? There is no reason you have to design that function to consume every number on the stack.

–David

Actually, do you really need debouncing? I just went back to your original post and reread what you were trying to do. It seems like bouncing would only cause a problem if it happens on a button that toggles the position of the servos. How about after toggling a servo, you just delay for, say, 250 milliseconds and then wait for the button to be released, and then delay for another 100 milliseconds? Would that be sufficient?

The basic structure of your code would be like this:

begin
  button_0_pressed if action0 end
  button_1_pressed if action1 end
  button_2_pressed if action2 end
  button_3_pressed if action3 end
  button_4_pressed if action4 end
  button_5_pressed if action5 end
repeat

–David