Android MaestroSSC USB Project

I have created a project on github (github.com/pryan1068/maestroSSC) that is intended to demonstrate how to use an Android device to control Maestro products that are attached via an On-The-Go (OTG) USB cable (Try EBay or Adafruit). I’m 90% there - the green LED flickers when I send data to the board, I don’t get the error LED, but I don’t get any servo movement. Connecting this same setup to a laptop works fine with Maestro Control Center.

Since I’m not making any progress, I thought I’d promote what I’ve got to let others take a stab at it if interested.

Pull down the project, connect your Android device via adbwireless, connect the Maestro board via an OTG cable, and run the project. Once it’s downloaded, you should be able to reconnect the USB cable and it will ask if you want to run MaestroSSC by default: check yes and press ok. The GUI should come up. Moving sliders should move servos (but they don’t). I suspect the issue lies in MaestroSerialDevice.setCommand().

I’ve read the user guide about 100 times. I’ve got power to the servos (it works when connected to laptop). I don’t have a USB sniffer. I’ve tried the compact protocol. All with no luck.

Any collaboration appreciated.

Class Diagram:

MaestroSSCActivity is a simple UI that has sliders for controlling the servos.
MaestroSSC contains the Maestro data format using Pololu protocol.
MaestroUSBDevice (Native USB) and MaestroSerialDevice (using the usb-serial-for-android library) are two ways to access the board. This is selected by the MaestroSSC constructor args.

1 Like

Hello. Thanks for working on this! Maybe one problem is that your Maestro is not in the right serial mode, which is “USB Dual Port”. Another problem is that you have the wrong second argument to sendCommand when you call it from setServoPosition. You shouldn’t need that second parameter anyway; in Java, arrays know how long they are.

Also, I have some advice about the code. How are you planning on implementing MaestroUSBDevice.sendCommand(byte[] buffer, int length)? You would have to implement a parser to interpret the bytes as serial commands and translate them into the right control transfer command. That seems a like a lot of work, and very roundabout. I would copy all the features of MaestroSSC into the MaestroSerialDevice class, and get rid of MaestroSSC. Then I would totally rewrite the IMaestroDevice to have high-level methods like setTarget and no low-level methods like sendCommand. Ideally, the methods in IMaestroDevice should have names that match the Usc class from Pololu USB SDK. It seems like whatever work setDevice is doing should be part of the constructor, if that is possible. Also, I think you should use a namespace other than “com.pololu” in order to avoid confusion if we ever release our own java class for the Maestro.

Let us know how it goes! If it works well, then maybe we’ll make more software like it.

–David

I am very interesting in this effort of make the android speak to the controller. I download this code and try some myself. Is there anyway possible to get access to update the git project ?

regard leif h

Leif, he might be able to give you access, but it is simpler if you just make a fork of his repository on github by clicking the Fork button at the top. Then you will have your own copy of the code and you can do whatever you want with it. It will be easy for pryan to pull your changes in later if he wants to, and you can also make a pull request to him to officially request that he does so.

–David

have you seen this project code.google.com/p/usb-serial-for-android/ ?

No, I have not seen that, but it looks like it could be useful for talking to the Maestro’s virtual COM ports. --David

David,

Thanks for your reply. I think my problem is largely in the implementation of sendCommand(). But let me answer your questions.

Maybe one problem is that your Maestro is not in the right serial mode, which is “USB Dual Port”.
My impression is that there is no setSerialMode() command, so it’s really more about how you connect to the board over USB (which virtual TTY device or which USB interface).

One Pololu-published option is to use the virtual serial device which appears upon plugging in the board. However, I believe this can be made even simpler to the user if we have a CDC ACM based class. This would eliminate the need for the usb-serial-for-android library, and would avoid conflicts with virtual device naming conventions (e.g. /dev/ttyACM0). This approach depends on which USB Device, Interface and Endpoint you send your commands to. When you plug in the board it surfaces a Pololu device with 5 interfaces (copied from lsusb command on linux):

