3 XBees Communication

Hi,

I want to configure 3 XBees. Two of them are sending data from sensors to a third one, so lets call them T1, T2 and R3. How do I configure them? I have already configured two XBees using PuTTY earlier using :

+++ ATMY1000 // This is the receiver "R3" ATDL1001 ATID1111 WR

and +++ ATMY1001 // This transmits the data of one sensor, so I will call this "T1" ATDL1000 ATID1111 WR

Now I want to include another XBee (with shield and board) connected to a sensor to become part of this network and send data to R3. Can I just include it by ATDL1000 or do I have to configure the other two again to make a network of three?

Its sufficient for me that T1 communicates with R3, and T2 also communicates with R3.

I just got your PM to join this thread.

I’m not that familiar with more than 2 XBEEs communicating. But once you get into a network type mode I doubt you can have T1 and T2 transmitting willy-nilly and expect R3 to understand who is saying what when, not to mention data collisions when T1 and T2 just happen to try to transmit simultaneously.

You may need to define a protocol where T1 and T2 don’t transmit anything unless asked. Then have R3 poll each individually. Either strictly alternating, or if one bit of data needs to be updated more often than the other by some pattern to match the data requirements. You may need to set up some sort of mesh-like network, but I’m not sure.

Anyone else more experienced with multi-XBee networks?

BTW, why are you using PuTTy to configure the XBees. Because you are using PuTTy, that tells me that you are on some Windows variant. Digi has a tool called XCTU that runs on Windows that provides a GUI for configuring XBee modules. Much easier to use than remembering what all the AT codes mean.

I’m about to head to bed so I don’t have the time to look anything up for you right now. I’ll try to get to this after work tomorrow. Hopefully I won’t forget. :wink:

Thanks Sembazuru. I am using PuTTY because I have already used that to configure two of the XBees, I just thought I'd use the the same for the third one.

What you said of the data collisions is correct. But just as the earlier code for the temperature sensor included unique start and stop bits to be identified, I thought I'd program this like that as well.

Sembazuru: Anyone else more experienced with multi-XBee networks?

I use XBee ZB modules (f.k.a. Series 2) and communication between three or more is straightforward using API mode. There are many, many types of XBee, so what kind of XBees are we talking about here, exactly? The part number for the one I use most often is XB24-Z7WIT-004. What part number do you have?

Andrew Rapp's XBee-Arduino library makes API mode easy.

Hi Jack,

I already configured the first two XBees in AT mode long time ago.

The first two XBees I have are S1 (Series 1) XBees from Digi International. I configured these using the AT commands typed in post 1.

The third XBee I bought recently is also from Digi International, XBee S2. I configured this in PuTTY using :

+++
ATMY        (this returned FFFE which I guess is the fixed address of the XBee)
ATDL1000    (this is the address of the receiver XBee)
ATID1111 (the PAN ID)
ATWR

I got an OK after these. I haven't programmed R3 (address of 1000) however to communicate the third XBee (address FFFE), because in configuring R3 I didn't type ATDLFFFE or something (is that even valid?).

I need help in configuring the three XBees such that T1 (address 1001), and T2 (address FFFE) can send data to R3 (address 1000). And R3 should be able to send pilot signals to T1 and T2.

Can I type the ATDL command for R3 twice, would that mean I configured it to communicate with both 1001 and FFFE?

Like this :

+++
ATMY1000
ATDL1001
ATDLFFFE
ATID1111
WR

Would both the ATDL commands be valid?

You have a problem because XBee 802.15.4 modules (f.k.a. S1) will not communicate with XBee ZB modules (f.k.a. S2).

Even if the three modules were the same type, programming for communication among three or more nodes in AT mode will be very cumbersome. Basically the controller will have to emulate the same "AT" commands as they would be typed in, sending the +++ command sequence, changing the destination address, parsing the output, observing guard times, etc. This is not my idea of fun and is why I strongly recommend API mode.

Okay. I will replace the new (S2) XBee with a new Series 1 XBee.

Consider I do that. Do I have to reconfigure all three of them again, even though two of them are configured to communicate already?

