Help reading SCA3000

Hi everyone,

I’m trying to use the SCA3000 accelerometer breakout board that I got from SFE. I’m kind of a noob so I don’t have to much confidence with this, especially since it doesn’t seem to quite work right. This is my first time to try using SPI.

Any help is greatly appreciated!

Here’s the behavior I’m seeing:

  • essentially nothing seems to work until I read the MODE register (0x14)

  • a STATUS read (0x02) after the MODE command will shift subsequent reads so that…

  • reading the device ring-buffer style (6, 1 byte reads, 3 MSB/LSB pairs) spits out something resembling accellerometer data on the LSB

  • it doesn’t matter much what address is specified for subsequent reads after the MODE register is read: data returned is always ring-buffer style, except the MSB doesn’t make sense.

  • the LSB values only vary 0-40 for 1g, and go 255->215 for -1g, plus or minus 40 is a long way from the 1300 step resolution the device is supposed to have.

  • pulling the reset pin low for a millisecond resets the SCA3000, and data reads will just return Zeros until the MODE register is read again.

Here’s the hardware setup:

I’m using a Seeduino board running at 3.3v to simplify the interface. I’ve got the SCA300 on a protoshield board with the CSB pulled high and connected to pin 10. MOSI, MISO, and SCK are directly connected to 11, 12, and 13 respectively. I’ve also pulled the RST high and connected it to pin 8 so I could try that.

Software:

  • Arduino SPI library

  • I found some example SPI code for the Atmel168, but when I tried it, it just hung on the device SPI writes, making me wonder about the SPSR definition.

I modified my code so I can send the SCA3000 various commands, as well as let it run in a loop doing XYZ reads. The hope was that some interactive hacking would reveal the solution, but it didn’t.

Here’s my Arduino sketch (for the brave):

// SCA3000 accelerometer test program

// Trying to make sense of the SCA3000 chip with the SFE breakout board.

// I’m running a Seeduino set to 3.3v to simplify the interface

// CSB (pin 10) and RST (pin 8) are both pulled HIGH

// MOSI, MISO, and SCK are direct hookup (pins 11, 12, & 13)

// Serial Commands :

// Rh - read address h (hex)

// RM - read 0x14, the MODE register

// X - pull reset pin LOW for 1 millisecond then HIGH

// Mi - set mode to i;

// Z - read x,y,z buffers in loop

// B - read BUF_DATA in loop

// any other key - will break looped reads

// looped reads are doing a 11-bit integer conversion, but from what I

// have seen it might as well be a 8-bit because the last 3 bits of the MSB

// seem to always be zero.

// the XYZ looped read is setup to display the upper and lower bytes in binary

/* Mystery demo sequence:

- start program

- send a Z command

- observe data stream, almost makes sense: values vary 0-42, 255-212 or so for +/- 1g

this isn’t at all what the datasheet says it outputs

- send a X command, resets SCA3000

- send a Z command: only reads zeros

- send a X command, resets SCA3000

- send a RM command: reads the MODE register

- send a R2 command: reads the 0x02 STATUS register

- send a Z command: now getting data again

- try skipping the STATUS read and notice how the XYZ bytes get shifted off

(and how the upper bytes don’t make any sense)

*/

#include <Spi.h>

#define UBLB(a,b) ( ( (a) << 8) | (b) )

#define UBLB19(a,b) ( ( (a) << 16 ) | (b) )

//Register Addresses from the SCA300 spec sheet

#define REVID 0x00 //ASIC Revision Number

#define OPSTATUS 0x02 //Operation Status

#define X_LSB 0x04 // x-axis LSB

#define X_MSB 0x05 // x-axis MSB

#define Y_LSB 0x06 // y-axis LSB

#define Y_MSB 0x07 // y-axis MSB

#define Z_LSB 0x08 // z-axis LSB

#define Z_MSB 0x09 // z-axis MSB

#define BUF_DATA 0x0F // Ring buffer output register

#define TEMP_LSB 0x12 // temperature LSB

