Antique voltmeter displays current air quality from the web – By Tom Igoe…

One thing that disappoints me about computers is how little character they possess. Antique instruments of information display, like Victorian pendulum clocks, barometers, and compasses, and Babbage’s calculating engines, have a presence that modern computers lack.

I dig the look of the iPod as much the next guy, but even the best manufacturing design today doesn’t match that old brass-and-hardwood handcrafted love. Desktop widgets replace the need for clocks, barometers and stock tickers, and multipurpose display hardware like the Ambient Orb also perform these functions. But because these things have little presence and are so easily reconfigured, it’s easy to forget what information they’re displaying. Does the meter’s sudden plunge mean my Google stock tanked, or that it’s going to rain tomorrow?

Many geeks, myself included, resist this trend by fanatically collecting old instruments for aesthetic reasons. I’ve been playing around with using antique instruments to display data from new sources. I have some beautiful, wood-encased voltmeters and ammeters from the early 20th century, which I rescued from my university’s physics department trash. They work quite well but they’re not as portable as my current multimeter, so I wasn’t using them, which is a tragedy for such proud, functional instruments. So I decided I’d take a cue from Ambient Devices ( make one of them into an air quality monitor.

First, I came up with the basic system. The meter I used is an analog voltmeter that ranges from 0–3V DC. That’s a good range to control from a microcontroller, so I decided to use my current favorite microcontroller module, the Arduino ( Setting up an air quality monitoring station seemed like more work than I wanted to do, but fortunately the data for local areas all over the United States is available online.

AirNow ( is an interagency website that reports the Air Quality Index (AQI) from local monitoring stations nationwide. I would connect the microcontroller to the internet using a Lantronix serial-to-Ethernet module (, then extract the data from AirNow’s page for my city, New York City.


Pulse Width Modulation

The system layout made sense (Figure A), but the microcontroller needed to send a varying voltage to the meter, and microcontrollers can’t output analog voltages. They can, however, generate a series of very rapid on-and-off pulses that can be filtered to give an average voltage. The higher the ratio of on-time to off-time in each pulse, the higher the average voltage (Figure B). This technique is called pulse width modulation (PWM).

For a PWM signal to appear as an analog signal, the device receiving the pulses has to react more slowly than the pulse rate. For example, you can pulse width modulate a dimming effect for an LED because the human eye can’t detect on-off transitions faster than about 30Hz.

Analog voltmeters are slow to react to changing voltages, so PWM will also work well for our antique display. To drive the meter, I would connect its positive terminal to an output pin of the microcontroller and its negative pin to ground. Then I could control its reading by pulse width modulating the micro’s output pin.


1:1 Scaling

The meter’s display reads from 0 to 150, and the AQI runs from 0 up to 500. But the EPA regards air that measures 150 as “unhealthy for all individuals,” so I decided to set my meter up to show the raw AQI, without any scaling. If I see the needle pegged at the high end, I’ll know I just shouldn’t breathe.

Parsing the AirNow Page

The next step was to get the data from AirNow’s website into some form that the microcontroller could read. The microcontroller can easily read in short strings and convert the ASCII into binary, but it would be tough to parse through all of the text on a web page and find the right string. So I decided to write a program on my own server that would parse the AirNow page, extract just the current AQI reading for New York, and save it someplace where the microcontroller could read it. The microcontroller could then request a TCP connection via the serial-to-Ethernet converter and read in the data.

AirNow’s page is formatted well for extracting the data (Figure C). The AQI number is shown clearly in text, and if you remove all of the HTML tags from the page source, it always appears on a line by itself following the line “AQI observed at hh:mm AM/PM:”. I wrote a short PHP script to read the page, strip out the HTML, and find those two lines. When it did, it returned the AQI value by itself like so:

< AQI: 43>

On my server, a cron job runs the PHP script periodically and writes the value returned into a file that’s accessible via Hypertext Transport Protocol (HTTP).

Serial-to-Ethernet Interface

The next step was to connect the microcontroller to the serial-to-Ethernet converter and then to the net. I used the Xport device from Lantronix, which makes it easy. Like other Lantronix devices, the Xport has a TCP/IP stack and simple web and telnet interfaces built in on the Ethernet side. On the serial side, it uses the same TTL serial protocol as most microcontrollers, including the Arduino, so hooking them up means simply connecting the micro’s transmit line to the converter’s receive line and vice versa (Figure E). I used an Xport for this project because I had a custom-printed circuit board designed for it, but if you’re new to these devices, you might want to start with the Lantronix Micro. The Micro has a simpler connector and can be wired to a solderless breadboard with an IDE connector and a ribbon cable.

Before you can connect the Lantronix device to the net, you have to configure it. Lantronix has a downloadable configuration utility for Windows, DeviceInstaller. For non-Windows users I have a couple of programs that will do the job (one for Java, one for Processing) available online at

On a network with DHCP enabled, the Lantronix devices will obtain an address automatically. Once you know the device’s address, you can telnet into it to configure its serial port and network settings. Here are the settings I used for this project:

*** Basic parameters
IP addr, gateway, netmask ( 8 bits)
*** Channel 1
Baudrate 9600, I/F Mode 4C, Flow 00
Port 10001
Remote IP Adr: --- none ---, Port 00000
Connect Mode : D4
Disconn Mode : 00
Flush Mode : 00

Communications and Code

The microcontroller then connects to a web server by sending the Lantronix device a connect string that specifies the numerical address of the server and the port number:


Once a connection is made, the Lantronix device returns a “C” to confirm. After that, any data sent in either direction passes right through between microcontroller and server, as through a serial port connection.

The full Arduino code for my microcontroller is online at It connects to the net with a method like this:

void xportConnect() {
   // send out the server address and
   // wait for a "C" byte to come back.
   // fill in your server's numerical address below:
   status = connecting;

Then it waits for the Lantronix device to return with a “C”:

if (status == connecting) {
   // read the serial port:
   if (Serial.available()) {
      inByte =;
         if (inByte == 67) { // 'C' in ascii
         status = connected;

Once it’s connected, it sends an HTTP request like this:

void httpRequest() {
   // Make HTTP GET request. Fill in the path to your version
   // of the CGI script:
   Serial.print("GET /~myaccount/ scraper.php HTTP/1.1n");
   // Fill in your server's name:
   Serial.print("HOST: www.myserver.comnn");
   status = requesting;

And the server replies:

HTTP/1.1 200 OK
Date: Fri, 14 Apr 2006 21:31:37 GMT
Server: Apache/2.0.52 (Red Hat)
Content-Length: 10
Connection: close
Content-Type: text/html; charset=UTF-8
< AQI: 65>

When you call this PHP script from a browser, you don’t see the header stuff at the top because the browser strips it out for you. In the Arduino program, I stripped the header out by ignoring all the bytes before the < sign. Then I took only the numeric characters from the remaining string, converted them to a binary value, and I had my Air Quality Index value.

The final step was to pulse width modulate the meter. This is simple in Arduino, using the analog Write command:

void setMeter(int desiredValue) {
   int airQualityValue = 0;
   // if the value won't peg the meter, convert it
   // to the meter scale and send it out:
   if (desiredValue <= meterScale) {
      airQualityValue = desiredValue * meterMax /meterScale;
      analog Write(meterPin, airQualityValue);

Das Blinkenlights

As a finishing touch, I added LEDs to 4 of the Arduino’s digital outputs so I could monitor the progress of the connection. LEDs hanging off of the Arduino’s digital I/O pins 6–9 indicate the status Disconnected, Connected, Connecting, and Requesting, respectively.

That’s all there is to it! The only remaining step is to build a false base for the meter that houses the electronics. Once that’s done, you’ve got an attractive way to track local air quality, and you’ve put a well-made instrument back into useful service.

Tom Igoe teaches physical computing and sustainable technology development at the Interactive Telecommunications Program (ITP) at NYU. He hopes one day to work with monkeys.


Make: Arduino

Arduino Starter Kit

Getting Started with Arduino

Make 11 - DIY Wheels

From MAKE 11 – Page 133. To get MAKE, subscribe or purchase single volumes.