WiFi Divining RodJill and I decided it might be fun, and maybe even informative, to repurpose magical artifacts for technological purposes as a final assignment in our Tangible User Interfaces course. I had heard about Eva Hornecker’s work (PDF) in which her students used a divining rod as a sort of “physical sketch” for a tour guide device, and thought that it might be cool to make a divining rod that would help you find strong Wi-Fi signal. So Jill found a nice branch, I wrote a little Python, we both went to town with a Dremel and some rubber bands, et voila: the Wi-Fi Divining Rod. A DC motor in the tip of the rod vibrates more intensely the stronger the signal from the strongest local Wi-Fi network.

Ready to DivineIt was a lot of fun to make, and even more fun to see people’s reactions. The simple act of exploring the unseen radio landscape of a space might be familiar to anyone who’s walked around watching bars on their cell, but somehow doing so with a big branch made it feel so much more inquisitive, and maybe even a little magical.

Read on for design sketches, code, and the other ideas. But if you don’t, make sure you check out all the other cool projects from this class!

Designs

Divining Rod SketchWi-Fi Divining Rod: Alternate Interactions

Wi-Fi Divining Rod: Possible Displays

Materials

  • 1 large, Y-shaped branch
  • 1 breadboard
  • 1 DC motor
  • 1 wine cork
  • 1 Arduino microcontroller (we used a Arduino NG Rev. C, ATmega168)
  • 1 diode (1N4004)
  • 1 transistor (TIP120)
  • 2 AA batteries and cartridge
  • 1 Apple laptop
  • jumper wires

Electronic Construction

The circuit was the simple one we used in our lab on DC motors:

DC Motor Curcuit from TUI

We kept the Arduino board rubber banded to the breadboard for simplicity, with a USB cable running to the laptop, and long jumpers running to the DC motor in the branch.

Physical Construction

We found a solid branch thick enough to house the DC motor and used a saw to remove a segment from the top of the end of the single branch of the rod. We used the sanding bit on a Dremel to hollow out a region for the DC motor, leaving a platform for it to sit on. We stuck a disk cut from a wine cork on the end of the motor so it was off-center, causing the motor to vibrate when it spun. We wrapped the motor in thin bubble wrap and glued it into the compartment, re-attaching the segment we sawed off with rubber bands. The wires ran out of a hole we drilled through the branch.

Here’s a pic of the Dremeling process (before we realized the sanding bit was a better bet). I might post some pics of the finished internals soon.

Carving out the Actual Rod

Arduino Code

 
/*
 * Divining Rod Arduino
 * Modified from one pot fades one motor by Dave Nguyen
 * which was a modified version of AnalogInput
 * by DojoDave <http://www.0j0.org>

 * http://www.arduino.cc/en/Tutorial/AnalogInput 
 */
 
// serial input vars
char serInString[100];
char cmd;
 
int motorPin = 9; // select the pin for the Motor

int val;
 
void setup() {
  Serial.begin(9600);
  analogWrite(motorPin, 0);

}
 
void loop() {
  readSerialString(serInString, 100);
  cmd = serInString[0];
 
  if (cmd == ’s’) { // if we should spin

    val = atoi(serInString+1);
    analogWrite(motorPin, val);
    Serial.print(“Spin: “);
    Serial.println(val);
  }
 
  resetSerialString(serInString, 100); 
  delay(200); // Python does weird things if the lines come in too fast

}
 
//read a string from the serial and store it in an array
//you must supply the array variable
void readSerialString (char *strArray, int maxLength) {
  int i = 0;

 
  if(!Serial.available()) {
    return;
  }
  while (Serial.available() && i < maxLength) {

    strArray[i] = Serial.read();
    i++;
  }
}
 
void resetSerialString (char *strArray, int length) {

  for (int i = 0; i < length; i++) {
    strArray[i] = \0′;
  }

}

Python Code

wifi.py

This is a trivial little module that just snarfs XML on Airport output from a script that ships with OS X. Found out about the script by looking through the AirPort Radar Dashboard widget. You’ll need plistlib, but I think that ships with both MacPython and the MacPorts Python dist. If anyone knows how to do this on another OS, leave a comment!

 
“”“Module to detect 802.11 WiFi networks on Mac OS X using an AirPort Card.”“”
 
import plistlib

from os import popen
 
