Hey guys,
I'm working on a project with a group of other engineers to control a robotic arm with the space navigator, embedded-system style. I'm responsible for turning the HID stream into PWM or TTL serial using an MCU running bare metal (no OS).
I just downloaded the spacenav driver (http://spacenav.sourceforge.net/) and am browsing through the code right now. I've never directly parsed HID input, and I have yet to figure out the space navigator's protocol. I have the USB 2.0 spec sheet open as well, because I haven't really studied USB before.
I happen to have a spare CortexM4 chip (STM32F4) lying around, so I might use that for its USB host capabilities. Ideally, I'd like something a bit smaller but I've yet to find anything I like.
I would very much like to hear ideas/suggestions for chips to use or methods for parsing the data. Or a specific place to look that explains the space navigator's protocol (looks like it sends 14-byte packets).
Robotic Arm Control via Embedded System MCU
Moderator: Moderators
Re: Robotic Arm Control via Embedded System MCU
Hi Caesar517,
Have a look at the topic about RawInput sample software here. The code is for Windows but the parsing of the raw data ought to be same as on Linux.
Have a look at the topic about RawInput sample software here. The code is for Windows but the parsing of the raw data ought to be same as on Linux.
Re: Robotic Arm Control via Embedded System MCU
Thanks for pointing me there, although I'm not sure how much it would have helped (the example doesn't explain what the code is trying to do).
I ran a hexdump from /dev/hidraw0 and reverse engineered the packet structure. Then a quick C program to parse and print the data. It didn't take me long. Here's the code, in case anyone is interested: (the comment block I have there is the kind of thing I'd been hoping to find earlier)
I just wrote code to send it over a USB-Serial connection to a Leaflabs Maple (ARM chip) to do the PWM, because I had it lying around. I'm going to try using a MAX3421E and an ATTINY AVR chip for the PWM. I've never worked with an embedded USB host, so who knows how that will go.
As always, suggestions and ideas are welcome.
I ran a hexdump from /dev/hidraw0 and reverse engineered the packet structure. Then a quick C program to parse and print the data. It didn't take me long. Here's the code, in case anyone is interested: (the comment block I have there is the kind of thing I'd been hoping to find earlier)
Code: Select all
/* Randy Westlund
* Space Navigator Project
* 7/24/12
*
* This program is designed to parse the HID stream from the space navigator and
* convert it to serial commands, which can be sent to a maple. In this way, I
* will demonstrate control of an arm with the space navigator prior to moving
* it to an embedded system.
*/
/* NOTE: link with -lpthread to print errno from gdb */
#include <stdio.h>
int main(int argc, char **argv)
{
int x, y, z, rx, ry, rz; /* position and rotation */
FILE *infile;
infile = fopen("/dev/hidraw0", "rb"); /* open HID stream read-only, binary */
if(!infile){ printf("ERROR: cannot open file\n"); return 1; }
/* PACKET PROTOCOL
* The space navigator sends packets over the HID stream. If the stick is not
* at rest, position packets are sent at 60Hz. The packet is 14 bytes:
*
* byte 00: always 0x01 -- signals position info to follow
* byte 01: xx_low -- the low byte of x position
* byte 02: xx_high -- the high byte of x position
* byte 03: yy_low
* byte 04: y_high
* byte 05: zz_low
* byte 06: zz_high
* byte 07: always 0x02 -- signals rotation info to follow
* byte 08: rx_low -- the low byte of x rotation
* byte 09: rx_high -- the high byte of x rotation
* byte 10: ry_low
* byte 11: ry_high
* byte 12: rz_low
* byte 13: rz_high
*
* Values range from -350 to 350. All values are 0 when the stick is not being
* touched (no packets are sent in this state). When the stick is moved in a
* positive direction, values increase from 0. When the stick is moved in a
* negative direction, values decrease from 0xFFFF.
*
* Positive x = to your right. Positive x rotation = toward yourself
* Positive y = toward yourself. Positive y rotation = to your left
* Positive z = down. Positive z rotation = clockwise
*
* When a butten press event (depress or release) occurs, a button packet is
* sent after the current packet finished transmittting. It is 3 bytes:
*
* byte 0: always 0x03 -- signals button state to follow
* byte 1: (left button = 0x01) | (right button = 0x02)
* byte 2: always 0x00
*
* As such, byte 1 can take values 0x00, 0x01, 0x02, or 0x03.
*/
while(1)
{
unsigned char readbuff[14]; /* max packet size = 14 */
*readbuff = fgetc(infile); /* read the first char of the packet */
switch(*readbuff)
{
case 0x01: /* position/rotation packet */
fread(readbuff+1, sizeof(char), 13, infile); /* read rest of packet */
x = (int)((readbuff[2]<<8) | readbuff[1]); /* add high and low bits */
if(readbuff[2] & 0xF0) x = (65535 - x) * -1; /* if negative */
y = (int)((readbuff[4]<<8) | readbuff[3]); /* now for y */
if(readbuff[4] & 0xF0) y = (65535 - y) * -1;
z = (int)((readbuff[6]<<8) | readbuff[5]); /* now for z */
if(readbuff[6] & 0xF0) z = (65535 - z) * -1;
rx = (int)((readbuff[9]<<8) | readbuff[8]); /* same for rotation */
if(readbuff[9] & 0xF0) rx = (65535 - rx) * -1;
ry = (int)((readbuff[11]<<8) | readbuff[10]);
if(readbuff[11] & 0xF0) ry = (65535 - ry) * -1;
rz = (int)((readbuff[13]<<8) | readbuff[12]);
if(readbuff[13] & 0xF0) rz = (65535 - rz) * -1;
printf("x=%4d\ty=%4d\tz=%4d\t", x, y, z); /* print all values */
printf("\trx=%4d\try=%4d\trz=%4d\n", rx, ry, rz);
break;
case 0x03: /* button event packet */
fread(readbuff+1, sizeof(char), 2, infile); /* read rest of packet */
switch(readbuff[1])
{
case 0x00:
printf("buttons released\n");
break;
case 0x01:
printf("left button pressed\n");
break;
case 0x02:
printf("right button pressed\n");
break;
case 0x03:
printf("both buttons pressed\n");
break;
default: printf("ERROR: bad button packet\n");
}
break;
default: /* bad header */
printf("ERROR: not at beginning of packet, try again");
fclose(infile);
return 1;
} /* end switch(*readbuff) */
} /* end while(1) */
fclose(infile);
return 0;
} /* end main() */
As always, suggestions and ideas are welcome.