Sending Temperature using Xbee

I have a project where I want to transmit temperature reading to my arduino using Xbee modules (Series 1). I'm basing my code on Wireless Temperature Sensor | Project Lab. I removed the code that drives the LCDs and I'm left with:

// WIreless Temp Sensor Project
// http://nootropicdesign.com/projectlab/2009/11/01/wireless-temperature-sensor/
// SRG - deleted the code that displays the temp on LCD display


#define NUM_DIGITAL_SAMPLES 12
#define NUM_ANALOG_SAMPLES 4

int tempF; //Temp in F
int packet[32];
int digitalSamples[NUM_DIGITAL_SAMPLES];
int analogSamples[NUM_ANALOG_SAMPLES];

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

}

void loop() {
  readPacket();
}

void readPacket() {
  if (Serial.available() > 0) {
    int b = Serial.read();
    if (b == 0x7E) {
      packet[0] = b;
      packet[1] = readByte();
      packet[2] = readByte();
      int dataLength = (packet[1] << 8) | packet[2];

      for(int i=1;i<=dataLength;i++) {
        packet[2+i] = readByte();
      }
      int apiID = packet[3];
      packet[3+dataLength] = readByte(); // checksum

      printPacket(dataLength+4);

      if (apiID == 0x92) {
        int analogSampleIndex = 19;
        int digitalChannelMask = (packet[16] << 8) | packet[17];
        if (digitalChannelMask > 0) {
          int d = (packet[19] << 8) | packet[20];
          for(int i=0;i < NUM_DIGITAL_SAMPLES;i++) {
            digitalSamples[i] = ((d >> i) & 1);
          }
          analogSampleIndex = 21;
        }

        int analogChannelMask = packet[18];
        for(int i=0;i<4;i++) {
          if ((analogChannelMask >> i) & 1) {
            analogSamples[i] = (packet[analogSampleIndex] << 8) | packet[analogSampleIndex+1];
            analogSampleIndex += 2;
          } else {
            analogSamples[i] = -1;
          }
        }
      }
    }

    int reading = analogSamples[1];  // pin 19
    
    // convert reading to millivolts
    float v = ((float)reading/(float)0x3FF)*1200.0;

    // convert to Fahrenheit.  10mv per Fahrenheit degree
    float f = v / 10.0;

    // round to nearest int
    tempF = (int)(f+0.5);
    Serial.print("Temp: ");
    Serial.println(tempF);
  }
}


void printPacket(int l) {
  for(int i=0;i < l;i++) {
    if (packet[i] < 0xF) {
      // print leading zero for single digit values
      Serial.print(0);
    }
    Serial.print(packet[i], HEX);
    Serial.print(" ");
  }
  Serial.println("");
} 


int readByte() {
    while (true) {
      if (Serial.available() > 0) {
      return Serial.read();
    }
  }
}

The receiving Xbee is plugged into an Xbee shield from Sparkfun.
In the serial monitor I'm getting:
7E 00 0A 83 00 01 20 00 01 04 00 03 FF 54
Temp: 0
7E 00 0A 83 00 01 1F 00 01 04 00 03 FF 55
Temp: 0
7E 00 0A 83 00 01 20 00 01 04 00 03 FF 54
Temp: 0

but I'm getting zero when I try to get the temperature. The voltage going into pin 19 on the Xbee transmitting is about 1.8 volts.

Any idea why my reading on pin 19 is zero according the Xbee on the receiving end?

--Scott

I'm looking at the output:

7E 00 0A 83 00 01 20 00 01 04 00 03 FF 54

Lining this up with the code, it looks like b = 0x7E, so you want to read some more stuff.

The amount of stuff to read is defined by 0x00 and 0x0A, for a total of 10 bytes.

The next value, 0x83, is stored in apiID, and then the packet array is printed (as shown above).

Then, you check to see is apiID is 0x92. It doesn't appear to be, so a bunch of code is skipped.

Then, you set analogChannelMask to packet[18]. You only read, as near as I can tell, 10 values into packet, so, packet[18] is undefined, but probably 0.

At the end of the next bit of code, I think analogSamples contains 4 values, all -1.

Then, reading is set to analogSamples[1], which is the second value in the array. Why the 2nd value?

It appears that reading is -1, so, dividing that by 0x3FF (a magic number) and mulitplying by 1200.0 (another magic number) should result in v being -1023/1200 = -0.8525.

This is then divided by 10, resulting in f being -0.08525, which is then rounded to the nearest int, 0.

Seems pretty straightforward, to me.

Of course, I could be completely wrong, and you could add some Serial.print statements in strategic places to prove it.

When I do a serial print on reading I get zero.

I'm new to c programming and I don't really understand all the code, once I do perhaps I can better debug this. I was looking at the following line of code and I don't know what it's doing. Can someone explain it?

int digitalChannelMask = (packet[16] << 8) | packet[17];

--Scott

Scott, I'm not sure your XBee is configured correctly. The frame size is far too small. I'm not sure what API value 83 is -- it's not in my XBee manual. Did you configure it to send analog samples from pin 19? That is, did you set parameter D1 to value '2'? And are you running the API firmware on both devices? I used ZNet 2.5 devices, so I'm not sure how all this works on a Series 1 device....

