BWSN Uploading to Xively

Here is the video for Chapter 8 “More to Love”.

I was hoping to get the processing sketch to upload a week or two of sensor data until I made a post about it, but I kept running into issues. (Laugh)

You can see my Xively device page here. This show’s my XBee temperature sensor network at room, fridge and freezer temperatures.

So the original source code for this project can be found on the author’s website here. Unfortunately this code is not up to date. The book and the downloaded code upload to Pachube. BUT Pachube changed its name twice since this book was writtien, first to Cosm then to Xively.

In light of these changes, I’ve fixed the processing sketch to upload to Xively using the EEML library. So step by step:
1) Login to Xively and create an account. Once you have a free developer account/login, create a device. The device page will give you a FeedID and an API Key which will need to be placed in the Processing sketch.
2) Download the code from the author’s website here.
3) Download the EEML(Extended Environments Markup Language) jar library from here and place it in the “code” directory of the Processing sketch.
4) Replace the Processing sketch provided by the author’s website and use this sketch instead. Replace the FeedID and apiKey with the ones provided by your Xively device webpage:

/*
 * Draws a set of thermometers for incoming XBee Sensor data
 * by Rob Faludi http://faludi.com
 *
 * Oct 2013 - Updated to use EEML, upload to Xively and display both Celsius and
 *   Fahrenheit display numbers.
 */

// Using EEML for Xively connection http://xively.com/
// EEML Processing.org library available at http://www.eeml.org/library/
import eeml.*;

// used for communication via xbee api
import processing.serial.*; 

// xbee api libraries available at http://code.google.com/p/xbee-api/
// Download the zip file, extract it, and copy the xbee-api jar file 
// and the log4j.jar file (located in the lib folder) inside a "code" 
// folder under this Processing sketch’s folder (save this sketch, then 
// click the Sketch menu and choose Show Sketch Folder).
import com.rapplogic.xbee.api.ApiId;
import com.rapplogic.xbee.api.PacketListener;
import com.rapplogic.xbee.api.XBee;
import com.rapplogic.xbee.api.XBeeResponse;
import com.rapplogic.xbee.api.zigbee.ZNetRxIoSampleResponse;

String version = "1.10";

//  *** Replace with the Serial Port (Com Port) Number for your local Xbee *** 
int SerialPortNumber = 1;


// *** REPLACE WITH YOUR OWN PACHUBE API KEY AND FEED ID ***
String XivelyBaseURL="https://api.xively.com/v2/feeds/";
String apiKey="ADD_THE_XIVELY_APP_KEY_FOR_YOUR_DEVICE_HERE";
String feedID="ADD_THE_XIVELY_FEEDID_FOR_YOUR_DEVICE_HERE" + ".xml";

DataOut dOut;  // Data object used to upload to Xively

// create and initialize a new xbee object
XBee xbee = new XBee();

int error=0;

// Defines the thermometer ID starting at zero and incrementing.
// Used to differentiate streams on Xively
int thermID = 0;  

// used to record time of last data post
float lastUpdate;

// make an array list of thermometer objects for display
ArrayList thermometers = new ArrayList();
// create a font for display
PFont font;

void setup() {
  size(800, 600); // screen size
  smooth(); // anti-aliasing for graphic display


  // You’ll need to generate a font before you can run this sketch.
  // Click the Tools menu and choose Create Font. Click Sans Serif,
  // choose a size of 10, and click OK.
  font =  loadFont("SansSerif-10.vlw");
  textFont(font); // use the font for text

  // The log4j.properties file is required by the xbee api library, and 
  // needs to be in your data folder. You can find this file in the xbee
  // api library you downloaded earlier
  PropertyConfigurator.configure(dataPath("")+"log4j.properties"); 
  // Print a list in case the selected one doesn't work out
  println("Available serial ports:");
  println(Serial.list());
  try {
    // opens your serial port defined above, at 9600 baud
    xbee.open(Serial.list()[SerialPortNumber], 9600);
  }
  catch (XBeeException e) {
    println("** Error opening XBee port: " + e + " **");
    println("Is your XBee plugged in to your computer?");
    println(
      "Did you set your COM port in the code near line 27?");
    error=1;
  }
  
  // Create the XivelyURL and setup
  String FeedURL = XivelyBaseURL + feedID;
  dOut = new DataOut(this, FeedURL, apiKey); 
}


