Building a Dataq Compatible Data Acquisition System

 

 I have been using a Dataq DI-154RS for some time now, but have become increasingly dissatisfied. The 154 doesn’t have a true (high impedance) differential input, so you have to use an external instrumentation amp. It lacks an onboard excitation voltage for sensors like pressure transducers and load cells. It has only a fixed, single-pole filter that is not easy to modify, and the onboard code is proprietary so you cant implement something like an onboard digital filter. What I DO like about  Dataq is  their WinDaq/Lite software, which is free and does everything that you need for real data acquisition (calibration, scale setting, graphing, real-time display, FFT, etc.). I find the LabJack deficient in those areas for instance. The Dataq DI-194/154RS only works to a maximum of 240 samples per second (I think), but that is a high enough sampling rate for many applications. By comparison, the RDAS only samples at 200 samples per second and has similar software deficiencies as the LabJack. I also like the idea of using a standard command set. Where would the modem world have been without the Hayes command set?

It turns out that other people have had the same thought. Here is a link to a group, NEES,  that is interested in a low cost array of earthquake sensors using the Dataq DI-154RS for low cost data acquisition.

I thought it would be nice to have a data acquisition board that is compatible with the WinDaq Lite software, has a better architecture, and is open source. I have been using 8-bit AVR controllers for some other projects, so that is the controller I selected.

I also admit that I enjoy reverse engineering things. It can be a lot of work, but I always learn something. A really great book on reverse engineering is Hacking the X-Box by Bunny Huang.

Serial Port Hardware

The Dataq and PC serial ports use the RS232 physical communications protocol. Let’s review that and then take a look at the Dataq specifically. Devices which use serial cables for their communication are split into two categories: DCE (Data Communications Equipment) and DTE (Data Terminal Equipment.) Data Communications Equipment are devices which connect to your computer, like an external modem, or a DATAQ, while Data Terminal Equipment is your PC.

Below is a schematic of a DTE device connected to a DCE device via an RS232 monitor board. It also shows two additional connections for a second PC to monitor the traffic. I used a simple home built RS232 monitor like this to capture some traffic between a PC and the Dataq. I was then able to look at the protocol  in more detail as shown below. Notice that for the male DTE connector pin 1 is on the upper left as you look at the connector. For the female DCE connector pin 1 is on the upper right. I didn’t notice that little fact when I was laying out the monitor board and it caused me all sorts of grief.

One other problem I had related to the grounding of the monitor board. Since RS232 is a single-ended connection there has to be a decent ground connection between the DTE and DCE’s. Normally this ground should be provided by pin 5 on the cables. Or so I thought. The house I was living in at the time I did the traffic capture had the old style two prong outlets. I had the PC running the WinDaq software plugged into one wall and the monitor PC plugged into another wall. I could not get the monitor to capture anything but gibberish. After a couple of hours of checking out the PCB board, cables, and connectors, I dragged out my oscilloscope to look at the signals. I plugged the ‘scope into a three prong power strip that one of the PC’s was connected to. As soon as I hooked up the ‘scopes ground clip to the monitor board ground, it started working! Apparently the ground through the cable wasn’t good enough. Plugging both computers into the same power strip solved the problem.

There are two possible types of cables to use: straight and null modem. A straight cable has a male connector on one end and a female connector on the other. A null modem cable has a female connector on each end. All cables here are assumed to be straight, although the PCB has jumper options to allow it to be configured as a straight modem or null modem connection (using straight cables in all cases).

DCE connectors are female while DTE connectors are male. The cables that plug into them, of course, must have the opposite sex. Therefore, when using straight cables (as opposed to null modem cables), the PC end of the cable must be female (it connects to the male DTE connectors on your computer), the DCE connector on the Dataq is female, and the Dataq end of the cable should be male. Clear?

Rx and Tx are relative to the DCE device, so transmit will capture DCE traffic (e.g. a DATAQ) and receive will capture DTE traffic (e.g. the PC).

RS232 Signal Levels

A "Space" (logic 0) will be between +3 and +25 Volts.

A "Mark" (Logic 1) will be between -3 and -25 Volts.

The region between +3 and -3 volts is undefined.

An open circuit voltage should never exceed 25 volts. (In Reference to GND)

A short circuit current should not exceed 500mA and the driver should be able to handle this without damage. I wonder how many drivers actually meet this spec?

An RS232 connector can be either 25 pin or the more common 9 pin as used on PC serial ports. Here’s the pin names for the 9 pin connector:

RS232 DTE Pin Assignments (DB9 PC signal set)

Pin 1 Data Carrier Detect

Pin 2 Received Data

Pin 3 Transmit Data

Pin 4 Data Terminal Ready

Pin 5 Signal Ground

Pin 6 Data Set Ready

Pin 7 Request To Send

Pin 8 Clear To Send

Pin 9 Ring Indicator

Early model Dataq power

What I call an  early model is marked “DI194 Rev G”. Vin is taken from pins 4,6,7 which are tied together. The regulator is an LM317LM (U1) which is a series regulator.

Late model Dataq power