Interface 0: Command Port, Communications Class
endpoint 1: IN, Transfer Type = Interrupt

Interface 1: Command Port, CDC Data Class
endpoint 2: OUT, Transfer Type = Bulk
endpoint 2: IN, Transfer Type = Bulk

Interface 2: TTL Port, Communications Class
endpoint 3: IN, Transfer Type = Interrupt

Interface 3: TTL Port, CDC Data Class
endpoint 4: OUT, Transfer Type = Bulk
endpoint 4: IN, Transfer Type = Bulk

Interface 4: Vendor Specific Class

Based on this I used Interface 1 with endpoint 2 for sending/receiving commands.

Another problem is that you have the wrong second argument to sendCommand when you call it from setServoPosition. You shouldn’t need that second parameter anyway; in Java, arrays know how long they are.
Since I use one array for all the commands, I wasn’t sure if sending the extra bytes would cause a problem. I do need to simplify that though.

How are you planning on implementing MaestroUSBDevice.sendCommand(byte buffer, int length)? …
The code is implemented and available at github.com/pryan1068/maestroSSC. I’m under the impression that I can just push that entire buffer over the command port and it will magically work. Once I figure out how to do the low-level USB transfer command correctly, I can clean up the code.

I would copy all the features of MaestroSSC into the MaestroSerialDevice class, and get rid of MaestroSSC.
The intention is to separate the data format from the device(s). This will allow us to use the same data format class on the PC side, and simply use a PC-centric device class over there. Otherwise we end up maintaining the same code in multiple places.

Also, I think you should use a namespace other than “com.pololu” in order to avoid confusion if we ever release our own java class for the Maestro.
The intention was to create this for Pololu, and transfer ownership. I can change it to a fictional namespace.

Let us know how it goes! If it works well, then maybe we’ll make more software like it.
I think we’re on the leading edge of a increasing smartphone presence in the USB devices space. The adoption of products is accelerated and made easier through good API documentation and example code.

Leif,

As David eluded, I would just fork the code at this point. Once we get something up and running, I’ll grant others write access or we can create a new project.

The MaestroSerialDevice actually uses usb-serial-for-android. When I couldn’t get the native USB approach to work, I tried to use usb-serial-for-android. It also did not work. This really makes me think the problem lies in the construction of the data packet (See MaestroSSC.setServoPosition()).

Pat.

One thought I keep coming back to is the constraint of using Java byte[] in Android’s USB API.

Java byte is an 8-bit, 2’s compliment data type which can store -128 to 127. While it cannot store the 170 (0xAA) that we need for the Pololu setTarget command, we can fake it by passing in -42. These have the same binary equivalent.

The question then arises: what does it look like when it arrives at the servo board? Is there a debug mode that echoes all received USB commands out the TTL port by chance?

1 Like

Hello, Pryan.

You still need to use the Maestro Control Center to set the Maestro’s serial mode to USB Dual Port if you want to send serial commands to the Maestro’s Command Port (interfaces 0 and 1). The default serial mode is “UART, detect baud rate”, and in that mode it will not listen to commands from the USB virtual COM port. I am not sure if understood this suggestion the first time I said it, because you never confirmed that you have set the Maestro’s serial mode.

This code still looks wrong to me:

	public void setServoPosition(int servoNumber, int position) {
		this.servoPosition[servoNumber] = position;

		// Pololu Protocol expects you to pass in how many QUARTER-seconds.
		position = position * 4;

		dataArray[0] = (byte) 0xAA;
		dataArray[1] = (byte) deviceNumber;
		dataArray[2] = (byte) CMD_SET_POSITION;
		dataArray[3] = (byte) servoNumber;
		dataArray[4] = (byte) (position & 0x7F);
		dataArray[5] = (byte) ((position >> 7) & 0x7F);

		sendCommand(dataArray, 4);
	}

The second argument to sendCommand is supposed to be a number of bytes, right? The number should be 6.

