Arduino XBee Sensor (Temperature, Tilt, Compass, and Ambient Light)
I finally put together an Arduino Pro Mini, the XBee, the MMA7260Q, the HMC6352, the TEMT6000 and the DS1620.
This is how it looks like on the prototyping board (ugly, I know)
Hopefully it would look better when I move it to the PCB. But before doing that I need to get the battery power set up. (I’m waiting for a MAX756 DC-DC voltage regulator and CoilCraft inductors to arrive)
The XBee uses the API mode and I implemented a Zigbee Application Layer protocol stack in Arduino instead of using the Serial interface like the rest of Arduino-XBee project that I’ve seen.
I implemented the Temperature Measurement Cluster ID (0x0402) and the Illuminance Measurement Cluster ID (0x0400) (see Zigbee Cluster Library ). Then I also two non-standard cluster ids, the Tilt Measurement Cluster ID (0xFC00) and the Heading Measurement Cluster ID (0xFC01).
Below you can find the main project file, from here I pass the control to the Xbee library and control the sleep cycle of the Arduino. The XBee library will invoke the callbacks (fgetTemperature, etc) when it needs the data. ie. it receives a read command. The callbacks(function pointers) point to functions in the main file (readDs1620, readTEMT6000, etc) and the callbacks are implemented in this main file which also contains the calls to ds1620 library, hmc6352 library and mma7260q library. To see the source files of the libraries download the sources. I also made independent libraries for ds1620, hmc6352 and mma7260q each one containing an example.
<span class='line'>/*
</span><span class='line'> XBee sensor
</span><span class='line'> ZCL Temperature Measurement Cluster
</span><span class='line'>*/
</span><span class='line'>
</span><span class='line'>#define ledPin 13
</span><span class='line'>
</span><span class='line'>#define lcdPin 6
</span><span class='line'>#define temt6000Pin 3
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>#include <stdlib.h>
</span><span class='line'>#include <math.h>
</span><span class='line'>
</span><span class='line'>#include <Stdio.h>
</span><span class='line'>#include <SoftwareSerial.h>
</span><span class='line'>#include <avr/power.h>
</span><span class='line'>#include <avr/sleep.h>
</span><span class='line'>#include "XBeeLibrary.h"
</span><span class='line'>#include "ds1620.h"
</span><span class='line'>#include <Wire.h>
</span><span class='line'>#include "hmc6352.h"
</span><span class='line'>#include <math.h>
</span><span class='line'>#include <float.h>
</span><span class='line'>#include <limits.h>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>int xaxis = 0;
</span><span class='line'>int yaxis = 1;
</span><span class='line'>int zaxis = 2;
</span><span class='line'>
</span><span class='line'>float xa=0;
</span><span class='line'>float ya=0;
</span><span class='line'>float za=0;
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>int minx = INT_MAX;
</span><span class='line'>int maxx = 0;
</span><span class='line'>int miny = INT_MAX;
</span><span class='line'>int maxy = 0;
</span><span class='line'>int minz = INT_MAX;
</span><span class='line'>int maxz = 0;
</span><span class='line'>
</span><span class='line'>float g0x = 0;
</span><span class='line'>float g0y = 0;
</span><span class='line'>float g0z = 0;
</span><span class='line'>
</span><span class='line'>//long time1 = 0;
</span><span class='line'>//long time2 = 0;
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>// set up a new serial port
</span><span class='line'>SoftwareSerial lcdSerial = SoftwareSerial(lcdPin,lcdPin);
</span><span class='line'>byte pinState[16];
</span><span class='line'>byte backlightValue = 0x9D;
</span><span class='line'>
</span><span class='line'>// set up ds1620
</span><span class='line'>Ds1620 ds1620 = Ds1620(7/*rst*/,8/*clk*/,9/*dq*/);
</span><span class='line'>
</span><span class='line'>//set up hmc6352
</span><span class='line'>Hmc6352 hmc6352;
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>void setup() {
</span><span class='line'> pinMode(ledPin, OUTPUT);
</span><span class='line'> pinMode(lcdPin, OUTPUT);
</span><span class='line'> pinMode(temt6000Pin,INPUT);
</span><span class='line'> pinMode(xaxis,INPUT);
</span><span class='line'> pinMode(yaxis,INPUT);
</span><span class='line'> pinMode(zaxis,INPUT);
</span><span class='line'>
</span><span class='line'> // set the data rate for the SoftwareSerial port
</span><span class='line'> lcdSerial.begin(4800);
</span><span class='line'>
</span><span class='line'> Serial.begin(9600);
</span><span class='line'> delay(100);
</span><span class='line'>
</span><span class='line'> // configure ds1620
</span><span class='line'> ds1620.config();
</span><span class='line'>
</span><span class='line'> //initial calibration of the MMA7260q
</span><span class='line'> minx = 173.0;
</span><span class='line'> miny = 192.0;
</span><span class='line'> minz = 258.0;
</span><span class='line'>
</span><span class='line'> maxx = 766.0;
</span><span class='line'> maxy = 720.0;
</span><span class='line'> maxz = 914.0;
</span><span class='line'>
</span><span class='line'> g0x = 469.0;
</span><span class='line'> g0y = 456.0;
</span><span class='line'> g0z = 586.0;
</span><span class='line'>
</span><span class='line'> lcdPrint("RST\r\n");delay(100);
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>long start =0;
</span><span class='line'>
</span><span class='line'>void loop()
</span><span class='line'>{
</span><span class='line'> struct zigbee zb;
</span><span class='line'> unsigned long time;
</span><span class='line'> unsigned long time2;
</span><span class='line'>
</span><span class='line'> xbee_init(&zb);
</span><span class='line'> zb.fgetTemperature = &readDs1620;
</span><span class='line'> zb.fprogress = &toggleLedPin;
</span><span class='line'> zb.ferror = &lcdPrint;
</span><span class='line'> zb.fprint = &lcdPrint;
</span><span class='line'> zb.fprintHex = &lcdPrintBuffer;
</span><span class='line'> zb.fgetHeading = &readHmc6352;
</span><span class='line'> zb.fgetIlluminance = &readTEMT6000;
</span><span class='line'> zb.fgetTilt = readMMA7260Q;
</span><span class='line'>
</span><span class='line'>
</span><span class='line'> XBee xbee(&zb);
</span><span class='line'>
</span><span class='line'> time = 0;
</span><span class='line'> time2 = 0;
</span><span class='line'>
</span><span class='line'> //lcdPrintInt(millis());
</span><span class='line'> while(1)
</span><span class='line'> {
</span><span class='line'> xbee.serve();
</span><span class='line'> if ((millis()-time) > 500) {
</span><span class='line'> time = millis();
</span><span class='line'> toggle(ledPin);
</span><span class='line'> //lcdSerial.print("ab\r\n");
</span><span class='line'> }
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'> int pfx /*valx*/ = analogRead(xaxis); // read the value from the sensor
</span><span class='line'> int pfy /*valy*/ = analogRead(yaxis); // read the value from the sensor
</span><span class='line'> int pfz /*valz*/ = analogRead(zaxis); // read the value from the sensor
</span><span class='line'>
</span><span class='line'>
</span><span class='line'> autoZeroCalibration(pfx,pfy,pfz);
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'> if ((millis()-time2) > 25000) {
</span><span class='line'> goToSleep();
</span><span class='line'> time2 = millis();
</span><span class='line'> }
</span><span class='line'>
</span><span class='line'> }
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>void goToSleep()
</span><span class='line'>{
</span><span class='line'> /* Now is the time to set the sleep mode. In the Atmega8 datasheet
</span><span class='line'> * http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf on page 35
</span><span class='line'> * there is a list of sleep modes which explains which clocks and
</span><span class='line'> * wake up sources are available in which sleep modus.
</span><span class='line'> *
</span><span class='line'> * In the avr/sleep.h file, the call names of these sleep modus are to be found:
</span><span class='line'> *
</span><span class='line'> * The 5 different modes are:
</span><span class='line'> * SLEEP_MODE_IDLE -the least power savings
</span><span class='line'> * SLEEP_MODE_ADC
</span><span class='line'> * SLEEP_MODE_PWR_SAVE
</span><span class='line'> * SLEEP_MODE_STANDBY
</span><span class='line'> * SLEEP_MODE_PWR_DOWN -the most power savings
</span><span class='line'> *
</span><span class='line'> */
</span><span class='line'>
</span><span class='line'> set_sleep_mode(SLEEP_MODE_IDLE); // sleep mode is set here
</span><span class='line'>
</span><span class='line'> sleep_enable(); // enables the sleep bit in the mcucr register
</span><span class='line'> // so sleep is possible. just a safety pin
</span><span class='line'>
</span><span class='line'> power_adc_disable();
</span><span class='line'> power_spi_disable();
</span><span class='line'> power_timer0_disable();
</span><span class='line'> power_timer1_disable();
</span><span class='line'> power_timer2_disable();
</span><span class='line'> power_twi_disable();
</span><span class='line'>
</span><span class='line'> digitalWrite(ledPin, 0);
</span><span class='line'> lcdPrint("slp\r\n");
</span><span class='line'>
</span><span class='line'> sleep_mode(); // here the device is actually put to sleep!!
</span><span class='line'>
</span><span class='line'> digitalWrite(ledPin, 1);
</span><span class='line'> lcdPrint("wk\r\n");
</span><span class='line'>
</span><span class='line'> // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
</span><span class='line'> sleep_disable(); // first thing after waking from sleep:
</span><span class='line'> // disable sleep...
</span><span class='line'>
</span><span class='line'> power_all_enable();
</span><span class='line'>
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>void toggleLedPin() {
</span><span class='line'> toggle(ledPin);
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>float readDs1620()
</span><span class='line'>{
</span><span class='line'> ds1620.start_conv();
</span><span class='line'> int raw_data = ds1620.read_data();
</span><span class='line'> ds1620.stop_conv();
</span><span class='line'> float temp = raw_data / 2.0;
</span><span class='line'> return temp;
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>float readHmc6352()
</span><span class='line'>{
</span><span class='line'> hmc6352.wake();
</span><span class='line'> float a = hmc6352.getHeading();
</span><span class='line'> hmc6352.sleep();
</span><span class='line'>
</span><span class='line'> return a;
</span><span class='line'>
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>void toggle(int pinNum) {
</span><span class='line'> // set the LED pin using the pinState variable:
</span><span class='line'> digitalWrite(pinNum, pinState[pinNum]);
</span><span class='line'> // if pinState = 0, set it to 1, and vice versa:
</span><span class='line'> pinState[pinNum] = !pinState[pinNum];
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>int readTEMT6000()
</span><span class='line'>{
</span><span class='line'> int val = analogRead(temt6000Pin);
</span><span class='line'> return val;
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>float readMMA7260Q(int a)
</span><span class='line'>{
</span><span class='line'> int pfx /*valx*/ = analogRead(xaxis); // read the value from the sensor
</span><span class='line'> int pfy /*valy*/ = analogRead(yaxis); // read the value from the sensor
</span><span class='line'> int pfz /*valz*/ = analogRead(zaxis); // read the value from the sensor
</span><span class='line'>
</span><span class='line'> autoZeroCalibration(pfx,pfy,pfz);
</span><span class='line'>
</span><span class='line'> int fx = (pfx - g0x);
</span><span class='line'> int fy = (pfy - g0y);
</span><span class='line'> int fz = (pfz - g0z);
</span><span class='line'>
</span><span class='line'> float ax = (fx*2.0)/((maxx-minx));
</span><span class='line'> float ay = (fy*2.0)/((maxy-miny));
</span><span class='line'> float az = (fz*2.0)/((maxz-minz));
</span><span class='line'>
</span><span class='line'>
</span><span class='line'> float rho = atan(ax/sqrt(pow(ay,2)+pow(az,2)))*(57.2957795129); //57.2957795129 degrees per rad
</span><span class='line'> float phi = atan(ay/sqrt(pow(ax,2)+pow(az,2)))*(57.2957795129);
</span><span class='line'> float theta = atan(sqrt(pow(ay,2)+pow(ax,2))/az)*(57.2957795129);
</span><span class='line'>
</span><span class='line'> switch (a)
</span><span class='line'> {
</span><span class='line'> case 1:
</span><span class='line'> return rho;
</span><span class='line'> break;
</span><span class='line'> case 2:
</span><span class='line'> return phi;
</span><span class='line'> break;
</span><span class='line'> case 3:
</span><span class='line'> return theta;
</span><span class='line'> break;
</span><span class='line'> default:
</span><span class='line'> break;
</span><span class='line'> }
</span><span class='line'> return 0;
</span><span class='line'>
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>void autoZeroCalibration(int pfx, int pfy, int pfz)
</span><span class='line'>{
</span><span class='line'> //if ((pfx < minx)||(pfy < miny)||(pfz < minz)||(pfx > maxx)||(pfy > maxy)||(pfz > maxz)) {
</span><span class='line'> // autozero calibration
</span><span class='line'> if (pfx < minx) minx = pfx;
</span><span class='line'> if (pfy < miny) miny = pfy;
</span><span class='line'> if (pfz < minz) minz = pfz;
</span><span class='line'>
</span><span class='line'> if (pfx > maxx) maxx = pfx;
</span><span class='line'> if (pfy > maxy) maxy = pfy;
</span><span class='line'> if (pfz > maxz) maxz = pfz;
</span><span class='line'>
</span><span class='line'> g0x = ((maxx - minx)/2)+minx;
</span><span class='line'> g0y = ((maxy - miny)/2)+miny;
</span><span class='line'> g0z = ((maxz - minz)/2)+minz;
</span><span class='line'>
</span><span class='line'> //printValues();
</span><span class='line'> //}
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>////////////////////////////////////
</span><span class='line'>//// Print functions
</span><span class='line'>//////////////////////////////////
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>//void lcdPrintError(char *msg)
</span><span class='line'>//{
</span><span class='line'>// lcdSerial.print("err:");
</span><span class='line'>// lcdSerial.print(msg);
</span><span class='line'>//}
</span><span class='line'>
</span><span class='line'>void lcdPrint(char *msg)
</span><span class='line'>{
</span><span class='line'> //lcdSerial.print(millis());
</span><span class='line'> //lcdSerial.print(":");
</span><span class='line'> // lcdSerial.print(msg);
</span><span class='line'>
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>//void lcdPrintInt(long i)
</span><span class='line'>//{
</span><span class='line'>// lcdSerial.print(i, DEC);
</span><span class='line'>//}
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>//void lcdPrintHex(byte *hex, int size)
</span><span class='line'>//{
</span><span class='line'>// for(int i=0;i<size;i++)
</span><span class='line'>// {
</span><span class='line'> //lcdPrintHex(hex[i]);
</span><span class='line'>// }
</span><span class='line'>//}
</span><span class='line'>
</span><span class='line'>void lcdPrintBuffer(byte *hex, int size)
</span><span class='line'>{
</span><span class='line'> //lcdPrintHex(hex,size);
</span><span class='line'> //lcdPrintBuffer(hex,size,"buf");
</span><span class='line'>}
</span><span class='line'>
</span><span class='line'>//void lcdPrintBuffer(byte *hex, int size, char *msg)
</span><span class='line'>//{
</span><span class='line'>
</span><span class='line'> //int i = 0;
</span><span class='line'> //while (i<size) {
</span><span class='line'> //lcdSerial.print(msg);
</span><span class='line'> //lcdSerial.print(" ");
</span><span class='line'> //lcdSerial.print(i);
</span><span class='line'> //lcdSerial.print("-");
</span><span class='line'> //lcdSerial.print(i+7);
</span><span class='line'> //lcdSerial.print("\r\n");
</span><span class='line'> //int len = min(size-i,8);
</span><span class='line'> //lcdPrintHex(hex+i,len);
</span><span class='line'> //lcdSerial.print("\r\n");
</span><span class='line'> //i=i+8;
</span><span class='line'> //}
</span><span class='line'> // lcdSerial.print("\r\n");
</span><span class='line'>//}
</span><span class='line'>
</span><span class='line'>//void lcdPrintHex(byte hex)
</span><span class='line'>//{
</span><span class='line'>// lcdSerial.print((hex&0xF0)>>4,HEX);
</span><span class='line'>// lcdSerial.print((hex&0x0F),HEX);
</span><span class='line'>//}</span>
This is the java gui I built to test the implementation
The DEV-08165 is a Serial USB Board from Sparkfun. I use it to get logs from the Arduino, the logs are outputed in serial form on pin 6 , because the serial port (USART) on the ATmega is being used for the XBee communication. The DEV-8772 is the board to program the Arduino Pro Mini, the pro mini does have an USB connector on board like other Arduinos, you need the DEV-8722 to program it/power it