Omega HH506RA Serial Protocol

March 22, 2010 | Embedded Software

Today I had to figure out how to read temperature from an Omega HH506RA thermocouple signal conditioner/datalogger. The HH506RA is a nice little unit that reads temperature from two thermocouples (type K, J, N, T, R, S, or E). It can display the temperature on its LCD screen and communicate them to a PC over RS232 or USB. The PC connection is optically isolated, which is very nice because RS232 and USB are both notorious for causing noise problems via ground loops.

I've put information on how to communicate with the unit up here for future reference.

I found some information on an NI forum and on Matlab's file exchange site.. The info on the NI site is a bit hard to read and not quite correct, so here's a quick and clear rundown.

Serial Port Setup

If you're using the USB cable, its actually just a virtual serial port, which will likely show up on your PC as COM1 to COM4. If you're using the RS232 cable, then obviously you need a serial port on the PC. Either way, you need to set the serial port up for 2400 baud, 7 data bits, even parity, 1 stop bit, and no flow control.

Communication Overview

To read the temperatures from the unit, send this string over the serial port: #001Nrn. r means the carriage return character (ASCII 13). n means the newline character (ASCII 10). You'll get back a string that follows this pattern:  TTTTt TTTTtABrn. The first and second  TTTTt groups represent the temperature and thermocouple type for the first and second thermocouple. The AB section is some miscellaneous information.

Temperature

The  TTTT part will be a hexadecimal string representing the temperature in tenths of a degree Celcius. For example,  017A represents 37.8 degrees Celcius. If the number is negative, it will start with a minus sign instead of a space. For example -00C2 means -19.4 degrees C.

Thermocouple Type

The t part represents the thermocouple type (set using the buttons on the HH506RA). The codes go as follows:

Code Type
0 K
1 J
2 T
3 E
4 N
5 R
6 S

Miscellaneous Info

According to the document on the NI site, the A digit above represents Celcius/Fahrenheit while the B character represents battery. I've only ever seen them as 0 in my testing.

Example

Bringing it all together:
You send: #001Nrn
You receive: -00B20 02C1200rn
Decoding: The first thermocouple is a type K that is reading -17.8 degrees C. The second thermocouple is a type T reading 70.5 degrees C.

Errors

If you send a bad command, you'll get a a response of Errrn. I've found that to be a handy way to resynchronize communication in case something goes wrong. Just send rn and read for a response until you see Errrn. Once you get that response, you know the unit is ready to accept a command again.

Other Commands

The document on the NI forum has some additional details. For example, you can read the unit's ID with the command %IDRrn. The ID is the set of digits you send in the read temperature command #001Nrn. You can change the unit's ID so that it responds to #005Nrn by sending a command along the lines of %001I005rn. I don't really see the point of this, so I haven't bothered testing it. The unit can log data, which can then be read back with the command #001Srn.

Watch out for compiler bugs related to volatile variables

February 3, 2010 | Embedded Software

At my new job, I've been diving back into embedded software development. During my time as an industrial automation application engineer, I stopped working quite as close to the hardware. It's definitely fun to be digging around in registers and assembly code again.

I came across this article called on Embedded.com called "When good compilers go bad, or What you see is not what you execute". It talks about all kinds of ways that compilers can end up generating incorrect (or sometimes technically correct but unexpected) source code. I've got a rule of thumb when debugging that in that vast majority of cases "you didn't discover a bug in the compiler/operating system". It's good to keep in mind that that isn't always the case.

One reference that especially caught my eye was to a paper entitled "Volatiles Are Miscompiled, and What to Do about It". They looked at 13 different C compilers and found out that all thirteen of them had issues with volatile variables.

Volatile variables are very frequently used in the embedded world. Memory-mapped hardware registers (used to read digital and analog inputs for example) are often accessed via volatile pointers in C. The volatile qualifier tells the C compiler "this variable could change at any time, so every time I ask you to read or write it, I really mean it!"

The authors of the paper found that every single compiler they tested failed to correctly implement the volatile qualifier in at least a few of their test cases. However, there are a few things you can do to help protect yourself:

  • Don't turn on optimization. They couldn't prove it, but they're pretty sure that all (and definitely most) of these kinds of bugs are caused by the compiler's optimizer.
  • Wrap uses of volatile variables in function calls. Because functions are (obviously) used way more than volatile variables, there tend to be very few bugs related to incorrect optimizations of function calls. Hiding volatile accesses in function calls makes it much less likely the access to a volatile variable will get optimized away (assuming the function isn't inlined).
  • Don't use volatile variables for inter-thread communication, use semaphores instead. This one deserves a post all of its own.
  • When the code just absolutely has to work, verify the assembly or machine code your compiler produces by hand.

One thing I really liked about the paper is that they used valgrind to help automate their testing. When I've run into some really tough to debug memory and pointer issues, valgrind has always been a huge help. I wish it were easier to use it in embedded programming projects.