Like I said, I have data going from T1 (address 1001) to R3 (address 1000), and also would need data to go from T2 (new one I will be getting) to R3 (address 1000).

I only need R3 to act as a master and send pilot signals to these on when to transmit the data. I have never done API mode. Guide me on the whole thing.

I have a FTDI breakout board to configure the XBees, and a USB A to mini B cable.

I did the AT commands in PuTTY. What should I download and install for configuring in API? I would appreciate it if you could guide me through this.

V_88: Okay. I will replace the new (S2) XBee with a new Series 1 XBee.

Consider I do that. Do I have to reconfigure all three of them again, even though two of them are configured to communicate already?

Like I said, I have data going from T1 (address 1001) to R3 (address 1000), and also would need data to go from T2 (new one I will be getting) to R3 (address 1000).

I only need R3 to act as a master and send pilot signals to these on when to transmit the data. I have never done API mode. Guide me on the whole thing.

I have a FTDI breakout board to configure the XBees, and a USB A to mini B cable.

I did the AT commands in PuTTY. What should I download and install for configuring in API? I would appreciate it if you could guide me through this.

Insufficient time to "guide you on the whole thing" unless perhaps enormous amounts of money were involved. Else you'll need to see if you can't find some initiative and learn some other way. I work almost exclusively with XBee ZB modules and have never used XBee 802.15.4 modules in API mode. I think it's similar, but there may be differences, I don't know exactly, but that is the tree I would bark up first. The library I linked above is one place to start. The product manual is another. Try things for yourself. You'll make mistakes, but everyone does and that's a good way to learn. If you get stuck, ask good questions on the forum. Best of luck.

Okay

Hey,

If anyone could give me some input on this, it would be great. I was wondering if I could configure the receiver (R3 address 1000) to broadcast the pilot signal. http://forum.arduino.cc/index.php?topic=104648.0 From this thread user faludi mentioned ATDLFFFF command for broadcasting. So would it work if I configured my XBees this way? :

On T1 I use :

+++
ATMY1001
ATDL1000     // address of receiver
ATID1111
WR

On T2 I use:

+++
ATMY1002     // I will change my present S2 with an S1, so I guess I can assign this address to it
ATDL1000
ATID1111
WR

On R3 I use:

+++
ATMY1000
ATDLFFFF        // broadcasting 
ATID1111
WR

This way even though R3 broadcasts the pilot signals and both receive all those signals, they will only respond with data if the pilot signal is the one of the two types. By that I mean, I have a separate type of pilot signal asking for T1 to transmit and another for T2, and they would respond with data only for their respective signals.

I will get my Series 1 XBee tomorrow probably, but before then, I want to know if this logic would work.

I think it worked after I changed the PAN ID to FFFF for all of them. I will be back when I need help. I need to get a hall sensor and temperature sensor to send data to the receiver and the receiver should break that data packet and display the temperature and RPM.

I hope there are users ready to help.

Thanks all.

Is anyone still in this thread? :~

Now that the XBees can communicate as required, I need to send data from the two sensors to the receiver. I have are a temperature sensor (MLX90614) and a hall sensor (A3144)

The way to do this I am thinking is to send the temperature and hall sensor data continuously. Since the temperature sensor samples in periods of time, and the hall sensor sends it based on the number of revolutions, I thought it would be a good idea for the receiver to read a packet every time it gets one from the hall sensor. In that time instant, the receiver will consider the latest temperature data packet. This way the receiver doesn’t have to send any pilot signals to the transmitters on when to send data, they can transmit continuously.

So I am going to have three codes - T1 (temperature sensor connected with xbee and board), T2 (hall sensor connected with xbee and board) and R3 (receiver of both data).

I already have the temperature sensor sending data to the receiver and displaying it in the COM port (thanks Sembazuru! But I may need your help again!). I don’t think I need to change the T1 code. But I need to have to modify the code of the receiver because it has to wait for the hall sensor data and then display that with the latest temperature data.

I also have to change the code of the hall sensor board (T2) for it to be able to transmit the data to the receiver. At this point the hall snesor works wired, but I want to make it wireless. So what I have now :

