XBEE and EMG unit

FIXME 180105 enklere og flere detalier

Today we will look at communication with this EMG unit:

 
  • 2 channels EMG unit

    • XBEE communication

    • Three axis accelerometer

    • a number of digital channels

Analog (muscle) signals from the EMG unit looks like:

 

Realtime plot of some of channels

The EMG device interfaces to the outer world by use of a XBEE wireless serial device.

  • protocol IEEE 802.15.4

  • baudrate 115200

  • binary protocol

In your end we will have a XBEE to serial module which you interface by a standard serial port running with baudrate 115200.

Communication protocol

  • We are going to develop and test a protocol.

  • we will do it in C

  • you can do it in any other language

  • we will do it on an Arduino due to easy interface to serial XBEE

 

Here you see a printout of the protocol

It seems that all integers are 16 bit two's complement. with MSB byte first.

You PC and Arduino is little endian.

Exercise 1:

Here is a code snippet - explain what is happening

char msb,lsb; // here you have your msb and lsb byte
int intNumber;

...
intNumber = (msg << 8) | lsb;

lsb = (char) (intNumber & 0xff);
msb = (char) ( (intNumber >> 8) & 0xff);

Protocol as statemachine

This is a statemachine which in each state read the proper values from serial port or XBEE.

 

The code for a skeleton state machine is here:


CODE IS WITHOUT ANY WARRANTY

typedef void *(*pF)(void); // for typecast for calling function

#define NEXT(x,y,z) return ((void *)x)

void *g7E();
void *gLgt();
void *gFrID();
void *gS();
void *gOptB();
void *gChIndM();
void *gAdcV();
void *gChkS();

 void *start()
{
NEXT(g7E,start,n);
}

void *g7E()
{
  // read bytes from serial port until we get a 0x7e
  do {
    while (! Serial.available() );
    until ( Serial.read() == 0x7E);  // yes we got it
  NEXT(gLgt,g7E, n); // next state
}

void *gLgt()
{
  NEXT(gAPI,gLgt, n);
}

void *gAPI()
{
   NEXT(gSAdr,gAPI,n);
}

void *gSAdr()
{
  NEXT(gRSI,gSAdr,n);
}

void *gRSI()
{
  NEXT(gOptB,gRSI,n);
}

void *gOptB()
{
  NEXT(gChInd,gOptB,n);
}

void *gChInd()
{
NEXT(gAdcV,gChInd,n);
}


void *gAdcV()
{
   NEXT(gChkS,gAdcV,n);
}

void *gChkS()
{
NEXT(doStuff,gChkS, n);
}

void *doStuff()
{
NEXT(g7E,doStuff,again);
}


pF statefunc = start, oldfunc = NULL;  // whrere to start


void main() {


  while(1) {
    oldfunc = statefunc;               // if you want to see/track the previous state  }
    statefunc = (pF)(*statefunc)();    // next state is called here
    // here statefunc is last and oldfunc is last last
  }
  return ;                            // should not get here
}

You might comsider just read a fixed number of bytes after your 0x7e starting character.

Two other ways of showing packinformation

 
 

Exercise

  1. Is the protocol (starting with 0x7e) reliable in the sense

    1. are you sure you are at start in a telegram when receiving 0x7e

  2. Is packet length fixed ?

  3. Can it run a serial connection configured with 7 databits in a byte ?

  4. Explain going to and from msb and lsb byte to a true 16 bit twos complement integer (See code above)

  5. Code

    1. A CC simulator on Arduino which sends the format above

    2. and code a receiver

    3. Connect the two Arduinos with tx->rx and rx <- tx and gnd <-> gnd

    4. Test

  6. Exchange arduino connection with your xbee board

  7. Test

For receing Arduino it's recommended to use a MEGA because it has 4 serial ports. In that case you can use Serial1 to connect to simulator or Xbee and Serial (Std port) to debugging messages on the screen

An Arduino acting like …

// #include <looptime.h>
// EMG simulator

/*
   Format for XBEE packets
    0 0x7e  - start ch
    1 0x00  - length msb
    2 0x14  - length lsb
    3 0x83  - API frame identifier
    4 0x56  - senders address
    5 0x78  - senders address
    6 0x43  - received signal strength - RSI
    7 0x00  - option byte
    8 0x01  - nu,mber of samples
    9 0x3e  - channel indicator mask n/a A5 A4 A3 A2 A1 A0 D8
   10 0xe0  - channel indicatot mask  D7 D6 D5 D4 D3 D2 D1 D0
   11 0x00  - digital sample (msb)
   12 0x40  - digital sample (lsb)

   13 0x??  - Acc Z (msb)
   14 0x??  - Acc Z (lsb)

   15 0x??  - Acc Y (msb)
   16 0x??  - Acc Y (lsb)

   17 0x??  - Acc X (msb)
   18 0x??  - Acc X (lsb)

   19 0x??  - EMG ch1 (msb)
   20 0x??  - EMG ch1 (lsb)

   21 0x??  - EMG ch2 (msb)
   22 0x??  - EMG ch2 (lsb)

   23 0x??  - checksum

   The checksum is all bytes added together excl the first three. The sum including(!) the checksum shall give 0xff
   Strategy sum all bytes excluding first three. The checksum will be 0xff - sum
*/
char dataPkg[24] = {0x7e, 0x00, 0x14, 0x83, 0x56, 0x78, 0x43, 0x00, 0x01, 0x3e,
                    0xe0, 0x00, 0x40, 0x02, 0x9b, 0x02, 0x1a, 0x02, 0x05, 0x00,
                    0x00, 0x00, 0x05, 0x47
                   };  // from datasheet

char hexTable[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};


void setup() {
  Serial.begin(115200);
  while (! Serial) // wait until serial port is up and running
  { }

  delay(500); // just to be sure
}

char chksum(char *pk, int lgt)
{
  char s = 0;
  for (int i = 0; i < lgt; i++)
    s += *(pk + i);
  return s;
}

void dmpPkgASCII(char *d, int lgt)
{
  while (lgt--) {
    Serial.print("x");
    Serial.print(hexTable[0x0f & (*d >> 4) ]);
    Serial.print(hexTable[*d & 0x0f]);
    Serial.print(" ");
    d++;
  }
  Serial.println("");
}

void dmpPkg(char *d, int lgt)
{
  while (lgt--) {
    Serial.write(*d);
    d++;
  }
  Serial.println("");
}

void mkPkg(char *d)
{
  int v;
  char msb, lsb;
  /*
       analog 0,1,2,3,4,5 to Z, Y, X, ch1, ch2
       We can use a loop bq z,y,x,ch1,ch2 is in line in the package
  */

  //for test only
  goto yy;
  for (int i = 0; i < 5; i++) { // z,y,x, ch1 ch2
    v = analogRead(0 + i); // A0, A1, ...
    lsb = (char)(v & 0xff);
    msb = (char)(0xff & (v >> 8));

    //  Z, Y, X, ch1, ch2
    d[i + 13] = msb; // 13 is msb for the first value : Z
    d[i + 14] = lsb;
  }
  // do chksum
yy:
  // calculate chksum for first 23 bytes and subtract from 0xff to get value for chksum.
  d[23] = 0xff - chksum(d + 3, 23 - 3); // offset 3 bq first 3 bytes is not part of chksum (dont ask why)

}

void loop() {
  looptime(1000); //we will loop with 100 msec ~ 10 Hz
  mkPkg(dataPkg);
  dmpPkgASCII(dataPkg, 24);
  //dmpPkg(dataPkg, 24);
}