Maestro 6 channel Subroutine Overflow 0x0080 error

Hi there
I’ve done some reading about this error but I can not find the solution to my script. Probably because I am new to this and I do not understand enough about programming in general :slight_smile: What i do understand is that the 6 channel maestro is limited to 10 subroutines and my subroutines and stack continue to add up as the script is stepped through. I do not understand how to write the script so that is drops each subroutine as it finishes it. Could it be that I just need to upgrade to a 12, 18 or 24 channel maestro. Can someone help me?
Here is my script to my over engineered Sofa Beer Fridge. I have tried to write some #notes so that you understand what I’m trying to do.
thanks Justin

# SOFA BEER FRIDGE CODE
main_loop: 
begin 
start_sequence
repeat quit  
 
### Sequence subroutines: ###
 
sub start_sequence
wait_for_button_closed_10ms
500 3986 3986 3986 frame_3..5 # Frame 0 (ensure all motor switches to open)  
1 get_position 500 greater_than if load_beer else normal_deliver endif  #check proximity sensor if beer is in the delivery draw
return quit

sub load_beer
500 8000 frame_5 # Frame 5 start despencing motor (ready beer in delivery draw)  
2000 delay
1 get_position 500 less_than if normal_deliver endif  # waiting for proximity sensor low (detect beer in draw) 
2000 delay
1 get_position 500 less_than if normal_deliver endif
2000 delay
1 get_position 500 less_than if normal_deliver endif
2000 delay
1 get_position 500 less_than if normal_deliver endif
2000 delay
1 get_position 500 greater_than if 500 3986 frame_5 endif  #If fridge empty, after 10 sec sequence cancelled (There has to be a better way than this)   
start_sequence
return quit

sub normal_deliver
500 3986 frame_5 
wait_for_button1_closed_10ms
500 8000 frame_3# Frame 1 start draw deliver  
check_draw
return quit


sub check_draw
2000 delay
1 get_position 500 greater_than if beer_removed endif
2000 delay
1 get_position 500 greater_than if beer_removed endif
2000 delay
1 get_position 500 greater_than if beer_removed endif
2000 delay
1 get_position 500 greater_than if beer_removed endif
2000 delay
1 get_position 500 less_than if beer_remain endif
return quit
 
sub beer_remain  
500 3986 frame_3 # Frame 2 stop draw deliver 
1000 delay  
500 8000 frame_4 # Frame 3 start draw return
wait_for_button2_closed_10ms
500 3986 frame_4 # Frame 4 stop draw return
start_sequence
return quit


sub beer_removed
500 3986 frame_3 # Frame 2 stop draw deliver
1000 delay  
500 8000 frame_4 # Frame 3 start draw return
wait_for_button2_closed_10ms
500 3986 frame_4 # Frame 4 stop draw return 
1500 delay 
500 8000 frame_5 # Frame 5 start beer ready  
wait_for_button1_closed_10ms
500 3986 frame_5 # Frame 6 
start_sequence
return quit
 
 
sub frame_3..5
  5 servo
  4 servo
  3 servo
  200 delay 
  return quit

sub frame_3
  3 servo
  200 delay
  return quit

sub frame_4
  4 servo
  200 delay
  return quit

sub frame_5
  5 servo
  200 delay
  return quit


#START SWITCH

# Wait for the button to be pressed for at least 10 ms.
sub wait_for_button_closed_10ms
  get_ms
  begin
    # reset the time on the stack if it is not pressed
    0 get_position 500 less_than
    if
      get_ms over minus 10 greater_than
      if drop return endif
    else
      drop get_ms
    endif
  repeat quit


#PROXIMITY SWITCH
 
# Wait for the button to be pressed for at least 10 ms.
sub wait_for_button1_closed_10ms
  get_ms
  begin
    # reset the time on the stack if it is not pressed
    1 get_position 500 less_than
    if 
      get_ms over minus 10 greater_than
      if drop return endif
    else
      drop get_ms
    endif
  repeat quit

#LIMIT SWITCH

# Wait for the button to be pressed for at least 10 ms.
sub wait_for_button2_closed_10ms
  get_ms
  begin
    # reset the time on the stack if it is not pressed
    2 get_position 500 less_than
    if 
      get_ms over minus 10 greater_than
      if drop return endif  
    else
      drop get_ms
    endif
  repeat quit

