Changing channel modes

Hello,
Does a ROS node exist for the Maestro server board series?
If not would it be worth while to write one? as the boards seem fairly useful

Hello,

We are not familiar with ROS, and we do not know of anyone who has used it with our products. If you decide to try it, we would be interested to hear if you get it working.

- Kevin

I will be looking into it shortly
Currently behind on orders and my new lathe is still broken

I have written a ROS node in c++ that will listen to a topic for a custom msg type and send the appropriate commands to the mni maestro controller
1st byte is the channel
bytes 2&3 are the position (4000-8000)
Seems to work fine, even with multiple publishers

Now I need to get parameters and read data so I can use the ports for I/O
I don’t know how to do any of this, their are not many examples
Also I cant seem to find much info on configuring the ports, a lot of the examples are in c# and I cant understand it too well

Hello

I am writting a ROS node in c++ using libusb for the servo controller and I have a few questions

  1. What is the format for the byte when setting the channel modes?
    I assume its something like
    bit0 = channel 0 servo mode/io mode
    bit1 = channel 0 input/output

  2. What are the commands for reading and writing data from the channels?
    Do I just read the channel position or set a target acordingly?
    I have worked out how to read and write parameters, but not read the result from commands

  3. of which how do I read the channel position?

Hello,

I merged your last post with your earlier topic, since the other one contains important information about what you are doing.

  1. Are you using our USB SDK as a reference? I highly recommend getting it and looking through the C# code in Usc.cs. The file protocol.h should also be included by your C++ project. Also, what method are you using to send commands to the Maestro? It sounds like you are trying to send serial commands, which is almost certainly not the way you should be doing it if you are using libusb - you should be using our custom control transfers (shown in Usc.cs) instead. Channel configuration, in particular, is not accessible through the serial interface.

  2. Yes, that is what you do. You can also write speeds and accelerations, and you get to read back the target, as well. Again, you should be using the custom control transfer versions of the commands, as done in Usc.cs.

  3. Look at Usc.cs, in particular the getVariables() command which corresponds to an IN control transfer with request=0x83.

-Paul

For setting channel modes, you should look at setUscSettings in Usc.cs. This function takes a UscSettings object and converts it in to a series of setRawParameter calls. Each call to setRawParameter does a USB control transfer to set one of the Maestro’s parameters. This is the relevant section of that function:

            byte ioMask = 0;
            byte outputMask = 0;
            byte[] channelModeBytes = new byte[6]{0,0,0,0,0,0};

            for (byte i = 0; i < servoCount; i++)
            {
                ChannelSetting setting = settings.channelSettings[i];

                key.SetValue("servoName" + i.ToString("d2"), setting.name, RegistryValueKind.String);

                if (microMaestro)
                {
                    if (setting.mode == ChannelMode.Input || setting.mode == ChannelMode.Output)
                    {
                        ioMask |= (byte)(1 << channelToPort(i));
                    }
                    
                    if (setting.mode == ChannelMode.Output)
                    {
                        outputMask |= (byte)(1 << channelToPort(i));
                    }
                }
                else
                {
                    channelModeBytes[i >> 2] |= (byte)((byte)setting.mode << ((i & 3) << 1));
                }

                // Make sure that HomeMode is "Ignore" for inputs.  This is also done in
                // fixUscSettings.
                HomeMode correctedHomeMode = setting.homeMode;
                if (setting.mode == ChannelMode.Input)
                {
                    correctedHomeMode = HomeMode.Ignore;
                }

                // Compute the raw value of the "home" parameter.
                ushort home;
                if (correctedHomeMode == HomeMode.Off) home = 0;
                else if (correctedHomeMode == HomeMode.Ignore) home = 1;
                else home = setting.home;
                setRawParameter(specifyServo(uscParameter.PARAMETER_SERVO0_HOME, i), home);

                setRawParameter(specifyServo(uscParameter.PARAMETER_SERVO0_MIN, i), (ushort)(setting.minimum / 64));
                setRawParameter(specifyServo(uscParameter.PARAMETER_SERVO0_MAX, i), (ushort)(setting.maximum / 64));
                setRawParameter(specifyServo(uscParameter.PARAMETER_SERVO0_NEUTRAL, i), setting.neutral);
                setRawParameter(specifyServo(uscParameter.PARAMETER_SERVO0_RANGE, i), (ushort)(setting.range / 127));
                setRawParameter(specifyServo(uscParameter.PARAMETER_SERVO0_SPEED, i), normalSpeedToExponentialSpeed(setting.speed));
                setRawParameter(specifyServo(uscParameter.PARAMETER_SERVO0_ACCELERATION, i), setting.acceleration);
            }

            if (microMaestro)
            {
                setRawParameter(uscParameter.PARAMETER_IO_MASK_C, ioMask);
                setRawParameter(uscParameter.PARAMETER_OUTPUT_MASK_C, outputMask);
            }
            else
            {
                for (byte i = 0; i < 6; i++)
                {
                    setRawParameter(uscParameter.PARAMETER_CHANNEL_MODES_0_3 + i, channelModeBytes[i]);
                }
            }