// draw loop executes continuously
void draw() {
  background(224); // draw a light gray background
  // report any serial port problems in the main window
  if (error == 1) {
    fill(0);
    text("** Error opening XBee port: **\n"+
      "Is your XBee plugged in to your computer?\n" +
      "Did you set your COM port in the code near line 20?", width/3, height/2);
  }
  SensorData data = new SensorData(); // create a data object
  data = getData(); // put data into the data object
  //data = getSimulatedData(); // uncomment this to use random data for testing

  // check that actual data came in:
  if (data.value >=0 && data.address != null) { 

    // check to see if a thermometer object already exists for this sensor
    int i;
    boolean foundIt = false;
    for (i=0; i <thermometers.size(); i++) {
      if ( ((Thermometer) thermometers.get(i)).address.equals(data.address) ) {
        foundIt = true;
        break;
      }
    }

    // *** ENABLE THIS CODE FOR LM335 temperature sensor ****
    // process the data value into a Celsius temperature reading for
    // LM335 with a 1/3 voltage divider
    //   (value as a ratio of 1023 times max ADC voltage times 
    //    3 (voltage divider value) divided by 10mV per degree
    //    minus zero Celsius in Kevin)
    // float temperatureCelsius = (data.value/1023.0*1.2*3.0*100)-273.15;

    //    // *** ENABLE THIS CODE FOR TMP36 temperature sensor ****
    //    // process the data value into a Celsius temperature reading for
    //    // TMP36 with no voltage divider
    //    //   (value as a ratio of 1023 times max ADC voltage times 
    //    //    minus 500 mV reading at zero Celsius
    //    //    times 100 to scale for 10mv per degree C)
    float temperatureCelsius = ((data.value/1023.0*1.25 - .5) *100); 
    
    println(" temp: " + round(temperatureCelsius) + "˚C");

    // update the thermometer if it exists, otherwise create a new one
    if (foundIt) {
      ((Thermometer) thermometers.get(i)).temp = temperatureCelsius;
    }
    else if (thermometers.size() < 10) {
      thermometers.add(new Thermometer(data.address,35,450,
      (thermometers.size()) * 75 + 40, 20, data.numericAddr, thermID++));
      ((Thermometer) thermometers.get(i)).temp = temperatureCelsius;
      
      int thermometerFeedID = ((Thermometer) thermometers.get(i)).thermometerID;
      println("Init thermometerFeedID: " + thermometerFeedID);
      
      // Init Xively here for each Temp Bar
      dOut.addData(thermometerFeedID,
                       "TempSensor_" + thermometerFeedID ,
                      (float)40.0, (float)-10.0);
      dOut.setUnits(thermometerFeedID, "Celsius","C","basicSI");
    }

    // draw the thermometers on the screen
    for (int j =0; j<thermometers.size(); j++) {
      ((Thermometer) thermometers.get(j)).render();
    }
    // post data to Xively every minute
    if ((millis() - lastUpdate) > 60000) {
      for (int j =0; j<thermometers.size(); j++) {
        ((Thermometer) thermometers.get(j)).dataPost();
      }
      lastUpdate = millis();
    }
  }
} // end of draw loop


// defines the data object
class SensorData {
  int value;
  String address;
  long numericAddr;
}


// defines the thermometer objects
class Thermometer {
  int sizeX, sizeY, posX, posY;
  float maxTemp = 40; // max of scale in degrees Celsius
  float minTemp = -10; // min of scale in degress Celsius
  float temp; // stores the temperature locally
  String address; // stores the address locally
  long numAddr; // stores the numeric version of the address
  int thermometerID;  // Feed ID of this object
  float temp_f;  // temp in F