Hello.

Note that the 6-channel Maestro can handle 10 levels of recursion (e.g. subroutines calling other subroutines); it is not limited to only 10 subroutines. This should be more than enough to do what you need based on your script, so you should not need to upgrade to a 12-, 18-, or 24-channel Maestro for that reason.

There are essentially 2 problems with now your script is currently set up. One is that extra values are getting left on the stack, which will eventually cause it to overflow. The second is that your subroutines are calling the start_sequence subroutine instead of returning back to it (eventually causing the error when it reaches more than 10 levels of recursion).

Skimming through your script, it looks like a few of your subroutines have hard coded delays, but you are still passing them a value when you call them, causing an extra value to be left on the stack that the subroutine never uses. This seems to be the case for frame_3..5, frame_3, frame_4, and frame_5. For example, frame_3 looks like this:

sub frame_3
  3 servo
  200 delay
  return quit

So when you call it, all you need to do is put the desired target value of servo 3 on the stack, before calling it. It will then move servo 3 to that target and delay for the hard-coded 200ms. However, when you call it, you seem to be putting 2 values on the stack: 500 followed by your set target value (e.g. 500 8000 frame_3 in normal_deliver), so after moving the servo to the set target value and delaying for 200ms, 500 is still there. Depending on how you want it to work, you could either remove the extra 500 when you call these subroutines, or remove the 200 before the delay in the subroutines, which would require you to specify the delay every time you call them.

As far as the recursive subroutine layers, I suggest removing the start_sequence call in your load_beer, beer_remain, and beer_removed subroutines. The return command will send the script back to where the subroutine was called from. This might cause some unwanted behavior from your check_draw subroutine, but that can get handled by rethinking how that subroutine works. If that does happen and you would like some help getting it to work how you want, could you explain how you want it to work compared to how it is currently working and post an updated version of your script?

Brandon

Hi Brandon,
I’m finally back on this project. I have implemented some of your suggested changes. I however after rewriting the script, hit a wall. I have no idea how to program a time out loop. As you will see in the load_beer and normal_deliver subroutines. I have tried to write a subroutine where it will continuously check a state 1 get_position 500 less_than until a timer has counted down.
This is to wait for a button to be closed (if not) after 8 seconds it will continue the sequence but it needs to continuously check the state of the button during the 8 second timer. Are you able to guide me on how to write this code?

# SOFA BEER FRIDGE CODE
main_loop: 
begin 
wait_for_button_closed_10ms
500 3986 3986 3986 frame_3..5 # Frame 0 (ensure all motor switches to open)  
1 get_position 500 greater_than if load_beer else normal_deliver endif  #check proximity sensor if beer is in the delivery draw
repeat

### Sequence subroutines: ###

sub beer_remain  
500 3986 frame_3 # Frame 2 stop draw deliver 
1000 delay  
500 8000 frame_4 # Frame 3 start draw return
wait_for_button2_closed_10ms
500 3986 frame_4 # Frame 4 stop draw return
return quit


sub beer_removed
500 3986 frame_3 # Frame 2 stop draw deliver
1000 delay  
500 8000 frame_4 # Frame 3 start draw return
wait_for_button2_closed_10ms
500 3986 frame_4 # Frame 4 stop draw return 
1500 delay 
500 8000 frame_5 # Frame 5 start beer ready  
wait_for_button1_closed_10ms
500 3986 frame_5 # Frame 6 
return quit

sub load_beer 
500 8000 frame_5 # Frame 5 start despencing motor (ready beer in delivery draw) 
		begin
			1 get_position 500 less_than if normal_deliver endif
		while
			5000 delay  
			1 get_position 500 greater_than if 500 3986 frame_5 endif # Frame 6 
			return quit
		repeat     
	      
sub normal_deliver
500 3986 frame_5  
wait_for_button1_closed_10ms
500 8000 frame_3# Frame 1 start draw deliver 
		begin 
			1 get_position 500 greater_than if beer_removed endif
		while 
			5000 delay 
			1 get_position 500 less_than if beer_remain endif
			return quit 
		repeat
    
     
