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
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.
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.
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
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.
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.
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