def scan(cmd=None):
  “”“Scan for wifi networks, return a list of network dictionaries.
 

  scan() returns output from a script included in OS X.  Each network dict 
  contains the following keys:
   - 
 
  @type   cmd: str
  @param  cmd: Shell command to execute to get wifi ntwk info in XML.
  @rtype:      list
  @return:     List of network dictionaries.
  ““”
  if cmd == None:
    cmd = ‘/System/Library/PrivateFrameworks/Apple80211.framework/Resources/airport -s -x’

 
  try:
    ntwks = plistlib.readPlist(popen(cmd))
  except:
    print “Failed to find networks.  The command ‘%s’ may not exist.” % cmd

  else:
    return ntwks
 
 
def strengths(cmd=None, max_rssi=100):
  “”“Return networks with only the SSIDs and the signal strenghts.

 
  Each network dict only contains a ‘name’ (SSID) and a ’strength’, where 
  ’strength’ = max_rssi - RSSI*-1.  The list of networks is sorted by 
  strength.  max_rssi defaults to 100.
 
  @type   cmd: str
  @param  cmd: Shell command to execute to get wifi ntwk info in XML.
  @type   max_rssi: int
  @param  max_rssi: Estimated max abs(RSSI) for your card (it varies). 
  @rtype:   list
  @return:  List of abbreviated network dictionaries.
  ““”
  ntwks = scan(cmd)

  strengths = [{‘name’: ntwk[‘SSID_STR’], 
                ’strength’: (ntwk[‘RSSI’] + max_rssi),
                ‘encrypted’: ‘WEP’ in ntwk.keys() or ‘WPA_IE’ in ntwk.keys()} 
                for ntwk in ntwks]

  strengths.sort(key=lambda a: a[’strength’], reverse=True)
 
  return strengths
 
 

if __name__ == ‘__main__’:
  while True:
    sths = strengths()
    for ntwk in sths:
      print “%(name)-30s : %(strength)5s%%” % ntwk

 
    print

diviningrod.py

Again, real simple. The “normalize” method probably isn’t perfect, but it worked for this project.

 
“””
diviningrod.py
by Ken-ichi Ueda, 2007
““”
 

import wifi
from optparse import OptionParser
from serial import Serial
 
ARDUINO_DEV = ‘/dev/tty.usbserial-A4001nLM’ # you need to set this the tty for your own board

 
def normalize(x, xmin, xmax, nmin=0, nmax=255):
    “”“Normalize x from an xmin <-> xmax range to nmin <-> nmax”“”

    n = nmax*(x - xmin) / xmax + nmin
    if n > nmax: n = nmax
    if n < nmin: n = nmin
 
    return n

 
def main():
    parser = OptionParser()
    parser.add_option(‘-o’, ‘–open-only’, action=’store_true’, 
                      dest=’open_only’, default=False,
                      help=“Only scan for open, unencrypted networks”)

    (options, args) = parser.parse_args()
 
    ser = Serial(ARDUINO_DEV, 9600)
 
    try:
        while 1:
          ntwks = wifi.strengths()

          if options.open_only:
              ntwks = [n for n in ntwks if not n[‘encrypted’]]

          currvelo = ntwks[0][’strength’]
          print ‘Network: %s’ % ntwks[0][‘name’]
          print ‘Strength: %d’ % ntwks[0][’strength’]

          normvelo = normalize(currvelo, 0, 70, 0, 150)
          print ‘Normalized Velocity: %d’ % normvelo
          ser.write(’s%d’ % normvelo)

          # ser.write(’s%d’ % currvelo)
          print
 
    except (KeyboardInterrupt, SystemExit):
      print ‘Spinning motor down to 0…’

      ser.write(’s%d’ % 0)
      print
      print
      print ‘Laters.’

      ser.close()
 
if __name__ == ‘__main__’:
    main()

Other Ideas

Book Review Pendulum Scry

Diviners traditionally used pendulums to answer “yes” or “no” questions, like, “Will this baby be a girl?” We thought it would be cool to put a bar code reader in the bob that would read the bar code off books and transmit it up wires in the string to a device that made wireless queries to Amazon or another service. Amazon would return a rating, resulting in the pendulum swinging toward and away from your body to indicate “yes” and across your body to indicate “no,” in response to the question, “Should I read this book?”

Book Review Pendulum: Sketch

Surf Scrying Pool

I always liked the fact that in Brief Lives, one of Neil Gaiman’s excellent Sandman graphic novels, one of the characters had a “scrying pool” that sloshed around more violently when his perusers were near. Traditionally, water scrying involved discerning patterns in the ripples and reflections of water. We thought it might be useful if the degree of agitation in a pool of water indicated the sea conditions at your favorite surfing spot.

Surf Scrying Pool: Interaction Loop

And here’s a really similar project by the artist Shelly Farnham!

Thanks!

Many thanks to everyone who helped us out, and special thanks to Kimiko for teaching such a unique class!