A Minimal Arduino Library for Processing Serial Commands

Here’s some nice music to listen to while you read this long and boring article:

Note: there have been updates to this library to include support for SoftwareSerial since this post was originally published.  The github version is the most current.

For lots of software projects, I end up writing stupid little utility libraries so I don’t have to keep re-writing crap over and over. Hence my earlier SerLCD and ADT7310 libraries I’ve published here. Here’s another dinky little thing I wrote because I was unsatisfied with the alternatives. Frequently I write code that has to interact with a host computer. The Arduino boards have a very nice, very easy to use USB-to-Serial connection on them, so you just have to write your host program to send serial bytes and you can communicate with the arduino board. Frequently what I end up needing is the arduino to respond to some commands sent from the host computer. Things like “turn off that relay” or “process this” or “move this motor to this position and stop” or “holy shit the building is burning down everybody run”. If you RTFM, the arduino libraries tell you to use the “Messenger” library for doing serial command processing (which I did, a long time ago). Unfortunately, as you then follow the trail, you are suggested in the modern age to instead of Messenger to use CmdMessenger. Stupid documentation in a Wiki. There are also things like Firmata and Bitlash, but they’re really more extensive than just having your arduino program talk to your host program. There’s nothing really wrong with CmdMessenger. It works fine. I just don’t like it. I don’t like that it depends on two other libraries (Arduino Streaming Library and Arduino Base64 Library ). I also don’t exactly see why you need base64 except to run the demo code. I also don’t like the messenging format. The commands back and forth end up just being enum’s of the commands you’ve added handlers for, so the commands all look… strange. From the example in the CmdMessenger library:

// Try typing these command messages in the serial monitor!
// 4,hi,heh,ho!;
// 5;
// 5,dGhlIGJlYXJzIGFyZSBhbGxyaWdodA==;
// 5,dGhvc2UgbmFzdHkgY29udHJvbCA7OyBjaGFyYWN0ZXJzICws==;
// 2;
// 6;

(The actual commands shouldn’t make any sense unless you’ve looked at what the demo code does, I just repeat it here to show the formats). All the commands become “number,something,something, …” and the number depends on what order you’ve added the handlers, so you have to do your own bookkeeping to know which number belongs to the enum of the command handlers. There’s nothing wrong with this. If you’re writing code on a host computer, the host just wants consistent format. Frequently when I’m testing I sit with the serial monitor window open and fire down commands myself. There are also times when debugging that it’s nice to just look at the commands and understand what the hell is going on. So I finally got fed up and wrote my own SerialCommand library. I based a lot of the conceptual idea off how Dreamcat4 organizes CmdMessenger. You initialize a SerialCommand object by pushing pointers to functions and associating them with a received string. The string is case-sensitive. e.g. –

// Setup callbacks for SerialCommand commands
SCmd.addCommand("ON",LED_on);       // Turns LED on
SCmd.addCommand("OFF",LED_off);        // Turns LED off
SCmd.addCommand("HELLO",SayHello);     // Echos the string argument back
SCmd.addCommand("P";,process_command);  // Converts two arguments to integers and echos them back
SCmd.addDefaultHandler(unrecognized);  // Handler for command that isn't matched  (says "What?")

The second parameter is just the function to call when that command is received:

void LED_on()
  Serial.println("LED on");

A SerialCommand object maintains a char[] buffer, that fills with characters from the Serial() stream. When the terminator character is received (in my case, the carriage return, or ‘\r’), it then scans the buffer looking for the first word in the buffer to be the command (delimiter defaults to the space character, but you can configure that in the library). The buffer is of fixed size (configurable), and just wraps around if you overwrite the end, so it doesn’t crash but might give unintended results if you try to receive a command larger than the buffer. There is a .clearBuffer() routine, which you can use to zero out the buffer to be sure. The buffer also zeros out when a command gets recognized and processed. In the handler, you can call the .next() function to parse out the next token. It comes out as a pointer to a char string, so you can use atoi() or whatever to convert to a numeric format or do further parsing. You can keep calling next() until it returns NULL, meaning no more arguments.

void SayHello()
  char *arg;
  arg = SCmd.next();    // Get the next argument from the SerialCommand object buffer
  if (arg != NULL)      // As long as it existed, take it
    Serial.print("Hello ");
  else {
    Serial.println("Hello, whoever you are");

Essentially the whole library is just an interface to a string buffer and the strtok_r() command. It doesn’t do a lot, but it does what I want. Maybe you’d like it, but I doubt it. You can download the library here or from my repository on github. My github version is the most current one. It includes a stupid demo program. You won’t like it, it won’t do what you want, you won’t like my coding style, and if you do end up using it you’ll tell your boss you wrote it yourself and strip out my attribution and LGPL license, but hey. It works for me, that’s why I wrote it. You’ll probably like CmdMessenger a lot better. As well, it was pointed out to me by Stephen Lake that Stefan Rado (github user kroimon) has made a modified version available on github, which you might find more appealing. As usual, unzip it and drop the folder into your libraries folder (e.g. My Documents\Arduino\libraries, on Windows). In the tradition of useless sample output, here’s some useless sample output: and because apparently you’re not allowed to post about arduino on the intarweb without including a picture of an arduino, here’s arduino Mega running the SerialCommand demo program. Not like you can actually tell, but hey.

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.