Every Set Raw Parameter request tells the Maestro to modify part of its EEPROM. The EEPROM is only rated for 100,000 write cycles, so be careful to not call Set Raw Parameter thousands of times in a loop. It’s OK if you write the same value every time because the Maestro is smart enough to do nothing if the EEPROM already has the correct value, but if you alternate between two different values in a loop it could be bad.

Please note that if you are setting your channel to be an input then the channel’s HomeMode should be Ignore, it’s Min should be 0, its Max should be 1023, and the speed and acceleration limits should be 0 (disabled). If you are setting a channel to be an output, then the minimum should be 3986, the maximum should be 8000, and the speed and acceleration limits should be 0 (disabled).

Also, to make the new channel settings take effect you will have to send a Reinitialize request to the Maestro. Ideally this should be done once, after you are done modifying all of the settings. See the “reinitialize()” function in Usc.cs.

See the “setTarget” and the “getVariables(out ServoStatus[] servos)” commands in Usc.cs. To read data from an input channel, you should look at the position variable in the ServoStatus struct.

–David

My cpp skills are bad in itself, I dont understand csharp…

I set position like this
libusb_bulk_transfer(handle, 0x02, m_command, sizeof(m_command), &as, 1000);

I set parameter like this - setting home
libusb_control_transfer(handle, CTRL_OUT, 0x82, 5590, index, 0, 0, 1000);

I read them like this - reading home position
libusb_control_transfer(handle, CTRL_IN, 0x81, 0, index, buf, 2, 1000);

I read commands like this - reading current position
libusb_bulk_transfer(handle, 0x02, m_command, 2, &as, 1000);
libusb_bulk_transfer(handle, 0x82, result, 2, &as, 1000);

When I print result and buf I get 4 characters not 2, but the 1st and last character match the value I set it to???

Hello.

Are you using the Pololu USB SDK as a reference? If you have a specific question about what a line of C# code in the SDK does, you can ask us.

You should not use bulk transfers for now. The bulk transfers you are trying to use access the Maestro’s virtual Command Port and will only work if the Maestro is in the right serial mode. Everything you can do with bulk transfers can also be done with control transfers, and the control transfers give you access to more features such as changing the settings. So I recommend that you don’t call libusb_bulk_transfer, and you should ignore the documentation of the Maestro’s serial interface because it doesn’t apply to the control transfers.

–David

I was using a protcol.h reference file from another program I found
I am trying to use the SDK as a reference

So I should be using
"REQUEST_SET_TARGET value and servo" command to set the servo target or digital output

and REQUEST_GET_SERVO_SETTINGS to get the position or inputs
struct servoStatus

They shouldnt be a problem

as for channel modes

PARAMETER_CHANNEL_MODES_xx_xx
Im not sure what values to set what indexs to set the channel modes
What is the format of “channelModeBytes[i]”

Good. I don’t think protocol.h provides complete documentation. You should be looking at the SDK and using protocol.h just to avoid having to type lots of constants in to your program.

The code that sets channelModeBytes[i] is in Usc.cs and I actually posted it earlier in this thread. The key line is here:

channelModeBytes[i >> 2] |= (byte)((byte)setting.mode << ((i & 3) << 1));

where i iterates from 0 to servoCount-1. Let me know if you need help understanding that line. Please read Usc.cs so you can see the context that line appears in. If you get Visual Studio C# Express edition, you can easily click around to find the definitions for things like “setting.mode”.

–David

That line of code does what I thought was going on, the problem was which bit had what effect

I have never used C# or any GUI based development program
I also do not normally have/use any MS products.

public enum ChannelMode { Servo=0, ServoMultiplied=1, Output=2, Input = 3, }
This is all I needed to know, I was almost right in guessing which bit did what
So if I wanted to set
channel 0 servo
channel 1 input
channel 2 output
channel 3 servo
The binary representation I’d set the parameter byte to is
0,0,1,0,1,1,0,0

Assuming that the bits above are listed with least-significant bit (LSb) first, then yes, you are correct.

–David

Their must be someting wrong with my code
I can only seem to read and write 1 or 2 parameter bytes at a time which I can index, the function also returns the number of bytes I ask for.
e.g. If I asked for one byte I get one if i ask for 2 I get 2, If I ask for x it returns saying its transfered x but the string only has 2 bytes

