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 or RS-422 serial port, most are now moving to USB, a faster and more complicated serial protocol. However, even if there is not an RS-232/RS-422 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 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 PDA's, 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 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 Xtra for Director

Director (as of version 6) has no tools built into the program to communicate with serial ports, but it can access them using an Xtra. Xtras are libraries of commands that allow you to extend Director's functionalities. They extend Director similarly to how plug-ins extend a browser, or extensions or DLL's extend an operating system. There are a few different xtras available to access the serial ports; we'll be using one made by Geoff Smith, available at Physical Bits.

This xtra allows you to access the serial ports on the computer you are on, set the data rate and other communication parameters, see how many bytes are waiting in the serial input buffer, read the bytes from the buffer in various formats, and write to the output buffer in various formats. These functions are similar to the various commands discussed to access the microcontroller's serial ports in the serial output notes.

Like other xtras, the first thing you should do when using the xtra is to set up your startmovie and stopMovie handlers to open and close the xtra, as follows:

global serialObject
global myVar1, myVar2, myVar3

on startMovie
  	clearglobals
  -- variables for setting up the xtra.
  -- Fill in your own values from the email
  -- that you get from the licence registrar:
    
  -- fill in your email address below (see physicalbits.com for details)
  programmerName = "youremail@some.com"
  serialnum  =  "xxxx-xxxx-xxxx-xxxx"   -- physicalbits.com will mail you this number
  licenseLength = 90
  
    -- fill in the name of your serial port below (e.g. COM1 on Windows):
  mySerialPort = "/dev/cu.USA19QW191P1.1"

  
  
  	-- make a new instance of the xtra (mac OSX)
  	openxlib the pathname & "serialXtra.osx"

  	-- use this line instead for Windows:
  	-- openxlib the pathname & "serialXtra.xtr"
  	
  	
  	serialObject =  new (xtra "SerialXtra", programmerName, serialnum, licenseLength ) 
  
  	-- check that it has been created correctly     
  	if objectP( serialObject ) then
    	put serialObject 
  	else
    	alert("Instance not valid")
  	end if
  
--	open the serial port:
  	serialObject.openPort(mySerialPort)

  
  	-- set the data rate, start bits, etc:
  	serialObject.setProtocol(9600, "n", 8, 1)
end

on stopMovie  
  -- dispose of the xtra and close the xtra file
  serialObject.closePort()
  set serialObject to 0
  	closeXlib 
end 

Now you can start using the other commands. Below are some details on the most useful ones, and suggestions as to how to use them.

serialObject.ReadNumber()

This command reads the first byte in the serial input buffer and gives it to you as a decimal number, 0 to 255. Typically, you put that number into a variable, like this:

inputVar = serialObject.readNumber()

Each time you read a byte from the buffer, it's removed from the buffer.

serialObject.readChar()

This command is similar to readNumber. It takes the first byte off the serial input buffer and gives it to you as the ASCII character corresponding to whatever the value of the byte is. So if the value was 65, for example, readChar would give you "A". Like readNumber, each time you read a byte from the buffer using readChar, it's removed from the buffer.

serialObject.readString()

This command is a little different; instead of reading the buffer one byte at a time, it takes all the bytes of the buffer and gives them to you as a single string. Once you've got the string, you can read the individual characters of it using the Lingo commands for reading strings and parts of strings.

You can also read the values as hexadecimal:

serialObject.readHex()

This command reads the entire serial input buffer and gives you each byte as a hexadecimal string, separated by spaces. It's useful if you're working with the data as MIDI or some other format that's usually written in hexadecimal.

The commands to write data out the serial port are similar in format to the read functions, but they have an extra parameter: whatever it is you want to send out the serial port.

serialObject.writeNumber(outputVar)

This command takes a number (outputVar) and sends it out the serial port. outputVar has to be an integer from 0 to 255.

serialObject.writeChar(outputVar)

This command does the same thing as writeNumber, but it expects outputVar to be an alphanumeric character.

serialObject.writeString(outputStringVar)

This command takes a multi-byte string of characters and sends them out the serial port. You can concatenate strings together into one string before sending them.

serialObject.writeHex(outputVar)

This command sends only one byte out, but it expects outputVar to be a two-byte string in hexadecimal format. This means that each character can only be the numbers 0-9 or the characters A-F. For example:

serialObject.writeHex("A3")

WriteHex is useful if you have data in hexadecimal form; otherwise it's easier to use writeChar or writeNumber.

serialObject.charsAvailable()

This command tells you how many bytes are waiting to be read in the serial input buffer. It can be very useful. If you know that you need to receive five bytes from the microcontroller before doing anything, for example, you can wait until charsAvailable(gSerialPort) = 5, then do whatever has to be done.

serialObject.flushInputBuffer()

This command empties everything from the serial input buffer. For example, if the microcontroller is sending data faster than Director is reading it, sometimes you want to flush the input buffer so that you get the latest reading from the chip.

serialObject.MeaningOfLife()

This command gives you 42.

Techniques for using the serial Xtra

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 Director 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 serial call and response example in the examples section of the site for more details on this method.