  Thermometer(String _address, int _sizeX, int _sizeY, 
  int _posX, int _posY, long _numAddr, int _ID) { // initialize thermometer object
    address = _address;
    sizeX = _sizeX;
    sizeY = _sizeY;
    posX = _posX;
    posY = _posY;
    numAddr = _numAddr;
    thermometerID = _ID;
  }

  void dataPost() {
    // add and tag a datastream
    int thermometerFeedID = thermometerID;
    println("thermometerFeedID: " + thermometerFeedID);
    
    
    //Update Xively feed objects
    dOut.update(thermometerFeedID, (float) temp);
    println("posting to Xively...");
    int response = dOut.updatePachube(); // update the datastream
    println(response);  // Should be 200 OK success

  }

  void render() { // draw thermometer on screen
    noStroke(); // remove shape edges
    ellipseMode(CENTER); // center bulb
    float bulbSize = sizeX + (sizeX * 0.5); // determine bulb size
    int stemSize = 30; // stem augments fixed red bulb 
    // to help separate it from moving mercury
    // limit display to range
    float displayTemp = round( temp);
    if (temp > maxTemp) {
      displayTemp = maxTemp + 1;
    }
    if ((int)temp < minTemp) {
      displayTemp = minTemp;
    }
    // size for variable red area:
    float mercury = ( 1 - ( (displayTemp-minTemp) / (maxTemp-minTemp) ));
    // draw edges of objects in black
    fill(0); 
    rect(posX-3,posY-3,sizeX+5,sizeY+5); 
    ellipse(posX+sizeX/2,posY+sizeY+stemSize, bulbSize+4,bulbSize+4);
    rect(posX-3, posY+sizeY, sizeX+5,stemSize+5);
    // draw grey mercury background
    fill(64); 
    rect(posX,posY,sizeX,sizeY);
    // draw red areas
    fill(255,16,16);

    // draw mercury area:
    rect(posX,posY+(sizeY * mercury), 
    sizeX, sizeY-(sizeY * mercury));

    // draw stem area:
    rect(posX, posY+sizeY, sizeX,stemSize); 

    // draw red bulb:
    ellipse(posX+sizeX/2,posY+sizeY + stemSize, bulbSize,bulbSize); 

    // show text
    textAlign(LEFT);
    fill(0);
    textSize(10);

    // show sensor address:
    text(address, posX-10, posY + sizeY + bulbSize + stemSize + 4, 65, 40);

    // show maximum temperature: 
    temp_f = maxTemp * 9/5 + 32;
    text(temp_f + "˚F", posX+sizeX + 5, posY); 
    text(" " + maxTemp + "˚C", posX+sizeX + 5, posY + 10); 

    // show minimum temperature:
    temp_f = minTemp * 9/5 + 32;
    text(" " +temp_f + "˚F", posX+sizeX + 5, posY + sizeY);
    text(minTemp + "˚C", posX+sizeX + 5, posY + sizeY + 10);
    
    // Conver the temp to F before display
    temp_f = temp * 9/5 + 32;

    // show temperature:
    text(round(temp_f) + "˚F", posX+2,posY+(sizeY * mercury+ 14));
    text(round(temp)   + "˚C", posX+2,posY+(sizeY * mercury+ 24));
  }
}

// used only if getSimulatedData is uncommented in draw loop
//
SensorData getSimulatedData() {
  SensorData data = new SensorData();
  int value = int(random(650,670));
  String address = "00:13:A2:00:12:34:AB:C" + str( round(random(0,2)) );
  data.value = value;
  data.address = address;
  data.numericAddr = unhex(data.address.replaceAll(":", ""));
  delay(200);
  return data;
}

