Pololu Robotics & Electronics
Menu
My account Comments or questions? About Pololu Contact Ordering information Distributors

Pololu Forum

X-y joystick and two servos


#1

Hi all!

This is the first time for me trying to write a script for my Micro Maestro 6ch.

I’ve done this many many times on the Arduino but now I’m stuck with the code.
I used to (on the duino) read the input from X and Y, smooth/average those readings and send the commands to each servo.

I guess it should be very easy to do with the Micro Maestro, but how? I’ve read the manual many times but I feel so dumb!

I figured out how to set both “input” and “servo”, and that is everything I’ve managed to do so far.

Any help would be really appreciated!


#2

Hello.

I suggest starting slow if you are unfamiliar with the Maestro scripting language, which is based on the Forth programming language. You might try writing simple code to use one axis of your joystick to control one servo. If your joystick outputs simple analog signals, you can start with the “Using an analog input to control servos” example in the “Example Scripts” section of the Maestro user’s guide. This example does not do any averaging, but it is a good place to start. You can try adding averaging using the mathematical commands found in the “Command References” section of that same user’s guide if you find it is necessary for your application.

If you try writing your own script to do this and run into problems, you can post it here along with a description of the problem and I would be happy to take a look.

Brandon


#3

Thank you Brandon;

here’s my attempt to get the values on both servos and it does indeed work as intended.
Read from 2 (x) and 3 (y) and move servos 0 and 1.

begin
  2 get_position
                           # I should do this channel average here, I guess...
  4 times 4000 plus
  0 servo
  3 get_position
                          # and the other channel here.
  4 times 4000 plus
  1 servo
repeat

I can not understand how to write a code to store and average both analog values… in C ++ I usually created an array and divided it by the number of readings. I read the guide but I can not make a logical sense, and honestly I’m ashamed of it!

Thank you again for your time!


#4

The general idea for taking an average is the same with the Maestro, except since you cannot store samples in an array, you store them on the stack. For example, a very simple way to do it would be to take 10 readings of channel 2 by calling 2 get_position ten times, then calling plus 9 times to add them all together, and get an average with 10 divide. Of course this is not a very efficient way of doing it and will only get less efficient if you want to average more samples. I wrote a quick subroutine to take the average as an example of what it could look like:

sub average

  dup rot  #this saves the number of samples to the bottom of the stack

  begin 1 pick while   #check the number of times it has looped
    dup get_position     #read channel and save it lower on stack
    rot 1 minus rot
  repeat
  drop drop            #once the channel is sampled, we can clear the unecessary looping info

  begin depth 2 greater_than while #add together the samples taken
    plus
  repeat

  swap divide   #divide by the number of samples taken

return   #return the average reading

To use this subroutine, you can add it to your script (outside of your BEGIN/REPEAT loop) and call it with the channel number and the number of samples you want to average (e.g. 2 20 average will take an average of 20 readings on channel 2). Please note that this subroutine uses the depth command, so it will not work correctly if there are other values on the stack when it is called (other than the channel number and number of samples). Also, keep in mind that values on the stack cannot exceed 32767 or they will overflow (and loop back -32768), so taking too many samples can cause problems if the sum of all of the sampled readings exceeds this limit.

There are probably more efficient or faster ways to do this, and I would still encourage anyone wanting to learn more about the Maestro scripting language to try writing their own version of a subroutine that does this. It is a good way to get a better understanding of the stack commands and how they work.

Brandon


#5

Ok I’m finally starting to understand how it work. It’s quite different from what I am used to.

I’ve been able to sample up to 28 times per channel without problem. This, in addition to a low pass filter, did what I wanted.

begin
  2 28 average
  4 times 4000 plus
  0 servo
  3 28 average
  4 times 4000 plus
  1 servo
repeat

sub average
  dup rot
  begin 1 pick while
    dup get_position
    rot 1 minus rot
  repeat
  drop drop
  begin depth 2 greater_than while
    plus
  repeat
  swap divide
return

It’s a real shame that the VREF pins on the PIC are not available. This would improve really the overall analog quality. Is it selectable? Or is it fixed with a cap to 5V?

Off-topic:

I’ve succesfully used an output for turning high and low a led. Is it possible to run a command on startup? I would like to see a light up led if the input is in a certain position.

Let me explain the code flow:

  • I turn on the device
  • if the thumbstick (the one I must/want to use is springless, hence no center return) is off the dead center (with a little hysteresis) the led stay off and the script doesn’t proceed (both servos stay off), no signal.
  • if the joystick if in the middle, the led turn high, and the script proceed.

Thank you!


#6

I am glad it worked for you!

The analog readings use the Maestro’s 5V voltage as their reference.

If you want to add a safe-start feature like you described to the start of your code, you can do a while loop (before your current BEGIN/REPEAT loop) that checks both inputs and does not proceed until they are within some acceptable neutral range. When a Maestro channel is configured as an output it will go high when the set target on that channel is greater than 1500 microseconds and low when it is less than 1500 microseconds, so if you want to have an LED indicate that when it is safely running, you can set the target of that channel low before the while loop and high afterward. If you try adding this safe-start feature and get stuck, you can post what you have so far here and I would be glad to take a look.

Brandon


#7

