How to use SpaceOrb under Linux.

-- or --

Unsupported Secrets of the SpaceOrb Protocol

Brett Viren



NEWS:
  • 19990308: SpaceOrb Joystick driver in official Linux Joystick driver package.
    Noticed this on Freshmeat that version 1.2.14 contains the S.O. driver.

  • 19990123: Another SpaceOrb hacker: John Stone.
    I just got email from this guy. He has also figured out the orb's protocol and has coded up at library to access it. Check out his stuff at: http://jedi.ks.uiuc.edu/~johns/projects/. It seems that he is actively developing (unlike me [grin]) so turn to him for fresher code!

  • 19981111: Linux SpaceOrb Kernel Joystick driver.
    by David Thompson. For more info see http://shimano.me.utexas.edu/~dcthomp/spaceorb.html. He also tells me that his SpaceOrb gives a different length initial or ``R'' packet than what my original code looks for. He scans for a '\r' instead. Currently the test code I have here doesn't have this fix, so beware. Also, he is looking for testers for his kernel module so contact him if you are interested.

  • 19981109: Molecular Viewer App using SpaceOrb.
    Mark Harris has integrated the SpaceOrb into the O Molecular Viewer See http://alpha2.bmc.uu.se/markh/notes/joystick.html for more info.

  • 8/97: SpaceOrb XInput Driver
    written by Louis Kruger louie@louie.org
    Down load it from his site:
    http://www.cs.princeton.edu/~lpkruger/spaceorb/

  • 7/97: SpaceOrb Driver in Linux SVGAlib
    written by Eric Sharkey sharkey@fatman.physics.sunysb.edu
    Down load it from sunsite:
    libs (328k) or source (638k)




  • For those who do not know, the orb is a serial device that provides 10 bit digitization of 3 translational and 3 rotational movements, or 6 degrees of freedom. SpaceTec has come up with an excellent game controller for 3D games like Quake, Descent, etc. Unfortunately...

    SpaceTec hasn't released the protocol that the orb uses, and can't currently support cool OSes, like Linux. This protocol is needed to write 3rd party free (in the GPL sense) software. SpaceTec's lack of Un*x support in general, and Linux in particular, leaves out a large number of potential users, but this is a business decision and I am definitely not one to understand the making of money. So this is (thankfully) their problem.

    My problem, is that I thought it might be a cool thing to write a 3D event display program for the research experiment I am getting my PhD on (ie, an excellent way to waste time I should be spending on my research, not to mention eating and sleeping).

    So I set out to see what work on this (at the time) new controller has been done out in net land. I didn't find much except for a few other interested people. With their help and a lot of staring at the output of the serial port, the orb's protocol has been understood. What to do with it now, is another story, but I thought others would be interested in this first chapter.

    Below is the results of this work. The information was obtained by treating the Orb as a black box (or, better yet, a black ball). The protocol was figured out with out any input from SpaceTec, so it is free from any legal restraints. However, I don't guarantee anything. See the GPL for law-crud specifics, and have fun.

    So on to how the SpaceOrb works.


    The below is from my experiences. If this information is incorrect for you or has other errors, please let me know. Also, if you have more information, I would like to hear of it.


    The orb's packet structure:

    The very first byte of a newly initialized orb is `\r'. After this, the orb will send three kind of packets:
    1. A one time only greeting giving version info and SpaceTec's trade mark. This, I call an `R' packet. (For why, see below.)

    2. A button press/release with out any orb motion. This is a `K' packet.

    3. Orb motion with current button state. This is a `D' packet.

    These packets are usually delimited by a Carriage Return. However, sometimes there will be more than one packet before a terminating CR. Also, usually it will take more than one call to read(2) before an entire packet is read from the serial port.

    The packets are of, so called, type `R', `K' or `D' because their first byte is one of these values when printed as ASCII characters. It is this byte that can be used to anticipate what will follow, when reading in the packet.

    The structure of each packet type is expanded on below:

    `R' packet
    The greeting packet is 52 bytes long, including the terminating CR. For instance, my orb prints:
    R Spaceball (R) V4.26 28-Jun-96 Copyright (C) 1996z\r
    
    The `R' is the packet flag.

    `K' packet
    The button press/release packet is a 6 byte packet, including the terminating CR. This packet contains information about a button press or release event. There is no information about motion. It's flag is 75, or ASCII `K'. The values of the packet are as such:
    [`K'] [t] [buttons] [0] [??check sum??]
    Each element in a [ ] pair is a byte. Here are the meanings:
    [`K']
    The packet flag.

    [t]
    The time, in milliseconds from the last button event. Values are 0 - 100, although, usually > 3. If there is no button press in 100 ms, a packet showing current button state and 100 ms elapsed time will be sent.

    [buttons]
    The state of the buttons just before, and triggering, the packet. The state of buttons is an OR of all buttons pressed. The value of individual buttons are as follows:

    • 0: No buttons
    • 1: Button A
    • 2: Button B
    • 4: Button C
    • 8: Button D
    • 16: Button E
    • 32: Button F
    • 64: Reset Button. (This button is on the bottom and will reset the current orb position to zero.)

    [0]
    This byte is always zero, as far as I can tell. Maybe reserved for future expansion.
    [??check sum??]
    This byte appears to be a check sum, although I haven't really looked into this. (Anyone?)

    `D' packet
    The motion packet has been the most difficult packet to figure out. This is mostly due to the fact that the 6 degrees of motion data is encoded into 10 bits spread out over 9 bytes. To further complicate matters, the string ``SpaceWare'' is XORed into the data. However, armed with this information and a bit of bit twiddling, one can gain access to the motion packet.

    The packet is laid out like this:

    [`D] [buttons] [(9 bytes of data)] [??check sum??]
    Here are the explanations of the individual bytes.
    [`D']
    The packet flag.

    [buttons]
    The state of the buttons. See above for button explanation. If this state changes while there is motion, it will trigger a `K' packet.

    [(9 bytes of motion data)]
    This is the meat of the packet. It contains 9 7bit bytes, with the MSB unneeded. To turn these into translations and rotations, one needs to XOR these 9 bytes with the 9 characters from the string ``SpaceWare'', ignore the MSB of each bytes and strip off 10 bits for each d.o.f. Here is an attempt to show the structure:
        c[0]    c[1]    c[2]    c[3]    c[4]    c[5]    c[6]    c[7]    c[8]
    xdddddddxdddddddxdddddddxdddddddxdddddddxdddddddxdddddddxdddddddxddddddd 
    xdddddddxddd           dxdddddddxdd           ddxdddddddxd
          t[0]  ddddxdddddd       t[2] dddddxddddd      r[1]  ddddddxdddd ddd 
                      t[1]                   r[0]                   r[2]
    
    c[0] through c[8] are the 9 7bit bytes. An x signifies a MSB that should be ignored but is left in the diagram to show the structure. A d is a data bit. The t[0] through t[2] are the translational degrees of freedom and the r[0] through r[2] are the rotational. Again, the bit marked x is not included in to the d.o.f. value but is only showed in the diagram to show the structure. All bits more significant than the bit at the x are right shifted (>> in C). Once the 10 bit values are formed, any value over 511 should have 1024 subtracted from it in order to express the negative range of motion. This will then give values -512 to 511 for t[i] and r[i].

    [??check sum??]
    Again, this byte appears to be a check sum and seems to be Mored with the character `!'.
    This is the format of the data, but one must be able to read the data from a correctly configured serial port. Below is how to do that.

    Format of serial data:

    The orb sends data in 7bit characters, at 9600 bps, however, I have found that setting the serial port to 8bit, no parity, I am able to distinguish CR from a data character that accidentally is 13. After reading in a packet, all data characters can be ANDed with 0177 (octal) to mask off the high bit.

    To figure out how to do this, I looked in the source code to Miquel van Smoorenburg's minicom and basically did cut and paste programming. Much appreciation goes out to van Smoorenburg for placing minicom under a license that lets me do this (GPL)! Because of this, I suppose the code that I append to this page is also covered by such a license. I am no lawyer, nor do I want to be so I will also say that in addition to GPL I grant anyone the right to do anything with this code that suits their desire as long as it doesn't infringe on the GPL. Basically have fun, but be cool.

    Anyways, I still don't understand everything, and new nothing when I started this, about serial ports, so I will just let the code explain for those that care (see below).

    Acknowledgments

    I would like to thank some of the people who helped me get this code working, with out whom, I would have had a much tougher time finishing, if I finished it at all. So in no particular order, Jason Lyons (JasonL@warwick.net, http://www.geocities.com/SiliconValley/Lab/2353) for helpful email and suggesting to use minicom to read the orb directly. This led to tearing out minicom code to set up the serial port. Mickael Sweet (mike@easysw.com) for his helpful serial page: http://www.easysw.com/~mike/serial/ and suggestions. My office mate, Eric Sharkey (sharkey@superk.physics.sunysb.edu) for general unix hacking solutions. And as I say above, much thanks go to Miquel van Smoorenburg for the serial port initialization code. If I have forgotten anyone, my apologies.

    The Code

    Well, enough of the drivel, here is the code. It is in 2 parts, the routines that do the work and a test program. To compile, just do:

    gcc -I./ -g -Wall -c -o sorb.o sorb.c
    gcc -I./ -g -Wall -c -o sorbT.o sorbT.c
    gcc -I./ -g -Wall -o sorb sorb.o sorbT.o
    
    (You can, of course, skip the debugging/warning flags)

    If you want to browse the code, the routines for initializing and reading are in sorb.c, the test program is in sorbT.c and this header file, sorb.h is needed. If you want to download this source, a Makefile and a precompiled Linux ELF executable, grab, sorb.tgz

    To use, plug in your SpaceOrb to a free serial port. sorb will default to /dev/ttyS0, to use another device use the -d /dev/ttyxx option. For other options, see the sorbT.c source.

    You don't have to, but if anyone uses/improves on this code I would be interested to hear about your work.

    This was developed on Linux, and may or may not work on other flavors of Unix.

    Enjoy,
    Brett Viren

    bviren@superk.physics.sunysb.edu
    http://bock.physics.sunysb.edu/~bviren


    Last modified: Fri Apr 30 18:30:57 EDT 1999