Introduction: Proximity-Sensing Pocket Squares

My friends and I like to coordinate for our mutual friends' weddings.  Our friends L & E got married recently and we wanted to do something special to mark the occasion.  We settled on something elegant and simple -- pocket squares that could visualize the number of people within a certain proximity.  For each pocket square I sewed 4 LEDs to a LilyPad Arduino and programmed it to display the number of other pocket-square-wearers present, using the XBee's RSSI data for coarse grain distance approximation.



In this Instructable, I will show you how to make your own set of proximity-sensing pocket squares. 

I used the following materials per person:
  • 1 pocket square (doesn't have to be silk--in fact, a more sturdy material works a little bit better to help retain the shape)
  • 4 lights (I used white lilypad LEDs)
  • 1 LilyPad Simple Board)
  • 1 LiPo Battery
  • 1 XBee (Series 1)
  • 1 XBee Breakout board (I designed my own XBee Breakout for the LilyPad)




Step 1: Construction

To make the pocket square patch, cut out a small piece of fabric (I cut 4.5"x3.5"  to give myself some extra room).  Next, sew the plus side of each of the 4 LilyPad LEDs to an individual petal (PIN) on the LilyPad.  I used pins 5, 6, 9, and 10.  Lastly, using conductive thread, sew all the minus sides of the LilyPad LEDs together and connect to pin 11 (we will declare pin 11 ground using the Arduino software). 

Good work!  Now you're done with the construction!

I designed my own XBee Breakout Boards that easily plug into the male header on the LilyPad.  You could use the Lilypad XBee Breakout and sew the boards back to back. 

Step 2: RSSI Discussion

The proximity-sensing depends on the XBee's ability to process RSSI data.  Here's a brief overview of how this works:

The Received Signal Strength Indicator (RSSI) is a measurement of power of the incoming packet. This means that as the signal travels further distances. the signal will attenuate and the RSSI value will decrease. This also means that if modules are separated by a barrier (brick wall) or if the incoming packet bounces around (reflections) before hitting the XBee, then the signal will also attenuate and the RSSI value will also decrease. This often leads to noisy and unreliable data.  But there is hope.  While we probably can't get fine grain detail of distance, we can tease out coarse grain information: "are the modules relatively close or relatively far?"

The attached image shows the RSSI values as a function of angle for the XBee modules with the chip antenna.  (For other antenna configurations please see the RF Module Antenna Manual).  As you can see, the pattern is highly anisotropic and we should expect that the orientation of the XBees will greatly effect the RSSI value.

I have also contacted Digi and asked if they would send globular data to show the radiation pattern in 3D.  I'll post it here when I receive it.

Step 3: XBee Discussion and Configuration

Before we continue, I should mention that there is a crucial difference between the XBee Series 1 and Series 2.  Aside from the fact that Series 2 uses the ZigBee protocol and Series 1 does not, Series 1 processes the power of the packet and encodes the value into the RX packet that the XBee API has access to.  Series 2 also processes the power of the packet; however, it does not encode the value into the RX packet.  Digi changed the structure of the RX packets for Series 2 and you have two choices to get the RSSI value.  You can either stop receiving packets and query the XBee to tell you what the RSSI value is (ATDB and this takes quite a bit of time) or you can use a trick discussed on the Arduino Message Board, where the RSSI value is represented through a PWM timer.

With that said, I chose XBee Series 1 to easily get access to the RSSI data.  We will need to use the API mode for the XBee to be able to fully process the RX packet.  My XBee settings were:

Channel: F
PAN ID: 3141
DH: 0
DL: 0 (I used the XBee Arduino API to set the value for broadcast mode 0xFFFF)


I have also attached the XCTU profile I used for those wanting to use my exact settings.  And if you need help configuring your XBee for API mode please see my other Instructable on this topic.

Step 4: Raw Data

This page, as well as the next two, are a discussion of RSSI data and filtering schemes.  If you wish to skip to the next stage of construction and the code, please go to step 7.

In order to set a threshold for the pocket squares' ability to detect each other, we need to get an idea of what our data looks like and how RSSI values are correlated to distance.  I configured 3 modules: A Base Module receiving data and streaming the RSSI value to my computer and two Mobile Modules that can be moved to any location.  The first plot shows an example trial run.  The two Mobile Modules were started at a distance 2 feet away in the same orientation.  At TimeStep 125 Module 2 was moved to a distance of 10 feet, then at TimeStep 300 Module 1 was moved to the same location as Module 2.  At TimeStep 480 Modules 1 and 2 were then moved back to their starting locations; however, Module 1 was rotated 180 degrees relative to the orientation of Module 2.

