[libusb] linux/windows set position with unmanaged C++

Hello again, is there a way to retreive the servo position ?

Yes. The Maestro has a native USB command for getting an array of structs where each struct holds data about one channel. This struct has a “position” field that holds the current width of pulses that the Maestro is sending. The command is implemented in the Pololu USB SDK in the Usc class. The method you should look at is getVariables(out ServoStatus[] servos). The exact protocol you need to use depends on whether you have a Mini Maestro or a Micro Maestro and you can see those details in the source code.

–David

I already used it in windows but in linux, using libusb I use libusb_control_transfer(device_handle, 0x40, REQUEST_SET_TARGET, position, servo, 0, 0, (ushort)5000); for “setposition” but don’t know how to do a getposition…

I find some code about getvariable to get all servos variables in one time, I will see if it works…

Hi,

I realize it’s some time since this thread was active. However, I’m posting in the hope that somebody has some C code for getposition on the Maestro in USB mode. I’ve tried to adapt some of the other source examples without success, and am now stalled until I can figure it out so any assistance would be gratefully received.

On a slightly different note, I’m going to have to learn a bit more about how USB resources work under Linux, as scanning for the Maestro on every pass and then releasing again is killing my timing! (Will get worse once I have getpostion as well!) Again, if anyone has C code for an efficient way to get the USB resource during init, and then not release it until the application exits, that would also be gratefully received…

Cheers,
Simon.

Hello, Simon.

Unfortunately, we do not have any C code using the Maestro’s native USB. However, we do have some C++ examples in the Pololu USB Software Development Kit (SDK) for the Maestro that you might find helpful. Also, you might find it helpful to look at this thread on implementing the Get Position command in C++ using the SDK.

By the way, if you are using libusb, you can store the libusb_device_handle pointer in a global variable that gets set at the start of your program. You can find more information about the device handling and enumeration of libusb here.

- Amanda

Hi Amanda, Thanks for the fast reply.

hello every body,
I would like to thank carrotSnack for his work. I tested the code and works fine.
Here the code compiled with Visual C++ 2012 on Windows 10 x64.
I used the channel 1 of the servo.

Also in order to build libusb you have to download the zip file from libusb.org/.
Then go to msvc folder and open the visual studio project called libusb.
Then go to configuration manager and change the active solution platform to x64 and Press build.
then go to output folder \libusb-1.0.9\x64\Debug\lib you will find the generated output file libusb.lib.

In another project , create a console application add carrotSnack files and then Right Click on project property
then in C/C++ then General then Additional Include libraries and add the absolute path to \libusb-1.0.9\libusb (this folder contains libusb.h)
in Linker General you go to Addtional Library directory and you add \libusb-1.0.9\x64\Debug\lib
in Linker Input you go to Addtional Dependencies and you add \libusb-1.0.9\x64\Debug\lib\libusb-1.0.lib

With this you should be compiling the project successfully.

Have a nice project :smiley: :smiley:

…but did you manage to read the current position?

Using the examples, with a REQUEST_GET_SERVO_POSITION (0x87), I always get a response of LIBUSB_ERROR_PIPE, control request was not supported by the device.

Using some of the binaries provided (Usc.dll or MaestroControlCenter) I can see that the position can be read, but not using the request above.

Sadly, it’s time to trawl for recommendations on another USB servo controller, because I simply can’t get this one to work…

Hello, Simon.

I am sorry you are having trouble getting the position of a servo in a C program using the Maestro’s native USB interface. The Pololu USB SDK does not define the constant REQUEST_GET_SERVO_POSITION that you mentioned, but it does define REQUEST_GET_SERVO_SETTINGS, which has the value 0x87. That request is only supported on the Mini Maestros. If you have a Micro Maestro 6-channel servo controller, you should use REQUEST_GET_VARIABLES (0x83) to get the positions of all the servos. I recommend referring to the files Usc.cs and Usc_protocol.cs in the Pololu USB SDK to see how these requests work. If you would like help getting your code to work, please tell me what kind of Maestro you have and post your C code here, along with a description of how it is behaving.

–David

Hi David,