// queries the XBee for incoming I/O data frames 
// and parses them into a data object
SensorData getData() {

  SensorData data = new SensorData();
  int value = -1;      // returns an impossible value if there's an error
  String address = ""; // returns a null value if there's an error

  try {
    // we wait here until a packet is received.
    XBeeResponse response = xbee.getResponse();
    // uncomment next line for additional debugging information
    //println("Received response " + response.toString()); 

    // check that this frame is a valid I/O sample, then parse it as such
    if (response.getApiId() == ApiId.ZNET_IO_SAMPLE_RESPONSE 
      && !response.isError()) {
      ZNetRxIoSampleResponse ioSample = 
        (ZNetRxIoSampleResponse)(XBeeResponse) response;

      // get the sender's 64-bit address
      int[] addressArray = ioSample.getRemoteAddress64().getAddress();
      // parse the address int array into a formatted string
      String[] hexAddress = new String[addressArray.length];
      for (int i=0; i<addressArray.length;i++) {
        // format each address byte with leading zeros:
        hexAddress[i] = String.format("%02x", addressArray[i]);
      }
      // join the array together for a numeric address:
      long numericAddress = unhex(join(hexAddress,""));
      data.numericAddr = numericAddress;
      print("numeric address: " + numericAddress);
      // join the array together with colons for readability:
      String senderAddress = join(hexAddress, ":"); 
      print("  sender address: " + senderAddress);
      data.address = senderAddress;
      // get the value of the first input pin
      value = ioSample.getAnalog0();
      print("  analog value: " + value ); 
      data.value = value;
    }
    else if (!response.isError()) {
      println("Got error in data frame");
    }
    else {
      println("Got non-i/o data frame");
    }
  }
  catch (XBeeException e) {
    println("Error receiving response: " + e);
  }
  return data; // sends the data back to the calling function
}

This Processing sketch will automatically create a thermometer when a new XBee address is found. It will then automatically create a device channel and begin uploading the last data sample it got from the Xbee device once a minute.

Some advice:
a) The XBee nodes are based on the Chapter 5 thermometer TMP36 remote sensor nodes.
b) The Processing sketch can be tested without hardware to verify that it uploads to Xively successfully. To do this comment in the “data = getSimulatedData();” line in the sketch. It will create three fake thermometers and fill them with random test data.
c) Be careful when restarting the Processing sketch. It will upload to channel one, two, three etc based on which XBee address appears first. So turn off the XBee’s before restarting the Processing sketch and power them on in the order you want them to appear on the website. First XBee sample found will thereafter be uploaded to channel one, second on channel two, etc. If your not careful, this can mess up your channel data.

Some issues:
i) I don’t know if I trust the temperature sample data coming out of the XBee’s. Looking at my Xively webpage and comparing the data provided to my other test devices, I can see that the room temperature given is warmer than it actually is in the room. Reads high 70’s when my weather station temperature reads low 70’s. The fridge temperature should be higher than zero because I don’t find any frozen water or juice when I pull them out of the fridge. And the freezer seems about 10 or so degree’s colder than I expect it to be. I don’t know what’s the issue with these readings. I’ll have to investigate that.
ii) The Processing sketch has died on me in two different ways.
1) The sketch kept running and uploading the last data sample to Xively but the console read “Unable to read start byte”. This tells me I need to update the text processing in the sketch to be more robust.
2) The sketch will stop processing sample data. I believe this is an issue with my USB drivers which I will have to look into.
iii) Be careful when restarting the Processing sketch. Sample data will be uploaded to Xively based on what XBee address appears first. This is something I will have to address in my future XBee software projects.

Last but not least, I really don’t need to use a Processing sketch to capture this sample data. If I have sample data appearing on a website, I won’t need to look at the thermometer graphs. I should be using the Connect port to push this data to Xively.

And thats a wrap! Chapter 8 “More to Love” was the last chapter in the “Building Wireless Sensor Networks” book. I have to say over the last two months where I worked through the projects that it has been a learning experience. Hope you enjoyed these posts.

Posted in BWSN | 1 Comment

BWSN Twitter Reader Demo

Video covering “Chapter 7 – Over the Borders” featuring a twitter reader and a mailbox web application:

