Source Code. Projects. Nerd Stuff. Art Stuff.

Interpreting Serial Data

From Tom Igoe’s blog.

When serial data is passed from one device to another, it is trasmitted one byte at a time. You have a few options in determining how you want each device to interpret those bytes, for instance determining when the beginning and end of the transmission is, and how the bytes in between are read.

Before any information is transmitted, both devices need to agree on how it’s transmitted, the rate at wich they’ll exchange data, how many bits per byte, etc. Generally, 8 bits, no parity, one stop bit is a good standard, and somewhere between 2400 and 9600 baud is a decent rate for small amounts of data.

Computers use numbers to represent alphanumeric characters (letters and numbers and punctuation) in bytes. There is a standard code, called the ASCII code (American Standard Code for Information Interchange), that assigns each number or letter a specific byte value from 0 to 255. For example, capital A is ASCII value 65. You can find a chart anywhere on the internet that gives the ASCII, binary and alphanumeric conversions. One example is found here. ASCII is a very common code used by many devices as part of their serial protocol. ASCII is convenient, in that any ASCII symbol can be represented in one byte, but it’s also limited.

The size of the byte has historically been hardware dependent and no definitive standards existed that mandated the size. The de facto standard of eight bits is a convenient power of two permitting the values 0 through 255 for one byte. If you’re only sending one number, and that number is less than 255, you know it can fit in a byte. This is an easy message to transmit. If you’re sending more than that (and you usually are), things get a little more complicated. The receiving computer has to know when the message starts and when it ends.

Different serial devices will use different codes to perform this action, but in Arduino, if you want to convert a byte’s raw value to the ASCII characters representing it, use the DEC modifier, like so:

Serial.print(myVar, DEC);

If the value of myVar was 134, this line would return three bytes, containing the characters “1″, “3″, and “4″.

One of the biggest challenges when communicating serially is making sure you get all the bytes, and that you get them in the right order. If the computer (PC or microcontroller) was doing something else at the instant that it should have been listening to the serial port, or it its input buffer has overflowed (i.e. if you’ve received more data than the computer has processed), you may lose some data. There’s no single way of making sure you get the right data, but there are some fundamentals you can check:

1. Did I receive the same number of bytes I sent?

For example, if the microcontroller is sending 3 variables, for 3 sensors, the computer needs to receive all three in order to act on them.

2. Did I get the bytes in the right order?

If the sender were sending “ABC” over and over, as “ABCABCABCABC” etc, it’s possible that the receiver might not start receiving at the beginning, and get “BCABCABCABCABCA” instead. This is a problem if, say, A is the right switch, B is the center switch, and C is the left switch. In order to avoid this, it’s sometimes useful to send some value at the start or the end of your data string that’s a constant number, and different from the other values. This is often referred to as a header byte.

In Arduino, that might look like this:

char A;
char B;
char C;
char headerByte = 101;

void setup() {
  Serial.begin(9600);
}
void loop() {
  // generate values for A, B, and C here

  Serial.print(A, BYTE);
  Serial.print(B, BYTE);
  Serial.print(C, BYTE);
  Serial.print(headerByte, BYTE);
}
Another alternative to this method of “punctuating” your serial message is to set up a “call and response” method. For example, the sending device may wait until it receives a request for data, then send one string of data, then wait for another request. This way, the receiver knows that it will only ever have one string of data in its buffer at a time. In this case, you need to make sure that the receiver’s serial buffer can fit as many bytes as the sender sends out in one string, and that the sender can receive a byte and wait before sending. For example:
// Find out if anything is in the queue.
if (Serial.available() > 0) {
// If there is data in the input buffer,
// get the first byte of it:
char inByte = Serial.read();
        if (inByte == 'A') {
            // send bytes out here:
            Serial.print(A, BYTE);
            Serial.print(B, BYTE);
            Serial.print(C, BYTE);
        }

}

 

3. Are my bytes part of a larger variable?

Let’s say you’re sending an integer variable from the BX-24. An integer is two bytes long, so the PC will receive two bytes when you send it an integer. To convert those two bytes to a single number again, use this formula:

integerVar = (byte1Var * 256) + byte2Var

If this confuses you, think of it as analogous to how you convert a number in hexadecimal (base 16) to a decimal (base 10) value. In this case, each byte is a digit in base 256.

Try it by sending a constant number larger than 256 first, to make sure you have the formula right.

4. Is my data getting garbled in transit?

Serial communication is tricky, in that you need to have both sides properly grounded to a common ground, and both receive and transmit wires properly connected and insulated. all kinds of electrical noise can get in the system and interfere with your transmission. To minimize the time you spend troubleshooting, it’s best to start out by sending a constant message. For example, if you know you’re planning to send out three bytes of data to represent sensor data as above, don’t start out by sending the actual values from the sensors. Start out by sending constant values. For example:

 Serial.print(65, BYTE);
 Serial.print(66, BYTE);
 Serial.print(67, BYTE);

When you see that the receiver is getting the same values you’re sending consistently, then you can change your code so that you care sending the actual values of the sensors. the hardware setup alone introduces enough variable conditions and points of possible failure to begin with. Because of this, it’s best to make your code (the thing you can control) constant until you have the hardware under control.