We recently needed to make a system by which we can send sensor data from a freely moving animal wirelessly. One simple way to do this is by using XBee modules from Digi. These modules are fairly small (about the size of a quarter) and are incredibly easy to use in a new project. We received a pair from SparkFun to play with.
Here is the information on XBee:
Product: https://www.sparkfun.com/products/11215 ($22.95)
Datasheet: http://www.sparkfun.com/datasheets/Wireless/Zigbee/XBee-Datasheet.pdf
We also got accessories:
XBee Shield: https://www.sparkfun.com/products/10854 ($24.95)
XBee Explorer Dongle: https://www.sparkfun.com/products/9819 ($24.95)
Also, I downloaded the X-CTU software from Digi: http://ftp1.digi.com/support/documentation/90001003_A.pdf
My plan was to use the on-board AD on the XBee to sample an analog signal (max sampling rate 1 kHz) and to send it to a receiving XBee and then to Arduino (an old Demilanove) for processing and output using an I2C DAC.
For DAC, I got MCP4725 breakout from Sparkfun:
https://www.sparkfun.com/products/8736 ($4.95)
Overall, this project cost: $100.75 not counting the Arduino, which I already had on hand.
First, I configured the two XBee modules to talk to each other using X-CTU and XBee Explorer Dongle.
Transmit Module Settings:
DL = 1234 //Destination Address Low
MY = 4321 //Source Address
BD = 7 //Interface Data Rate: 115200
D0 = 2 //Digital input 0 is ADC
IT = 4 //Samples before transmitting = 4
IR = 1 //Sampling rate = 1 (1kHz)
Receive Module Settings:
DL = 4321 //Destination Address Low
MY = 1234 //Source Address
BD = 7 //Interface Data Rate: 115200
AP = 1 // API Enable
IA = 4321 // I/O Input Address
Next, I put the Transmit module on the dongle on a breadboard hooked up to a signal source (in this case a pressure sensor). I put the Receive module on the Arduino Shield and also hooked up the DAC on a separate breadboard. The setup looks like so:
To communicate with I2C, I am using the Wire library for Arduino. To read packets from the XBee, I wrote a really simple little script:
Averaging 4 samples let's me effectively improve the ADC resolution to 12 bits fro the 10 bit native on the XBee.
It works overall. Here is a 10 Hz sine curve passed at 4 samples per packet compared to the original:
If I do not do any averaging and just send one sample at maximum settings data looks like:
Feeding this signal into a proper ADC like one from National Instruments sampling at 1 kHz, it should be fine to resolve and time-stamp individual samples.
NOTE: Reading the data using Arduino and then passing it to DAC is better than filtering the native PWM of the XBee. This is because filters that can cut out the PWM frequency of the XBee (which runs at 15.6 kHz) will yield significant amount of distortion for signals near the cutoff. For example, Digi suggests using the following RC combination to filter: 850 Ohm, 1 uF. Fc = 187 Hz. However, using this filter with signals at 50 Hz, you will get a lot of phase distortion. This does not happen with a DAC. Further, the XBee actually receives the data as digital values. It makes no sense to convert those to PWM unless you absolutely can't afford to have the Arduino/DAC.
In conclusion, however, for faster signals, we will need a different radio. Perhaps a bluetooth one.
Here is the information on XBee:
Product: https://www.sparkfun.com/products/11215 ($22.95)
Datasheet: http://www.sparkfun.com/datasheets/Wireless/Zigbee/XBee-Datasheet.pdf
We also got accessories:
XBee Shield: https://www.sparkfun.com/products/10854 ($24.95)
XBee Explorer Dongle: https://www.sparkfun.com/products/9819 ($24.95)
Also, I downloaded the X-CTU software from Digi: http://ftp1.digi.com/support/documentation/90001003_A.pdf
My plan was to use the on-board AD on the XBee to sample an analog signal (max sampling rate 1 kHz) and to send it to a receiving XBee and then to Arduino (an old Demilanove) for processing and output using an I2C DAC.
For DAC, I got MCP4725 breakout from Sparkfun:
https://www.sparkfun.com/products/8736 ($4.95)
Overall, this project cost: $100.75 not counting the Arduino, which I already had on hand.
First, I configured the two XBee modules to talk to each other using X-CTU and XBee Explorer Dongle.
Transmit Module Settings:
DL = 1234 //Destination Address Low
MY = 4321 //Source Address
BD = 7 //Interface Data Rate: 115200
D0 = 2 //Digital input 0 is ADC
IT = 4 //Samples before transmitting = 4
IR = 1 //Sampling rate = 1 (1kHz)
Receive Module Settings:
DL = 4321 //Destination Address Low
MY = 1234 //Source Address
BD = 7 //Interface Data Rate: 115200
AP = 1 // API Enable
IA = 4321 // I/O Input Address
Next, I put the Transmit module on the dongle on a breadboard hooked up to a signal source (in this case a pressure sensor). I put the Receive module on the Arduino Shield and also hooked up the DAC on a separate breadboard. The setup looks like so:
Transmit Module with pressure sensor input. |
Receive Module with Arduino and DAC. |
To communicate with I2C, I am using the Wire library for Arduino. To read packets from the XBee, I wrote a really simple little script:
The Arduino is plenty fast to handle all the data from the XBee. However, I was not pleased to see that the real-world transmission rate for the XBee capped out at 1 packet every 5 msec or 200 Hz. This means that if you are sampling and transmitting as fast as possible, you take only one sample every 5 msec in the end. This leads to a minimum 5msec lag in the system. If you acquire more samples as in my case (4). You get a 10 msec lag and a sampling rate of 100 Hz.
#include <Wire.h>
#define BUFFER_LEN 64
// constants
#define START_DELIMITER 0x7E
#define API_ID 0x83
#define PACKET_LEN 0xA //0x10
#define N_SAMPLES 0x01
#define MCP4725_ID 0b01100000
int i;
byte buffer[BUFFER_LEN];
byte L12[2];
byte dac_bits[2];
// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(115200);
Wire.begin();
for (i=0;i<10;i++)
pinMode(i+2,OUTPUT);
dac_bits[0] = 0;
dac_bits[1] = 0;
}
// the loop routine runs over and over again forever:
void loop() {
int len;
unsigned char firstByte;
int value;
word adc_val;
if (Serial.available()) {
firstByte = Serial.read();
if (firstByte==START_DELIMITER) // start of a packet
{
Serial.readBytes((char*)L12,2);
len = word(L12[0],L12[1]);
if (len==PACKET_LEN) { // check for API packet of right length
Serial.readBytes((char*)buffer,len);
// add 4 samples together
value = 0;
for (i=0;i<N_SAMPLES;i++)
value+=word(buffer[(i*2)+8],buffer[(i*2)+9]);
adc_val = value * (4 / N_SAMPLES);
// DAC Communication
Wire.beginTransmission(96);
Wire.write(0b01000000); // write dac register command [c2 c1 c0 x x pd1 pd0 x] for write dac c2 = 0, c1 = 1, c0 = 0
for (i=0;i<8;i++) bitWrite(dac_bits[0],i,bitRead(adc_val,i+4)); // shift bits around for making i2c compatible
for (i=0;i<4;i++) bitWrite(dac_bits[1],i+4,bitRead(adc_val,i));
Wire.write(dac_bits[0]);
Wire.write(dac_bits[1]);
Wire.endTransmission();
// END DAC Communication
}
}
}
}
Averaging 4 samples let's me effectively improve the ADC resolution to 12 bits fro the 10 bit native on the XBee.
It works overall. Here is a 10 Hz sine curve passed at 4 samples per packet compared to the original:
If I do not do any averaging and just send one sample at maximum settings data looks like:
Feeding this signal into a proper ADC like one from National Instruments sampling at 1 kHz, it should be fine to resolve and time-stamp individual samples.
NOTE: Reading the data using Arduino and then passing it to DAC is better than filtering the native PWM of the XBee. This is because filters that can cut out the PWM frequency of the XBee (which runs at 15.6 kHz) will yield significant amount of distortion for signals near the cutoff. For example, Digi suggests using the following RC combination to filter: 850 Ohm, 1 uF. Fc = 187 Hz. However, using this filter with signals at 50 Hz, you will get a lot of phase distortion. This does not happen with a DAC. Further, the XBee actually receives the data as digital values. It makes no sense to convert those to PWM unless you absolutely can't afford to have the Arduino/DAC.
In conclusion, however, for faster signals, we will need a different radio. Perhaps a bluetooth one.
It's such an impressive post on Xbee Sniffer. Bee modules are good for low cost applications and consume less power.
ReplyDeleteHello I am attempting to do something similar to what you have done. My project requires me to send a signal such as sine wirelessly and when received show the signal on a gui. I have all the parts excepts the DAC that you've used. Could you explain something's such as pin configuration you used and the code, please.
ReplyDeleteThank You