Thanks for your reply - I had been following various code examples (notably libusc) which do seem to have checking for channelCnt == 6 (implying support Micro Maestro) but also use the invalid REQUEST_GET_SERVO_SETTINGS (0x87).

(Apologies for the typo in the macro name, as I’d been using my Windows PC to type on this forum, rather than the Linux box I’m developing on, and so that was an error on my part)

Using REQUEST_GET_VARIABLES (0x83) I’m now able to correctly get the positions of all six axes. For what it’s worth, the code I’m now using is below.

Thanks,
Simon.


const unsigned short	vendorId = 0x1ffb;
unsigned short		productIDArray[]={0x0089, 0x008a, 0x008b, 0x008c};
unsigned char		buffer[MICRO_MAESTRO_VARIABLE_BUFFER_LEN];
libusb_context 		*ctx=0;
libusb_device 		**device_list=0;
libusb_device 		*device;
int 			count;
int			i, Id;

microMaestroVariables	uMaestroVariables;
servoSetting		servo[NUMBER_OF_SERVO_CHANNELS];
int			retval, src_offset;


libusb_init(&ctx);
count = libusb_get_device_list(ctx, &device_list);

for(i=0;i<count;i++)
{
	device = device_list[i];
	{
		for( Id=0; Id<4; Id++ )
		{
			if(deviceMatchesVendorProduct(device, vendorId, productIDArray[Id]))
			{
				// OK, we've found a Pololu controller
				libusb_open(device, &device_handle);
				break;
			}
		}
	}
}


// the data in the returned buffer is tightly packed, 7 bytes for each channel
// but servo[].position is a short and so will be aligned on an even address by the compiler
// get variables from controller into intermediate buffer
retval = libusb_control_transfer(device_handle, 0xC0, REQUEST_GET_VARIABLES, 0, 0, &buffer[0], MICRO_MAESTRO_VARIABLE_BUFFER_LEN, 5000 );
if( retval != MICRO_MAESTRO_VARIABLE_BUFFER_LEN )
{
	perror( "Invalid response from Pololu Micro Maestro" );
	return( retval );
}

// get the servo data from the controller
for( i=0; i<NUMBER_OF_SERVO_CHANNELS; i++ )
{
	// do some motor movement, just to demonstrate
	libusb_control_transfer(device_handle, 0x40, REQUEST_SET_TARGET, (1000 + i * 100) * 4, i, 0, 0, (ushort)5000);

	src_offset = sizeof( microMaestroVariables ) + i * LENGTH_OF_SERVO_SETTINGS_BUFFER;

	// get all elements of servoSetting
	memcpy( &servo[i], &buffer[src_offset], LENGTH_OF_SERVO_SETTINGS_BUFFER );
	printf( "Servo %d, offset %3d: Position: %4d, Target: %4d\n", i, src_offset, (unsigned int )(servo[i].position / 4), (unsigned int )(servo[i].target / 4 ) );

	// don't really need to memcpy all seven bytes, as in the line above
	// get just two bytes of position if that's all that's required
	printf( "Servo %d, offset %3d: Position: %4d\n", i, src_offset, (unsigned int )((buffer[src_offset+1] << 8 | buffer[src_offset]) / 4) );
}


libusb_close(device_handle);

libusb_free_device_list(device_list, 0);
libusb_exit(ctx);

return 0;

I’ve created a modified header file based on the various code examples to meet my needs, so in my project, microMaestro.h is:

/*
 * microMaestro.h
 *
 *  Created on: 30 Mar 2016
 *      Author: SC
 */

#ifndef MICROMAESTRO_H_
#define MICROMAESTRO_H_

#define	NUMBER_OF_SERVO_CHANNELS		6
#define	MICRO_MAESTRO_VARIABLE_BUFFER_LEN	140
#define	LENGTH_OF_SERVO_SETTINGS_BUFFER		7