What I call a late model is marked  “DI194/154 Rev G” Vin is taken from pins 4,6,7 which are tied together. The regulator is an LM336LM (U1) which is a shunt regulator.

Dataq command set

Communication with the Dataq is at 4800 baud. Dataq nicely documents this basic command set to work with. Is this all there is or is there something they are not telling us? One way to find out is to hook up an RS232 port monitor and capture some traffic.

Commands

Cmd

Data

Default

Action

C

0 thru 15

0

Channels 000 thru 1111

D

0, 1

0

0 = output square wave

1= input

L

0...255

0

Ls byte of counter

M

0...255

0

Ms byte of counter

S

0, 1

0

ADC stop, start

R

Z

 

reset

E

key

N.A.

 

N

Z

 

Return 10 byte serial number of the device. If you run WinDaq, you can also inqure the serial number from the About dialog box

Send NULL before any command, which can be sent out by keying in <Ctrl-2> under VT100 mode if you are using HyperTerminal

 

    

Captured Traffic From The Dataq

The Dataq is powered by the serial port which isn’t turned on until the port is commanded to start sending data. When the Dataq window is closed, the port power is turned off. Other than that, no commands are sent to shut down the Dataq. The PC side will send three Command B’s looking for a response. If it doesn’t get one, or doesn’t like what is coming back, the PC will display a “Serial Device Not Found” message. In addition, the Dataq must respond to at least the first two commands or the software will report that the serial device is not responding. All of the responses look kind of random but that maybe due to the limitations of the port monitoring software I was using. In the software I wrote, I assumed each command was acknowledged by just echoing the command back to the PC. The chart below is from the Dataq’s point of view, so Rx refers to traffic initiated by the PC and Tx refers to traffic generated by the Dataq (e.g. the PC side software will send out the Cmd-B). All of the traffic was captured by home built RS232 capture board and using some software I found on the web (not sure exactly what it was). You can download an ExpressPCB file if you want to build your own boards. This file is a corrected version of the one I actually used, so check it out carefully before you send in your order.

Here’s a picture of the board.

Here’s the traffic I captured from a typical successful start-up sequence.

 

Rx (hex)

Tx (hex)

ASCII

Comments

--

FF

 

Power up; one or two bytes of noise; first byte

--

FF

 

Power up; second byte

00

 

null

Command prefix

42

 

B

Dataq initialization command (The infamous “Killer B”)

 

AF

 

Unknown response

01

 

soh

Start of heading

 

7F

 

Unknown response

00

 

null

Command prefix

53

 

S

ADC stop, start

 

57

 

Unknown response

30

 

0

Stop ADC; ASCII 0

 

F7

 

Unknown response

00

 

null

Command prefix

4E

 

N

Return 10 byte serial number of the device (first half of command)

 

AF

 

Unknown response

 

AD

 

Unknown response

5A

 

Z

Return 10 byte serial number of the device (second half of command)

 

30

0

Byte1

 

30

0

Byte2

 

30

0

Byte3

 

30

0

Byte4

 

30

0

Byte5

 

30

0

Byte6

 

30

0

Byte7

 

30

0

Byte8

 

30

0

Byte9

 

31

1

Byte10 (serial number is always “0000000001”)

00

 

null

Command prefix

43

 

C

Set analog channel (0-3) (specific channel or total number of channels?)

 

5F

 

Unknown response

33

 

3

Channel 3

 

77

 

Unknown response

00

 

null

Command prefix

44

 

D

Set digital channel mode (0 = output square wave, 1= input)

 

D7

 

Unknown response

30

 

0

Set digital channel to output square wave; ASCII 0

 

F7

 

Unknown response

00

 

null

Command prefix

 

57

 

Unknown response

53

 

S

ADC stop, start

31

 

1

Start ADC; ASCII 1 (converter is free running after this point)

 

7F

 

Byte 1 of reading

 

3E

 

Byte 2 of reading

 

AB

 

Byte 3 of reading

 

4F

 

Byte 4 of reading

 

 

 

Etc.

There are a couple of surprises here. First, the command that starts the whole process, Cmd+B is not documented. On the NEES Grid website, they dub this command the “Killer Bee”. Funny guys! Second, the serial number is always returned as a “0000000001”. We can also see more detail about the command structure. Some commands, such as SOH, are one byte commands. Some commands such as the Killer Bee command are two bytes. Still others, such as the Cmd+D which has a mode argument, are three byte commands.

I hooked up a spare board I had with an RS232 driver and an Atmega8 microcontroller to try out some code to fake out the Dataq handshake and command sequence. This is C code written for a CodeVisionAVR compiler. Shown here is just the Dataq relevant code and doesn’t include the RS232 code or the initialization code. I’ll post all of it once I get the whole thing working. By the way, I am not a very experienced coder, so if there is an easier way to implement this decision tree I’d appreciate hearing about it.