There are a few things I should point out here. 
  1. The RSSI value reported from the XBee increases as the distance increases
  2. The data, as expected, is pretty noisy with lots of random peaks. 
  3. Also as expected, orientation can change the RSSI value.  Looking at the last hundred TimeSteps of Module 1 we see that there is a 5 point increase compared to the start. 
  4. We can see a difference between  modules at 2 feet versus modules at 10 feet.  This means we can detect coarse grain distance (at least +/- 5 feet)!  Now all we need is the right filter...

Step 5: Filtering Data

Wow! There are sooooo sooo sooo soo many ways to filter data.  The world is noisy and to pull out the useful information we need to filter the noise.  Our data can have sudden spikes or valleys.  That means the if we filter our data through averaging or a moving average our we would have to take many many points to filter out those spikes an valleys.  If we look at the data we could probably guess as to what the value should be versus measured value.  The Kalman Filter can mathematically help us do just that.  The Kalman Filter calculates what a value should be based on the difference between the measured and a expected value.

The Kalman Filter has been used in loads of applications, from missile guidance systems to cleaning up accelerometer data. 

Here are some good examples of Kalman filters and Arduinos:
Accelerometer example: http://www.youtube.com/watch?v=qtFyR1vLH2U
Another Kalman Arduino Example: http://www.starlino.com/imu_kalman_arduino.html
The Keyglove (Please fund!)

Step 6: Kalman Filter

Interactive-Matter has a good post on filtering sensor data with the Kalman Filter and even has a library already to go for the Arduino.   For those interested in the foundational principles and a good introduction to the Kalman filter please read: http://www.cs.unc.edu/~welch/media/pdf/kalman_intro.pdf

I will also try and explain the filter here.

Recursively solve:
p = p+q;
k = p / (p + r); (gain)
x = x + k * (rssi_data – x);
p = (1 – k) * p;

That's it! 

Initial Conditions:
x = x_o (best guess)
p = p_o (estimated error)
r = r_o (sensor noise)
q = q_o (process noise) value between 0 and 1


Parameters in bold remain static through the simulation

So how do we choose our parameters?  Parameter choice is difficult and often requires lots of massaging.  To get some idea of how we should choose q and r, let's look at some extreme behavior:
  • If q is much bigger than r, then k =1 and x is just the measured value. 
  • if r is really small (very very close to 0), then k  = 1  and x is just the measured value
This means that q/r or r/q shouldn't be too big or too small and r should be too small.   Again, you will have to massage the parameter to get the data that you want.  I selected Kalman 3 because it's a good balance between lag time and filtering noise, where as Kalman 4 filters the noise nicely, but lags too much behind the data.

Actually, the mathematics/programming can be made even simpler if you assume that the process noise (q) and the sensor noise (r) don't change over time.  If you are convinced you have the right parameters, the Kalman Gain (k) will eventually stabilize to a single value.  You can pre-calculate what that value is and simplify the filter to:
x = x + k * (rssi_data – x);

For those also interested in Kalman filters used for RSSI data please read: http://www.cs.tut.fi/sgn/arg/heln/Publications/CSCC2001_Helen.pdf

Step 7: Folding Your Pocket Square

Okay, now we're done with the construction and the data analysis.  Time to build the pocket square!
  1. To fold the patch into the pocket square, first lay the square out flat. 
  2. Place the patch upside-down in the upper-right-hand corner of the lower left quadrant, angling it so that the patch points toward the center of the square, with the lights at the top. 
  3. Fold the pocket square in half length-wise, then fold in half again, making a square that is one-quarter of the original size. 
  4. Now rotate this square 45 degrees (so that it looks like a diamond instead of a square) and fold the pointy sides in (like a burrito).  The patch should be nestled in the upper half of the burrito. 
  5. Lastly, fold the bottom half of the burrito up to meet the top.
  6. Turn the pocket square over--now the lights should show through the handkerchief and the folded pocket square should be ready to fit into your pocket!  (Blunt side down, pointy side up, lights facing out.)

Step 8: Adding Friends and Code

After you have completed one patch and square, simply add another, and another, and another.  For more and more, you will need more and more XBee Boards, more lights, more patches, more LilyPads, more pocket squares and more friends!

Lastly, I have attached the code I used for the project.  As always, lots of improvements can be made and hopefully I will get around to it tomorrow or the next day.

MakerBot Challenge

Second Prize in the
MakerBot Challenge