#define TEMP_MSB 0x13

#define MODE 0x14 // Operational mode: mode selection, output buffer, freefall detection

#define BUF_COUNT 0x15 // number of unread data samples

#define INT_STATUS 0x16 // interrupt status register

#define I2C_RD_SEL 0X17 //Register address for I2C read operation

#define CTRL_SEL 0X18 //Register address pointer for indirect control registers.

#define UNLOCK 0x1E //Unlock register

#define INT_MASK 0x21 //Interrupt mode configuration mask

#define CTRL_DATA 0x22 //data to/from which conf address is in CTRL_SEL register

#define BUF_EN (1<<7)

#define BUF_8BIT (1<<6)

#define FFD_EN (1<<4)

#define RESET_PIN 8

byte b1,b2;

int Ax,Ay,Az;

unsigned int temp,n;

char ibuf[10];

int c=0;

short s;

int val;

char cmd;

byte addr;

boolean xyz=false;

boolean buf_read=false;

char* fmtSpace(int i, char *buf)

{

int j,k;

for (j=0;j<7;j++) buf[j]=0;

itoa(i,buf,10);

j=0;

if (i<10000) j=1; // less than 5 characters, 1 space

if (i<1000) j=2;

if (i<100) j=3;

if (i<10) j=4;

for (k=5-1;k>=0 ;k–)

{

buf[k]= (k-j>=0)? buf[k-j] : ’ ';

}

return buf;

}//end fmtSpace

void binaryPrint(byte in_b)

{ int i,j;

byte b;

for (i=7;i>=0;i–)

{

if (in_b & 1<<i)

Serial.print(‘1’);

else

Serial.print(‘0’);

}

}//end binaryPrint

void readXYZregisters()