I can only seem to read all the servo settings at once, but I can’t index them

The Get Parameter and Set Parameter requests can only be used to read/write 1 or 2 bytes at a time.

That’s correct. The Mini Maestro’s Get Servo Settings request returns the status/settings of all the channels and it doesn’t have a feature for requesting the status of an individual channel.

You should have noticed that Usc.cs doesn’t read/write more than 2 parameter bytes at a time, and doesn’t ever fetch the status of a single channel. You should be aware that you’re trying to do things that none of our example code does.

–David

OK, So my code should working the way its meant too
However I noticed if I set the speed and acceleration parameters when I get the parameter data it is correct, but when I get the servo settings it just shows the current target and speed of all the servos and the speed and accel are all zero?

You need to send the Reinitialize command before most parameters will take effect, including the starting speed limit parameter and starting acceleration limit parameter.

There is another request that changes the speed or acceleration limit of a servo, and it takes effect immediately, and it is only stored in RAM. This is the one you probably want to use. I think you should look for “setSpeed” and “setAcceleration” functions in Usc.cs.

–David

OK got that working

Hi all. Im using ROS and a 6channle controler. Im trying to track down the previous efforts to write a standerdish node for ROS. ive encounted another case of the controler being used with ROS! :smiley:

-A working ros node that proporly comunicats with the controler, documanting usage examples for data I/O to ROS gobal ints, Channle servo postions, and Channle servo speed controlers. I think its safe to say that the “tertle sim” method works control.

Im running ROS, with the Telop Nodes biuld to receve the PS3Joystick data that drive the tirtle sim around. Ive found working examples of the controler driving a RC car. Im on the other hand driving tank style and have 2 speed controlers, a (pan) & (tilt) postion head, and a servo controled relay camera head lights!! Im really liking this controler, and may even move up the # of control channles with a another board, I would realy like to have funtional operation of the controler in a ROS node. for telop control.

Here is the blog: spinlock.blogspot.com/2012/02/si … n-ros.html


Example CODE from spinlock.blogspot.com/2012/02/si … n-ros.html

#!/usr/bin/env python
import roslib; roslib.load_manifest('wombot_servo')
import rospy
import serial

from art_msgs.msg import SteeringCommand
from art_msgs.msg import SteeringState
#serialBytes[0] = 0x84; // Command byte: Set Target.
#serialBytes[1] = channel; // First data byte holds channel number.
#serialBytes[2] = target & 0x7F; // Second byte holds the lower 7 bits of target.
#serialBytes[3] = (target >> 7) & 0x7F;   // Third data byte holds the bits 7-13 of target.


#GO HOME  0xAA, device number, 0x22

#Get Moving State 0xAA, device number, 0x13
## if 0x00 
##then no servos are moving, 
##if 0x01 
##then Its moving 
##
##Stop Script 0xAA, device number, 0x24

##0xAA, device number, 0x07, channel number, speed low bits, speed high bits
## low speeds 5 to a value of 140
## unlimited speeds 0
##
##
##
class PololuDriver(object):
    def __init__(self):
        rospy.init_node('pololu_driver')
        rospy.on_shutdown(self.ShutdownCallback)
        rospy.Subscriber("steering/cmd",SteeringCommand, self.SteeringCallback)

        port = '/dev/ttyACM0'

        try:
            self.ser = serial.Serial(port)
            self.ser.open()
            self.ser.write(chr(0xAA))
            self.ser.flush()
        except serial.serialutil.SerialException as e:
            rospy.logerr(rospy.get_name()+": Error opening or initialising port "+port)
            exit(1)

    def DegToTicks(self,degrees):
        #quick and dirty,   assume 30 is near-extent
        ticks = (max(-30,min(30,degrees))/30.0)*480
        return int(ticks + 1500)

    def run(self):
        rospy.spin()

    def get_command(self, channel, target):
        target = target * 4
        serialBytes = chr(0x84)+chr(channel)+chr(target & 0x7F)+chr((target >> 7) & 0x7F)
        return serialBytes

    def ShutdownCallback(self):
        rospy.loginfo("Shutting down")
        if hasattr(self, 'ser'):
            self.ser.write(self.get_command(0,0))
            self.ser.write(self.get_command(1,0))
            self.ser.write(chr(0xA2))
            self.ser.close()

    def SteeringCallback(self,data):
        ticks = self.DegToTicks(data.angle)
        rospy.loginfo(rospy.get_name()+": Request Type="+str(data.request)+" Angle="+str(data.angle)+" Ticks="+str(ticks) )
        ticks = self.DegToTicks(data.angle)
        self.ser.write(self.get_command(0,ticks))
        self.ser.write(self.get_command(1,ticks))


#Init and run
PololuDriver().run()