Thank you Brandon! I tried to add the function I was talking about, but I used an IF rather than a WHILE, because my goal is to stop the script completely if the thumbstick is off the “dead-center”, so I’m forced to turn off and restart the whole thing (I know it seems weird, but this would be the best option for my use).

I do not know if I did things properly, the script seems to start but the output does not go high. I’m pretty sure that this is a badly written code situation (step by step all points are highlighted, but nothing happens).

The averaging loop, however, works properly.

safestart:
ud_stick lr_stick logical_and
if
  6000 4 servo #led turn high
else
  32767 delay  #very long delay
endif

5000 delay   #normal delay before the loop start, not needed but I like it

begin
  2 28 average
  4 times 4000 plus
  0 servo
  3 28 average
  4 times 4000 plus
  1 servo
  middle_led  #subroutine for the led during normal use, so I can turn off the driver when the thumbstick is centered
repeat

sub average
  dup rot
  begin 1 pick while
    dup get_position
    rot 1 minus rot
  repeat
  drop drop
  begin depth 2 greater_than while
    plus
  repeat
  swap divide
return

sub ud_stick
  up_stick down_stick logical_and
  return

sub lr_stick
  left_stick right_stick logical_and
  return

sub up_stick
  2 get_position 550 greater_than
  return

sub down_stick
  2 get_position 500 less_than
  return

sub left_stick
  3 get_position 550 greater_than
  return

sub right_stick
  3 get_position 500 less_than
  return

sub middle_led  #this does only turn the led high so I can be safe before turning everything off
  ud_stick lr_stick logical_and
  if
   6000 4 servo
  else
   2000 4 servo
  endif
  return 

#8

It looks like your subroutines are checking if the input is outside of the neutral zone, then doing a LOGICAL_AND command with them, which should never be true since the input cannot be greater than 550 and less than 500 at the same time, so the command to set the output high will never run. I suspect it will work correctly if you swap the GREATER_THAN and LESS_THAN commands in the up_stick, down_stick, left_stick, and right_stick subroutines.

Also, the way you have it now (with an IF/ENDIF statement), the script will only check the inputs once, and if they are not within the neutral zone it will simply delay for 32 seconds and continue anyway. If you want the script to stop running completely, you can use the QUIT command. The Maestro will need to be power cycled (or reset in the software) before it will start again.

Brandon


#9

It does work as intended now! Maybe somebody could be interested in the code, so here it is:

500 delay #rise time for analog voltage
4000 5 servo #error led off
4000 4 servo #status led off

ud_stick lr_stick logical_and
if                 #if the thumbstick is centered, blink a couple of times and start the script
  6000 4 servo
  500 delay
  4000 4 servo
  500 delay
  6000 4 servo
  500 delay
  4000 4 servo
  500 delay
  6000 4 servo
  500 delay
  4000 4 servo
  500 delay
else                #otherwise turn on a red led and quit the script
  6000 5 servo
  quit
endif

begin
  2 28 average
  4 times 4000 plus
  0 servo
  3 28 average
  4 times 4000 plus
  1 servo
  middle_led
repeat

sub average
  dup rot
  begin 1 pick while
    dup get_position
    rot 1 minus rot
  repeat
  drop drop
  begin depth 2 greater_than while
    plus
  repeat
  swap divide
return

sub ud_stick
  up_stick down_stick logical_and
  return

sub lr_stick
  left_stick right_stick logical_and
  return

sub up_stick
  2 get_position 550 less_than
  return

sub down_stick
  2 get_position 480 greater_than
  return

sub left_stick
  3 get_position 550 less_than
  return

sub right_stick
  3 get_position 480 greater_than
  return

sub middle_led
  ud_stick lr_stick logical_and
  if
   6000 4 servo
  else
   2000 4 servo
  endif
  return

Just a couple of remaining questions:

  • Can I supply both vin (say 12v) and keep it plugged in via usb? this would be good for programming (since usb is 5v and the voltage drop caused by all the components would cause a lower voltage reference for analog values)
  • If I supply power via vin, the red status led blink 4 times, via usb it doesn’t. why?
  • in the code above, I used “get_position” in all the subroutines. this is ok and work, but It would be better to use the averaging subroutine. However, if I try to replace “2 get_position” with “2 5 average”, the script doesn’t start and I suspect an overflow issue. but why? isn’t the “drop drop” command supposed to clear those stacks?

Thank you again for your support and patience!


#10

Yes. It is safe to have an external power supply connected at the same time that USB is connected; in such cases the processor will be powered from the external supply.

When the Maestro is reset in some other way than being initially powered up, the red and/or yellow LEDs blink four times to indicate the reset condition. When the red LED blinks four times, this is due to a brownout reset. This occurs when the Maestro’s 5V line drops below about 3.0V, usually due to low batteries or an inadequate power supply.

Like Brandon mentioned, his average subroutine uses the depth command, so it will not work correctly if there are other values on the stack when it is called (other than the channel number and number of samples). It looks like this is happening in your ud_stick and lr_stick subroutines after up_stick and left_stick are called, respectively, where the result of those subroutines is on the stack when the average subroutine in down_stick and right_stick are called.

By the way, the first two answers to your questions can be found under the “Micro Maestro Pinout and Components” and “Indicator LEDs” sections of the Maestro’s user’s guide.

-Jon