// copied from: pololu-usb-sdk/Maestro/protocol.h
// These are the values to put in to bRequest when making a setup packet
// for a control transfer to the Maestro.  See the comments and code in Usc.cs
// for more information about what these requests do and the format of the
// setup packet.
enum uscRequest
{
    REQUEST_GET_PARAMETER = 0x81,
    REQUEST_SET_PARAMETER = 0x82,
    REQUEST_GET_VARIABLES = 0x83,
    REQUEST_SET_SERVO_VARIABLE = 0x84, // (also clears the serial timeout timer)
    REQUEST_SET_TARGET = 0x85,   // (also clears the serial timeout timer)
    REQUEST_CLEAR_ERRORS = 0x86, // (also clears the serial timeout timer)
};

// this structure copied from: pololu-usb-sdk/Maestro/protocol.h
//
struct servoSetting
{
    unsigned short	position;
    unsigned short	target;
    unsigned short	speed;
    unsigned char	acceleration;

    // this is seven bytes long.
    // making an array of this struct will naturally add a padding byte
    // so that the next element aligns to an even address
    // sizeof( servoSetting ) also returns 8 bytes, so need to
    // #define LENGTH_OF_SERVO_SETTINGS_BUFFER 7
};

typedef struct servoSetting servoSetting;


// this structure modified from: pololu-usb-sdk/Maestro/Usc/Usc_protocol.cs
//
struct microMaestroVariables
{
	/// <summary>
	/// The number of values on the data stack (0-32).  A value of 0 means the stack is empty.
	/// </summary>
	unsigned char stackPointer;

	/// <summary>
	/// The number of return locations on the call stack (0-10).  A value of 0 means the stack is empty.
	/// </summary>
	unsigned char callStackPointer;

	/// <summary>
	/// The error register.  Each bit stands for a different error (see uscError).
	/// If the bit is one, then it means that error occurred some time since the last
	/// GET_ERRORS serial command or CLEAR_ERRORS USB command.
	/// </summary>
	unsigned short errors;

	/// <summary>
	/// The address (in bytes) of the next bytecode instruction that will be executed.
	/// </summary>
	unsigned short programCounter;

	/// <summary>Meaningless bytes to protect the program from stack underflows.</summary>
	/// <remarks>This is public to avoid mono warning CS0169.</remarks>
	short buffer[3];

	/// <summary>
	/// The data stack used by the script.  The values in locations 0 through stackPointer-1
	/// are on the stack.
	/// </summary>
	short stack[32];

	/// <summary>
	/// The call stack used by the script.  The addresses in locations 0 through
	/// callStackPointer-1 are on the call stack.  The next return will make the
	/// program counter go to callStack[callStackPointer-1].
	/// </summary>
	unsigned short callStack[10];

	/// <summary>
	/// 0 = script is running.
	/// 1 = script is done.
	/// 2 = script will be done as soon as it executes one more instruction
	///     (used to implement step-through debugging features)
	/// </summary>
	unsigned char scriptDone;

	/// <summary>Meaningless byte to protect the program from call stack overflows.</summary>
	/// <remarks>This is public to avoid mono warning CS0169.</remarks>
	unsigned char buffer2;

	// NOTE: C# does not allow fixed arrays of structs; after these variables,
	// 6 copies of servoSetting follow on the Micro Maestro.

	// 6x 7 byte structure of servo setting follows in libusb_control_transfer
};

typedef struct microMaestroVariables microMaestroVariables;


#endif /* MICROMAESTRO_H_ */
1 Like

Hello ,
the files cosine works well on my Ubuntu20.0, I can build it with g++ and then control the maestro board with the compiled file.
but, when I adapted the cosine.cpp into ROS package, there is an error which says:
fatal error: libusb.h: No such file or directory,
I have already installed the libusb library by : sudo spt-get install libusb1.0-dev
and I added the headers “#include <libusb.h>” and “#include <libusb-1.0/libusb.h>”

do you have any clue about this?

thank you.

If you write #include <libusb-1.0/libusb.h> in Ubuntu after installing libusb1.0-dev, that should work. You can remove the line that is giving you the error, which is probably #include <libusb.h>.

–David

thank you David. yes, that worked.

now, another problem: I added “#include <libusb-1.0/libusb.h>”,

