Subject: Mac-Glove code

From: Jonathan Blossom <[email-masked]>
Date: Thu, 23 Jan 92 13:41:49 EST

Hello! As promised, here are my Macintosh (THINK C 5.0) routines
for reading the glove through the Mac's modem or printer port.
  My Power Glove is connected to a Motorola EVBU, which talks to the
Mac through an RS-232 line. The EVBU is centered around a 68HC11
controller, for which Ron Menelli has written code that will put the
glove in hi-res mode and read data packets. That code is available in
the glove-list-archives at the karazm ftp site.
   I chose the EVBU rather than the EVB because it was almost
identical and about half price (I paid ~$85 for mine). Unfortunately,
it only includes the 512k of EEPROM on the 'HC11 chip itself - not
much room for user programs - but it's enough to fit version one of
Ron's code.
   Somewhere in the archives is a diagram of which glove pin to
connect to which EVBU pin. Ilong ago chopped the Nintendo connector
off my glove and soldered it direct. You can power the glove directly
off the EVB/EVBU board if you want, too. That's what I'm doing.
   The glove pin number (wire color) - EVB/EVBU pin number connections
are:
      (Looking at the glove connector:   1 2 3 4
					   7 6 5)
	Glove:			EVBU:
	pin 1 (black)		pin 59/60		GND
	pin 2 (orange)		pin 30			Clock
	pin 3 (yellow)		pin 29			Latch (Reset)
	pin 4 (green)		pin 34			Data
	pin 5 and 6		--unconnected--
	pin 7 (red)		pin 57/58		+5v

   All of this and the necessary code is available in the archive. If
you have questions, I'd be happy to do what I can to answer them.
   Have fun!
       -Jon

-----cut here for glove.h-----
/***********************************************************************
   Macintosh - PowerGlove interface code
     Written for a Macintosh connected throught the modem port to a
Motorola EVBU
   running Ron Menelli's code to read the Glove. Expects the glove to
be running
   *before* it is called.
     This includes the deglitching and dehysterisis code written by
Dave Stampe
   ([email-masked]) 17 Oct 1991, available in the
glove-list-archive
   at the karazm.math.uh.edu ftp site.

     This code is *not* completely tested! It seems to work fine in
providing data,
   but the serial I/O could be beefed up a bit. I haven't tested the
button codes;
   they're direct from Dave Stampe's stuff. Let me know if you
encounter any
   errors.
   
     Good Luck!
     Jon Blossom ([email-masked])  20 January, 1992
 ***********************************************************************/

#ifndef _POWER_GLOVE_
#define _POWER_GLOVE_

#include <Serial.h>

#define A_BUTTON        0x0A        /* Glove button codes */
#define B_BUTTON        0x0B
#define START_BUTTON    0x82
#define SELECT_BUTTON   0x83
#define CENTER_BUTTON   0x00
#define UP_BUTTON       0x0D
#define DOWN_BUTTON     0x0E
#define LEFT_BUTTON     0x0C
#define RIGHT_BUTTON    0x0F

typedef struct glove {
    short   x, y, z;                        /* x,y: 3mm precision, z:

14mm */
    short   rot;                            /* rotation 0-11, in 30-40

degree incs */
    short   thumb, index, middle, ring;     /* bend in fingers 0-3, 3
= tight bend */
    short   buttons;
} GloveState;

void    OpenGlove(void);                /* Initialize the power glove
*/
void    CloseGlove(void);               /* Close the serial port
*/
void    GetGlove(GloveState *s);        /* Read the current glove
state */

extern GloveState   theGlove;           /* Handy global for storing
current state */

#endif _POWER_GLOVE_

-----cut here for glove.c-----

/***********************************************************************
   Macintosh - PowerGlove interface code
     Written for a Macintosh connected throught the modem port to a
Motorola EVBU
   running Ron Menelli's code to read the Glove. Expects the glove to
be running
   *before* it is called.
     This includes the deglitching and dehysterisis code written by
Dave Stampe
   ([email-masked]) 17 Oct 1991, available in the
glove-list-archive
   at the karazm.math.uh.edu ftp site.

     This code is *not* completely tested! It seems to work fine in
providing data,
   but the serial I/O could be beefed up a bit. I haven't tested the
button codes;
   they're direct from Dave Stampe's stuff. Let me know if you
encounter any
   errors.
   
     Good Luck!
     Jon Blossom ([email-masked])  20 January, 1992
 ***********************************************************************/

