Robotic Arm Control via Embedded System MCU

Questions and answers about 3Dconnexion devices on UNIX and Linux.

Moderator: Moderators

Post Reply
Caesar517
Posts: 3
Joined: Thu Jul 19, 2012 3:02 pm

Robotic Arm Control via Embedded System MCU

Post by Caesar517 »

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).
ngomes
Moderator
Moderator
Posts: 3321
Joined: Mon Nov 27, 2006 7:22 am
Contact:

Re: Robotic Arm Control via Embedded System MCU

Post by ngomes »

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.
Caesar517
Posts: 3
Joined: Thu Jul 19, 2012 3:02 pm

Re: Robotic Arm Control via Embedded System MCU

Post by Caesar517 »

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)

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() */
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.
Post Reply