when building in ROS the errors come :slight_smile:
[ 95%] Built target publisher_set_position
/usr/bin/ld: CMakeFiles/subscriber_get_position.dir/src/maestro_getpost_sub.cpp.o: in function deviceMatchesVendorProduct(libusb_device*, unsigned short, unsigned short)': maestro_getpost_sub.cpp:(.text+0x1600): undefined reference to libusb_get_device_descriptor’
/usr/bin/ld: CMakeFiles/subscriber_get_position.dir/src/maestro_getpost_sub.cpp.o: in function setTarget(int, int)': maestro_getpost_sub.cpp:(.text+0x1691): undefined reference to libusb_init’
/usr/bin/ld: maestro_getpost_sub.cpp:(.text+0x16a4): undefined reference to libusb_get_device_list' /usr/bin/ld: maestro_getpost_sub.cpp:(.text+0x1718): undefined reference to libusb_open’
/usr/bin/ld: maestro_getpost_sub.cpp:(.text+0x174c): undefined reference to libusb_control_transfer' /usr/bin/ld: maestro_getpost_sub.cpp:(.text+0x175c): undefined reference to libusb_close’
/usr/bin/ld: maestro_getpost_sub.cpp:(.text+0x1781): undefined reference to libusb_free_device_list' /usr/bin/ld: maestro_getpost_sub.cpp:(.text+0x178d): undefined reference to libusb_exit’

To fix undefined reference errors to libusb functions, you need to add the -lusb-1.0 option to the linking step of your build process. That is an option that your build system needs to pass to GCC or your linker when it is producing the executable or shared library that uses libusb. I am not familiar with how you are building your software, but you might look for a place to add libraries, “ldflags”, or “linker flags”.

–David

thank you David.

I resolved the problems.

On ROS it’s about redefining the CMakelist.txt file.

Hi, David.

I’m coming back again with new problems. :laughing:

I’m using the library ‘libusb-1.0’ to control the maestroboard on Ubuntu 20.0.

With this library, I can set the target position to the motor with the function ‘libusb_control_transfer’ in the form of ‘libusb_control_transfer(device_handle, 0x40, REQUEST_SET_TARGET, position, servo, 0, 0, (ushort)5000);’ where REQUEST_SET_TARGET is defined as 0x85

I reied to set speed to the motor with ‘libusb_control_transfer(device_handle, 0x40, REQUEST_SET_SPEED, target speed, servo, 0, 0, (ushort)5000);’ where REQUEST_SET_SPEED is defined as 0x87 according to the Maestro user guide. but the speed didn’t change when running this function.

I also found out that in the user guide, for setting target the Compact protocol is : 0x84, but in the code for setting target the ‘REQUEST_SET_TARGET is defined as 0x85’ works well. I’m a little confused about this.

So, the question is how to set motor speed with the function ‘libusb_control_transfer’ ?

Thank you in advance.

– Lisheng

Hello, Lisheng.

I think you were looking at the documentation of the Maestro’s serial interface in the user’s guide. The Maestro’s serial interface is different from the native USB interface that you are accessing when you run libusb_control_transfer. To learn the format of the Maestro’s native USB commands, I recommend referring to the source code of the Usc class in the Pololu USB SDK:

https://github.com/pololu/pololu-usb-sdk/blob/master/Maestro/Usc/Usc.cs

That code uses some constants that are defined here:

https://github.com/pololu/pololu-usb-sdk/blob/master/Maestro/Usc/Usc_protocol.cs

To set the speed, you should be able to do something like this:

libusb_control_transfer(device_handle, 0x40, 0x84, speed, servo, 0, 0, 5000);

–David

Hi David,

thank you for the reply.

I just realized that I am using the ‘Mini Maestro 6’ board, the user guide says ‘speed control apply to Mini Maestro 12, 18, and 24 only’, so with the Mini Maestro 6 board, the motor speed can not be controlled/redefined (even with the native USB interface). am I right for this?

All of the Maestros support servo speed and acceleration limits. You can try out those features using the “Status” tab or “Channel Settings” tab of the Maestro Control Center. The command I mentioned above for setting the speed works on all the Maestros.

Could you send a screenshot showing the part of the user’s guide you are referring to?

–David