Decoding data from the Magellen Space Mouse

Written by Paul Bourke
December 2006

The following gives introductory information to handling the data from the Magellan Spacemouse, a 6 degree of freedom input device. The version of the mouse used here is based upon the serial interface, communication settings are: 8 databits, 1 stop bit, no parity, and 9600 baud. This note is not considered an exhaustive guide but covers most of the "useful" aspects, the rest are mostly guessable once the material covered here is understood.

All communication with the mouse is done with standard printable ascii characters. Communication and data terminations/separators are using either ascii character 13 or 10, that is, carriage return or linefeed. The mouse can tell the application which of the buttons is pressed as well as the position and orientation from the 6 DOF puck. All communication with the mouse has the form of a single character that identifies the type of data (for example: "k" for key, "d" for position/angle data, and so on), this is followed by the data associated with that data type, and finally a line termination. All data is encoded as single nibbles, so two functions usually required are those that convert to and from nibbles (4 bits, so 0..15). For example, functions to encode and decode bytes for the Spacemosue are as follows.

unsigned char EncodeSpacemouse(int n)
{     
   int codes[16] = {'0','A','B','3','D','5','6','G','H','9',':','K','<','M','N','?'};
   
   if (n >= 0 && n <= 15)
      return(codes[n]);
   else  
      return('0');
}        
/*
    Convert characters to nibbles
*/
int DecodeSpacemouse(char c)
{
   int n;

   switch (c) { 
   case '0': n = 0; break;
   case 'A': n = 1; break;
   case 'B': n = 2; break;
   case '3': n = 3; break;
   case 'D': n = 4; break;
   case '5': n = 5; break;
   case '6': n = 6; break;
   case 'G': n = 7; break;
   case 'H': n = 8; break;
   case '9': n = 9; break;
   case ':': n = 10; break;
   case 'K': n = 11; break;
   case '<': n = 12; break;
   case 'M': n = 13; break;
   case 'N': n = 14; break;
   case '?': n = 15; break;
   default: n = 0; break;
   }
  
   return(n);
}

There are a number of responses possible from the Spacemouse, the following lists all that are known by the author, comments are provided next to the more useful. In what follows the data from the Spacemouse until the termination character is read into a string "s", so the first element of the string s[0] is the identifier.

b Beep
c
p
e Error, generally a communication error
k Key press. The data consists of 3 bytes that should be decoded as:
key = DecodeSpacemouse(s[1]) + DecodeSpacemouse(s[2])*16 + DecodeSpacemouse(s[3])*256;
m A mode change has occured. A mode consists of things like a change between just supplying position, or orientation. Mode 1 is just position is supplied, 2 is just orientation, and 3 is both. A single data bytes follows and should be decoded as:
mode = DecodeSpacemouse(s[1]);
z Reset
n Zero radius
q Sensitivity. Two bytes are supplied as data corresponding to the sensitivity of the position and orientation. They are decoded individually as
sensitivity1 = DecodeSpacemouse(s[1]);
sensitivity2 = DecodeSpacemouse(s[2]);
d Ths is the psition and orientation information. 24 bytes are supplied as data, 4 for each of 3 cartesian coordinates and 3 orientation angles. They are decoded as follows
p.x = ConvertSpacemouse(&s[1]);
p.z = ConvertSpacemouse(&s[5]);
p.y = ConvertSpacemouse(&s[9]);
thetax = ConvertSpacemouse(&s[21]);
thetay = ConvertSpacemouse(&s[13]);
thetaz = ConvertSpacemouse(&s[17]);
Note that it is the applications responsibility to scale these into appropriate ranges. The angle conventions also depend on the applications conventions.
v Version number follows.

There are a number of commands that can be sent to the Spacemouse, they generally follow a similar format to the above. In particular there are three things one generally should do to initialise the device and ensure it is working OK, these are listed below.

\rvz\r Reset the Spacemouse
vQ\r Request the version number, this can be checked for something expected like the word "Magellan".
kQ\r The requests that button presses result in a message being sent.
m3\r Puts the device into mode 3 where translation and orientation are reported.

Note that the user may control some aspects of the device using the buttons in conjuntion with the "*" button. Thee will be reported and the application can choose to take action or not depending on the result.




Decoding data from the MicroSoft Serial Mouse

Written by Paul Bourke
April 2003

The old MicroSoft serial mouse, while no longer in general use, can be employed to provide a low cost input device, for example, coupling the internal mechanism to other moving objects. The serial protocol for the mouse is 1200 baud, 7 bit, 1 stop bit, no parity. The pinout of the connector is standard serial as shown below.


PinDescription
1DCDData carried detect
2RDReceive data
3TDTransmit data
4DTRData terminal ready
5SGSignal ground
6DSRData set ready
7RTSRequest to send
8CTSClear to send
9Ring

Every time the mouse changes state (moved or button pressed) a three byte "packet" is sent to the serial interface. For reasons known only to the engineers, the data is arranged as follows, most notably the two high order bits for the x and y coordinates share the first byte with the button status.

D6D5D4D3D2D1D0
1st byte1LBRBY7Y6X7X6
2nd byte0X5X4X3X2X1X0
3rd byte0Y5Y4Y3Y2Y1Y0
Where
  • LB is the state of the left button, 1 = pressed, 0 = released.

  • RB is the state of the right button, 1 = pressed, 0 = released

  • X0-7 is movement of the mouse in the X direction since the last packet. Positive movement is toward the right.

  • Y0-7 is movement of the mouse in the Y direction since the last packet. Positive movement is back, toward the user.

Sample C code to decode three bytes from the mouse passed in "s", the button and position (x,y) are returned.

/*
   s should consist of 3 bytes from the mouse
*/
void DecodeMouse(unsigned char *s,int *button,int *x,int *y)
{
   *button = 'n'; /* No button - should only happen on an error */
   if ((s[0] & 0x20) != 0)
      *button = 'l';
   else if ((s[0] & 0x10) != 0)
      *button = 'r';
   *x = (s[0] & 0x03) * 64 + (s[1] & 0x3F);
   if (*x > 127)
      *x = *x - 256;
   *y = (s[0] & 0x0C) * 16 + (s[2] & 0x3F);
   if (*y > 127)
      *y = *y - 256;
}