Xbee Radio Received Signal Strength and Data Graphing Program

This Processing program takes a string of values in the serial port. It assumes the string is the API string from a Maxstream XBee radio. It parses the string and graphs the signal strength and the analog values represented in the string. See the XBee product manual for more info on the protocol.

The XBee radio in this program is attached to the computer’s serial port using an FTDI USB-to-serial module like this one from Spark Fun.

Technorati Tags: , ,


/*  Xbee Packet Grapher
 Reads a packet from an Xbee radio and parses it, then graphs it.
 The packet should be 22 bytes long. It should be made up of the following:
 byte 1:     0x7E, the start byte value
 byte 2-3:   packet size, a 2-byte value  (not used here)
 byte 4:     API identifier value, a code that says what this response is (not used here)
 byte 5-6:   Sender's address
 byte 7:     RSSI, Received Signal Strength Indicator (not used here)
 byte 8:     Broadcast options (not used here)
 byte 9:     Number of samples to follow
 byte 10-11: Active channels indicator (not used here)
 byte 12-21: 5 10-bit values, each ADC samples from the sender 
 
 Created 20 Mar. 2007
 by Tom Igoe
 */

import processing.serial.*;

Serial xbee;                    // input serial port from the Xbee Radio
int[] packet = new int[22];     // with 5 samples, the Xbee packet is 22 bytes long
int byteCounter;                // keeps track of where you are in the packet

int fontSize = 18;              // size of the text on the screen
int lastReading = 0;            // value of the previous incoming byte

int rssi = 0;                    // received signal strength
int address = 0;                 // sender's address
int average = 0;                 // average of the sensor data

int firstRectPos = 25;           // horizontal pos of the first graph bar

void setup () {
  size(400, 300);                // window size

  // create a font with the second font available to the system:
  PFont myFont = createFont(PFont.list()[2], fontSize);
  textFont(myFont);

  // get a list of the serial ports:
  println(Serial.list());   
  // open the serial port attached to your Xbee radio:
  xbee = new Serial(this, Serial.list()[0], 9600);
}

void draw() {
  // set the background:
  background(0);
  // if you have new data and it's valid (>0), graph it:
  // write the numbers:
  text("Xbee Radio Signal Strength test", 10, 20);
  text("From: " + hex(address), 10, 40);

  text ("RSSI: -" + rssi + " dB", 10, 60);
  text("Sensor avg:" + average, 10, 80);

  // note that these graph bars aren't proportional, they just show change.
  // RSSI should range from 0 to -92 dBm
  drawBar(92 - rssi, 50, 0);
  // average should range from 0 - 1023, so divide by 4 to keep it
  // in the vertical space of the window:
  drawBar(average/4, 50, 1);
}

void drawBar(int rectHeight, int rectWidth, int rectNum ) {
  if (rectHeight > 0 ) {

    // draw the rect:
    stroke(23, 127, 255);
    fill (23, 127, 255);
    int rectPos = firstRectPos + (rectNum * 75);
    rect(rectPos, height-rectHeight, rectWidth, height);
  } 
}

void serialEvent(Serial xbee) {
  // read a byte from the port:
  int thisByte = xbee.read();
  // if the byte = 0x7E, the value of a start byte, you have a new packet:
  if (thisByte == 0x7E) {   // start byte
    // parse the previous packet if there's data:
    if (packet[2] > 0) {
      parseData(packet);
    }
    // reset the byte counter:
    byteCounter = 0;        
   
  }
  // put the current byte into the packet at the current position:
  packet[byteCounter] = thisByte;
  //  increment the byte counter:
  byteCounter++;
}

/* 
 Once you've got a packet, you need to extract the useful data. 
 This method gets the address of the sender and the 5 ADC readings.
 It then averages the ADC readings and gives you the result.
 */
void parseData(int[] thisPacket) {
  int adcStart = 11;              // ADC reading starts at the 12th byte
  int numSamples = thisPacket[8];  // number of samples in the packet
  int[] adcValues = new int[numSamples];   // array  to hold the 5 ARDC readings
  int total = 0;                  // sum of all the ADC readings

  // read the address. It's a two-byte value, so you
  // add the two bytes as follows:
  address = thisPacket[5] + thisPacket[4] * 256;

  // get RSSI:
  rssi = thisPacket[6];

  // read numSamples 10-bit analog values, two at a time
  // because each reading is two bytes long:
  for (int i = 0; i < numSamples * 2;  i=i+2) {
    // 10-bit value = high byte * 256 + low byte:
    int thisSample = (thisPacket[i + adcStart] * 256) + thisPacket[(i + 1) + adcStart];
    // put the result in one of 5 bytes:
    adcValues[i/2] = thisSample;
    // add the result to the total for averaging later:
    total = total + thisSample;
  }
  // average the result:
  average = total / numSamples;

  // print the sender and the average reading:
  print("From: " + hex(address) + ":   ");
  print(average + "\t");
  println("Signal Strength:" + rssi);
}