This code:
int digitalChannelMask = (packet[16] << 8) | packet[17];

sets the integer variable digitalChannelMask from the 2 bytes stored in packet[16] and packet[17]. The integer value is 16 bits wide. First shift the byte in packet[16] to the left 8 bits, then OR it with the byte in packet[17].
Example:
packet[16] is 00000110 (or 0x06)
packet[17] is 10010111 (or 0x97)

then the 16 bit integer is 0000011010010111 (or 0x0697)

Yes, I did set D1=2 for Pin 19, but I may not have put it in API mode. Sounds like I should have AP=1 and it's zero by default. I don't think I changed the default AP setting. I'll have to check when I get a chance.

Thanks for explaining the bit shifting and OR function. I wonder why something so complex is necessary to pull out the analog data from the packet.

--Scott

Using API mode is not a programming change. You have to write the API firmware to the device using Digi's X-CTU application. You are probably running the "transparent mode" firmware that lets you use AT commands. API mode let's you do much more. X-CTU only runs on Windows (which is why I have to keep an old Windows machine running)

See http://www.digi.com/support/kbase/kbaseresultdetl.jsp?kb=125

You'll need a USB interface hooked to the XBee. I built one by connecting an XBee breakout board to a Sparkfun FTDI USB breakout: SparkFun USB to Serial Breakout - FT232RL - BOB-12731 - SparkFun Electronics

Try googling for "XBee firmware upgrade" and you'll find more info.

Then you'll need to configure the XBees again with correct destination addresses, analog sampling parms, etc. It's quite a hassle, IMHO.

I used X-CTU to config the Xbees originally. I'll fire it up again tonight and see if I can figure out the API mode.

--Scott

I changed AP=1 on the Xbee transmitter and receiver, no change. Maybe this code only works with Series 2.

--Scott

Tug on this old thread :slight_smile:

7E 00 0A 83 00 01 1F 00 01 04 00 03 FF 55

7E -Correct packet header byte

00 0A -Payload = Decimal 10 bytes. (Bytes 4-13 this example)

83 -Packet type=input line states, 16 bit source address

00 01 - 16 bit source address (MY setting) of transmitting XBee

1F - RSSI value, or rec'd signal strength (-31dBv, pretty good!)

00 -Broadcast options

01 -Sample count (decimal 1 in this example)

04 00 -Channel Indicator

03 FF - A/D output for DIO 1 ? (03FF is out of range)

55 -Checksum

I am also trying to adapt this code to my use. Thanks ScottG for rewriting it without the LED.

Part of the problem at this time is that the byte count in the packet has to be set in the code for my particular setup. At least thats the 'Theory of the Minute' :-/

Righto - after putting the correct firmware on my XBees (XB-24ZB) I now have the IO Sampling option and I have set everythign up so that I now get output every 5 secs at the co-ordinator xbee / arduino.

Problem is, I still have Temp: 0

Output:

emp: 0
7E 00 12 92 00 13 A2 00 40 64 F7 5D 1B 91 01 01 00 00 01 02 89 86 
Temp: 0
7E 00 12 92 00 13 A2 00 40 64 F7 5D 1B 91 01 01 00 00 01 02 88 87 
Temp: 0
7E 00 12 92 00 13 A2 00 40 64 F7 5D 1B 91 01 01 00 00 01 02 87 88 
Temp: 0
7E 00 12 92 00 13 A2 00 40 64 F7 5D 1B 91 01 01 00 00 01 02 86 89 
Temp: 0
7E 00 12 92 00 13 A2 00 40 64 F7 5D 1B 91 01 01 00 00 01 02 87 88 
Temp: 0
7E 00 12 92 00 13 A2 00 40 64 F7 5D 1B 91 01 01 00 00 01 02 87 88 
Temp: 0
7E 00 12 92 00 13 A2 00 40 64 F7 5D 1B 91 01 01 00 00 01 02 87 88 
Temp: 0
7E 00 12 92 00 13 A2 00 40 64 F7 5D 1B 91 01 01 00 00 01 02 88 87 
Temp: 0
7E 00 12 92 00 13 A2 00 40 64 F7 5D 1B 91 01 01 00 00 01 02 88 87 
Temp: 0
7E 00 12 92 00 13 A2 00 40 64 F7 5D 1B 91 01 01 00 00 01 02 89 86 
Temp: 0
7E 00 12 92 00 13 A2 00 40 64 F7 5D 1B 91 01 01 00 00 01 02 89 86 
Temp: 0
7E 00 12 92 00 13 A2 00 40 64 F7 5D 1B 91 01 01 00 00 01 02 88 87 
Temp: 0

And the Arduino code (which is exactly the same as post 1:

// WIreless Temp Sensor Project
// http://nootropicdesign.com/projectlab/2009/11/01/wireless-temperature-sensor/
// SRG - deleted the code that displays the temp on LCD display


#define NUM_DIGITAL_SAMPLES 12
#define NUM_ANALOG_SAMPLES 4

int tempF; //Temp in F
int packet[32];
int digitalSamples[NUM_DIGITAL_SAMPLES];
int analogSamples[NUM_ANALOG_SAMPLES];

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

}

