Library to handle SpaceBall, SpaceMouse and SpaceOrb on serial port. Gets access to 3D rotation and translation vector as well as button status. (USB is not supported)

Dependents:   SpaceBall_Example

Library to handle SpaceBall, SpaceMouse and SpaceOrb on serial port. Gets access to 3D rotation and translation vector as well as button status. (USB is not supported)

All handling and decoding is done in the RX interrupt and the vector values can be read out asynchronously with different coordinate mappings.

Example:

#include "mbed.h"
#include "SpaceBall.h"
 
PwmOut led[] = {(LED1), (LED2), (LED3), (LED4) };
SpaceBall SBall(p9, p10);   // tx, rx, bSOrb
 
int main() {
    SBall.Init();
    
    while(1) {
 
        led[0] = abs( SBall[TX] ) + abs( SBall[TY] ) + abs( SBall[TZ] );
        led[1] = abs( SBall[RX] );
        led[2] = abs( SBall[RY] );
        led[3] = abs( SBall[RZ] );
        
        wait_us(500);
    }
}

In this exaple the 4 LEDs are powered dependent on force at the Spaceball. LED1 shows the sum of all translation forces. LED2 to LED4 shows the rotation forces.

For more information about SpaceBall devices see manufactorers page http://www.3dconnexion.com

For connecting a SpaceBall (or SpaceMouse or SpaceOrb) to mbed see page wiki/Serial-Connection

Example: SpaceBall 4000

/media/uploads/jocis/spaceball1.jpg

SB.cpp

Committer:
jocis
Date:
2014-09-03
Revision:
4:f953792e45cb
Parent:
2:a7c0fcd157f7

File content as of revision 4:f953792e45cb:

#include "SpaceBall.h"

#define Sleep(x) wait_ms(x)

//extern Serial pc;

///////////////////////////////////////////////////////////////////////////////

/*
    Convert characters to nibbles
*/
int DecodeSM(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);
}

///////////////////////////////////////////////////////////////////////////////

void SpaceBall::InitSB()
{
    m_resetoccured=0;

    m_spaceball4000 = 0; /* re-determine which type it is     */
    m_leftymode4000 = 0; /* re-determine if its in lefty mode */

    _fScaleT = 1.0 / 5000.0;
    _fScaleR = 1.0 / 2000.0;

    if (!m_resetoccured) {
        m_resetoccured=1;
        _serial.printf ( "@\r" ); /* force reset */
        }

    Sleep(10);

   // pc.printf("Sending initialization sequence to spaceball...\n");

    _serial.printf ( "CB\r" );
    Sleep(10);
    _serial.printf ( "NT\r" );
    Sleep(10);
    _serial.printf ( "FTp\r" );
    Sleep(10);
    _serial.printf ( "FRp\r" );
    Sleep(10);
    _serial.printf ( "P@r@r\r" );
    Sleep(10);
    _serial.printf ( "MSSV\r" );
    Sleep(10);
    _serial.printf ( "Z\r" );
    Sleep(10);
    _serial.printf ( "BcCcC\r" );
    
    Sleep(10);
    _serial.printf ( "\rvz\r" );
    Sleep(10);
    _serial.printf ( "vQ\r" );
    Sleep(10);
    _serial.printf ( "kQ\r" );
    Sleep(10);
    _serial.printf ( "m3\r" );
}

///////////////////////////////////////////////////////////////////////////////

void SpaceBall::ProcessSB ( char data )
{
    switch (data) {
        case '\r':   //0xd:
            _data[_idx] = 0;
            ProcessPacketSB();
            _idx = 0;
            _escape = 0;
            break;
        case '^':
            if (!_escape) {
                _escape = 1;
                break;
            }
            _escape = 0;
        case 'M':
        case 'Q':
        case 'S':
            if (_escape) {
                _escape = 0;
                data &= 0x1f;
            }
        default:
            if (_escape)
                _escape = 0;
            if (_idx < SPACEBALL_MAX_LENGTH)
                _data[_idx++] = data;
            break;
    }
}

///////////////////////////////////////////////////////////////////////////////

