An Arduino Library for the ADT7310 SPI Temperature Sensor

(There is no music associated with this post, either)

I wanted a temperature logger for monitoring one of my labs. In the first iteration I used an Adafruit Data Logging Shield with a TMP36 sensor, pretty much as they describe in their fridge logger tutorial.

Problem: the problem that always comes up with microcontroller measurements. That stupid 10-bit A/D converter. I realize that microcontrollers aren’t supposed to be high-accuracy data acquisition systems, but damn I wish they were at least 12-bit. 10-bit means 1024 (2^10) discrete measurement levels. That’s great when reading a potentiometer position, but it introduces a lot of quantize error with things like the TMP36.

Quantizing sucks

The TMP36 works from -40 C to +125C, with 750mV output at 25 C and 10mV/C range. If you use the TMP36 with the 3.3V source and reference, that means that 10-bits gives you… 3.22 mV resolution per bit.

Example: The arduino A/D reads a value of 200 (0.645 V with a 3.3V reference). That corresponds to temperature (0.645-0.5)*100 = 14.45 C. If the A/D reads 201 (one bit higher), that corresponds to a temperature of 14.77 C. So you can see that your temperature quantizes to 0.32 C. You can’t read 14.6 C, it clips to either 14.45 or 14.77. If you’re monitoring room temperature that kinda sucks.

Who cares

So, what to do? You can get a more sensitive temperature sensor. The TMP37 has a sensitivity 20 mV/C. Still not a lot of bits in the range I want to use it in. If the lab gets above 100 C I figure it’s time to get the fuck out anyway.

Another solution You could get a different outboard A/D and use it to read the TMP36. I’ve used MCP3304’s (12/13-bit SPI A/D), but that’s a lot of effort just to read one sensor. the TMP’s are only really rated at +/- 1 C at 25 C anyway, so at some point the sensor itself is going to be the limit.

The route I went was: find a temperature sensor with a digital output (SPI, I2C, whatever) that has higher resolution, skipping the A/D issue. For no good reason I picked the Analog Devices ADT7310. It has an SPI interface, doesn’t require calibration, and is 16-bit capable (0.0078 C resolution). They were also fairly cheap ($3 from digikey).

Package issues

First issue was just wiring the stupid thing up. I wanted to use it with that Adafruit Logger Shield I mentioned earlier. The ADT7310 is an SOIC package, and the Logshield doesn’t have any footprint placement for SOIC parts (the protoshield does, but the logshield just has some through-hole places). A long time ago I shunned surface-mount parts because I solder like a gorilla, but in these times if you want IC’s you have to go surface-mount. At least it wasn’t a BGA.

To solve this first problem, I used a couple of different little SOIC-pin header adapter boards. The first couple I built I adapted from a 20-pin SOIC board I had lying around, so I cut the board short with a dremel. Nice but the spacing is wide, so it covers lots of holes. A later one I use some really sexy small adapters from Sparkfun. $2.95 was a little much, but still cheaper than making my own board.

I’m old, half blind, and my hands aren’t all that steady. All of that can be compensated for by doing something easy: solder under a microscope. I bought a cheap stereo dissecting scope from eBay a few years ago and it makes soldering all these fine pitch things a dream. Use a fine-tip iron, thin solder, and a solder fluxpen and yer golden.

Here’s some of the completed modules.

I put on a 0.1 uF capacitor, as recommended (ADT7310 needs it), and the two recommended pullup resistors for the alarm pins. I don’t know if the chip works just as good without the pullups, but it was easy to put them on. Shows how small this package is when it’s dwarfed by the through-hole resistors. In a better design you could use some SMT chip resistors.

Some issues found during the breadboard stage

First thing, is this silly chip is really fussy. That decoupling cap on the power? Yeah, you really need that. I had a hell of a time getting the communication over SPI to work with this chip, and a lot of it was fixed by adding the decoupling cap, and using shorter leads to the breadboard. The chip would happily clock along and drop an error every few measurements. Decoupling cap dropped that to a few dozen measurements, and short leads meant I could run tens of millions of measurements (no joke) without errors.