At T1 (I think this doesn’t need to be changed, right?)

#include <SoftwareSerial.h>
#include <i2cmaster.h>

SoftwareSerial XBeeTX(2, 3); // RX, TX

const byte dev = 0x5A<<1; // This is a constant, so use the const keyword to allow the compiler to decide how best to optimize
const char data_start = '*'; // This is a constant, so use the const keyword to allow the compiler to decide how best to optimize
const char data_format[] = "%c%02X%02X"; // %c is a single character, %02X is a 2 character ASCII encoded hex number representing 1 byte
char dataStream[6]; // data_format will result in 5 characters plus 1 NULL character automatically applied by the sprintf() function so we need a 6 element array as a buffer
byte data_low;
byte data_high;
byte pec;

void setup()
{
  Serial.begin(9600);
  XBeeTX.begin(9600);
  i2c_init(); //Initialise the i2c bus
  PORTC = (1 << PORTC4) | (1 << PORTC5);//enable pullups
}

void loop()
{
  i2c_start_wait(dev+I2C_WRITE);
  i2c_write(0x07);

  // read
  i2c_rep_start(dev+I2C_READ);
  data_low = i2c_readAck(); //Read 1 byte and then send ack
  data_high = i2c_readAck(); //Read 1 byte and then send ack
  pec = i2c_readNak();
  i2c_stop();

  // sprintf() method of output
  
  sprintf(dataStream, data_format, data_start, data_high, data_low); // 1(data_start) + 2(data_high) + 2(data_low) + 1(NULL) = 6 bytes
  XBeeTX.print(dataStream);
  Serial.print(dataStream); // for diagnostics
  Serial.println(F(" Sent over XBee.")); // also for diagnostics, using the F() macro to avoid wasting SRAM with diagnostic messages
  delay(2000); // adjust to what ever delay you want.
}

At T2 (the wired hall sensor code, I need to make this wireless)

volatile byte revolutions;                                                   
unsigned int rpm;
unsigned long timeold;
unsigned long k;

void setup()
{
  Serial.begin(9600);
  attachInterrupt(0, rpm_fun, FALLING);
  revolutions = 0;
  rpm = 0;
  timeold = 0;
  k = 0;
}

void loop()
{
  if (revolutions >= 10)                         //** Update RPM every 10 counts**
 { 
   
    k = millis() - timeold;
    rpm = 60000/k*revolutions;          // **calculate the revolutions per minute
    timeold = millis();
    revolutions = 0;
    Serial.print("RPM: ");
    Serial.println(rpm,DEC);
  }
}

void rpm_fun()
{
  revolutions++;
}

At R3 (I need to modify this to receive and display even the hall sensor data)

#include <SoftwareSerial.h>

SoftwareSerial XBeeRX(2, 3); // RX, TX

const char data_start = '*'; // This is a constant, so use the const keyword to allow the compiler to decide how best to optimize
const char data_format[] = "%c%02X%02X"; // for sprintf() %c is a single character and %02X is a 2 character ASCII encoded hex number representing 1 byte
char dataStream[6]; // data_format will result in 5 characters plus 1 NULL character automatically applied by the sprintf() function so we need a 6 element array as a buffer
boolean newReading = false; // Because we use this before explicitly setting it, just to make sure the default is false we set it here.
byte readPosition;
byte data_low;
byte data_high;
char data_in;

void setup()
{
  Serial.begin(9600);
  XBeeRX.begin(9600);
}