void SpaceBall::ProcessPacketSB ( void )
{
    int i;

    if (_idx < 2) return;

    switch (_data[0]) {

        case 'D':                                       /* Ball data */
            if (_idx != 15) return;
            for (i = 0; i < 6; i++) {
                _axis[i] = (short)( (_data[2*i + 3] << 8) | _data[2*i + 2] );
            }
            DoChangedAxis();
            break;

        case 'd':                                       /* Ball data */
        //pc.printf("d%d",_idx);
            if (_idx != 25) return;
            for (i = 0; i < 6; i++) {
                _axis[i] = (short)( (
                    DecodeSM(_data[4*i+4]) | (DecodeSM(_data[4*i+3])<<4) | (DecodeSM(_data[4*i+2])<<8) | (DecodeSM(_data[4*i+1])<<12) )
                    ^ 0x8000 );
            }
            DoChangedAxis();
            break;

        case 'K':                                       /* Button data */
            if (_idx != 3) return;
            /* Spaceball 2003A, 2003B, 2003 FLX, 3003 FLX, 4000 FLX       */
            /* button packet. (4000 only for backwards compatibility)     */
            /* The lowest 5 bits of the first byte are buttons 5-9        */
            /* Button '8' on a Spaceball 2003 is the rezero button        */
            /* The lowest 4 bits of the second byte are buttons 1-4       */
            /* For Spaceball 2003, we'll map the buttons 1-7 normally     */
            /* skip 8, as its a hardware "rezero button" on that device   */
            /* and call the "pick" button "8".                            */
            /* On the Spaceball 3003, the "right" button also triggers    */
            /* the "pick" bit.  We OR the 2003/3003 rezero bits together  */

            /* if we have found a Spaceball 4000, then we ignore the 'K'  */
            /* packets entirely, and only use the '.' packets.            */
            if (m_spaceball4000)
                break;

            _buttons =
                ((_data[1] & 0x10) <<  8) | /* 2003 pick button is ??? */
                ((_data[1] & 0x20) <<  9) | /* 3003 rezero button      */
                ((_data[1] & 0x08) << 11) | /* 2003 rezero button      */
                ((_data[1] & 0x07) <<  4) | /* 5,6,7    (2003/4000)    */
                ((_data[2] & 0x30) <<  8) | /* 3003 Left/Right buttons */
                ((_data[2] & 0x0F));        /* 1,2,3,4  (2003/4000)    */

            DoChangedButtons();
            break;

        case 'k':                                       /* Button data */
        //pc.printf("k%d",_idx);
        //pc.printf("<%s>\r\n",_data);
            if (_idx != 4) return;
            _buttons = DecodeSM(_data[1]) | (DecodeSM(_data[2])<<4) | (DecodeSM(_data[3])<<8);

            DoChangedButtons();
            break;

        case '.':                                       /* Advanced button data */
            if (_idx != 3) return;
            /* Spaceball 4000 FLX "expanded" button packet, with 12 buttons */

            /* extra packet validity check, since we use this packet type */
            /* to override the 'K' button packets, and determine if its a */
            /* Spaceball 4000 or not...                                   */

            /* if we got a valid '.' packet, this must be a Spaceball 4000 */
            m_spaceball4000 = 1; /* Must be talking to a Spaceball 4000 */

            /* Spaceball 4000 series "expanded" button press event      */
            /* includes data for 12 buttons, and left/right orientation */
            _buttons =
                (((~_data[1]) & 0x20) << 10) |  /* "left handed" mode  */
                ((_data[1] & 0x1F) <<  7)    |  /* 8,9,10,11,12        */
                ((_data[2] & 0x3F)      )    |  /* 1,2,3,4,5,6 (4000)  */
                ((_data[2] & 0x80) >>  1);      /* 7           (4000)  */

            /* set "lefty" orientation mode if "lefty bit" is _clear_ */
            if ((_data[1] & 0x20) == 0)
                m_leftymode4000 = 1; /* left handed mode */
            else
                m_leftymode4000 = 0; /* right handed mode */

            DoChangedButtons();
            break;

        case 'E':                                       /* Device error */
        case 'e':                                       /* Device error */
            //printk(KERN_ERR "spaceball: Device error. [%s]\n", spaceball->data + 1);
            break;

        case '?':                                       /* Bad command packet */
            //printk(KERN_ERR "spaceball: Bad command. [%s]\n", spaceball->data + 1);
            break;

        case 'C': /* Communications mode packet */
        case 'F': /* Spaceball sensitization packet */
        case 'P': /* Spaceball update rate packet */
        case 'M': /* Spaceball movement mode packet */
        case 'N': /* Null region packet */
        case '\r': /* carriage return at poweron */
        case '\v': /* XON at poweron */
            /* eat and ignore these packets */
            break;

        case '@': /* Reset packet */
        case 'z': /* Reset packet */
            /* if we get a reset packet, we have to re-initialize       */
            /* the device, and assume that its completely schizophrenic */
            /* at this moment, we must reset it again at this point     */
            m_resetoccured=1;
            //Init();
            break;

        case 'b': /* Beep */
        case 'c': /*  */
        case 'p': /*  */
        case 'm': /* mode changed */
        case 'n': /* Zero radius */
        case 'q': /* Sensitivity */
        case 'v': /* Version number follows */
            /* eat and ignore these packets */
            break;
            
        default:
            //pc.printf("<%s>\r\n",_data);
            break;
    }
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////