void loop() {
  readPacket();
}

void readPacket() {
  if (Serial.available() > 0) {
    int b = Serial.read();
    if (b == 0x7E) {
      packet[0] = b;
      packet[1] = readByte();
      packet[2] = readByte();
      int dataLength = (packet[1] << 8) | packet[2];

      for(int i=1;i<=dataLength;i++) {
        packet[2+i] = readByte();
      }
      int apiID = packet[3];
      packet[3+dataLength] = readByte(); // checksum

      printPacket(dataLength+4);

      if (apiID == 0x92) {
        int analogSampleIndex = 19;
        int digitalChannelMask = (packet[16] << 8) | packet[17];
        if (digitalChannelMask > 0) {
          int d = (packet[19] << 8) | packet[20];
          for(int i=0;i < NUM_DIGITAL_SAMPLES;i++) {
            digitalSamples[i] = ((d >> i) & 1);
          }
          analogSampleIndex = 21;
        }

        int analogChannelMask = packet[18];
        for(int i=0;i<4;i++) {
          if ((analogChannelMask >> i) & 1) {
            analogSamples[i] = (packet[analogSampleIndex] << 8) | packet[analogSampleIndex+1];
            analogSampleIndex += 2;
          } else {
            analogSamples[i] = -1;
          }
        }
      }
    }

    int reading = analogSamples[1];  // pin 19
    
    // convert reading to millivolts
    float v = ((float)reading/(float)0x3FF)*1200.0;

    // convert to Fahrenheit.  10mv per Fahrenheit degree
    float f = v / 10.0;

    // round to nearest int
    tempF = (int)(f+0.5);
    Serial.print("Temp: ");
    Serial.println(tempF);
  }
}


void printPacket(int l) {
  for(int i=0;i < l;i++) {
    if (packet[i] < 0xF) {
      // print leading zero for single digit values
      Serial.print(0);
    }
    Serial.print(packet[i], HEX);
    Serial.print(" ");
  }
  Serial.println("");
}


int readByte() {
    while (true) {
      if (Serial.available() > 0) {
      return Serial.read();
    }
  }
}

Can anyone shed any light ?? Cheers.

    int reading = analogSamples[1];  // pin 19

1?
It would be worth printing all 4 values in analogSamples, as well as the value in reading.

    float v = ((float)reading/[glow](float)0x3FF[/glow])*1200.0;

Why not just put 1023.0 here?

I will give your suggestions a go - that code is a copy and paste jobby from this thread - I was just trying to learn a bit more about XBee API comms and the Arduino. I will post my results !

Actually - I also worked out that the tutorial on:
http://nootropicdesign.com/projectlab/2009/11/01/wireless-temperature-sensor/

was based on a different temp sensor. My temp sensor is the one that came with my inventors kit - the TMP36. It give 10mv for each one degree rise Celcius with a 500mv offset at 0 degrees.

I have been tryign to make sense of the output of the code / sensor so that I can modify the output calculation in this code that is based on a different sensor (10mv / F)

Cheers.

I have been tryign to make sense of the output of the code / sensor so that I can modify the output calculation in this code that is based on a different sensor (10mv / F)

First, you need to actually get readings that vary as a result of changes in temperature. Then, converting the voltage to a temperature is trivial.

Yes - I have that now.

What I dont understand is what that 'reading' value is ?? I would have expected the value off the ADC pin 19 would be a float, not an INT value.

int reading = analogSamples[1];  // pin 19

I would have expected the value off the ADC pin 19 would be a float, not an INT value.

Why? The same kind of ADC that the Arduino uses is used on the XBee (ADC = analog to digital converter).

The output is an integer value in the range 0 to 1023.

Of course - apologies. It's almost midnight here and I'm not thinking straight :o

Does anyone know where the 1200 come from in the mv calculation ? Do I actually have to measure realtime mv from the sensor then apply a conversion factor to the ADC output ?

you have to look at the rest of the code to understand how the integer value is converted to a temperature.

    int reading = analogSamples[1];  // pin 19

    // convert reading to millivolts
    float v = ((float)reading/(float)0x3FF)*1200.0;

    // convert to Fahrenheit.  10mv per Fahrenheit degree
    float f = v / 10.0;

    // round to nearest int
    tempF = (int)(f+0.5);

The two-byte value in the packet represents a value on a scale of 0-1023 (0x - 0x3FF). The maximum voltage on the analog pin is 1200mv, so scale to that range by multiplying by 1200. The result is millivolts present on the analog pin. Now that we have millivolts, converting to temperature is easy because the sensor outputs 10mv per degree.

Code:

    float v = ((float)reading/(float)0x3FF)*1200.0;

Why not just put 1023.0 here?

PaulS, I used 0x3FF because the XBee datasheet describes the analog pins in terms of hexadecimal values. I just kept the code consistent with the datasheet so people can make the connection.