The world's first "robot" was arguably Herbert Televox, built by Westinghouse. At the time, Herbert's ability to listen to commands over the phone and report back data was ground-breaking. Herbert Televox itself was really a standard electric box with a complex assortment of switches, timing and buzzers. The box was placed into a robot-shaped cutout when demonstrating its capabilities.
|Herbert Televox. The box in its stomach is an advanced (for the 1920's) control unit; the robot body is simply the 1920's version of cheap plywood.|
I built a scale model of Herbert Televox for a talk with early-in-career programmers where I work. The scale model had the ability to detect a phone being picked up and measure the depth of a water reservoir. I wanted a live demo with a phone connected to the scale model Herbert Televox, and a water sensor from the Herbert Televox into a little model reservoir. The reservoir is really just a small glass dish. Then I'd pick up the phone, actually dial some numbers, and then Herbert Televox would buzz once, twice, or three times based on the reservoir depth.
What I needed for a demo
To power the smarts of my Herbert Televox I used a Particle Photon, an Arduino-based microcontroller with Wi-Fi capability. After struggling with debugging with just an LED, I added a small Adafruit_SSD1306 OLED display. In order that I added them, the Herbert Televox abilities are:
- Can detect a phone going off-hook and light the blue LED
- Can write to an OLED display with debugging information
- Can read the water sensor and split it into one of three different water levels
- Can ring a small buzzer based on the level of the water
- Can wait a period of time after the phone goes off-hook before buzzing the buzzer
The Arduino base of the Particle Photon makes some of this very easy. For example, it was easy to set up an analog to digital convert (ADC) for the phone off-hook detection. But the nature of programming the Arduino made handling timing awkward; I'll show my solution.
Connecting a phone and writing to an OLED
|Diagram of a rotary phone showing the handset, the cradle in which it's placed, and the on-hook/off-hook switch|
Although a phone jack is large enough for 6 wires, and is normally wired up with four wires (the inner four), a classic rotary phone works off of just two wires: one red, and the other green. The two wires form a loop with a certain resistance. Every operation of the phone (taking the handset off-hook, dialing, and talking) will change the resistance across the wires in a way that can be easily detected by the Arduino.
To connect my demo phone to the Particle Photon Arduino, I used a RJ11 right-angle jack breakout board which I bought from Amazon, but it's also available for less straight from the maker along with an enticing set of other boards. I plugged the phone into the jack and ran some wires from the nice terminal strips on the breakout board to the Arduino.
|CZH Labs D-1039 Phone Breakout board (<$10)|
|Wiring setup for measuring phone resistance|
In particular, the connections are
- Put the Particle Photon into a breadboard
- Terminal 3 (green) to ground
- Terminal 4 (red) to a spare row on the breadboard
- From the same row, connect a 1K resistor to Vcc
- From the same row, connect a wire to A0 on the Photon
The resistance of the phone and the fixed 1K resistor forms a voltage divider that runs from Vcc to ground. When the phone is on-hook, it has a rather high resistance so that the voltage at A0 will be close to Vcc and an AnalogRead of the pin will return 4096 (the maximum value). When the phone is off-hook AnalogRead of pin A0 will read about 1680 with a variation of about 50.
The actual code to read the phone off-hook starts at line 57
// To blink the LED, first we'll turn it on...
phone = analogRead (phonePin);
measure = analogRead (measurePin);
speed = phone/64;
// on hook = 4096 off hook = 1680+-50 or so
int isOffHook = (phone < 2500);
// A BUNCH OF OTHER CODE
// Report the analog phone value via blue LED
if (isOffHook) hookTimeDelta += speed;
if (isOffHook) hookTimeDelta += speed;
When the phone is off-hook (a person has picked it up), I track the amount of time that the phone has been off-hook. That's because I will need to delay actually buzzing the buzzer for 2 seconds or so after the phone is picked up. When the phone is not off-hook, the hookTimeDelta is reset to zero (it get reset to zero a lot, but that’s OK).
I get a speed to flash the blue LED based on the phone analog value; I use a constant 64 to make for a "nice" flash speed (not too fast, not too slow). This value was picked based on observation.
After mucking around trying to debug the analog values using just the LED flashes, I got smart and wired up an OLED so that I'd get a little display. This was super useful; debugging these microcontrollers without a decent debugger is pretty painful. On each loop, I erase the OLED and print the phone analog reading; that's how I know the exact range of analog readings for the phone.
Quick and easy water sensor (list #3)
The water sensor is just a length of insulated twisted-pair telephone wire (I snagged 25 feet of 50-pair telephone wire from a dumpster 20 years ago, and have been using it as my go-to wire ever since). I scraped off 1-cm long sections of insulation in three places: at the very end and then twice more spaced apart by about 3 cm. Then I taped the wires together so that the bare sections are right next to each other just a few millimeters apart.
This crude sensor was then read in by analog pin A1 in the exact same way that the phone analog value is read: the water sensor is part of a voltage divider. One of the water sensor wires is at ground, the other is the mid-point, and then there's a resistor from the mid-point to Vcc. I used a 1K resistor because it was supplied as part of the Photon Particle kit. I then wrote the resulting values into the OLED debugging screen, and discovered that it wasn’t nearly sensitive enough.
The easy way to pick a fixed resistor for these simple voltage dividers is to use a resistor that's approximately the same as the variable resistance that you're measuring. For the water sensor, 1K was much too low, so I scrounged up a 10K resistor from a different kit.
Once I had a set of analog reading from the water sensor that seemed to be far enough apart that they would be clearly distinguished, I simply dipped and undipped the water sensor to get some useable split-points and then set an nbuzz variable to be the number of buzzes that I wanted.
Ringing a buzzer (list #4)
The Photon Particle kit I have includes a small buzzer. I followed the instructions, connecting it to digital pin D0 and initializing that pin with the pinMode function to be an OUTPUT pin. There are two commands, tone and noTone to turn the buzzer on and off. After a few experiments, I settled for buzzing the buzzer at 40 Hz for 200 milliseconds with a 500 millisecond gap between them. At the same time I set up a didBeep variable which starts at 0 (false) and is set to 1 (true) when I beep. The variable is also set back to 0 whenever the phone is on-hook. This ensures that I only beep once per off-hook event.
Waiting to buzz (list #5)
It's not good just to buzz as soon as the phone is picked up. For an effective demo, the presenter will need to pick up the phone, dial, and then pause, all while explaining what the device will do. I solved this problem with two variables: the hookTimeDelta time count that's increased by the time value passed in to each delay() function, and an didBeep variable that says whether I've done the beep for any particular off-hook event.
The actual code for deciding to buzz is then just
if (isOffHook == 1 && hookTimeDelta > 2000 && !didBeep)
didBeep = 1;
This will guarantee that the buzzes only happen once, and after a delay. Dialing is a different story. I decided against actually detecting the phone dial digits for the much simpler detection of whether the phone is dialing at all. Detecting the phone dialing is trivial: when rotary (technical: pulse-dial) phones are dialing, internally the phone is just going on and off hook. The existing code that detects on and off hook is sufficient to pause the buzzer while dialing.
The way the delay works is that every time the phone is seen to be on-hook (which includes the pulses from dialing), the hookTimeDelta value is reset to zero. This means that the buzzing will be automatically delayed every time the phone is dialed!
From a presenter point of view, the demo involves picking up the handset and talking while dialing. So long as the dialing keeps happing, the buzzing is delayed. The presenter can choose when to stop dialing and let the buzzing happen!
The good and the bad
Three great parts of using the Particle Phone Arduino Wi-Fi controller:
- The over-the-air downloads worked very smoothly. I could edit my code in their editor and press the "download now" button to flash the chip; this was as fast as could be expected.
- Debugging by writing to an OLED was a real time saver, especially since there no other obvious debugger in their environment.
- The simple A2D conversions were super useful for connecting the water sensor and phone.
Some less than good parts:
- The default Photon setup is
that connecting to the development Wi-Fi is required for the program to run. I
learned this on the day of my presentation, far from my development area.
The actual code didn't work for the demo at all.
The solution is to put SYSTEM_THREAD(ENABLED); in your code
- The Arduino concept of "setup" and "loop" was awkward in practice. This little robot really needs a state machine, but that's not the simple path for Arduino.
- The Particle Photon
documentation didn't mention what the actual OLED type was, even though
it's part of the Particle Photon dev kit. I had to poke around for too
long to find the libraries and then figure out how to use the libraries.
Turns out it's an Adafruit SSD1306.
Herbert Televox was an awesome robot, even if it was just the 1920's version of cheap plywood. Making a modern version was an effective prop for a bigger presentation.
You can see the original Herbert Televox at the http://www.themansfieldmuseum.com/