The XIG Internet Gateway software is a very powerful tool.  With the help of the ConnectPort x2 it bridges the gap between the XBee nodes and the wider internet.  This allows items on the XBee network to upload data (using GET) and fetch from the internet.  Robert Faludi includes API info, arduino sketch and PHP examples on his website here.

Some comments on this project:

  • The twitter reader code has been updated from the book and the latest version (v1.06 seen in the video above) is available on the author’s website here.
  • The internet gateway software XIG python scripts loaded on the ConnectPort X2 can be downloaded from here.  I’m using the Ver1.5.1b19 beta software in the video demo.
  • The website demo I wrote uses two PHP files.  One is the main page where the user can submit a msg to be stored in the SQL database and another that the arduino fetches that pulls from the SQL database to be displayed on the LCD.
  • I didn’t find much information on how to connect the 10K trimpot but after some experimenting I realized that Pin 3 of the LCD should be attached to the middle pin and VCC to one outer pin and GND to the other outer pin.
Posted in BWSN | Leave a comment

BWSN Powerswitch Tail Demo

New video, this covers “Chapter 6 – Sleeping, Then Changing the World” with Powerswitch Tails:

This project was relatively easy. It built on the sensor boards constructed from “Chapter 5 – API and a Sensor Network”. The only change is to remove the temperature sensor and in its place put a NPN Transistor. I’m using the 2N3904 from SparkFun as provided in the BWSN Basic Kit.  This project uses the two Powerswitch Tail II’s provided in the BWSN Advanced Kit.  In the video above, I show the use of two PST2 and one PST1 which I had left over from another project.  The connections are the same.

Some helpful advice:

  • You can test your circuit without having to plug the PST into a wall socket.  If you have connected the NPN to the XBee correctly and used the Processing program provided, the light on the PST will light up.  The PST1  will also give an audible thunk as it engages the relay.
  • The PST2 will not engage the relay if it is not connected to power.  I.E. if it’s not connected to a lamp or other electrical appliance, it will not make a noise as it will not enable the relay.  BUT if under a load, it will engage the relay and make the noise.  Clear as mud?  Good.
  • The processing sketch used in this example can be downloaded from Robert Faludi’s BWSN site.  Download the “Simple_Actuator_Network.zip”
  • If you are using the latest Processing, the examples in the book won’t work exactly right. They changed the way the com ports get setup. Instead of setting the “mySerialPort” string, you have to choose the correct COM port from the “Serial.list()” array. See the code example below.
try {
// opens your serial port defined above, at 9600 baud
// ## (2013-Aug-27) Updated Processing, now have to
// ## select the serial port from the "serial.list" array.
// ## IE "serial.list()[X]" where X is the COM port
// ## in the array.
xbee.open(Serial.list()[2], 9600);
}
  • There aren’t many explanations for wire the hookup’s for the PST.  After looking at the schematic’s I connected Pin 1 “+in” to the 9v input of the battery and connected Pin 2  “-in” to the collector of the NPN transistor and left Pin 3 “Ground” floating.
Posted in BWSN | Leave a comment

BWSN Remote Temperature Sensors

New demo video, this one is about remote temperature sensors. Check it out:

What’s great about this chapter is that you don’t need an arduino. All the remote sensors are using xbee’s and the control is coming from the Processing sketch over the serial port.

Some helpful advice:

  • After assembling your caps and chip for the voltage regulator verify it is putting out the correct voltage with a multimeter. DON’T connected anything to the voltage regulator until you verify this step.
  • Since Pin 20 (AD0) on the xbee is setup for this project as an analog read, you can verify that the xbee is transmitting by attaching its input to ground (to read the lowest possible temp) or to the 3.3v rail (to read the highest possible temp). This verifies the xbee is communicating.
  • The BWSN Basic Box from Sparkfun includes a TMP36 chip which is different from the LM335 mentioned in the book. The TMP36 is easier to setup but uses a different formula to get the actual temperature. An updated processing sketch can be found on Robert Faludi’s BWSN page referencing this: “TMP36 Instructions: Simple Sensor Network”.
  • If you are using the latest Processing, the examples in the book won’t work exactly right. They changed the way the com ports get setup.  Instead of setting the “mySerialPort” string, you have to choose the correct COM port from the “Serial.list()” array.  See the code example below.