sub frame_3..5
  5 servo
  4 servo
  3 servo
  200 delay 
  return

sub frame_3
  3 servo
  200 delay
  return

sub frame_4
  4 servo
  200 delay
  return

sub frame_5
  5 servo
  200 delay
  return


#START SWITCH

# Wait for the button to be pressed for at least 10 ms.
sub wait_for_button_closed_10ms
  get_ms
  begin
    # reset the time on the stack if it is not pressed
    0 get_position 500 less_than
    if
      get_ms over minus 10 greater_than
      if drop return endif
    else
      drop get_ms
    endif
  repeat


#PROXIMITY SWITCH
 
# Wait for the button to be pressed for at least 10 ms.
sub wait_for_button1_closed_10ms
  get_ms
  begin
    # reset the time on the stack if it is not pressed
    1 get_position 500 less_than
    if 
      get_ms over minus 10 greater_than
      if drop return endif
    else
      drop get_ms
    endif
  repeat

sub wait_for_button1_open_10ms
  get_ms
  begin
    # reset the time on the stack if it is not pressed
    1 get_position 500 greater_than
    if 
      get_ms over minus 10 greater_than
      if drop return endif
    else
      drop get_ms
    endif
  repeat

#LIMIT SWITCH

# Wait for the button to be pressed for at least 10 ms.
sub wait_for_button2_closed_10ms
  get_ms
  begin
    # reset the time on the stack if it is not pressed
    2 get_position 500 less_than
    if 
      get_ms over minus 10 greater_than
      if drop return endif  
    else
      drop get_ms
    endif
  repeat

To have a script constantly check a button and only move on if the button was pressed or 8 seconds has passed, you will probably need to avoid any blocking code inside of the while loop. You can use the GET_MS command to put the current millisecond timer value on the stack, so this will allow you to do a simple subtraction with an IF statement to know when 8 seconds has gone by. As an example, here is a small script that does only this:

begin

    get_ms #get a reference time before entering the while loop
        begin
            1 get_position 500 greater_than while  #stay in the while loop unless the button is pressed
                dup #duplicate the reference time on the stack so you do not lose it
                get_ms  swap minus #get the current time and put it on the stack, then subtract it from the reference time
	8000 greater_than if   #compare the elapsed time to 8000ms 
                    drop  #if 8 seconds has passed, clear the reference time stored on the stack
                    goto skip   #and exit the while loop
                endif
         repeat
      skip:
  quit #this line is reached if the button is pressed or 8 seconds pass
  
repeat

If this example does not help you get the behavior you want, can you post an updated version of your script with a description of what it does when you try to run it?

Brandon

Hi Brandon
I tried your code but it did not work for me. I did however find a new way to do this. It seems like it clears the stack and operates as it should. Below is the new script. Can you see anything that would cause a failure?
Justin

# SOFA BEER FRIDGE CODE
main_loop:  
	begin 
	3986 3986 3986 frame_3..5 # Frame 0 (ensure all motor switches to open)  
wait_for_button_closed_10ms
	1 get_position 500 greater_than if load_beer else normal_deliver endif  #check proximity sensor if beer is in the delivery draw
	repeat

### Sequence subroutines: ###

sub load_beer 
8000 frame_5 # Frame 5 start despencing motor (ready beer in delivery draw) 
10	  	
		begin 
			dup 
			while 
			1 get_position 500 less_than if goto normal_delivery endif
			1000 delay
			1 minus
		repeat
drop
3986 3986 3986 frame_3..5 # Frame 0 (ensure all motor switches to open)   
return quit
 
normal_delivery:
drop
3986 3986 3986 frame_3..5
1000 delay
8000 frame_3# Frame 1 start draw deliver 
8
		begin 
			dup
			while 
			1 get_position 500 greater_than if goto skip_removed endif
			1000 delay
			1 minus
		repeat
drop 
3986 frame_3 # Frame 2 stop draw deliver 
1000 delay  
8000 frame_4 # Frame 3 start draw return
10	
		begin 
			dup
			while 
			2 get_position 500 less_than if goto skip_drawreturn endif
			1000 delay
			1 minus
		repeat