#include "glove.h"

#define PACKETSIZE 6                /* We'll be reading 6-byte packets
from the 
SCC */
#define QUERYCHAR "?"               /* Character to request a packet
from glove */
#define BUFSIZE 64                  /* Size of serial port buffer
(arbitrary) */
#define RING_MASK   0x03            /* Masks for reading finger data
*/
#define MIDDLE_MASK 0x0C
#define INDEX_MASK  0x30
#define THUMB_MASK  0xC0

#define XHYST 2                     /* Constants for hysterisis noise

reduction */
#define YHYST 2                     /* 2 eliminates +/-3 quanta of
noise
        */
#define XACC 8                      /* X, Y maximum accel/decel level.

Should  */
#define YACC 8                      /* be 6-10, but too high limits 
gesturing   */
#define XXTEND 2                    /* Stretches deglitching time

        */
#define YXTEND 1

#define PORTIN "\p.AIn"             /* Modem port. Replace with
"\p.Bin" and */
#define PORTOUT "\p.AOut"           /* "\p.Bout" for the Mac printer
port.   */

static char gloveData[PACKETSIZE];      /* Glove data buffer */
static short inRefNum = 0;              /* Current input reference
number */
static short outRefNum = 0;             /* Current output reference
number */
static char *inBuffer;                  /* Input Buffer */

GloveState  theGlove;                   /* Global glove state variable
*/

/********************************
  Macintosh Serial Port Routines
 ********************************/

void    CloseSerial(void)
    /* Close input and output serial drivers. Be sure to do this
before exiting
        your application! The Macintosh OS does not take care of it
for you, and
        it could cause some problems. */
{
    if (inRefNum != 0) {
        CloseDriver(inRefNum);
    }
    if (outRefNum != 0) {
        CloseDriver(outRefNum);
    }
}

Boolean OpenSerial(void)
    /* Open the modem port and set it to read the EVBU 
       Return FALSE if there is an error */
{
    OSErr   err;
    short   setup;

    err = OpenDriver(PORTIN, &inRefNum);            /* Open port input
*/
    if (err == noErr) {
        err = OpenDriver(PORTOUT, &outRefNum);      /* Open port
output */
        if (err != noErr) {                         /* Output open 
error */
            CloseDriver(inRefNum);
            inRefNum = outRefNum = 0;
        }
    }
    if (err != noErr) return FALSE;

        /* Now set port to 9600 baud, 8 data bits, no Parity, 1 stop
bit */
        /* This is the standard configuration for the EVBU.
 */
    setup = baud9600 | data8 | noParity | stop20;
    err = SerReset(outRefNum, setup);
    if (err == noErr)
        err = SerReset(inRefNum, setup);
    if (err != noErr) return FALSE;
    
        /* Create an input buffer for data read from the serial port
*/
    inBuffer = NewPtr(BUFSIZE);
    err = SerSetBuf(inRefNum, inBuffer, BUFSIZE);
    if (inBuffer == NULL && BUFSIZE != 0)
            err = -1;
    
    if (err != noErr) return FALSE;
    else return TRUE;
}

long    GetSerial(char *buffer)
    /* Read bytes from the port into the given buffer, return  of
bytes read */
{
    IOParam         PortPB;
    long            bytes;
    OSErr           err;
    
    err = SerGetBuf(inRefNum, &bytes);
    if (err == noErr && bytes > 0) {
        PortPB.ioRefNum = inRefNum;
        PortPB.ioBuffer = buffer;
        PortPB.ioReqCount = bytes;
        err = PBRead(&PortPB, FALSE);
    }
    if (err != noErr)
        bytes = -1;
    return bytes;
}

OSErr   PutSerial(char *buffer, long bytes)
    /* Send bytes from the buffer to the port */
{
    IOParam     PortPB;
    
    PortPB.ioRefNum = outRefNum;
    PortPB.ioBuffer = buffer;
    PortPB.ioReqCount = bytes;
    return PBWrite(&PortPB, FALSE);
}