void loop()
{
  if (XBeeRX.available())
  {
    data_in = XBeeRX.read();
    //Serial.println(data_in, HEX); // Show the recieved HEX value immediately as recieved for diagnostics.
    switch(readPosition)
    {
    case 0: // Waiting for the start of data token to be received. Ignore any serial data until data_start is received.
      {
        if (data_in == data_start)
          readPosition++;
        break;
      }
    case 1: // data_start processed. Current character should be ASCII representation of the high nybble of data_high.
      {
        data_high = HexCharToValue(data_in)<<4;
        readPosition++;
        break;
      }
    case 2: // high nybble of data_high processed. Current character should be ASCII representation of the low nybble of data_high.
      {
        data_high = HexCharToValue(data_in)+data_high;
        readPosition++;
        break;
      }
    case 3: // low nybble of data_high processed. Current character should be ASCII representation of the high nybble of data_low.
      {
        data_low = HexCharToValue(data_in)<<4;
        readPosition++;
        break;
      }
    case 4: // high nybble of data_low processed. Current character should be ASCII representation of the low nybble of data_low.
      {
        data_low = HexCharToValue(data_in)+data_low;
        readPosition = 0; // Finished receiving expected data structure. Return readPosition to zero to wait for the next data_start token
        newReading = true;
      }
    }
  }

  if (newReading == true) // newReading is a boolean type
  {
    sprintf(dataStream, data_format, data_start, data_high, data_low); // reconvert converted serial input to verify the deconversion process worked
    //Serial.print(dataStream); // diagnostics

    // This whole section of declarations should properly be at the top of either loop() as local variables, or at the top of the sketch as globals. If globals, tempFactor should be a constant
    double tempFactor = 0.02; // double and float are the same datatype. Only use one of the two to avoid confusion unless there is a really good reason not to.
    unsigned int tempData; // floating point math is executionally expensive on AVR processors. All we really need here is a 16-bit holding bin, polarity is not important.
    float kelvin; 
    float celsius; 
    

    tempData = ((data_high & 0x007F) << 8) + data_low; 
    kelvin = (tempData * tempFactor); 

    celsius = kelvin - 273.15; 

    Serial.print(F(" Celsius: ")); // Save SRAM by using F() macro for constant text messages
    Serial.println(celsius);
    newReading = false;
  }
}

byte HexCharToValue (char x)
{
  byte value;
  char upper; // Should match x, so make this a char. (will save 1 byte of stack space in the process
  byte first_result;

  if (isxdigit(x))
  {
    upper = toupper(x);
    first_result = upper - 0x30;
    if (first_result < 0x0A)
    {
      value = first_result;
    }
    else
    { 
      value = first_result - 0x10;
      value = value + 9; //
    }
  }
  else
  {
    value = 0;
  }

  return value;
}

If I was sending data from the Hall sensor with XBee to send data wireless, I would the code would be like this. I don’t have the circuits with me now, but if someone could check and guide me?

I basically changed the code for the temperature sensor. But I am not sure how Arduino would treat the
** **unsigned int** **

So would this code for the transmitter’s side using the hall sensor work? I am mostly unsure about the treatment of unsigned int.

#include <SoftwareSerial.h>

SoftwareSerial XBeeTX(2,3); //RX, TX

volatile byte revolutions;                                                   
unsigned int rpm;
unsigned long timeold;
unsigned long k;
const char data_start = '(';  // would '(' be good for a start character?
const char data_format[] = "%c%04X"; //"%02X" was used for 8-bit, so "04X" for 16-bit?
char dataStream[6]; // data_format has 5 characters plus 1 NULL

void setup()
{
  Serial.begin(9600);
  XBeeTX.begin(9600);
  attachInterrupt(0, rpm_fun, FALLING);
  revolutions = 0;
  rpm = 0;
  timeold = 0;
  k = 0;
}

void loop()
{
  if (revolutions >= 5)                         //** Update RPM every 5 counts**
 { 
   // READ AND CALCULATE

    k = millis() - timeold;
    rpm = 60000/k*revolutions;          // **calculate the revolutions per minute
    timeold = millis();
    revolutions = 0;
    
    // sprintf() method of output

    sprintf(dataStream, data_format, data_start, rpm); 

   // 1(data_start) + 4(rpm) + 1 NULL = 6 bytes? 
   
    XBeeTX.print(dataStream);   
// I removed the delay after this because its sampling 
//the data after every 5 counts anyway. So that should be enough delay, if there is a 
//great delay I felt it won't be accurate to the moment of it being captured.

  }
}

void rpm_fun()
{
  revolutions++;
}