drop 
3986 frame_4 # Frame 4 stop draw return 
1500 delay 
8000 frame_5 # Frame 5 start despencing motor (ready beer in delivery draw) 
10		
		begin 
			dup 
			while 
			1 get_position 500 less_than if goto main_loop endif	
			1000 delay
			1 minus
		repeat
drop
3986 3986 3986 frame_3..5 # Frame 0 (ensure all motor switches to open)   
return quit


sub normal_deliver 
drop
3986 3986 3986 frame_3..5
1000 delay
8000 frame_3# Frame 1 start draw deliver 
8
		begin 
			dup
			while 
			1 get_position 500 greater_than if goto skip_removed endif
			1000 delay
			1 minus
		repeat
drop 
3986 frame_3 # Frame 2 stop draw deliver 
1000 delay  
8000 frame_4 # Frame 3 start draw return
10	
		begin 
			dup
			while 
			2 get_position 500 less_than if goto skip_drawreturn endif
			1000 delay
			1 minus
		repeat
drop 
3986 frame_4 # Frame 4 stop draw return 
1500 delay 
8000 frame_5 # Frame 5 start despencing motor (ready beer in delivery draw) 
10		
		begin 
			dup 
			while 
			1 get_position 500 less_than if goto main_loop endif	
			1000 delay
			1 minus
		repeat
drop
3986 3986 3986 frame_3..5 # Frame 0 (ensure all motor switches to open)   
return quit


 
	skip_removed: 
drop 
3986 frame_3 # Frame 2 stop draw deliver
1000 delay  
8000 frame_4 # Frame 3 start draw return
10	
		begin 
			dup
			while 
			2 get_position 500 less_than if goto skip_drawreturn endif
			1000 delay
			1 minus
		repeat
drop 
3986 frame_4 # Frame 4 stop draw return 
1500 delay 
8000 frame_5 # Frame 5 start despencing motor (ready beer in delivery draw) 
10		
		begin 
			dup 
			while 
			1 get_position 500 less_than if goto main_loop endif	
			1000 delay
			1 minus
		repeat
drop
3986 3986 3986 frame_3..5 # Frame 0 (ensure all motor switches to open)   
return quit


skip_drawreturn:
drop 
3986 frame_4 # Frame 4 stop draw return 
1500 delay 
8000 frame_5 # Frame 5 start despencing motor (ready beer in delivery draw) 
10		
		begin 
			dup 
			while 
			1 get_position 500 less_than if goto main_loop endif	
			1000 delay
			1 minus
		repeat
drop
3986 3986 3986 frame_3..5 # Frame 0 (ensure all motor switches to open)   
return quit

    
      
sub frame_3..5
  5 servo
  4 servo
  3 servo 
  return

sub frame_3
  3 servo
  return

sub frame_4
  4 servo
  return

sub frame_5
  5 servo
  return
 

#START SWITCH

# Wait for the button to be pressed for at least 10 ms.
sub wait_for_button_closed_10ms
  get_ms
  begin
    # reset the time on the stack if it is not pressed
    0 get_position 500 less_than
    if
      get_ms over minus 10 greater_than
      if drop return endif
    else
      drop get_ms
    endif
  repeat


#PROXIMITY SWITCH
 
# Wait for the button to be pressed for at least 10 ms.
sub wait_for_button1_closed_10ms
  get_ms
  begin
    # reset the time on the stack if it is not pressed
    1 get_position 500 less_than
    if 
      get_ms over minus 10 greater_than
      if drop return endif
    else
      drop get_ms
    endif
  repeat

sub wait_for_button1_open_10ms
  get_ms
  begin
    # reset the time on the stack if it is not pressed
    1 get_position 500 greater_than
    if 
      get_ms over minus 10 greater_than
      if drop return endif
    else
      drop get_ms
    endif
  repeat

#LIMIT SWITCH

# Wait for the button to be pressed for at least 10 ms.
sub wait_for_button2_closed_10ms
  get_ms
  begin
    # reset the time on the stack if it is not pressed
    2 get_position 500 less_than
    if 
      get_ms over minus 10 greater_than
      if drop return endif  
    else
      drop get_ms
    endif
  repeat

I do not see anything obvious that would cause a failure. However, your code has a lot going on, so I could have missed something. If you run into any errors or problems, you can post details and I’d be happy to help troubleshoot them.

Brandon