{

// if (!c) // my loop counter

// Serial.println("Ax Ay Az ");

b1 = Spi.transfer(0x05); //x axis msb

b2 = Spi.transfer(0x04); //x axis lsb

binaryPrint(b1);

Serial.print(’ ');

binaryPrint(b2);

b1 &= B00000111; //just 11 bit so clear all but last 3 of msb

Ax = UBLB(b1,b2);

b1 = Spi.transfer(0x07); //y axis msb

b2 = Spi.transfer(0x06); //y axis lsb

Serial.print(" ");

binaryPrint(b1);

Serial.print(’ ');

binaryPrint(b2);

b1 = b1 & B00000111; //just 11 bit so clear all but last 3 of msb

Ay = UBLB(b1,b2);

b1 = Spi.transfer(0x09); //y axis msb

b2 = Spi.transfer(0x08); //y axis lsb

Serial.print(" ");

binaryPrint(b1);

Serial.print(’ ');

binaryPrint(b2);

b1 &= B00000111; //just 11 bit so clear all but last 3 of msb

Az = UBLB(b1,b2);

fmtSpace(Ax,ibuf);

Serial.print(" ");

Serial.print(ibuf);

fmtSpace(Ay,ibuf);

Serial.print(ibuf);

fmtSpace(Az,ibuf);

Serial.println(ibuf);

c++;

c = c % 20;

}

float getTemp()

{

float temp_in;

int b1, b2;

b1 = Spi.transfer(0x12); //msb

b2 = Spi.transfer(0x13); //lsb

// temp_in = read_register16(TEMP);

temp_in = float(int(UBLB(b1,b2)));

temp_in = temp_in -256*b2;

temp_in = (temp_in/(1.8 *b2)) + 23;

return temp_in;

}

void readRingBuffer()

// I’m sure this is wrong, but it’s just a stab at what happens if I

// do repeated reads of a given address

{

b1 = Spi.transfer(BUF_DATA); //x axis msb

b2 = Spi.transfer(BUF_DATA); //x axis lsb

binaryPrint(b1);

Serial.print(’ ');

binaryPrint(b2);

b1 &= B00000111; //just 11 bit so clear all but last 3 of msb

Ax = UBLB(b1,b2);

b1 = Spi.transfer(BUF_DATA); //y axis msb

b2 = Spi.transfer(BUF_DATA); //y axis lsb

Serial.print(" ");

binaryPrint(b1);

Serial.print(’ ');

binaryPrint(b2);

b1 = b1 & B00000111; //just 11 bit so clear all but last 3 of msb

Ay = UBLB(b1,b2);

b1 = Spi.transfer(BUF_DATA); //z axis msb

b2 = Spi.transfer(BUF_DATA); //z axis lsb

Serial.print(" ");

binaryPrint(b1);

Serial.print(’ ');

binaryPrint(b2);

b1 &= B00000111; //just 11 bit so clear all but last 3 of msb

Az = UBLB(b1,b2);

fmtSpace(Ax,ibuf);

Serial.print(" ");

Serial.print(ibuf);

fmtSpace(Ay,ibuf);

Serial.print(ibuf);

fmtSpace(Az,ibuf);

Serial.println(ibuf);

} // end readRingBuffer

int charToHex(char ch)

{

switch (ch) {

case ‘0’:

return 0x00;

case ‘1’:

return 0x01;

case ‘2’:

return 0x02;

case ‘3’:

return 0x03;

case ‘4’:

return 0x04;

case ‘5’:

return 0x05;

case ‘6’:

return 0x06;

case ‘7’:

return 0x07;

case ‘8’:

return 0x08;

case ‘9’:

return 0x09;

case ‘F’:

return 0x0F; //BUF_DATA

case ‘T’:

return 0x12;

case ‘U’:

return 0x13;

case ‘M’:

return 0x14; //mode

default:

return -1;

}

}

void setup()

{ Serial.begin(9600);

delay(20);

// Serial.print("RevID: ");

// Serial.println(Spi.transfer(0x00),DEC);

pinMode(RESET_PIN,OUTPUT);

digitalWrite(RESET_PIN,LOW);

delay(1);

digitalWrite(RESET_PIN,HIGH);

n = Spi.transfer(OPSTATUS);

Serial.print("status = ");

Serial.println(n);

int n = Spi.transfer(MODE);

Serial.print("mode = ");

Serial.println(n);

n = Spi.transfer(OPSTATUS);

Serial.print("status = ");

Serial.println(n);

delay(1000);

}// end setup

void loop()

{

if (xyz) readXYZregisters();

if (buf_read) readRingBuffer();

if (Serial.available())

{ delay(10);

buf_read=false;

xyz=false;

cmd = Serial.read();

Serial.print(cmd);

if (Serial.available())

{

addr = charToHex(Serial.read());

Serial.print(addr,HEX);

}

else

addr = -1;

while (Serial.available()) Serial.read(); // toss the rest

Serial.println();

switch (cmd) {

case ‘X’:

digitalWrite(RESET_PIN,LOW);

delay(1);

digitalWrite(RESET_PIN,HIGH);

break;

case ‘R’:

val = Spi.transfer(addr);

binaryPrint(val);

Serial.print(’ ');

Serial.println(val,DEC);

break;

case ‘M’:

Spi.mode(addr);

Serial.print("Mode returned: ");

Serial.println(val,DEC);

break;

case ‘B’:

buf_read=true;

break;

case ‘Z’:

xyz = true;

break;

}

}

}

No doubt I’m not the first to say that they could right the spec manuals a little clearer; nevertheless, after a week or so of reading it over and over :shock: Actually it was the product demo kit user manual macro descriptions that clued me in. I finally figured out the appropriate bit wrangling of the register address (<<2)and the returned results(>>3). I also managed to incorporate some sample code for a 16bit transfer which got the axis reads working.

If anyone wants my updated code I’ll post it.

I also just picked up one of these and would be greatly interested in the updated code. Im using the sca3000 breakout and arduino, first test will be through the default port’s. But im planning on running more than one with uniqe ss id’s. (by making any digitalport MISO MOSI,SS,SCLK etc)

Your help would be greatly appreciated.

I’ve written a sketch that takes serial commands and makes a stab at a calibration routine (saving various orientation values to EEPROM).

I noticed that the -1G and +1G values are different for each axis on my unit (slowly rotating the unit to observe the maximum or minimum value while holding the unit otherwise still) so clearly there is not only pitch/roll calibration but also normalization prior to pitch/roll calculations.

Any math wiz’s out there that would like to contribute to the pitch/roll calculation for calibration, the help would be greatly appreciated.

Notice that the readXYZ() function averages the data to reduce noise…

// SCA3000Cal
// accelerometer calibration routine - saves orientations to EEPROM

#include <Spi.h>
#include <EEPROM.h>
#define n_accel_cmds 6
// all commands are prefixed "AX"
char accel_cmd_tbl[n_accel_cmds][4]={"XYZ","CAL","CFG","ANG","TEM","LOG"};
#define accel_xyz 0
#define accel_cal 1
#define accel_cfg 2
#define accel_ang 3
#define accel_temp 4
#define accel_log 5
#define cmdline_size 20
#define max_parms 2
char cmdline[cmdline_size]; //don't need too many characters toss em if there's more
char parm[max_parms][7];
short cmd;
short cmd_motor;
short n_parms;
boolean prompt=true;
boolean echo=true;
boolean logging=false;
/*******************************************************************/

struct config_t {
  byte  UnitNo;   //0x00
  byte  UnitSize; //0x01
  long	SerialNo; //0x02
  char  Model[8]; //0x06
  byte	Mode;     //0x0E
  int   TopX;     //0x0F
  int   TopY;
  int   TopZ;
  int   BottomX; //0x
  int   BottomY;
  int   BottomZ;
  int	FrontX;  //0x
  int	FrontY;
  int	FrontZ;
  int	BackX;   //0x
  int   BackY;
  int   BackZ;
  int   LeftX;   //0x27
  int   LeftY;
  int   LeftZ;
  int   RightX;  //0x
  int   RightY;
  int   RightZ;
} cfg;

template <class T> int EEPROM_writeAnything(int ee, const T& value)
{
    const byte* p = (const byte*)(const void*)&value;
    int i;
    for (i = 0; i < sizeof(value); i++)
	  EEPROM.write(ee++, *p++);
    return i;
}

template <class T> int EEPROM_readAnything(int ee, T& value)
{
    byte* p = (byte*)(void*)&value;
    int i;
    for (i = 0; i < sizeof(value); i++)
	  *p++ = EEPROM.read(ee++);
    return i;
}

void printConfigVals()
{
  EEPROM_readAnything(0x00,cfg);
  Serial.print("UnitNo: ");
  Serial.println(cfg.UnitNo,DEC);// = 1;
  Serial.print("Size: ");
  Serial.println(cfg.UnitSize,DEC);// = 16;
  Serial.print("Model: ");
  Serial.println(cfg.Model);
  Serial.print("Serial: ");
  Serial.println(cfg.SerialNo);// = 1000001;
  Serial.print("Mode: ");
  Serial.println(cfg.Mode,BIN);
  Serial.print("Top:");
  Serial.print(cfg.TopX);
  Serial.print(',');
  Serial.print(cfg.TopY);
  Serial.print(',');
  Serial.println(cfg.TopZ);
  Serial.print("Bottom: ");
  Serial.print(cfg.BottomX);
  Serial.print(',');
  Serial.print(cfg.BottomY);
  Serial.print(',');
  Serial.println(cfg.BottomZ);
  Serial.print("Front: ");
  Serial.print(cfg.FrontX);
  Serial.print(',');
  Serial.print(cfg.FrontY);
  Serial.print(',');
  Serial.println(cfg.FrontZ);
  Serial.print("Back :");
  Serial.print(cfg.BackX);
  Serial.print(',');
  Serial.print(cfg.BackY);
  Serial.print(',');
  Serial.println(cfg.BackZ);
  Serial.print("Left: ");
  Serial.print(cfg.LeftX);
  Serial.print(',');
  Serial.print(cfg.LeftY);
  Serial.print(',');
  Serial.println(cfg.LeftZ);
  Serial.print("Right: ");
  Serial.print(cfg.RightX);
  Serial.print(',');
  Serial.print(cfg.RightY);
  Serial.print(',');
  Serial.println(cfg.RightZ);
}

#define UBLB(a,b)  ( ( (a) << 8) | (b) )
#define UBLB19(a,b) ( ( (a) << 16 ) | (b) )
//Register Addresses from the SCA300 spec sheet
#define REVID 0x00	//ASIC Revision Number
#define OPSTATUS 0x02   //Operation Status
#define X_LSB 0x04      // x-axis LSB
#define X_MSB 0x05      // x-axis MSB
#define Y_LSB 0x06      // y-axis LSB
#define Y_MSB 0x07      // y-axis MSB
#define Z_LSB 0x08      // z-axis LSB
#define Z_MSB 0x09      // z-axis MSB
#define BUF_DATA 0x0F   // Ring buffer output register
#define TEMP_LSB 0x12   // temperature LSB
#define TEMP_MSB 0x13
#define MODE 0x14       // Operational mode: mode selection, output buffer, freefall detection
#define BUF_COUNT 0x15  // number of unread data samples
#define INT_STATUS 0x16 // interrupt status register
#define I2C_RD_SEL 0X17  //Register address for I2C read operation
#define CTRL_SEL 0X18    //Register address pointer for indirect control registers.
#define UNLOCK 0x1E      //Unlock register
#define INT_MASK 0x21    //Interrupt mode configuration mask
#define CTRL_DATA 0x22   //data to/from which conf address is in CTRL_SEL register

#define BUF_EN (1<<7)
#define BUF_8BIT (1<<6)
#define FFD_EN (1<<4)

#define RESET_PIN 8

byte b1,b2;
int Ax,Ay,Az;
unsigned int temp,n;
char ibuf[10];
int c=0;
short s;
int val;


/***************************************************************/

#define SPI_CSB 0x04 // This is PB2 at the port
#define SPI_PORT PORTB

// Function takes in the 8-bit register address and returns 16-bit register value.
int transfer16(unsigned int Address)
{
  int result;
  result = 0;
  Address = Address << 2; // RW bit is set to zero by shifting the bit
                        // pattern to left by 2
  SPI_PORT = SPI_PORT & (~SPI_CSB); // Set CSB to zero
  SPDR = Address; // Write command to SPI bus
  while(!(SPSR & (1 << SPIF))); // Wait until data has been sent

  SPDR = 0x00; // Write dummy byte to line
  // in order to generate SPI clocks for data read
  while(!(SPSR & (1 << SPIF))); // Wait until data has been sent

  result = SPDR; // Get MSB of the result
  result = result << 8; // Shift the MSB of the result to left by 8
  SPDR = 0x00; // Write dummy byte to line
  // in order to generate SPI clocks for data read
  while(!(SPSR & (1 << SPIF))); // Wait until data has been sent

  result |= SPDR; // Get LSB of the result
  SPI_PORT = SPI_PORT | SPI_CSB; // Set CSB to one
  return result;
}

/**************************************************************/
#define X_AXIS 0
#define Y_AXIS 1
#define Z_AXIS 2

#define N_DATA 100
void readXYZ()
{ float accel;
  long x,y,z;
  x = 0;
  y = 0;
  z = 0;
  for (int i=0;i<N_DATA;i++)
  {  x = x + (transfer16(0x05)>>3); //x axis msb
     y = y + (transfer16(0x07)>>3);
     z = z + (transfer16(0x09)>>3);
     delay(5); // >=4 because SCA3000 updates 250 times/sec
  }
  // end min/max for loops
  
  if (x%N_DATA >= N_DATA/2)
    Ax = x/N_DATA + 1;
  else
    Ax = x / N_DATA;
  if (y%N_DATA >= N_DATA/2)
    Ay = y/N_DATA + 1;
  else
    Ay = y / N_DATA;
  if (z%N_DATA >= N_DATA/2)
    Az = z/N_DATA + 1;
  else
    Az = z / N_DATA;

  Serial.print(Ax,DEC);
  Serial.print(", ");
  Serial.print(Ay,DEC);
  Serial.print(", ");
  Serial.print(Az,DEC);
  Serial.print(", ");
  accel = sqrt(pow(Ax,2)+pow(Ay,2)+pow(Az,2))/1333.0;
  Serial.print(accel);
  Serial.print("   ");
}

float getTemp()
{ // I couldn't really make sense of the datasheet equation!
  int temp_in;
  int b1, b2;
  temp_in = transfer16(TEMP_MSB);
  b2 = temp_in <<8;
  b2 = b2 >>8;
  temp_in = temp_in >> 5;
//  Serial.print(b2,BIN);
  Serial.print(", Rawtemp val: ");
  Serial.println(temp_in);
  temp_in = temp_in -256;//*b2;
  temp_in = (temp_in/(1.8)) + 23;
  return temp_in;
}

#define CAL_REG 0x0F
#define CFG_REG 0x32
#define front 0
#define back 1
#define top 2
#define bottom 3
#define left 4
#define right 5

void saveAccelCal(short side)
{
  EEPROM_writeAnything(CAL_REG + side*6,    Ax );
  EEPROM_writeAnything(CAL_REG + side*6 +2, Ay );
  EEPROM_writeAnything(CAL_REG + side*6 +4, Az );
  Serial.println("Calibration data saved.");
}

//-----------------------------------------------------------------------
// ATN2: arctangent of y/x for two arguments                             
//       (correct quadrant; -180 deg <= ATN2 <= +180 deg)                
//-----------------------------------------------------------------------
float ATN2(float Y, float X)
{
  float RAD=0.0174532925199433;
  float AX,AY,PHI;
  if ((X==0.0) && (Y==0.0))
  {	 return 0.0; }
  else
  {	AX=abs(X);
	AY=abs(Y); 
	if (AX>AY) 
	{ PHI=atan(AY/AX)/RAD ; }
	else
	{ PHI = 90.0 - atan(AX/AY)/RAD; }
	if (X<0.0) PHI = 180.0-PHI;
	if (Y<0.0) PHI= -PHI;
	return PHI;
  }
};

/*-----------------------------------------------------------------------*/
/* POLAR: conversion of cartesian coordinates (x,y,z)                    */
/*        into polar coordinates (r,theta,phi)                           */
/*        (theta in [-90 deg,+90 deg]; phi in [0 deg,+360 deg])          */
/*-----------------------------------------------------------------------*/
void Polar(float X, float Y, float Z, float &R, float &THETA, float &PHI)
{ float RHO;
  RHO = X*X+Y*Y;
  R=sqrt(RHO+Z*Z);  
  PHI = ATN2(Y,X);
  if (PHI<0) PHI=PHI+360.0;
  RHO = sqrt(RHO);
  THETA = ATN2(Z,RHO);
}

void calcAngle()
{
// basic theory from data sheet (un-implemented)
//Ax' = cos(yaw)*cos(pitch)*Ax + (sin(yaw)*cos(roll)+cos(yaw)*sin(pitch)*sin(roll))*ay + (sin(yaw)*sin(roll)-cos(yaw)*sin(pitch)*cos(roll))*az
//Ay' = -sin(yaw)*cos(pitch)*Ax + (cos(yaw)*cos(roll)-
//       sin(yaw)*sin(pitch))*Ay+(cos(yaw)*sin(roll)+sin(yaw)*sin(pitch)*cos(roll))*Az
//Az' = sin(pitch)*Ax-cos(pitch)*sin(roll)*Ay+cos(pitch)*cos(roll)*Az
// where roll, pitch, and yaw are all between 0 and PI

//rotation on x axis gives roll -- involves y & z
//rotation on y axis gives pitch -- involves x & z
//         on Z axis gives yaw   -- involves x & y


// and now for something completely different

  float Ax1G,Ay1G,Az1G;
  float theta,phi,g;
  float Rx,Ry,Rz;
  
// a very crude hard coded attempt at normalization (hence the calibration routine in progress)
// I noticed that my unit varied on each axis for +1G and -1G values
// these are just the values I saw on my unit, change for yours!
  Az1G = 1318.0;
  Ay1G = 1353.0;
  Ax1G = 1452.0;
  Rx = Ax/Ax1G;
  Ry = Ay/Ay1G;
  Rz = Az/Az1G;
  
  Polar(Rx,Ry,Rz,g,theta,phi);
  
Serial.print("G,Theta,Phi : ");
Serial.print(g);
Serial.print(',');
Serial.print(theta);
Serial.print(',');
Serial.println(phi);
}

// END SCA300 routines
/*************************************************************************/

// utility functions

char myUpper(char ch)
{
  if (ch>96 && ch<0x7B)
    return ch^32; //XOR bit 6 
  else
    return ch;
}

/*********************************************************************/
// command functions

void searchAccelCmdTbl(boolean &match)
{
  while (!match && cmd<n_accel_cmds)
  {
    if (accel_cmd_tbl[cmd][0]==cmdline[2] && accel_cmd_tbl[cmd][1]==cmdline[3])
      match=true;
    else
      cmd++;
  }
}

//command line syntax:
//AXcmd[ p1[ p2]]
boolean parseCommandLine()
{
  int i;
  int j;
  boolean match=false;

  cmd=-1;
  if ((cmdline[0]=='A')&&(cmdline[1]=='X'))
  {
    cmd=0;
    searchAccelCmdTbl(match);
  }
  else
    return false;
  if (echo) Serial.print('>');
  if (echo) Serial.println(cmd);
  //got a command now get the parameters
  i=0;
  while (cmdline[i] && i<cmdline_size && cmdline[i]!=' ')
    i++;
  // index stopped at first space
  // eat space
  while (cmdline[i] && i<cmdline_size && cmdline[i]==' ')
    i++; 
  j=0;
  n_parms=0;
  while (cmdline[i] && i<cmdline_size)
  {
    if (cmdline[i]==' ')
    { parm[n_parms][j]=0;
      n_parms++;
      j=0;
    }
    else
      parm[n_parms][j++]=cmdline[i];
    i++;
  }
  if (echo) Serial.println(cmd);
  return true;  
} // end parse Command

void processCommand()
{
  Serial.print("processcmd: ");
  Serial.println(cmd);
  switch (cmd) {
    case accel_xyz:
      readXYZ();
      break;
    case accel_temp:
      Serial.println(getTemp());
      break;
    case accel_cal:
      saveAccelCal(atoi(parm[0]));
      break;
    case accel_cfg:
      printConfigVals();
      break;
    case accel_ang:
      calcAngle();
      break;
    case accel_log:
      logging = !logging;
      break;
    default:
      if (prompt) Serial.println('?');
  }
}

void processSerialCommand()
{
  int i;
  if (Serial.available())
  { delay(20);
    for (i=0;(Serial.available() && i<cmdline_size);i++)
    { 
      cmdline[i]=myUpper(Serial.read()) ;
    }
    if (i<cmdline_size) cmdline[i]=0;
    Serial.print('=');
    Serial.println(cmdline);
    if (parseCommandLine())
      processCommand();
    else
      if (echo) Serial.println("X");
  } // end serial incoming event stuff  
}// end process Serial Command


void setup()
{
  Serial.begin(9600);  
  Serial.println(":)");
  Serial.println("Commands start with AX");
  for (int i=0; i<n_accel_cmds;i++)
    Serial.println(accel_cmd_tbl[i]);
}

void loop()
{
  delay(100);
  processSerialCommand();
  if (logging)  
  {  readXYZ();
    calcAngle();
  }
}