while (1)   //Dataq command decision tree
      {  
      input = getchar();
      if (input == NULL)        //NULL
       {
        first_byte_flag = 1; //this is the first byte of a multibyte command
       }
      else if ((first_byte_flag == 1) && (input == 'B')) //killer B
                {
                 second_byte = 'B';
                 putchar ('B');  //acknowledge
                }  
      else if ((first_byte_flag == 1) && (second_byte == 'B') && (input = SOH)) //SOH
                {
                putchar(SOH); //acknowledge
                first_byte_flag = 0;
                second_byte = 0; //all done
                }
      else if ((first_byte_flag == 1) && (input == 'S'))  //ADC start/stop
                {
                second_byte = 'S';
                putchar ('S');  //acknowledge      
                }   
      else if ((first_byte_flag == 1) && (second_byte == 'S') && (input == '0')) //stop ADC
                {
                putchar('0'); //acknowledge
                //stop adc command goes here
                first_byte_flag = 0;
                second_byte = 0; //all done
                }
      else if ((first_byte_flag == 1) && (second_byte == 'S') && (input == '1')) //start ADC loop
                {
                putchar('1'); //acknowledge
                //start adc command goes here; 4.167ms timer interrupt loop for 240Hz sample rate
                start_LED();
                first_byte_flag = 0;
                second_byte = 0; //all done
                }
        else if ((first_byte_flag == 1) && (input == 'N')) //killer B
                {
                 second_byte = 'N';
                 putchar ('N');  //acknowledge
                }  
      else if ((first_byte_flag == 1) && (second_byte == 'N') && (input == 'Z')) //serial number check
                {
                putchar('Z'); //acknowledge
                printf("0000000001"); //format????
                first_byte_flag = 0;
                second_byte = 0; //all done
                }
        else if ((first_byte_flag == 1) && (input == 'C')) //set input channel x
                {
                 second_byte = 'C';
                 putchar ('C');  //acknowledge
                }
        else if ((first_byte_flag == 1) && (second_byte == 'C') && (input == '1')) //set input channel 1
                {
                putchar('1'); //acknowledge
                //set channel 1command goes here
                first_byte_flag = 0;
                second_byte = 0; //all done
                }
        else if ((first_byte_flag == 1) && (second_byte == 'C') && (input == '2')) //set input channel 2
                {
                putchar('2'); //acknowledge
                //set channel 2 command goes here
                first_byte_flag = 0;
                second_byte = 0; //all done
                }
        else if ((first_byte_flag == 1) && (second_byte == 'C') && (input == '3')) //set input channel 3
                {
                putchar('3'); //acknowledge
                //set channel 3 command goes here
                first_byte_flag = 0;
                second_byte = 0; //all done
                }
        else if ((first_byte_flag == 1) && (second_byte == 'C') && (input == '4')) //set input channel 4
                {
                putchar('4'); //acknowledge
                //set channel 4 command goes here
                first_byte_flag = 0;
                second_byte = 0; //all done
                }
        else if ((first_byte_flag == 1) && (input == 'D')) //set digital channel mode
                {
                 second_byte = 'D';
                 putchar ('D');  //acknowledge
                }
        else if ((first_byte_flag == 1) && (second_byte == 'D') && (input == '0')) //set digital channel output square wave
                {
                putchar('0'); //acknowledge
                //set digital channel output square wave command goes here
                first_byte_flag = 0;
                second_byte = 0; //all done
                }
        else if ((first_byte_flag == 1) && (second_byte == 'D') && (input == '1')) //set digital channel output square wave
                {
                putchar('1'); //acknowledge
                //set digital channel input command goes here
                first_byte_flag = 0;
                second_byte = 0; //all done
                }                  
      else      //don't know any other combinations so it is an error
                {
                error_LED();
                first_byte_flag = 0;
                second_byte = 0;  //all done
                } 
   } //end decision tree  

This code is probably not 100% correct, but when I programmed the microcontroller and hooked it up to the WinDaq Lite PC software no error message was generated and WInDaq Lite continued to run.

Once we have the command structure mastered, we have to get the data out. Dataq uses an unbelievably complicated bit packing scheme that packs 64 ADC data bits into an 8 byte packet for transmission. The bits then have to be unscrambled from the bytes per the table below.

Encryption Table of WinDaq Starter Kit DI-194/154

 

Bit Positions

Serial Bytes

B7

B6

B5

B4

B3

B2

B1

B0

Byte1

A2

A1

A0

0

D2

D1

D0

0

Byte2

A9

A8

A7

A6

A5

A4

A3

1

Byte3

B0

0

0

0

D2

D1

D0

1

Byte4

B7

B6

B5

B4

B3

B2

B1

1

Byte5

C0

0

0

0

D2

D1

D0

1

Byte6

C7

C6

C5

C4

C3

C2

C1

1

Byte7

E0

0

0

0

D2

D1

D0

1

Byte8

E7

E6

E5

E4

E3

E2

E1

1

Notes

Ax is the xth bit of ADC reading from channel one
Bx is the xth bit of ADC reading from channel two if enabled
Cx is the xth bit of ADC reading from channel three if enabled
Ex is the xth bit of ADC reading from channel four if enabled
Dx are the digital input bits