Would this work if I only have the Hall sensor transmitting data to the receiver? I really need guidance on this (which is why I post here). My eventual task is greater because I would have to send data from the Hall sensor as well as the temperature sensor and have the receiver actually recognize the two.

Since there is no synchronization between the transmitters, it makes it harder I guess.

Because there is no synchronization between transmitters with your 2 transmitter and 1 receiver you will need to change communication paradigms to the receiver polling the transmitters for responses. For this, you might want to start referring to the transmitters as "slaves" and the receiver as "master". The slaves won't transmit anything unless asked, so it is up to the master to request data from the slaves. This is commonly known as polling. From wikipedia's entry on Polling:

In a multidrop line arrangement (a central computer and different terminals in which the terminals share a single communication line to and from the computer), the system uses a master/slave polling arrangement whereby the central computer sends message (called polling message) to a specific terminal on the outgoing line. All terminals listen to the outgoing line, but only the terminal that is polled replies by sending any information that it has ready for transmission on the incoming line.

Basically, your master Arduino would send a message out over the XBees and both slaves will listen. If the polling message is for the temperature sensor Arduino (say the character "*"), it would respond with the temperature. May as well keep the current message format, but change the XBeeTX.print to XBeeTX.println which will add a CR and LF to the end of the message. Ignore the CR (0x0D) and use the LF (0x0A) as an end-of-message character. For the same polling message for the temperature sensor, the RPM sensor would throw out all received characters until it gets the LF, and then it would start listening for another character.

It might make sense in your setup for the temperature Arduino to only query the I2C temperature sensor when it gets polled for data. Then have the RPM Arduino to continually count Hall Sensor triggers and calculate new RPM values, and when it gets polled respond with the latest calculated value.

As far as the code you posted:

  • Yes, data_format[] as you define it will/should work. (kudos to you) :-)
  • Regarding RPM calculations, what is the range of RPM you are expecting (and thus what is the fastest and slowest that 5 rotations will be counted)? Will the maximum RPM be below the maximum value that an unsigned int can hold (65,535)?
  • More kudos to you, good on you for keeping your ISR (rpm_fun) short and sweet. I might choose a more suitable name like rpm_count, but that is mostly personal preference.
  • Something to be careful with your actual calculation (rpm = 60000/k*revolutions). You are doing integer math with longs and then storing the result into an int. Watch out for rounding errors and unintentional implicit casting truncating your values. I'm no expert on figuring these things out, so you might want to do some trials on your math in a scratch sketch. Give the largest and smallest values to the variables k and revolutions that you would expect to see, then run the formula. Check to see if the result matches what you expect. If not, you may have to do some explicit casting. Someone else may have to assist with this, my skills here aren't up to par yet. For all I know, because I haven't tested it yet outside my own mind, your formula may work properly for your situation. But I would devote some time trying to break this to understand any potential limitations.
  • When rebuilding the received message composed with "%c%04X", the second received byte when converted to a hex value will be shifted left 12 bits, the third byte shifted 8 bits and added to the second, the fourth byte shifted 4 bits and added to the previous two, and then the fifth byte shifted zero bits (i.e. not shifted at all) and added to the previous three.
  • Using an end-of-message (EOM) character will allow you to check each received character for the EOM character to trap some cases of malformed messages. If the EOM character arrives early, or doesn't arrive in the expected number of characters, the received message can be thrown out as bad.

Just some thoughts for you to ponder.

Sembazuru,

Hey I typed these codes, and I am not sure if there is a problem in everything or just the polling signals.

Okay I configured the XBees to communicate TX1 <==> RX and TX2 <==> RX. And I even tested them individually, with the point to point temperature sensor code. But I am having troubling with the actual code for point to multipoint now.

I have three codes : one for the receiver, one for the transmitter of the Hall sensor and one for the transmitter of the Temperature sensor.