Your current implementation of MaestroUSBDevice.sendCommand is wrong. You cannot just wrap up a Maestro serial command and send it as a control transfer. There is no easy translation between serial commands and equivalent control transfers. The serial commands are documented in the Maestro User’s Guide, while the control transfers are specified in the source code of the Usc class in the Pololu USB SDK.

–David

David,

Thanks for responding on a Sunday! That helps since I had some time to work this tonight. Here’s some good results:

You still need to use the Maestro Control Center to set the Maestro’s serial mode to USB Dual Port if you want to send serial commands to the Maestro’s Command Port (interfaces 0 and 1).
Wow. That’s the first time I’ve heard that. Didn’t realize I have to configure my board with Maestro Control Center first. I did not get that from the web site nor the user manual. Not trying to pick a fight here - just think it should be stated clearer if that’s truly the case.

This code still looks wrong to me
You’re absolutely right. I thought I responded to that in my last post but I obviously forgot. It should have been 6 vice 4.

You cannot just wrap up a Maestro serial command and send it as a control transfer.
So I reverse-engineered the Usc class as you suggested and found what I was looking for. Hopefully anyone else trying to do native USB to a Pololu board will find this post:

The Maestro board expects a USB Control Transfer call with the following parameters: Request Type=0x40, Request=(Pololu Compact Protocol Command), Value=(Command Parameter), Index=(Channel Number), Buffer=null, length=0, timeout=5000.

That is the essence of what I needed to get this working. Sorry I wasn’t more clear. The code now works and I’m cleaning it up to promote it to github.

I noticed that the Usc class uses 0x85 for setTarget() vice 0x84 as the user manual states. 0x85 works with my code while 0x84 does not. Possible error or did I read that wrong? Again, not picking a fight - just trying to understand.

Thanks for your help and patience, David.

Pat.

If the Usc class uses 0x85 for the request code, then 0x85 is correct.

Again, I don’t think it is possible to come up with an easy rule like this. The request codes for the USB control transfers and the list of serial command bytes are two disjoint lists and they are both arbitrary. Serial is different from control transfers. The control transfers are not documented in the user’s guide, while the serial commands are. I recommend reading the “Serial Interface” section of the Maestro user’s guide all the way through so you can understand the scope of that section and learn about the serial modes. The serial commands are for the Maestro’s Command Port and its TX/RX lines, while the control transfers comprise the Maestro’s native USB interface. --David

I’ve got setTarget() working. I’ve promoted the newer, simpler code to a new github project:

github.com/pryan1068/android-pololu-maestro-ssc

1 Like

Has anyone made any successful progress on this project since the last post?

I was going to attempt this myself, and Googled around and found this thread, but it seems that people gave up on it several months ago?

My project idea is using my Android smartphone to control a robotic rover using the Pololu Maestro micro 6-channel board via USB OTG connected cable.

I am able to successfully control the servos using a maestro micro-6 which is connected via USB to a Raspberry Pi running Raspbian Linux. I write commands to the device /dev/ttyACM0 to position the servos, and it’s working perfectly, so I figured it might work on an android smartphone running Linux in the same way.

One question I had was about writing to /dev/ttyACM0 though, and permissions. When I plug the maestro into the samsung galaxy s3, it creates a device /dev/ttyACM0, but the permissions are rw-------, so it looks like only root can read and write to it… I haven’t tried to create an android java program to try to write to it, but I’m thinking it will fail because of permissions… I’m thinking I might have to root my smartphone in order to get it to work, which I really don’t want to do…

So, anyone still following this thread? :slight_smile:

pryan,

I just now got your latest code working to control 4 servos connected to my Samsung Galaxy S3 phone which has a Maestro micro-6 servo controller plugged into it with a USB OTG cable. When I plug the OTG cable into the phone, the Maestro SSC automatically pops up, which is really nice!!

Nice work on the code!

So I assume you got everything working on your project, as well???