Friday, January 27, 2012

Wireless Remote Sensing with Arduino and Zigbee

In "Remote Sensing with Arduino" I described how to access light and temperature sensors on an Arduino board over Ethernet via a web server. In this article I'll describe how I've taken the same basic sensor platform and am now accessing it wirelessly via XBee radios.

Xbee is a family of tiny radios made by Digi International and based on Zigbee, the IEEE 802.15.4 standard for personal area networks (PAN) that are characterized by low power (one milliwatt), short ranges (about one hundred feet), and a pretty sophisticated network stack. Zigbee is capable of implementing a wireless mesh network, but the radios and firmware I am using in this application do something much simpler, so simple in fact that it requires no configuration and works right out of the box: a 9600 baud wireless serial connection.

I did this using a SparkFun Xbee Wireless Kit. The kit includes two XBee 2.4 gigaHertz series 1 radio modules, an XBee Arduino shield (some assembly required, so I had to break out the soldering iron to attach the Arduino headers), and an XBee Explorer. The Explorer is a tiny board with a socket for the radio module and an FTDI chip that performs the serial to USB conversions. The Explorer implements the PC/Mac end of the wireless serial connection. The Explorer requires the FTDI Virtual COM Port (VCP) drivers be installed on your PC or Mac. (I've written of my affection for FTDI and the quality of their products before, in "Deconstructing the AR.drone: Part 5".)

Below you can see the new setup. The breadboard at the top has exactly the same sensor setup as before. The Ethernet shield has been replaced on the Arduino board with the red XBee shield. The blue XBee radio module is plugged into a socket on the top of the shield. Jumper wires for power, ground, and the two sensors are plugged into the headers on top of the shield just as they were on the Ethernet shield. The Explorer is to the left with its own blue XBee radio module and a USB cable connecting it to my Mac. Note that there is no USB connection from the Arduino to my Mac; that's just a power cable from a wall wart powering the Arduino board. You could also power the Arduino side with something as simple as a nine volt transistor radio battery.

XBee Explorer and Arduino

Here's a side view where you can see the power cable coming into the Arduino, with the XBee shield plugged in on top of it similar to the Ethernet shield.

Arduino and XBee Shield

Here's a closeup of the XBee shield. Near the lower right on the shield you might be able to see a tiny silver surface-mounted slider switch, so small you need a something like a toothpick to throw it. The switch has two positions, labelled DLINE, in which you can load your Arduino board over its USB cable just as you normally would, and UART, in which the XBee hijacks the serial pins on the Atmel ATmega328 so that, for example, whatever your software displays over the microcontroller's serial port goes across the wireless XBee channel instead of the USB cable.

XBee Shield with XBee Module

And here's a closeup of the XBee Explorer. It is powered completely off the USB cable connected to my Mac. Although the XBee modules can be configured to do different things, right out of the box they implement a wireless serial connection and automatically pair with one another. Digi refers to this as transparent mode. Under the hood however, the radios are implementing a peer-to-peer network based on a Carrier Sense Multiple Access/Collision Avoidance (CSMA/CA) model.

XBee Explorer with XBee Module

So I loaded the FTDI drivers onto my Mac and restarted it. I plugged the USB cable from the XBee Explorer into my Mac and verified that a new serial device, /dev/tty.usbserial-A800f3aA, showed up. I hooked up the Arduino to its USB cable and downloaded my program over its own serial device, /dev/tty.usbmodem26431. I brought up the serial monitor tool in the Arduino IDE and verified that I was printing sensor values. I plugged in the power cable to the Arduino and removed the USB cable. I flipped the tiny switch on the XBee shield from DLINE to UART. I brought up the Arduino serial monitor tool again, this time using the Serial Port option in the IDE's Tools pull down menu to specify the new serial device from the XBee Explorer. And this is what I saw.

Screen Shot: XBee Wireless Serial Port

No, it is not 30.55°F in my office. Unfortunately I haven't replaced the temperature sensor I fried in "Another Victim of Electrostatic Discharge", so we'll ignore the actual temperature readings for now. But the Arduino is reliably reporting sensor readings wirelessly via its serial port once per second. Here's the complete program, which is mostly the same as before with all the Ethernet and Webduino stuff stripped out of it.

byte lightLevel = 0;
float temperatureCentigrade = 0;
float temperatureFahrenheit = 0;
unsigned long then = 0;

static const int lightAnalog = 4, lightDigital = A4;
static const int temperatureAnalog = 5, temperatureDigital = A5;

static const int oversample = 10;
static const float reference = 4.66; // Measured; nominally 5v, or 3.3v external.
static const boolean internal = true;

void sense(unsigned long now) {
int lightRaw = 0;
for (int ii = 0; ii < oversample; ++ii) {
lightRaw += analogRead(lightAnalog);
delay(1);
}
lightRaw /= oversample;
int temperatureRaw = 0;
for (int ii = 0; ii < oversample; ++ii) {
temperatureRaw += analogRead(temperatureAnalog);
delay(1);
}
temperatureRaw /= oversample;
float lightVolts = (lightRaw * reference) / 1023.0;
lightLevel = 100 - ((100UL * lightRaw) / 1023);
float temperatureVolts = (temperatureRaw * reference) / 1023.0;
temperatureCentigrade = (temperatureVolts - 0.5) * 100.0;
temperatureFahrenheit = (temperatureCentigrade * 1.8) + 32.0;
Serial.print(lightRaw);
Serial.print('=');
Serial.print(lightVolts);
Serial.print('v');
Serial.print('=');
Serial.print(lightLevel);
Serial.print('%');
Serial.print(' ');
Serial.print(temperatureRaw);
Serial.print('=');
Serial.print(temperatureVolts);
Serial.print('v');
Serial.print('=');
Serial.print(temperatureCentigrade);
Serial.print('C');
Serial.print('=');
Serial.print(temperatureFahrenheit);
Serial.println('F');
then = now;
}

void sense() {
sense(millis());
}

void setup() {
Serial.begin(9600);
pinMode(lightDigital, INPUT);
digitalWrite(lightDigital, LOW);
pinMode(temperatureDigital, INPUT);
digitalWrite(temperatureDigital, LOW);
if (!internal) {
analogReference(EXTERNAL);
delay(1);
}
sense();
}

void loop() {
unsigned long now = millis();
if ((now - then) > 1000) {
sense(now);
}
}


Some models of the Xbee radios do allow you to some configuration, in order to, for example, use them in other than transparent mode. Unfortunately, the tool to do this from Digi, X-CTU, is only available for Windows. But as James Sentman has pointed out, you can run X-CTU on the Mac, although to do so requires that you install MacPorts, a software package that makes it easier to port GNU-based open source applications to MacOS, and Wine, a software package that lets you run Windows applications on MacOS (and other OSes as well). Being a glutton for punishment, I of course did all of this, eventually running the X-CTU installer on the Mac under Wine, and then running X-CTU itself under Wine to talk to the XBee Explorer. However, you don't need to do any of this to accomplish what I've described here.

I found some of these references handy (in no particular order):

"XBee Manual" from SparkFun;
"XBee Data Sheet" from SparkFun;
"Configuring XBees" from Indestructables;

But once again, you don't need any of these to actually get this wireless setup working. The hardest part was, for me, not being a hardware guy, soldering the headers onto the XBee shield, and even that worked the first time. I was, however, careful to wear an ESD strap while putting all of this together.

Thursday, January 26, 2012

Another Tragic Victim of Electrostatic Discharge

If Chip spends more than a day trying to solve a software problem, it's a hardware problem. -- Mrs. Overclock's Law

This isn't always true, but it's been true often enough, going all the way back to 1976 when a recalcitrant disk controller on a PDP-11/05 caused my device driver to work in the morning but not in the afternoon after the lab had warmed up. I typically work with hardware engineers of such caliber that I am extremely reluctant to invoke this law. However, when it's my hands on the hardware, all bets are off.

After writing the article "Remote Sensing with Arduino" I anxiously awaited for Mrs. Overclock (a.k.a. Dr. Overclock, Medicine Woman) to return home from a hard day courageously saving lives so that I could demonstrate my latest project on her own laptop. Here is what she saw.

Analog Devices TMP36: Another Victim of ESD

Let me assure you that it is seldom if ever 38.77° Fahrenheit in my home office where the Arduino board was connected to the network. Convinced that I had somehow botched some recent software changes, or maybe knocked something loose on the breadboard, I spent several hours fiddling with the code, swapping jumper wires, peering at the Arduino source code that supports the analog pins, reading documentation about the analog-to-digital convertor in the Atmel ATmega328 microcontroller, and just generally wasting my time making all of this a lot harder than it needed to be. Everything pointed to the Analog Devices TMP36 temperature sensor as the problem.

Finally, first thing this morning, I invoked Mrs. Overclock's Law and found this excellent description of how to test the TMP36 by (as I was to find out only just now) none other than Limor Fried herself. Here's a close-up of the tiny TMP36 surrounded by probes to my multimeter and wires providing power right from the Arduino board.

Analog Devices TMP36: Surrounded by Probes

Initially the device reported 140 millivolts; that's around -33°F. A few minutes later it was 350mV or 5°F. Right now it reports 624mV or 54°F. Since I have my sleeves rolled up, this seems unlikely. This is a semiconductor device that had been working just fine.

Analog Devices TMP36: Testing

I have no one to blame but myself. While everything you see here is placed carefully on an anti-static mat, I am not always careful to ground myself using the anti-static wrist strap, to my woe. Especially in the dry winter months, it is not unusual for me to experience a spark here and there. I suspect I fried the part while putting my finger on it to warm it up during testing.

It's not really the semiconductor part that's the issue here, although if I had fried an OMAP microprocessor or some other expensive or hard to get component, it might have been. The TMP36 costs two bucks U.S. or less. It's the fact that my own carelessness cost me hours of my time. When you are self-employed and you bill by the hour, you are constantly aware of the value of your time in real dollars. Using a grounded wrist strap to protect against electrostatic discharge (ESD) would have saved me a lot of time and money.

I have a grounded ESD mat and wrist strap permanently wired up on my work table in my office.

Board Setup

I even carry a portable model in my tool bag.

Field Service ESD Mat

Back in my Bell Labs days we were required to take an annual refresher course in ESD practices. Or, I can just learn the hard way. If you lay hands on bare hardware, get yourself an ESD mat and wrist strap. And have the discipline to use them.

Tuesday, January 24, 2012

Remote Sensing with Arduino

If you have met me in meatspace then you know that I am so old as to be on the verge of being a brain in a jar connected to wires and tubes. When I started doing real-time development, it was on PDP-11s with just four kilobytes of memory. This goes a long way towards explaining my affection for microcontrollers like the tiny eight-bit sixteen mega-Hertz Atmel ATmega328 used on the Arduino Uno board. It has thirty-two kilobytes of non-volatile flash memory and a scant two kilobytes of volatile static random access memory (SRAM) all on-chip. (Update 2012-01-25: I got the SRAM size wrong in the original version of this article.) There is a skill, becoming rarer in these days of increasing software bloat, of writing code that will run on such a platform. Thanks to its resource constraints and powerful software toolkit, Arduino is an excellent platform with which to learn (or re-learn) this skill.

Why bother? Because it is microcontrollers just like this that are permeating our lives, whether we realize it or not: embedded in our appliances and toys and transportation and even computers, sometimes alongside much more powerful Intel or ARM processors that do the heavy lifting required by graphical user interfaces. Microcontrollers are the universal machines that are called upon to bridge the gap between the real world and the digital, between meatspace and cyberspace. It is a market that is growing even as the market for traditional high-end microprocessors is being impacted as the typical consumer finds less and less reason to own a desktop computer or even a laptop, finding their cyberneeds better met by a smartphone or tablet and more and more computation is moved into the cloud. Thanks to their relatively low price tags, microcontrollers are increasingly becoming replacements for more complex discrete circuitry for glueing other digital components together and for implementing complex behavior. Microcontrollers bring the added advantage of being able to have their firmware updated in the field; discrete electronics, not so much.

One application for tiny microcontrollers is remote sensing. Because of their small size and frugal power consumption, microcontrollers are useful for deploying digital control and sensing into the physical world, where they can gather intelligence and report back. Playing with remote sensing is easily done with Arduino with the addition of a Ethernet shield.

Shields in the world of Arduino are daughter boards that provide specialized hardware and peripherals. They are designed with interface pins protruding from their lower surfaces that mate with the connectors or headers on the Arduino board. Those digital connections that they need themselves they take, those that they don't or which can be shared with additional boards they connect to another set of headers on their top surfaces. The Ethernet shield adds an Ethernet RJ45 connector and supporting hardware to provide an Arduino board with a wired network connection. (There are shields which provide wireless connectivity as well; one step at a time.)

Here is a side view of my Arduino Uno board with the Ethernet shield plugged in on top of it. You can see the metal pins from the shield above plugged into the headers on the Uno below. A CAT5 Ethernet cable is connected to the shield on the left. Below that you can see USB and power cables connecting to the Uno below. Jumper wires are plugged into the headers on the shield.

Arduino + Ethernet Shield: Remote Sensing

The SparkFun Inventor's Kit for Arduino has all the hardware you need to do several projects that involve reading sensors. I chose to combine two of them, CIRC-09 ("Light") that uses a photoresister (a device whose resistance changes with the amount of light falling on it), and CIRC-10 ("Temperature") that uses a low voltage temperature sensor from Analog Devices. The TMP36 temperature sensor is a clever little device that is complex enough that its data sheet deserves a look.

Analog Devices TMP36 Data Sheet Title Page

Both of these devices can be hooked up to pins on the Arduino board and read using the analog-to-digital convertor built into the chip.

So what about the "remote" part? The Arduino software includes an Ethernet library that has a rudimentary internet protocol (IP) stack that, for example, responds to ping. (The Ethernet library supports DHCP but I gave the board a static IP address on the local private network.)

Terminal: Pinging Arduino

On top of that I added the Webduino software that allows you to build a tiny web server that runs on the Arduino board. This web server, when interrogated by the browser on my Mac desktop, responds with a measure of the amount of light impinging on the photoresistor and the temperature in both Centigrade and Fahrenheit. Here's what it looks like, right from Safari.

Web Browser: Remote Sensing

I compute the light measurement to look like a percentage, but some calibration might be necessary for other applications. Still, it is easy for the software to tell the difference between night and day or whether room lights are on or off.

Here's a photograph of the entire fixture from above. The photoresistor is on the left and the temperature sensor is on the right on the breadboard, but they are so tiny that the jumper wires make them hard to see. Green, yellow and blue are sensor wires (the green one is merely reading the power bus), red are for power, and brown are for ground.

Arduino + Ethernet Shield: Remote Sensing

Here is the complete source code for my remote sensing project. It's not long. The underlying libraries do most of the work.

#include <SPI.h>
#include <Dhcp.h>
#include <Dns.h>
#include <Ethernet.h>
#include <EthernetClient.h>
#include <EthernetServer.h>
#include <EthernetUdp.h>
#include <WebServer.h>
#include <util.h>
#include <avr/pgmspace.h>

byte lightLevel = 0;
float temperatureCentigrade = 0;
float temperatureFarenheit = 0;
unsigned long then = 0;

void getIndexHtml(WebServer & server, WebServer::ConnectionType type, char * urlTail, bool tailComplete) {
server.httpSuccess();
if (type == WebServer::HEAD) { return; }
server.print("<h1>");
server.print(lightLevel); server.print('%');
server.print(' ');
server.print(temperatureCentigrade); server.print('C');
server.print(' ');
server.print(temperatureFarenheit); server.print('F');
server.print("</h1>");
}

static const int referencePin = 3;
static const int lightPin = 4;
static const int temperaturePin = 5;

void sense(unsigned long now) {
int referenceRaw = analogRead(referencePin);
int lightRaw = analogRead(lightPin);
lightLevel = 100 - ((100UL * lightRaw) / 1023);
int temperatureRaw = analogRead(temperaturePin);
float temperatureVolts = (temperatureRaw * 5.0) / 1023.0;
temperatureCentigrade = (temperatureVolts - 0.5) * 100.0;
temperatureFarenheit = (temperatureCentigrade * 1.8) + 32.0;
Serial.print(referenceRaw);
Serial.print(' ');
Serial.print(lightRaw);
Serial.print(' ');
Serial.print(lightLevel);
Serial.print(' ');
Serial.print(temperatureRaw);
Serial.print(' ');
Serial.print(temperatureVolts);
Serial.print(' ');
Serial.print(temperatureCentigrade);
Serial.print(' ');
Serial.println(temperatureFarenheit);
then = now;
delay(1);
}

void sense() {
sense(millis());
}

static const byte __attribute__((__progmem__)) myMacAddress[] = { 0x90, 0xa2, 0xda, 0x0d, 0x03, 0x4c };
static const byte __attribute__((__progmem__)) myIpAddress[] = { 192, 168, 1, 223 };
static const byte __attribute__((__progmem__)) myGatewayAddress[] = { 192, 168, 1, 1 };
static const byte __attribute__((__progmem__)) mySubnetMask[] = { 255, 255, 255, 0 };

WebServer webServer;

#define countof(_ARRAY_) (sizeof(_ARRAY_)/sizeof(_ARRAY_[0]))
#define flash2sram(_TO_, _FROM_, _COUNT_) byte _TO_[_COUNT_]; memcpy_P(_TO_, _FROM_, sizeof(_TO_))

void setup() {
Serial.begin(9600);
flash2sram(tempMacAddress, myMacAddress, countof(myMacAddress));
flash2sram(tempIpAddress, myIpAddress, countof(myIpAddress));
flash2sram(tempGatewayAddress, myGatewayAddress, countof(myGatewayAddress));
flash2sram(tempSubnetMask, mySubnetMask, countof(mySubnetMask));
Ethernet.begin(tempMacAddress, tempIpAddress, tempGatewayAddress, tempSubnetMask);
webServer.setDefaultCommand(&getIndexHtml);
webServer.addCommand("index.html", &getIndexHtml);
webServer.begin();
sense();
}

void loop() {
unsigned long now = millis();
if ((now - then) > 1000) {
sense(now);
}
webServer.processConnection();
}


As is typical, I made this a little more difficult than it needed to be for pedagogical reasons.

To assist in debugging, I displayed a lot of the partial and final results over the serial port that is part of the ATmega328 microcontroller. This travels back over the USB cable to my desktop where it can be displayed using the serial monitor tool that is part of the Arduino development environment. Serial is a C++ reference to a pre-built object in the Arduino run-time system that provides an API to do this.

Serial Monitor: Remote Sensing

When all of your storage for volatile variables has to fit in two kilobytes of SRAM, it pays to put data that doesn't change somewhere else. You'll notice that, for example, the hard-coded IP addresses all have a mysterious __attribute__((__progmem__)) as part of their definitions. The __attribute__ keyword is a special directive to GNU C++ that can be used to communicate special implementation details to the compiler. The __progmem__ argument, specific to this family of microcontrollers, tells the compiler that the variable to which it is applied resides not in volatile SRAM but in non-volatile program memory, meaning the flash memory where executable code is normally stored persistently.

Access to program memory on this microcontroller has to be done through a different machine instruction, load program memory (lpm), than when accessing data memory (SRAM). If you are into this kind of thing, you will now realize that the ATmega328 is an example of a Harvard architecture, one in which data and instructions are stored and processed separately. The lpm instruction bridges these two worlds by allowing you to copy from program memory into temporary variables in data memory. (A von Neumann architecture is one in which data and instructions are stored in the same memory and may be accessed similarly to one another.)

The Arduino and underlying microcontroller software define C/C++ macros (for example, PROGMEM), data types (prog_char), and functions (memcpy_P), that simplify using this mechanism. But I coded the __attribute__((__progmem__)) explicitly because the use of the GCC __attribute__ directive is so common when developing close to bare metal, and for similar reasons, that I knew that many embedded developers reading this would immediately see something they recognize.

In the loop function, the main work loop, I only interrogate the sensors once a second. The call to processConnection looks to see if a web request has arrived, and if so processes it. The only request I've defined is an HTML GET for the web page index.html. Querying that web page on Arduino returns the latest sensed values.

It's pretty impressive what can be done within the resource footprint of a tiny microcontroller. This program takes a little more than half of the thirty-two kilobytes of flash, and a little less than a quarter of the two kilobytes of SRAM not including the stack. That's without much optimization on my part.

Small is beautiful.