I have used ‘*’ and ‘(’ as the data_start signals, and the same for the polling signals. The hall sensor sends data (starting with a ‘(’ character) when polled with ‘(’, and the temperature sensor sends data starting with a ‘*’ when polled with ‘*’

I think its the polling signals that I am messing up. Can you help me with this?

Temperature Sensor code (TX 1)

#include <SoftwareSerial.h>
#include <i2cmaster.h>

SoftwareSerial XBeeTX(2, 3); // RX, TX

const byte dev = 0x5A<<1; // This is a constant, so use the const keyword to allow the compiler to decide how best to optimize
const char data_start = '*'; // This is a constant, so use the const keyword to allow the compiler to decide how best to optimize
const char data_format[] = "%c%02X%02X"; // %c is a single character, %02X is a 2 character ASCII encoded hex number representing 1 byte
char dataStream[6]; // data_format will result in 5 characters plus 1 NULL character automatically applied by the sprintf() function so we need a 6 element array as a buffer
byte data_low;
byte data_high;
byte pec;
char data_in;

void setup()
{  
  Serial.begin(9600);
  XBeeTX.begin(9600);
  
  i2c_init(); //Initialise the i2c bus
  PORTC = (1 << PORTC4) | (1 << PORTC5);//enable pullups
}

void loop()
{  
  i2c_start_wait(dev+I2C_WRITE);
  i2c_write(0x07);

  // read
  i2c_rep_start(dev+I2C_READ);
  data_low = i2c_readAck(); //Read 1 byte and then send ack
  data_high = i2c_readAck(); //Read 1 byte and then send ack
  pec = i2c_readNak();
  i2c_stop();

if (XBeeTX.available())
{ 
  data_in = XBeeTX.read();
  if (data_in == data_start)
  { 
  sprintf(dataStream, data_format, data_start, data_high, data_low); 
  XBeeTX.print(dataStream);
  Serial.print(dataStream); 
  Serial.println(F(" Sent over XBee."));
  delay(100); // adjust to what ever delay you want.
  } 
}
}

Hall sensor code (TX 2)

#include <SoftwareSerial.h>

SoftwareSerial XBeeTX(2, 3);  // RX, TX

const char data_start = '(';
const char data_format[] = "%c%04X";
char dataStream[6];
volatile byte revolutions;                                                   
unsigned int rpm;
unsigned int rpm_send;
unsigned long timeold;
unsigned long k;
char data_in;

void setup()
{
  Serial.begin(9600);
  XBeeTX.begin(9600);
  attachInterrupt(0, rpm_fun, FALLING);
  revolutions = 0;
  rpm = 0;
  timeold = 0;
  k = 0;
}

void loop()
{ 
  if (revolutions >= 10)                         //** Update RPM every 10 counts**
 { 
   
    k = millis() - timeold;
    rpm = 60000/k*revolutions;          // **calculate the revolutions per minute
    timeold = millis();
    revolutions = 0;
 }
 
 if (XBeeTX.available())
  {
    data_in = XBeeTX.read();
    
  if(data_in == data_start)   // if polling signal has arrived
  { // send data
    sprintf(dataStream, data_format, data_start, rpm);
    XBeeTX.print(dataStream);
    Serial.println(F("Sent over XBee"));
    delay(100);
  }
}
}
void rpm_fun()
{
  revolutions++;
}

Receiver code

Okay, here is probably where I messed up in the XBeeRX.print(poll_1) and XBeeRX.print(poll_2), signals.

#include <SoftwareSerial.h>

SoftwareSerial XBeeRX(2, 3); // RX, TX

const char poll_1 = '*'; // This is a constant, so use the const keyword to allow the compiler to decide how best to optimize
const char poll_2 = '(';

const char data_format_1[] = "%c%02X%02X"; // for sprintf() %c is a single character and %02X is a 2 character ASCII encoded hex number representing 1 byte
const char data_format_2[] = "%c%02X%02X";
char dataStream_1[6]; // data_format will result in 5 characters plus 1 NULL character automatically applied by the sprintf() function so we need a 6 element array as a buffer
char dataStream_2[6];

boolean newReading_1 = false; // Because we use this before explicitly setting it, just to make sure the default is false we set it here.
boolean newReading_2 = false;
byte readPosition_1;
byte readPosition_2;

byte data_temp_1;
byte data_temp_2;

byte data_rpm_1;
byte data_rpm_2;

char data_in;

void setup()
{
  Serial.begin(9600);
  XBeeRX.begin(9600);
}

void loop()
{
  XBeeRX.print(poll_2)   // it sends polling signal 1 later
  delay(1000);
  
  // READING DATA
  if (XBeeRX.available())
  {         
    data_in = XBeeRX.read();
    switch (readPosition_1)
    { 
     case 0:
      {
    if (data_in == poll_1)
    readPosition_1++;
    break;
      } 
    case 1: 
      { 
        data_temp_1 = HexCharToValue(data_in)<<4;
        readPosition_1++;
        break;
      } 
    case 2:
      { 
        data_temp_1 = HexCharToValue(data_in)+data_temp_1;
        readPosition_1++;
        break;
      } 
    case 3:
      {  
        data_temp_2 = HexCharToValue(data_in)<<4;
        readPosition_1++;
        break;
      }  
    case 4:
      {  
        data_temp_2 = HexCharToValue(data_in)+data_temp_2;
        readPosition_1 = 0;  
        newReading_1 = true;
      } 
    } 
    
  // READING HALL SENSOR DATA
  
  switch(readPosition_2)
  {
    case 0:
    {
    if (data_in == poll_2)
     readPosition_2++;
     break;
    }
    
    case 1: 
      { 
        data_rpm_1 = HexCharToValue(data_in)<<4;
        readPosition_2++;
        break;
      }  
    case 2: 
      { 
        data_rpm_1 = HexCharToValue(data_in)+data_rpm_1;
        readPosition_2++;
        break;
      } 
    case 3: 
      {  
        data_rpm_2 = HexCharToValue(data_in)<<4;
        readPosition_2++;
        break;
      } 
    case 4: 
      {  
        data_rpm_2 = HexCharToValue(data_in)+data_rpm_2;
        readPosition_2 = 0; 
        newReading_2 = true;   
      }  
    } 
  }
  
  if (newReading_1 == true) // newReading is a boolean type
  {
    sprintf(dataStream_1, data_format_1, poll_1, data_temp_1, data_temp_2); // reconvert converted serial input to verify the deconversion process worked
    //Serial.print(dataStream); // diagnostics
    
    // This whole section of declarations should properly be at the top of either loop() as local variables, or at the top of the sketch as globals. If globals, tempFactor should be a constant
    double tempFactor = 0.02; // double and float are the same datatype. Only use one of the two to avoid confusion unless there is a really good reason not to.
    unsigned int tempData; // floating point math is executionally expensive on AVR processors. All we really need here is a 16-bit holding bin, polarity is not important.
    float kelvin; 
    float celsius; 
    
    tempData = ((data_temp_1 & 0x007F) << 8) + data_temp_2; 
    kelvin = (tempData * tempFactor); 
    
    celsius = kelvin - 273.15; 
    
    Serial.print(F(" Celsius: ")); // Save SRAM by using F() macro for constant text messages
    Serial.println(celsius);
    newReading_1 = false;
  }
  
  if (newReading_2 == true) // newReading is a boolean type
  {
    sprintf(dataStream_2, data_format_2, poll_2, data_rpm_1, data_rpm_2); // reconvert converted serial input to verify the deconversion process worked
    
    unsigned int rpm;  
    rpm = ((data_rpm_1 & 0x007F) << 8) + data_rpm_2; 
    
    Serial.print(F("RPM: ")); // Save SRAM by using F() macro for constant text messages
    Serial.println(rpm);
    XBee.print(poll_1);      // I poll one after data 2 has been read
    delay(100);
    newReading_2 = false;
  }
}

byte HexCharToValue (char x)
{
  byte value;
  char upper; // Should match x, so make this a char. (will save 1 byte of stack space in the process).
  byte first_result;

  if (isxdigit(x))
  {
    upper = toupper(x);
    first_result = upper - 0x30;
    if (first_result < 0x0A)
    {
      value = first_result;
    }
    else
    { 
      value = first_result - 0x10;
      value = value + 9; 
    }
  }
  else
  {
    value = 0;
  }

  return value;
}