The preceding episode of this saga shows you how to add some I/O expansion and two stepper motors to the vehicle control module. In this article, learn how to connect some sensors up to the board; after all, you can't have much of a robot until your electronics have some way of learning about the real world outside it. Below is a thumbnail version of the schematic for the circuit we're using in this episode. Click to see a larger version of the schematic, or download the .zip file containing a .sch file from the download table below. To view the .sch file, you will need the free version of Cadsoft's EAGLE PCB/schematic CAD software (see Resources).
Figure 1. Thumbnail schematic for the circuit
The simplest sensor
We will start with the humblest of sensors: a simple switch. You might need a couple of these to delimit the travel of your rudder, or if you're making something other than a robot, maybe you want some front-panel buttons (though if that's all you want, you can attach these to the PowerPC® in better ways). Switches are so simple that in fact the only reason I'm bringing them into the discussion here is so I can gently lead into a slightly more complex I2C configuration than the circuit you met last time. I'm going to assume that these switches will need to be read relatively infrequently (say, in the neighborhood of 50-60Hz). In keeping with the I/O usage philosophy I espoused in the last episode, this slow rate can safely be marooned on the other side of an I2C I/O expander. However, you might remember using up all the bits on your expander in the last episode. How can you add another block of I/O?
A side note about I2C addressing
Fortunately, all you need to do is wire another MCP23008 onto the I2C bus. The chip has a seven-bit I2C address; four bits of this address are fixed, and the remaining three can be configured in your external circuit by means of external strapping resistors on the A0, A1, and A2 lines. This allows you to connect up to eight MCP23008s to a single I2C bus without any ugly complexities. (This is a very common sort of arrangement on I2C peripherals, by the way -- for cost reasons, manufacturers will very rarely bring out more than a few address pins). The expander that drives the stepper motors is at address 0 (its full binary address is 01000000, where the bottom bit is actually the read/write flag). We'll add a second expander at address 1 (again, the full binary address byte for this second chip is 01000010). The code in i2c.c handles the translation from logical address (0-7) to physical address byte for you.
In the interest of full disclosure, if you look at the way I scan these switches, you'll see a rather large cheat in my debouncing algorithm. I simply scan at periodic intervals, and check for a change in input state. If there is a change, the new data remains pending until the next scan interval. At that time, if the change in state is still the same, the new data is latched into a status buffer. This is not a very advanced debouncing method, but it performs adequately in lab conditions (at least, with reasonably well-behaved switches). In Resources, you can read a very detailed article on debouncing techniques, accompanied by reams of actual real-world data. If you want to implement a more advanced debouncing method, I've wired the interrupt request line from the MCP23008 into one of the GPIOs on the microcontroller. Since this line can be configured as an open-drain output, you can add more I/O expanders and simply connect all their interrupt lines to the same point for a wired-AND configuration. An external pullup is not necessary, as the ATmega32 has on-chip pullups.
Promoting isolationist policies
One further subtlety I've added to this circuit is optical isolation of the input lines, accomplished very easily with a standard six-pin optocoupler and a couple of resistors. This additional circuitry serves two purposes: first, it protects the microcontroller from outrageous external events such as miswired connectors or electrostatic discharge, and it also provides a kind of level-shifting capability; you can interface practically anything to the input side of the optoisolators. You'll be very grateful of this isolation circuitry if you ever accidentally tap an unregulated battery line onto one of the inputs; it's much easier and cheaper to desolder an optocoupler than to replace the micro! Please note that if you want to get the full amenity of the ESD isolation, you will need to ensure that whatever you have outside the VCM box has a separate (or at least, isolated) power supply from the VCM. Otherwise an ESD event on your external hardware will propagate into the VCM through the common power rail, largely negating the benefit of the optocouplers. Note also that the series resistor I selected for the optocoupler LEDs is correct for a +12V external supply; you will need to tweak this if you run the common anode line to a different voltage.
You're getting warmer
Next, you want to monitor some temperature points in the vehicle. In the real E-2 submarine, I'm interested in several temperatures -- the two drive motors have a temperature sensor apiece, as does each battery. Another temperature sensor is thermally connected to the external environment so I can have an idea of what the water temperature is like. For the purposes of this article, I'll only implement two of these sensors. I'm using the Microchip MPC9801 12-bit I2C temperature sensor for this application; adding more measuring points is simply a matter of mounting the sensors where you need them, and wiring them onto the I2C bus. As with the MPC23008, there are three bits of user-configurable address; the overall device address byte is 1001xxxR, where xxx are address pins A2 through A0, and R is the R/W bit. Observe that there is no possibility of address collision with the MCP23008s, no matter what A2/A0 combinations you select for either. As a matter of interest, if you refer to the datasheet for the MCP9800 series, you'll see that the part is also offered in a couple of flavors that are packaged in a five-pin SOT23 (surface mount transistor) form factor. This tiny package doesn't have space for external address select pins, so Microchip offers one flavor that's hardwired at address 000 and another at address 101. Again, this practice is quite common with I2C sensors.
The MCP9801 also features a thermostat mode (this option is frequently provided on digital temperature sensor devices). This feature consists of a single open-drain output that goes active if the sensed temperature falls outside programmable limits, and it operates completely independently of the I2C logic, so you can use it as a hardware fail-safe even if the microcontroller crashes. Perhaps more importantly, you can even shut the micro down completely to save power, and leave the temperature sensor IC running to shut things down and wake the micro back up if unreasonable temperature excursions occur.
In the real E-2, I use the MCP9801's thermostat feature to kill charge current if the batteries get too warm, to stop the drive and compressor motors if any of them exceeds a nominal temperature threshold, to pause the compressor while filling the high-pressure air bottle, and to turn off some high-intensity halogen lamps if the temperature of the hull area surrounding the lamp's reflector rises too high (this might indicate heavily sedimented water, but the situation I'm most concerned with is turning on a lamp facing straight down into mud -- those lamps generate an incredible amount of heat). Note, however, that although this feature is usually described as a "thermostat" in other vendors' datasheets, and I use the word freely in this text, this part is not really suited to drive a thermal load without external intelligent assistance. The temperature alert feature, as Microchip terms it, is designed to provide a cutoff or warning signal, rather than an unsupervised process control input.
Coping with pressure
Next, you need to be able to sense a couple of pressures. The part I've selected for this is a Freescale MPXH6400AC6T1, which can measure from three to 58 PSIA and has an integrated hose barb. It is intended for automotive applications, but works well in the moderate pressure ranges encountered in the E-2 project. Again, in the real submarine quite a few spots need to be sampled; I'm interested in ambient pressure as a way to gauge the vessel's depth, as well as various pressures in the air lines leading from the high-pressure air bottle to the ballast tanks, as well as a couple of pneumatic linear actuators. The circuit you see here only implements two sensors, but again this can easily be extended to any number your application might require.
Note that the MPXH6400A series is only characterized for dry air use. You can, however, use it to measure external water pressure by using an air bubble behind a flexible diaphragm. That phrasing sounds really scientific and technical, so I'll freely admit that the "flexible diaphragm" in question is actually a plastic soda bottle. I drilled a hole in the bottom and epoxied a tube into the hole. I also glued the cap on tightly with more epoxy, after first removing the rubber seal. This arrangement has been tested at pressures up to two atmospheres, and it would probably withstand quite a bit more.
These pressure sensors provide analog outputs. We read them at a fairly low sample rate using the analog-to-digital channels of the ATMega32L. Some basic software filtering removes noise; we don't expect the values here to change very rapidly. You can find the relevant code in main.c.
Do you have any idea how fast you're going?
The last and most complex sensor we'll use is an Analog Devices ADXL322 MEMS (Micro Electromechanical Systems) accelerometer. MEMS is an exciting technology that straddles the border between "really, really small machines" and nanotechnology. The most common MEMS devices you'll encounter in robotics work are accelerometers and gyroscopes; various vendors including Analog Devices, Freescale, ST, and Kionix (among others) offer these sorts of parts. (By the way, the pressure sensor we're using above is also a MEMS device). If you browse the Resources in depth, you'll see quite a few other very interesting MEMS devices on the market, both sensors and actuators. I'm particularly intrigued by the possibility of building an electric motor the diameter of a human hair, though I can't yet think of a use for this device in any project I'm working on.
The ADXL322 is a two-axis +/-2G accelerometer. This means that it can measure acceleration in two dimensions, and these two dimensions are at right angles to each other. The sensor output saturates at +/-2G. Other typical ratings for accelerometers are +/-5G and +/-10G. Parts with higher acceleration ratings are used in applications such as car airbags, which need to be able to discern between high-speed and low-speed collisions.
The 2G accelerometer I'm discussing in this article is typically used for measuring roll and pitch of a vehicle or perhaps a video game controller. For example, you might use it in an auto-leveling circuit for a model aircraft. The accelerometer would be mounted with one axis -- the X axis, without loss of generality -- parallel to the stern-to-bow line of the craft, and the other axis (Y, in this case) running from port to starboard.
Figure 2. An accelerometer
The ADXL322 provides you with two analog voltage outputs corresponding to the X and Y acceleration vectors. When the device is parallel to the Earth's surface, both analog outputs are mid-scale. As you tilt the device towards the X+ direction, the X output gets closer to rail voltage; tilt it back towards the X- direction and the output heads towards 0V; similarly for the Y axis. In general, the roll and pitch (when the sensor is mounted as described above) are given by the relations:
Listing 1. Calculating pitch and roll
pitch = arcsin (X) roll = arcsin (Y)
where X and Y are numbers scaled from the voltage ranges provided by the chip to a -1.0 to +1.0 scale. Due to motion or noise, X or Y can exceed 1.0; you need to allow for this possibility.
An important point that might not immediately be obvious: A two-axis accelerometer can only tell you your vehicle's acute angle off vertical in the X and Y directions. It cannot completely resolve this information into a unique vehicle orientation. Consider the following two situations, where I roll a vehicle through 15 degrees and then through a further 150 degrees (note that these diagrams represent a stern view of the vehicle):
Figure 3. Indistinguishable but very different vectors
As you can see, the roll axis output is the same for both situations. The z-axis, if I had a way to measure it, would have changed sign, but the two-axis device simply cannot differentiate between the two possibilities.
Another point that sometimes seems very difficult to communicate is the following: An accelerometer only measures a single acceleration vector (in this case, I have the vector decomposed into two components; a three-axis accelerometer would give a third component, but the net result is still a single vector). Gravity is one component of this acceleration. Your hand pushing the device across the table might be another. A rocket motor launching you, the table, and the device into space would just be another component of the acceleration. You cannot separate out these components just by looking at the output of the accelerometer. In other words, if you have the accelerometer mounted as I describe above, the best you can do -- even with a three-axis accelerometer -- is to obtain a single vector that summarizes all linear acceleration forces acting on the vehicle.
Due to this fact, and also the lack of a z-dimension and the low-maximum-G sensor being used, the circuit I'm discussing here is totally unsuitable for inertial navigation. Building so-called "dead reckoning" inertial guidance circuits and developing the software for them is very challenging. To establish the motion vector of an object from a historical record of its acceleration vector, you need to sample rapidly and (numerically) integrate over time. To calculate the net displacement (position) of the object, you need to integrate the motion vector again. Errors in these processes are cumulative.
The consumer application where you are most likely to encounter such circuits is in very high-end GPS receivers. These devices try to keep track of their position using the GPS signals as far as possible. When the satellites are temporarily occluded (for example, when you drive into a tunnel!), the device keeps your position up to date, with reduced accuracy, using inertial navigation.
The MEMS device is read through the Analog/Digital Converter (ADC) channels, just like the pressure sensor. A slight nuance here is that due to impedance matching issues, you need to use an op-amp configured as a simple voltage follower between the sensor and the micro. You'll observe a few calibration constants in this code; you need to calibrate the zero reference (flat on the table) and the +/-1.0G voltages for both outputs. These constants will vary from unit to unit, due to differences in wafer orientation of the MEMS sensor inside its packaging, different mounting angles of the package on the PCB, and other random factors. The usual way of calibrating these devices is with a reference platform. (A piece of plywood with adjustable-height feet -- or, large bolts -- in each corner and a spirit bubble on it, is an adequate reference platform). You calibrate the zero position with the vehicle on the table; you then roll the vehicle through +/-90 degrees and pitch it through +/- 90 degrees to calibrate the range limits. You can store the constants thus measured in EEPROM; they will be valid unless the device is serviced and the orientation of the accelerometer with respect to the vehicle exterior is altered.
Tune in next time
This article has covered a lot of ground with respect to data acquisition. The next article returns primarily to the Kuro Box side of the equation to show you how to get that data back into the PowerPC, and some of the cool things you can do with the data thus gathered. Remember, you have a massively powerful 32-bit processor just chomping at the bit, waiting to do something interesting.
The downloads for this article are being updated. Please try to download later.
- A useful source of information on MEMS technologies is http://www.memsnet.org/. As a starting point, read this excellent introduction to MEMS technology, which provides some interesting background material (and microphotographs of MEMS structures).
- Jack Ganssle has some wise words about switch debouncing.
- You might enjoy this fun little video about Freescale's pressure sensors.
- In the Cogito README file, get details on how to use Cogito.
- In "Sockets programming in Python" (developerWorks, October 2005) learn the basic API for writing sockets as well as a module that eases socket server development, and then build a simple chat server using these techniques.
- "Inside the Linux kernel debugger" (developerWorks, June 2003) details KDB, the built-in kernel debugger in Linux, which allows you to trace the kernel execution and examine its memory and data structures.
Get products and technologies
- Download the source code referenced in this article from the download table above. The usual warning applies to Internet Explorer users - make sure to save the file as something.tar.gz!
- To read the schematic file, you will need the free version of Cadsoft's EAGLE PCB/schematic CAD software.
- Working by hand with the leadless package types used for most MEMS sensors is most irksome. Dimension Engineering, among others, sells accelerometers prepackaged onto handy prototyping boards.
- Download a free trial version of WebSphere Application Server Version 6.0.
- Build your next development project with IBM trial software, available for download directly from developerWorks.