Tom's Main Menu

Physical Computing Home

Intro to Physical Computing Syllabus

Networked Objects

Sustainable Practices

blog

Resources

code, circuits, & construction

my del.icio.us links

 

Serial to the Desktop

One of the most common uses for serial communication is to link a desktop computer with a microcontroller or other serial device. All desktop computers have a serial port of some form or another. While it used to be standard that all desktop computers had an RS-232 serial port, most are now moving to USB, a faster and more complicated serial protocol. However, even if there is not an RS-232 serial port available on your computer, there are plenty of USB-to-serial adaptors available. Through the serial port, you can create an entirely new physical interface for the desktop computer, or use the computer to control things too complex to manage on a microcontroller, like sound and video.

Almost any programming language or environment you use will have tools for accessing the serial ports on your computer. Although the examples below are given in a specific environment, the principles can be applied to most programming environment and device.

The first thing to keep in mind when working with a computer's serial port is that only one program can use the serial port at a time. If you have only one serial port, and you're using your computer to program the microcontroller, this means that you will have to close the serial port in the programming environment while you use the computer's serial port in another program, like HyperTerminal or zTerm or Director. If you get an error message saying something like "can't open serial port", it probably means that some other program is using the serial port. In addition to the programs already mentioned, some other programs that might commonly use the serial port include sync software for PDAs, printer drivers, MIDI interfaces, some networking extensions like the Macintosh's Appletalk, and others. If you can't open the serial port because it's already in use, check to see what other programs or utilities might already be using it.

The second thing to remember: always start with the simplest tool. A terminal program like HyperTerminal on the PC, or zTerm on or the screen command in the Terminal window the mac will allow you to do nothing but see the serial data coming in from your microcontroller. These are your best debugging tools on the desktop. Before you start to develop a more complex program, always check to see that you are getting serial data in to the desktop using a terminal program.

Once you've seen data passing back and forth in a terminal program, you're ready to start with another application.

Serial in Processing

Like many programming environments, Processing has a separate serial library for serial communication. This library allows you to access the serial ports on the computer you are on, set the data rate and other communication parameters, read the bytes from the serial input buffer, and write to the output buffer. These functions are similar to the various commands discussed to access a microcontroller's serial ports.

The first thing you have to do when using the serial library is to import the serial library into your sketch like so:

import processing.serial.*;
						

While you're at it, declare a global variable to hold an instance of the serial library like so:

Serial port;
						

Next, in the setup() method, you might want to get Processing to print a list of the available serial ports:

println(Serial.list());
						

Then, instantiate the library using the new() method, as follows:

port = new Serial(this, Serial.list()[0], 9600);

In this example, the first serial port available (usually COM1 on a PC) is used, at 9600 bps. Now you can start sending data back and forth.

To send a byte out, use the write() method. write() takes one of two data types: either an int, or an array of bytes. If you're sending data to a microcontroller, you could send it as an array of bytes, and let the microcontroller read the data byte-by-byte. For example, here's how to send three bytes:

byte[] myVar = new byte[3];
myVar[0] = 65;
myVar[1] = 66;
myVar[2] = 67;
port.write(myVar);

Whenever new incoming data is available, it's placed in a special place in memory called the serial buffer. A few methods from the serial library are used to deal with the serial buffer. available() reports the number of bytes available to read. read() reads the oldest available byte in the serial buffer. Here's a simple example of how to read a byte from the serial buffer:

 while (myPort.available() > 0) {
    int inByte = myPort.read();
    println(inByte);
  }
	

Here's another example that expects three bytes. As each byte comes in, it's added to an array. When the array is three bytes long, the individual bytes are converted to other data types and put into other variables, to be used elsewhere in the program. SerialEvent() is a special event called a callback function that gets called whenever there is new data available in the serial buffer:

void serialEvent(Serial port) {
  // if this is the first byte received, 
  // take note of that fact:
  if (firstContact == false) {
    firstContact = true; 
  }
  // Add the latest byte from the serial port to array:
  serialInArray[serialCount] = port.read();
  serialCount++;

  // If we have 3 bytes:
  if (serialCount > 2 ) {
    xpos = serialInArray[0];
    ypos = serialInArray[1];
    fgcolor = serialInArray[2];

    // print the values (for debugging purposes only):
    println(xpos + "\t" + ypos + "\t" + fgcolor);
    // Send a capital A to request new sensor readings:
    port.write(65);
    // Reset serialCount:
    serialCount = 0;
  }
}

Techniques for serial communication

There are many ways to use the serial xtra, and ultimately, you should come up with techniques that work best for you and your particular application. Following are a few general techniques that can be useful in some cases.

When exchanging sensor data with a microcontroller, it's often the case that you need to continually gather a series of bytes from the controller representing the values of various sensors attached to the microcontroller. As mentioned in the interpreting serial data notes, this can sometimes be tricky, since you need to make sure all the bytes arrive, and are read in the right order. There are two common ways of dealing with this problem: punctuating the data string with a constant byte at the start, or setting up a "call and response" system, so that the microcontroller sends only one set of data at a time.

Sending a constant header

If you decide to do this, first you need to know the range of values possible for each sensor. If you can reduce the range of values for each sensor so that each sensor is represented by one byte, you will simplify sending the data. With digital inputs, this is simple. A switch can only be on or off, so only two values are needed to represent all the possible states of a switch. With analog sensors, determine how much range you need. How much difference can the user discern? Is it a very rough control, where they can tell only small, medium, and large amounts of effect on the sensor, or do very small differences make a difference? Could they rate their effect on a scale from one to ten? one to 100? These questions will help you determine how large a range of sensitivity you need.

Once you know the range of values of each byte to be sent, you need to make up a unique byte or series of bytes to use at the start or end of your data string. This series needs to be different from any if the values that the data bytes can have. For example, if you had three analog sensors to send values for, and each one could give a range from 0 to 200, you could send a byte with the value 255 at the start of the string. None of the sensors can ever have a value of 255, so the receiving computer would know that if a value of 255 is received, this must be the header byte. Everything that follows it until another byte of value 255 is received can be presumed to be data.

If you have to use the entire range of values in a byte (0-255) to represent a sensor, then you could make up a unique string to use as a header. Again, choose a series that is impossible or very unlikely to come from your sensors.

Once you have decided on a header, make sure to always send the same number of bytes after it. For example, if you have three sensors, send the header and three bytes after it, even if one sensor is reading 0. This way, in director, you can write your program that if you read a byte with the header's value, the next three bytes will always be the sensor's values, in order, sensor1, sensor2, and sensor3.

Call and response

Another, often simpler, approach to sending multi-byte strings it to use a "call and response" system. Under such a system, the microcontroller is programmed to listen for a byte from the computer (a call), and to send one set of readings in response. The computer, in turn, is programmed to send the call byte, then continue to receive bytes until it gets as many as should be in the data string, then to interpret them, then to send out another call. This prevents the receiving computer from starting to read in the middle of a string.

See the Processing serial call and response example in the examples section of the site for more details on this method.