try {
// opens your serial port defined above, at 9600 baud
// ## (2013-Aug-27) Updated Processing, now have to
// ## select the serial port from the "serial.list" array.
// ## IE "serial.list()[X]" where X is the COM port
// ## in the array.
xbee.open(Serial.list()[2], 9600);
}
  • The Processing sketch provided on the BWSN website includes code for both the LM335 from the book and the TMP36 provided in the BWSN Basic Kit from sparkfun. Comment out the LM335 temp calculation code and comment in the TMP36 code to get the temperatures to display accurately.
Posted in BWSN, Source Code | 4 Comments

BWSN – Lighting Troubles

Finished “Chapter 4 – Romantic Lighting Sensor” of “Building Wireless Sensor Networks” Book.  Check out the hardware demo:

This chapter introduces the XBee API mode which allows the Arduino to send commands over the wireless bridge to a remote XBee.  I had the hardest time confirming that the sensor node was working.   Couldn’t figure it out.  The Sensor board accepted the AT commands just fine when plugged into the X-CTU.  The LED and photocell worked.   But still … nothing was working!  I finally figured out that when I reseated the XBee on the Arduino side that I accidentally seated it upside down in the daughter board.  (Note to self, buy a digital oscilloscope!)   Sensor board was sending … but the Arduino side wasn’t receiving!  (Dang)  Once I got that fixed everything started to work much better.

Or course some troubleshooting hints:

1) Before connecting the battery pack to the XBee on the sensor side, confirm that it is putting out ~3.3 V.
2) Connect up the battery pack to the XBee and connect power, the power light should light.
3) Verify the resistance of the photo resistor using a multimeter. If you have a mismatched resister for the voltage divider, the values out of the analog pin may be biased (IE the values read will not be as high or low as possible.)
4) The code in the book is out of date, in the “SetRemoteState()” method the “Serial.print(blah,BYTE);” won’t compile. The Arduino version 1.0 (or higher) no longer supports the BYTE keyword. Change these lines to read “Serial.write(byte(blah));” (byte in lower case)

Posted in BWSN | Leave a comment

BWSN – Better Doorbells

Here is a circuit demo of the “Chapter 3 – Doorbell Project” with feedback:

As I mentioned in the video, here is my code for sending data at the the button transitions instead of all the time when the switch is pressed.

Here is the doorbell button side:

/*
   State Based XBee doorbell - Button Side

   IF the button is pushed and this is a change of state it will send a "P" (for Pushed)
   IF the button is released and this is a change of state it will send a "R" (for released)

   Buzzer will send an 'A' when it gets the PUSHED status.
   Buzzer will send an 'B' when it gets the NOT_PUSHED status.

  Created by James Stokebrand, Aug 6, 2013.
  Released into the public domain.

  Based on the "Doorbell Feedback BUTTON" sketch from
  "Building Wireless Sensor Networks by Robert Faludi.
   Copyright 2011 Robert Faludi,
   ISBN 978-0-59680773-3"
   http://faludi.com

 */

// LED on pin 10
int LED = 10;

// Button on pin 2
int BUTTON=2;

// Button States
int PUSHED = 1;
int NOT_PUSHED = 2;

// Keep track of the current states.
int currentState = NOT_PUSHED;
int newState = NOT_PUSHED;

void setup() {
  // init the LED as an output.
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW); // Init to low (off)

  // Init the button as an input.
  pinMode(BUTTON, INPUT);

  // Initialize serial over XBee
  Serial.begin(9600);
}