Errors? Yeah. With some SPI devices, selecting them with the chip select does the reset and sets things up for a read/write and you’re good to go. The MCP3304 works like that, I believe. The ADT7310 powers up, gets its registers configured, and then you communicate with SPI. If the chip gets fussy or you lose a bit or a clock somewhere, it goes into sulk mode and won’t respond with anything other than “0” on output. The only way out of this is to do the soft reset – clock in 32 or more “1’s” on the SPI line and the ADT7310 resets itself. It also means you have to reconfigure your configuration registers if you changed them from the default powerup settings.

In the first few trials writing for this guy I was doing “bit-bang” SPI, where I was providing my own clock and control signals directly. This works great, but again mr. fussy ADT7310 was very specific about when the clocks had to come. Mysterious stuff like losing the last bit on a read, that sort of thing. Although I did get that working I then re-coded everything to use the Hardware SPI, as I was able to make the SD card (which also uses SPI) and the ADT7310 co-exist on the Arduino logshield without interfering. Using Arduino’s hardware SPI library is a lot easier, too – let it do all the heavy lifting.

The problem I had was trying to determine if you’ve lost communication sync. If the chip is sulking all it will do is crank out “0” on the output. If you were actually reading a temperature that was around zero that might make it hard to detect. The technique I eventually settled on was to do things the hard way: after reading the temperature, check the Tcrit setpoint register (the ADT7310 has a couple of setpoint registers that can hardware assert two pins on the chip for alarm interrupt purposes, the setpoints are set through writing to one of the registers, and can be read back). Register 0x04 (Tcrit) very nicely has a default value of 0x4980 (147 degrees) at powerup, so it’s got enough bits to give me confidence things are still reading right (0x06-Thigh has 0x2000 and 0x07-Tlow has 0x0500, not enough ‘set’ bits to be confident you’re reading them all correctly). Mind you, you can always load one of these registers with whatever value you wanted.

Something that would have really helped doing this was spying on the SPI bus with a logic analyser. Using an oscilloscope is okay, but it can be hard to track what you’re seeing if you don’t have storage. Although I figured out my bit-bang issues before it showed up, I broke down and bought a Saleae Logic Analyser. I hooked that shit up and it worked exactly as advertised. I should have bought it sooner.

So in general, my strategy was:

  • Read temperature register 0x02
  • Read Tcrit register 0x04, compare with known value (0x4980)
  • If you get 0x4980 from 0x04, the temp was probably fine. If not, you’re probably desynchronized and should reset the chip.

So once I had things actually working, I coded everything up into a functional ADT7310 Arduino Library. Like the SerLCD library I’ve previously discussed, putting it up as a proper object-oriented library makes things nice and clean and encapsulates a lot of the busy work so you don’t have to worry about it.

The ADT7310 has lots of operation modes (16-bit, 13-bit, continuous, one-shot, etc). I really only tested all this in 16-bit continuous mode. Everything is just SPI commands though, so it should work (cough). The Datasheet explains what all the modes do, and I include some #define‘s to make it simpler to initialize the chip. I didn’t include any support for the “continuous read” method (I didn’t really care about doing that), or reading the status of the Tlow/Tcrit/Thigh flags (since they don’t appear in the 16-bit read), but you can just pull the bits out yourself from the register 0x02 read value.

Download the library here. This version is now old, the github version is the current one.

You can also download it from github.

Again, like the SerLCD one I made it’s under LGPL because I really don’t give a crap, and I know all you people trying to do your homework aren’t paying attention to licenses anyway.

The library includes functions for reading and writing registers in 8/16 bit modes. It also has a reset function to reset the chip (but does not reprogram the mode, you do that yourself), and it includes a function to convert the 16/13/10/9bit values into temperatures. Why you would use this chip and not go for full on badass 16-bit mode is beyond me, but it’s there. The library also includes a demo example on using reading temperature and checking for desynchronization error. It does depend on the SPI.h library, which is included in the Arduino software package now. I’ve tested this under arduino-0021.

It’s one of those “it works for me” situations now, your mileage may vary. If you don’t like it, tough.