/****************
  Glove Routines
 ****************/

void    OpenGlove(void)
    /* Open the serial connection to the glove, make sure the EVBU is
in 
        request mode */
{
    OpenSerial();                           /* Open serial port */
    PutSerial("R", 1);                      /* Make sure glove is in 
request mode */
}

void    CloseGlove(void)
{
    CloseSerial();
}

void    deGlitch(GloveState *s)
    /* Perform deglitching on the glove data - remove spikes and jumps
by smoothing.
        From Dave Stampe's code */
{
    static int  x1 = 0, y1 = 0;         /* Last sampling */
    static int  x2 = 0, y2 = 0;         /* Second last sampling */
    static int  lx = 0, ly = 0;         /* Last good x, y speed */
    static int  lax = 0, lay = 0;       /* Bad data "stretch" counter
*/
    static int  lsx = 0, lsy = 0;       /* x, y "hold" values to
replace bad data */
    static int  lcx = 0, lcy = 0;       /* last x, y speed for
acceleration calc */
    int         vx, vy;
    int         x, y;
    
    x = s->x;
    y = s->y;
    
    if (s->buttons == 0) {              /* Reset on pushing "Center"
or "0" 
key */
        x1 = x2 = y1 = y2 = 0;
        lx = ly = lax = lay = 0;
        lsx = lsy = lcx = lcy = 0;
    }
    
    vx = x - ((x1+x2) >> 1);            /* Smoothed velocity */
    vy = y - ((y1+y2) >> 1);
    
    x2 = x1;
    x1 = s->x;
    y2 = y1;
    y1 = s->y;
    
    if (abs(lcx-vx) > XACC)             /* Check for extreme
acceleration */
        lax = XXTEND;
    if (lax == 0) lx = vx;              /* save only good velocity */
    lcx = vx;                           /* save velocity for next
accel. 
*/
    if (abs(lcy-vy) > YACC)
        lay = YXTEND;
    if (lay == 0) ly = vy;
    lcy = vy;
    
    if (lax != 0) {                     /* Hold x-position if glitch
*/
        s->x = lsx;
        lax--;
    }
    if (lay != 0) {                     /* Hold y-position if glitch
*/
        s->y = lsy;
        lay--;
    }
    
    lsx = s->x;                         /* Save position for x,y hold

*/
    lsy = s->y;
}
    
void    deHyst(GloveState *s)
    /* Remove low-level noise by adding hysterisis
        From Dave Stampe's code */
{
    static int  ox = -1000, oy = -1000;     /* last x,y for hysterisis
*/
    int         x, y;
    
    x = s->x;
    y = s->y;
    
    if (s->buttons == 0) ox = oy = 0;       /* Reset on "Center" or
"0" button */
    
    if (x-ox > XHYST) ox = x - XHYST;       /* X hysterisis */
    if (ox-x > XHYST) ox = x + XHYST;
    if (y-oy > YHYST) oy = y - YHYST;       /* Y hysterisis */
    if (oy-y > YHYST) oy = y + YHYST;
    
    s->x = ox;                              /* Replace data with 
good data */
    s->y = oy;
}

void    GetGlove(GloveState *s)
    /* Read a complete glove state from the serial port */
{
    short   fingers;
    
    PutSerial(QUERYCHAR, 1);        /* Read twice to make sure data is
current */
    GetSerial(gloveData);
    PutSerial(QUERYCHAR, 1);
    GetSerial(gloveData);
    s->x = gloveData[0];
    s->y = gloveData[1];
    s->z = gloveData[2];
    s->rot = gloveData[3];
    fingers = gloveData[4];                         /* Mask off 
individual fingers */
    s->thumb = (fingers & THUMB_MASK) >> 6;
    s->index = (fingers & INDEX_MASK) >> 4;
    s->middle = (fingers & MIDDLE_MASK) >> 2;
    s->ring = (fingers & RING_MASK);
    s->buttons = gloveData[5];
    
    deGlitch(s);
    deHyst(s);
}

---

From: [email-masked] (Tng Tai Hou)
Date: Fri, 24 Jan 92 09:32:15 SST

This is great stuff!!!
But just one more question. How does one get code into the controller
board? I am not hardware-inclined at all.

Thanks.