void loop() {
  // Read the state of the button
  if (digitalRead(BUTTON) == HIGH)
  {
    // If its pushed set the new state
    newState = PUSHED;
  }
  else
  {
    newState = NOT_PUSHED;
  }

  // Check if there is a state change.
  if (currentState != newState)
  {
    // State change ...
    if (newState == PUSHED)
    {
      // Notify Buzzer that the button's been pushed.
      Serial.print('P');
    }
    else // newState == NOT_PUSHED
    {
      // Notify Buzzer the button's been released.
      Serial.print('R');
    }
    // Save the new state
    currentState=newState;
  }

  // Check for communications.
  if (Serial.available() > 0)
  {
    char feedback = Serial.read();
    if (feedback == 'A')
    {
      // Buzzer confirms the button was pushed ... light up the LED.
      digitalWrite(LED,HIGH);
    }
    else if (feedback == 'B')
    {
      // Buzzer confirms the button was released ... turn off the LED.
      digitalWrite(LED,LOW);
    }
  }
}

And here is the buzzer side:

/*
State Based XBee doorbell - Buzzer Side

When the button is pushed should get a "P" (for Pushed) over serial.
Buzzer will send an "A" to acknowledge the button was pushed.

When the button is released should get a "R" (for Released) over serial.
Buzzer will send a "B" to acknowledge the button was released.

Created by James Stokebrand, Aug 6, 2013.
Released into the public domain.

Based on the "Doorbell Feedback BELL" sketch from
"Building Wireless Sensor Networks by Robert Faludi.
Copyright 2011 Robert Faludi,
ISBN 978-0-59680773-3"
http://faludi.com
*/

// Constants for the pin inputs.
int LED = 10;
int BELL = 5;

// State machine states.
int PUSHED = 1;
int NOT_PUSHED = 2;

// Set the current state
int currentState = NOT_PUSHED;

void setup() {
// Init the LED  as output
pinMode(LED, OUTPUT);
digitalWrite(LED, LOW); // Init to low (off)

// Init the BELL pin as output
pinMode(BELL, OUTPUT);

// Init serial with the XBee
Serial.begin(9600);
}

// the loop routine runs over and over again forever:
void loop() {

// State machine is serial driven.  Is there data available?
if (Serial.available() > 0)
{
// Have a char...
char feedback = Serial.read();

if (feedback == 'P')  // If the button was pushed... ack with an 'A' and buzz.
{
// Ack with an "A" char response.
Serial.print('A');
currentState = PUSHED;  // Change states to pushed.
digitalWrite(LED,HIGH);  // Turn the LED on.
}
else if (feedback == 'R') // For released ... ack with a 'B'
{
// Ack with a "B" response.
Serial.print('B');
currentState = NOT_PUSHED;  // Change states to released.
digitalWrite(LED,LOW);  // Turn the LED off.
}
}

// If the button is pushed ... sound the buzzer.
if (currentState == PUSHED)
{
digitalWrite(BELL,HIGH);
delay(10);
digitalWrite(BELL,LOW);
}
}

Instead of spamming the channel with char’s each time the button is detected low, I send a char on button high/low transitions. Lowers the number of chars sent across the XBee communication bridge.

Posted in BWSN | Leave a comment

The secret to my success is … exit holes.

One of the cool materials at Shapeways is Frosted Ultra Detail. This material prints in layers using a wax support material. If you design the model right some of the wax material can be trapped inside to make interesting effects. This material supports 0.01 mm embossed and engraved resolution which looks great. I redesigned my QR Code Business Card and printed it in Full Color Sandstone and Frosted Ultra Detail. Check it out:

THE secret to frosted ultra detail is to put an exit hole into each internal empty space. FUD Version 1 When I originally created this card I only placed one exit hole from left to right thinking this would indicate to Shapeways that the empty spaces inside the model should be allowed. NOPE! (Laugh) The maker must include a very small exit hole into each empty space inside the model.

This worked out much better on version 2: FUD Version 1

Posted in 3D Printed Objects, Project Vanity | Leave a comment