TweetFollow Us on Twitter

Control Freaks Alert

Volume Number: 16 (2000)
Issue Number: 3
Column Tag: Data Acquisition on the Macintosh

Control Freaks Alert!

by Tom Djajadiningrat

A clean interface to Beehive Technologies' ADB I/O using object-oriented Lingo

Introduction

The ADB I/O by Beehive Technologies is a device which you can connect to a Macintosh to communicate with external electronics. Simply put, the ADB I/O provides digital inputs (to read switches), digital outputs (to switch things on or off) and analog inputs with 8-bit resolution (to read all kinds of sensors). For the Macintosh platform, it has opened up areas of application to mainstream users that were previously the sole territory of electronic hobbyists or users with high end data acquisition hardware. A few of those areas are home automation, robotics, kiosks, scientific experiments and product simulation.

Many applications can communicate with the ADB I/O. One particularly powerful combination is the ADB I/O and Macromedia Director. Even if you intend to control your ADB I/O through AppleEvents or C/C++, you may find it worthwhile to test your set-up with Director. Director is easy to program, especially when it comes to the graphical user interface.

There is of course a downside to Director's ease of use: the temptation of sloppy programming. Director does not require you to declare local variables or provide function prototypes. Add Director's cryptic error messages and your program can turn into a tangled mess which is difficult to debug. While this is of course undesirable for any program, with a program that communicates with external devices, for example motors, the consequences of messy programming may be more severe.

This article aims to help you in battling messy Director code. By using Director's object-oriented features you will get to build a friendly and clean interface to the ADB I/O. In addition, in this article consistent naming for variables is used, which may be of help to you in writing your own scripts.

Required Hardware and Software

Required hardware:

  • A Macintosh with an ADB port or a Macintosh with a USB port and a Griffin Technology iMate USB to ADB adapter.
  • A Beehive Technologies' ADB I/O.

Required software:

  • ADB I/O XCMDs 1.0.1. This file comes with the ADB I/O. It is also downloadable from the Beehive Technologies website.
  • Macromedia Director 5 or 6 (Director 7 is not compatible with the ADB I/O XCMDs 1.01 file. A Director 7 compatible Xtra for the ADB I/O is currently in beta and downloadable from the Beehive Technologies website). Though this article explains things for Director 6, the differences with Director 5 are minimal and the sample files which accompany this article are provided in both Director 5 and 6 versions.

What You Need to Know

You need to be familiar with Macromedia Director and its scripting language Lingo. You need not have worked with object-oriented Lingo. What you need to know about object-oriented Lingo will be explained. You should be familiar with the capabilities of the ADB I/O, though a summary of what you need to know is provided.

Contents

We will start with an introduction to object-oriented Lingo. We will then cover the ADB I/O's hardware and software details relevant to this article. If you are already comfortable with these subjects, you may wish to skip them. Once we have established this ground, we start with building a Director Lingo object which forms a friendly and clean interface to the ADB I/O. At the end of this article you will find a section on troubleshooting and a section with suggestions for enhancements.

Object-Oriented Lingo

Instead of starting with a theoretical discussion of the advantages of object-oriented Lingo, we will dive straight in and put together a simple object-oriented Director movie. I think you will find that the virtues of object-oriented Lingo are much easier to understand once you have seen a concrete example. Our movie consists of three scripts: a parent script, a movie script and a frame script. First we will write the scripts, then we will test and discuss the resulting movie. Don't worry too much if you don't completely 'get it' when we write the scripts. All will be explained.

The parent script

A Director object is described in a parent script. Create a new movie first, then create a parent script by choosing Script from the Window menu (Figure 1). The script window opens. Name the script by typing testParentScript in the field at the top of the scripting window (Figure 2). To make sure that the script is a parent script, click on the info button (Figure 3) and choose parent from the type pop-up menu (Figure 4). Click OK.


Figure 1.Open the script window


Figure 2.Name the script


Figure 3.Press the info button


Figure 4.Make the script a parent script.

Now on to the parent script itself. It consists of three parts: a birthing handler, message handlers and properties. We will discuss each part in turn.

1. The birthing handler

This is where a child object is created. It typically has the form:

on new me
	return me
end

The birthing handler returns a pointer to a newly created child object. We will add one line of code to our new handler to put a message in the message box telling us that the birthing handler was indeed called.

on new me
	- print some feedback in the message window
	put "the birthing handler of testParentScript was called"

	- return a pointer to the new child object
	return me
end

2. Message handler

In a message handler you can specify a behaviour of an object. A message handler has the form:

on messageHandler me
end

We will create an message handler called hello which beeps and puts a message in the message box.

on hello me
	- beep once
	beep

	- print some feedback in the message window
	put "Hello World!"
end

3. Properties

A property is a variable that belongs to an object. It is declared at the top of an object's parent script, preceded by the Lingo keyword property, like this:

property pProperty

Within the parent script you can set or get a property by just its name:

set pProperty = 1
set lVariable = pProperty

When we test the Director movie, you will see how to get at a property from outside of the parent script. Notice how, to maintain an overview, we start a property with the letter p and a local variable with the letter l. For a full description of our naming convention for variables and parameters, please refer to Table 1.

Prefix Suffix Meaning Example
i   input parameter iChannel
l   local variable lResult
o   child object oADB1
p   property variable pUnit
  List list variable pChannel

Table 1.Variable and parameter naming conventions.

For now, we will create a single property called pProperty and two member functions. One to set the value of pProperty and one to get the value of pProperty.

property pProperty

on setProperty me, iValue
	set pProperty = iValue
end

on getProperty me
	return pProperty
end

The movie script

To create the movie script, bring up the script window if necessary and click on the + button to create a new script. Name the script movieScript by typing into the field at the top of the script window. Click on the info button. A dialog box appears. Make sure that movie is selected in the type popup-menu. Click OK.

In our movie script we will write two handlers. The first one, startMovie, is called when the movie starts, the second one, stopMovie, is called when the movie stops.

on startMovie
end

on stopMovie
end

In this example we will add some code to the startMovie handler only. First we print some feedback to the message window to show that the startMovie handler was called. We then create a global called oTestObject1. Finally, we assign to this global a child object of the testParentScript that we wrote previously by calling the birthing handler.

on startMovie

	- Print some feedback in the message window
	put "startMovie was called."

	- create a global oTestObject1
	global oTestObject1

	- make oTestObject1 a child object of the parentScript testParentScript
	- by calling the birthing handler.
	set oTestObject1 = new (script "testParentScript")

end

The frame script

We will now create a simple frame script to make our Director movie loop in frame 1. In the score window, double click on the score channel of frame 1 and add one line of code to the exitFrame handler.

on exitFrame
	go to the frame
end

Testing our movie

It is time to test our movie. If you get stuck at any point and think it is because you did not quite get the explanation above, you may wish to look at testOOMovie.dir. Run the movie and open the message box. Type

put oTestObject1

The message box should display

- <offspring "testParentScript" 2 876a72a>

indicating that oTestObject1 is a child object based on our testParentScript. Don't worry if the alpha-numeric string that you get differs from the one shown above. As this is a memory address it is in fact highly unlikely that it would be identical.

Now try the hello message handler. The syntax for sending a message to a child object is <messageHandler> <childObject>.

hello oTestObject1

The Mac should beep and the message box should display

- "Hello World!"

Now send the getProperty message to our child object. Since getProperty is a function (a handler which returns a value), you need to enclose its parameters in round brackets.

put getProperty (oTestObject1)

The message box shows that pProperty has the value void. No worries, pProperty has not been set yet. Now type:

setProperty oTestObject1, 1

Now try again:

put getProperty (oTestObject1)

And you will see that pProperty has changed its value from void to 1.

setProperty and getProperty are message handlers which we wrote ourselves. Another way to set and get properties of a child object outside of its parent script is:

set the <property> of <childObject> = <value>
set lVariable = the <property> of <childObject>

Notice how we need to add the Lingo 'the' keyword, now that we try to access a property outside of its parent script.

Let's try:

set the pProperty of oTestObject1 = 2
put the pProperty of oTestObject1

The message box shows that pProperty of oTestObject1 now has the value 2.

Play around with accessing properties through the message handlers setProperty and getProperty as well as with accessing the properties directly through the 'the' keyword. You will find that you can mix both methods.

What's the point of object-oriented Lingo?

Before we answer that question, try creating a second child object:

set oTestObject2 = new (script "testParentScript")

The message box responds with

- "new handler of testParentScript was called"

Try entering

put oTestObject2

The message box responds with

- <offspring "testParentScript" 2 876a6ee>

Notice how the address of oTestObject2 differs from the address of oTestObject1. The values of the properties are also individual to each child object. Try

setProperty oTestObject1, 7
setProperty oTestObject2, 666

Now type:

put getProperty (oTestObject1)

The message box shows that the property pProperty is now 7.

And try:

put getProperty (oTestObject2)

This time the message box responds that the property pProperty equals 666. That is pretty kewl: both child objects have a property called pProperty but each can hold a different value.

This example illustrates the three characteristics of object-oriented Lingo that we use in this article. The first is the ability to create multiple child objects based on the same parent script, which are all capable of listening to certain message through the message handlers defined in the parent script. The second characteristic is the ability to hide code - in our case a simple put statement - within the birthing handler of a parent script. This code is automatically executed when a child object is created. The third characteristic is the ability to assign individual property values to child objects. While these characteristics may seem trivial, we can use them to great effect in improving the cleanliness of our code. By hiding set-up details in the birthing handler, these details are prevented from littering your main program. Through the use of properties, variables can be encapsulated within an object. This greatly reduces the need for global variables. Global variables are a major source of bugs in Director because each time you forget to declare a variable as a global, Director will assume you are creating a new, local variable.

You have now seen enough of object-oriented Lingo to be able to understand the ADB I/O parent script that we will build later on. However, there is a lot more to object-oriented Lingo than you have seen so far. To learn more, you can have a look at Director's on-line help under parent script or have a look at Small (1999).

If you are familiar with some other object-oriented language such as C++ or Java, you will probably have worked out the relationship between Lingo and general object-oriented terminology by now. A parent script is a combination of a source and a header file in which you define an object, a birthing handler is a constructor, a message handler is the equivalent of a member function, a property is a data member and a child object is an instance.

The ADB I/O Hardware

Units

You can have a maximum of four ADB I/O units daisy chained to your Mac. Each ADB I/O needs to be set to its own unique ID number.

Ports

The ADB I/O has two ports, called port A and port B. Each port has four channels. The channels of port A and B differ in their capabilities.

Port A

Each of the four channels of port A can be configured as either a digital input or a digital output. Analog input is not possible on port A. Each channel can have either a relay or an opto-isolator installed. The ADB I/O comes with relays pre-installed on all channels of port A, making them suitable for digital output. If you wish to use any of these channels for digital input, you will need to replace a channel's relay by an opto-isolator. Through the use of relays or opto-isolators, external electronics are electrically isolated from the ADB I/O.

Port B

port B allows not only digital input and digital output, but also analog input and an analog reference voltage. However, you cannot freely mix all four channel types. Only the following configurations are allowed.

analog in, analog in, analog in, analog in
analog in, analog in, analog in, reference
analog in, analog in, digital in/out, digital in/out
digital in/out, digital in/out, digital in/out, digital in/out

Each channel of port B is equipped with a jumper. A jumper can be set to pull up, pull down or open. For digital input, the jumper needs to be set to either pull-up or pull-down. Note that to switch a channel from digital to analog, you not only need to change the configuration of port B through software, but you also need to move the jumpers.

The ADB I/O Software

To use the ADB I/O with Director 5 or 6, XCMDs are used. These come in a file called ADBIO XCMDs 1.01. You need to make sure that this file resides in the same folder as your Director movie. We can have a look at the functionality offered by the ADBIO XCMDs 1.01 file by creating a simple Director movie (If you get stuck during the creation of this movie you can take a peek at testXCMDs.dir). Start a new movie, create a movie script and add startMovie and stopMovie handlers like this:

on startMovie
	openXLib "ADB I/O XCMDs 1.0.1"
end

on stopMovie
	closeXLib "ADB I/O XCMDs 1.0.1"
end

Add a frame script in the score channel of frame 1 to loop the movie. Run the movie and open the message box. Now type

showXLib

A slew of Xtras, XCMDs and XFCNs show up. We are only interested in the ones that concern the ADBIO:

-	"ADB I/O XCMDs 1.0.1"
-	XCMD: Flash			Id:0		
-	XCMD: Picture			Id:4		
-	XCMD: Palette			Id:5		
-	XCMD: ADBReInit			Id:6701 
-	XCMD: CopyRes			Id:1077 
-	XCMD: ConfigureADBIO		Id:1200 
-	XCMD: SetADBIO			Id:1203 
-	XFCN: GetADBIO			Id:1201 

For now let's look at ConfigureADBIO, SetADBIO and GetADBIO. Here only a short description of these calls is given. For a full description please refer to the ADB I/O manual.

ConfigureADBIO

  • ConfigureADBIO has the syntax:
	ConfigureADBIO <Unit> ,<port>,¬
	<Channel1Type>, <Channel2Type>,¬
	<Channel3Type>, <Channel4Type>
  • What does it do? - With ConfigureADBIO you specify what function the four channels of a particular port of a particular ADB I/O unit should fulfill.
  • Parameters - The first parameter, Unit, is used to address a particular ADB I/O unit and can range from 1 to 4. The second parameter, port, is used to address either port A or port B of an ADB I/O. The remaining parameters specify what channel type you wish to assign to each channel of the port. Keep in mind that the capabilities of the ports differ. If you call ConfigureADBIO with unallowed channel types it will return an error message.
  • Return values - ConfigureADBIO returns either 0 (success) or an error string (failure).

SetADBIO

SetADBIO has the syntax:
	SetADBIO <Unit>, <Port>, <Channel>¬
	<Number> [,<'high'|'low'>] [,<Time>]
  • What does it do? - You can think of this call as three separate calls. All three versions apply to digital outputs only. The first version sets a channel of a port of a unit to high or low:
	SetADBIO <Unit>, <Port>, <Channel>, <'high'|'low'>

The second sets a channel of a port of a unit to high or low for a certain duration, after which it returns to the opposite state:

	SetADBIO <Unit>, <Port>, <Channel>, <'high"|'low'>, <Time>

The third sets a port of a unit in such a way that the values of its channels form the binary equivalent of a decimal number:

	SetADBIO <Unit>, <Port>, <Number>
  • Parameters - You already know the meaning of the Unit and Port parameters. Channel specifies the number of a channel and should therefore be in the range 1-4. 'high' means relay actived on port A and +5V on port B, 'low' means relay non-active on port A and 0V on port B. Time specifies a duration from 0-25.5 s in 0.1s increments through a integer ranging from 0 to 255. Number ranges from 0 (all channels 'low') to 15 (all channels 'high').
  • Return values - SetADBIO returns either 0 (success) or an error string (failure).

GetADBIO

  • GetADBIO has the syntax:
	GetADBIO(<Unit>, <port>[,'Number'])
  • What does it do? - You can think of this call as two separate calls. The first version:
	GetADBIO(<Unit>, <Port>)

returns a channel record with the channel type and channel value for each channel of a port:

	<Channel1Type>,<Channel1Value>
	<Channel2Type>,<Channel2Value>
	<Channel3Type>,<Channel3Value>
	<Channel4Type>,<Channel4Value>

The second version:

	GetADBIO(<Unit>, <port>, <"Number">)

returns the decimal equivalent of the binary pattern on the channels of a port.

Return values - GetADBIO returns four lines of channel type and channel value combinations (success) or a decimal number in the range 0-15 (success) or an error string (failure).

Trying it out

Make sure you have at least one ADB I/O unit hooked up to your Mac, run the Director movie you just created and open the message box (Note that according to the ADB I/O manual, your Macintosh should be turned off before you attach or disconnect an ADB I/O). Play about with the three main calls ConfigureADIO, SetADBIO and GetADBIO. For each of these calls, make sure you type the Director keyword put in front of it and that you enclose the call's parameters in brackets so that you get to see the return value in the message box. For example, type:

put configureADBIO (1, "A",¬
"digital out", "digital out",¬
"digital out", "digital out") 

No error should occur and the XCMD should answer with 0.

Now be evil and force some error messages, for example:

put ConfigureADBIO 1, "A",¬
"analog in", "analog in",¬
"analog in", "analog in"

The return value is an error message:

- "Error : Wrong combination of ChannelType parameters"

Clearly, the XCMD is not happy about analog in on port A because only digital in and out are allowed. Let's try something else:

put SetADBIO 1, "A", 5, "high"

Again the return value is an error message:

- "Error : Invalid Channel parameter"

This time the XCMD is not happy because there are only four channels on each port. Play about a bit more. Don't worry about the channel values in the channel record returned by GetADBIO. For the moment no external electronics need to be attached to the ADB I/O, so never mind the values of the channels and ports. Just try to get a general feel for the required parameters and for the return values of the three handlers. Notice how the error messages returned by the three handlers are actually pretty specific. We will make use of this when we build our Director object to control the ADB I/O.

A Director Parent Script for the ADB I/O

Now that we have covered the basics of both object-oriented Lingo and the ADB I/O, we can write a parent script to control the ADB I/O. The parent script is called ADB I/O and forms part of testADBI/OMovie.dir included with the accompanying code. First we discuss the handlers concerning initialization and configuration, then those for input and then those for output. Finally we try out the whole parent script.

Initialization and configuration

Listing 1 shows the parent script's properties, a birthing handler and a configurePort handler.

Listing 1


properties
property pDEBUGMessage, pUnit
property pChannelTypesAList, pChannelTypesBList
property pChannelValuesAList, pChannelValuesBList

new
This handler creates a child object and hides set-up details.
Example: set oADB1 = new (script "ADB I/O", 1, TRUE)
Possible return values: An object address (success) or 0 (failure).

on new me, iUnit, iDEBUGMessage

	- Requested error reporting.
	set pDEBUGMessage = iDEBUGMessage 

	- We are working with an object per ADB I/O unit.
	set pUnit = iUnit

	- These two lists hold the channel types
	- for port A and port B.
	set pChannelTypesAList = []
	set pChannelTypesBList = []
	- These two lists hold the channel values
	- for port A and port B.
	set pChannelValuesAList = []
	set pChannelValuesBList = []
	
	- The default initialization of the hardware is:
	- "digital out" for all channels of port A and
	- "digital in" for all channels of port B.
	- Call ConfigureADBIO for both port A and port B
	- with this default initialization.
	- According to the ADB I/O manual this is good practice.
	- The settings can always be changed later.
	
	- Initialize port A
	- Unit pUnit, port A, all channels digital out
	set lResult = configurePort(me, "A",¬"digital out", "digital out",¬
							"digital out", "digital out")

	if (lResult <> 0) then
		if pDEBUGMessage = TRUE then put lResult
		return 0
	end if

	- Initialize port B
	- Unit pUnit, port B, all channels digital in 
	set lResult = configurePort(me, "B",¬ "digital in", "digital in",¬
  		"digital in", "digital in")

	if (lResult <> 0) then
		if pDEBUGMessage = TRUE then put lResult
		return 0
	end if

return me
end

configurePort

This handler configures a port.
Example:
configurePort (oADB1, "A", 	"digital out", "digital out",¬
								"digital out", "digital out")
Possible return values:
Empty string "" (success) or an error string (failure).

on configurePort me,	iPort,	iChannel1Type, iChannel2Type,¬
													iChannel3Type, iChannel4Type

	- ConfigureADBIO returns either an empty string
	- "" (success) or an error string (failure).
	put configureADBIO (pUnit, iPort,¬
										iChannel1Type, iChannel2Type,¬
										iChannel3Type, iChannel4Type)¬
										into lResult

	if lResult = 0 then

		case iPort of
			"A":	set pChannelTypesAList =¬
										[	iChannel1Type, iChannel2Type,¬
											iChannel3Type, iChannel4Type]¬
		"B":	set pChannelTypesBList =¬
									[	iChannel1Type, iChannel2Type,¬
											iChannel3Type, iChannel4Type]
		end case

	else
		- An error occurred
		if (pDEBUGMessage=true) then put¬
		"ConfigureADBIO for port" && iPort && "of unit"¬
		&& pUnit && "failed."
	end if

	return lResult
end

Starting with the birthing handler, we come across the parent script's properties straight away. pUnit holds an integer with the ID number of an ADB I/O device which we pass into the birthing handler through the parameter iUnit.

pDEBUGMessage is a boolean property which we can turn on to get a bit more debugging info in the message window. We can turn it on through the birthing handler's parameter iDEBUGMessage. The chosen approach to debug messages is to report nothing while there are no errors. Only if there is an error will a debug message be printed to the message box. This is because writing to the message box considerably slows down a Director movie. If there are no errors we want to avoid any slow downs as they may cause timing problems.

Next is a set of four lists. pChannelTypesAList is a list which holds the channel types for port A. Similarly, pChannelTypesBList holds the channel types for port B. pChannelValuesAList is a list with the channel values of port A. Finally, pChannelValuesBList is a list with the channel values of port B. All lists are initialized as empty lists.

We use the birthing handler to hide some of the port set-up details of the ADB I/O. The default hardware configuration is all channels of port A configured as digital outputs and all channels of port B configured as digital inputs. We match the default hardware configuration by calling the message handler configurePort for port A and port B. If you look at configurePort you see that it calls ConfigureADBIO and that if it succeeds it sets either the property pChannelTypesAList or pChannelTypesBList to the requested channel types. If you need other settings for either port A or B you can call always call configurePort with the desired channel types after the child object has been created.

Note the flow of error messages: if ConfigureADBIO returns an error message, then configurePort returns an error message and the birthing handler returns 0. Also note the use of the property pDEBUGMessage. If it is true we print some error messages to the message window. ConfigureADBIO is pretty important. During debugging we need to know if it fails and why.

Reading from inputs

We will now look at the calls for getting information from channels and ports. There are four calls: getChannelDigital, getChannelAnalog, getPort and getPortDigital. Let's start with getChannelDigital (Listing 2).

Listing 2


getChannelDigital

This handler returns the value of a digital input channel.
Example: getChannelDigital (oADB1, "A", 1)
Possible return values: 0 (low), 1 (high) or an error string (failure).

on getChannelDigital me, iPort, iChannel

	- Make sure iChannel is within range 1-4. 
	if iChannel<1 OR iChannel>4 then
		set lResult = "Error : Invalid Channel parameter"
		return lResult
	end if

	- Get the channel types and values for this port.
	put GetADBIO(pUnit, iPort) into lResult

	if word 1 of lResult = "Error" then
		- GetADBIO returned an error.
		return lResult
	else
		- GetADBIO returned no error.
		- Extract the relevant line for the desired channel. 
		put line iChannel of lResult into lThisChannelString

		- Check whether whether our channel is a digital input
		- that is high, low, or not a digital input at all.
		case lThisChannelString of
				
			"digital in,high":
				- This is a digital input and it is high.				
				return 1
				
			"digital in,low":				
				- This is a digital input and it is low.				
				return 0
				
			otherwise:				
				- This channel does not allow digital input.				
				set lResult =	"Error: Channel" && iChannel &&¬
											"of port" && iPort && "of unit" &&¬
											pUnit && "is not a digital input."

				return lResult
				
		end case
	end if	
end

If you just want to look at a single digital input channel, getChannelDigital is the way to do it. First it makes sure iChannel is within the range 1-4, then it calls GetADBIO to retrieve an up-to-date channel record for the port you specify in the iPort parameter. Based on the iChannel parameter it extracts the relevant line from the channel record. It then determines whether the channel is high, low or not a digital channel at all. If the channel is high, getChannelDigital returns 1, if it is low it returns 0. If the channel is not a digital input channel, getChannelDigital returns an error message.

The next call we examine is getChannelAnalog (Listing 3).

Listing 3


getChannelAnalog

This handler returns the value of an analog input channel. Since analog input is only possible on port B, we do not need to pass the port as a parameter.
Example: getChannelAnalog (oADB1, 1)
Possible return values: 0-255 (success) or an error string (failure). 

on getChannelAnalog me, iChannel
	
	- Make sure iChannel is within range 1-4. 
	if iChannel<1 OR iChannel>4 then
		set lResult = "Error : Invalid Channel parameter"
		return lResult
	end if
	
	put GetADBIO(pUnit, "B") into lResult
	
	if word 1 of lResult = "Error" then
		- GetADBIO returned an error.
		return lResult
	else	
		- GetADBIO returned no error.
		
		- Extract the relevant line for the desired channel. 
		put line iChannel of lResult into lThisChannelString
		
		- Check whether analog in is allowed on this channel.
		if lThisChannelString contains "analog in" then
			- This channel does allow analog input.
			- Extract its value.
			put char 11 to length(lThisChannelString)¬
			of lThisChannelString into lValueString		
			
			return value(lValueString)
			
		else

			- This channel does not allow analog input.
			set lResult =	"Error : Channel" && iChannel &&¬
								"of port B of unit" && pUnit &&¬
								"is not a analog input."
			
			return lResult			
			
		end if
	end if
end

getChannelAnalog is the analog equivalent of getChannelDigital: it is the way to go if you need to monitor a single analog input channel. First it makes sure the parameter iChannel is within the range 1-4, then it calls GetADBIO to retrieve an up-to-date channel record for the port you specify in the iPort parameter. Based on the iChannel parameter it extracts the relevant line from the channel record. It then determines whether the channel allows analog input. If the channel is an analog input getChannelAnalog extracts the channel value. If the channel is not a analog input channel getChannelAnalog returns an error message.

But what if you need to monitor multiple channels? Calling getChannelDigital or getChannelAnalog repeatedly is not very efficient, as each time you call them, you in fact call GetADBIO which returns a channel record with the values of all the channels of a port. Reading from or writing to a port too often can make other input devices on the ADB bus act sluggishly (Beehive Technologies recommends not to read more than 12 times per second and not to write more than 6 times per second). A more efficient way to look at multiple channels is by means of getPort (Listing 4).

Listing 4


getPort

This handler gets both the channel types and the channel values of the
inputs of a particular port. The channel types are stored in
pChannelTypesAList or pChannelTypesBList, depending on the port. The
channel values are stored in pChannelValuesAList or pChannelValuesBList,
depending on the port.
Example: getPort (oADB1, "A")
Possible return values: 0 (success) or an error string (failure).

on getPort me, iPort

	set lResult = GetADBIO (pUnit, iPort)

	if word 1 of lResult = "Error" then
		- GetADBIO returned an error.
			return lResult
	else
		- GetADBIO returned no error.
		- Work out the channel value and the channel type.
		repeat with lChannel = 1 to 4

			set lThisChannelString = line lChannel of lResult

			case (lThisChannelString) of
				"digital in,low":
					set lChannelType = "digital in"
					set lChannelValue = 0

				"digital in,high":
					set lChannelType = "digital in"
					set lChannelValue = 1

				"digital out,low":
					set lChannelType = "digital out"
					set lChannelValue = 0

				"digital out,high":
					set lChannelType = "digital out"
					set lChannelValue = 1

				"analog ref,255":
					set lChannelType = "analog ref"
					set lChannelValue = 255

				otherwise:
					set lChannelType = "analog in"
					put char 11 to length(lThisChannelString) of¬
					lThisChannelString into lChannelValueString
					set lChannelValue = value(lChannelValueString)

			end case

			- Store the channel type and channel value in
			- their respective arrays.
			if (iPort = "A") then			 
				setAt pChannelTypesAList, lChannel, lChannelType
				setAt pChannelValuesAList, lChannel, lChannelValue
			else
				setAt pChannelTypesBList, lChannel, lChannelType
				setAt pChannelValuesBList, lChannel, lChannelValue
			end if
			
		end repeat
		
		return 0
		
	end if	
end

In getPort we finally make use of the properties pChannelValuesAList and pChannelValuesBList. getPort calls GetADBIO for the desired port. If GetADBIO fails, getPort returns an error message. If getPort executes successfully and returns a channel record, getPort looks at each line of the channel record, puts the channel type in either pChannelTypesAList or pChannelTypesBList, and the channel value in either pChannelValuesAList or pChannelValuesBList. In the process the values of the input channels are converted to integers: 0 or 1 for a digital input and 0-255 for an analog input. After calling getPort you can easily retrieve an input channel's value by using the standard Lingo list access call getAt. You will see how to use the getPort-getAt combo in more detail when we put the parent script into action.

We don't discuss GetPortDigital in detail here. If you look at the code, you will see that it is simply a wrapper for GetADBIO with use of the number parameter. It reads all four channels of a port, converts the binary pattern on the port to a decimal number and returns this number.

Writing to outputs

There are three calls for setting output channels and ports: setChannelDigital, setChannelDigitalDuration and setPortDigital. Let's start with setChannelDigital (Listing 5).

Listing 5


setChannelDigital

This handler sets a digital output channel high or low.
Example: setChannelDigital (oADB1, "B", 1, 1)
Possible return values:
0 (success) or an error string (failure)

on setChannelDigital me, iPort, iChannel, iChannelValue
	
	- Check whether the requested channel value is allowed.
	if iChannelValue<>0 AND iChannelValue<>1 then
		- The requested value is not allowed.
		set lResult = "Error: channel value must be 0 or 1"
		return lResult
	end if

	- Try setting the channel to the requested value.
	if iChannelValue=0 then
		set lResult = SetADBIO(pUnit, iPort, iChannel, "low")
	else
		set lResult = SetADBIO(pUnit, iPort, iChannel, "high")
	end if

	return lResult

end

setChannelDigital is a convenient way of setting a single digital output channel. First it checks whether the requested channel value is 0 or 1. If the channel value is not 0 or 1, setChannelDigital returns an error. Otherwise it uses a call to SetADBIO to try to fulfill the request.

We are not going to cover setChannelDigitalDuration or setPortDigital in detail. If you look at the accompanying code, you will see that setChannelDigitalDuration is similar to setChannelDigital. The main difference is that the former uses the Time parameter in SetADBIO to set a digital output channel for a certain duration. This feature is available on port A only. With SetPortDigital you can set a whole port in one go, providing all the channels are digital outputs.

Taking It All for a Spin

Ok, let's try to put our parent script to the test. If you get stuck in this section, you may wish to peek ahead at the trouble shooting section towards the end of this article.

External electronics

To test the parent script you need to hook up some external electronics to the ADB I/O (Note that according to the ADB I/O manual, your Macintosh should be turned off before you attach or disconnect electrical devices from the ADB I/O). Connect an LED to each channel of port A. Hook up potentiometers to channel 1 and channel 2 of port B. Connect an LED to channel 3 of port B. Finally, connect a push-to-make switch to channel 4 of port B, which connects the channel to ground when you press the switch. If you feel unsure about how to connect these components, have a look at the application notes on the Beehive Technologies website.

Initialization and configuration

Have a look at the startMovie handler in the movie script of testADBIOMovie.dir. There are only three lines of code:

global oADB1 
openXLib "ADB I/O XCMDs 1.0.1"
set oADB1 = new(script "ADB I/O", 1, TRUE)

The three lines should look familiar to you from the testOOMovie.dir and the testXCMDsMovie.dir. The first line creates a global variable called oADB1. The second opens the library with the XCMDs. The third line assigns to the global variable oADB1 a child object based on the ADB I/O parent script by calling the birthing handler. As iUnit parameter we pass 1 (the unit ID number) and as iDEBUGMessage we pass true.

Before we start playing around with the handlers remember that we took the pessimistic approach to debugging messages: we only get to see bad news. No message is good news. Now open the message box, delete any text that litters it, and play the movie. If the message box remains empty that is good news: the birthing handler and the embedded calls to configurePort for port A and port B executed successfully. If you do get an error message you should be able to figure out what is wrong. After all, we carefully passed back the original error message from ConfigureADBIO.

We can still get some kind of confirmation that the birthing handler executed successfully. Let's look at the channel types of port A by typing:

put the pChannelTypesAList of oADB1

The message box responds with:

- [	"digital out", "digital out", "digital out",¬
		"digital out"]

That is good. It is default hardware configuration for port A which we requested in the birthing handler. Now look at the channel types of port B. Type:

put the pChannelTypesBList of oADB1
- [	"digital in", "digital in", "digital in",¬
		"digital in"]

Again, that is what we expected. It is the default hardware configuration for port B which we requested in the birthing handler.

From the electronic components you can infer how we want the ports to be configured. All channels on port A need to be digital outputs. On port B, channels 1 and 2 need to be analog inputs, channel 3 needs to be a digital output and channel 4 a digital input. Since the desired configuration of port A matches the default configuration we do not need to reconfigure port A. The desired configuration of port B differs from the default configuration so we call:

put configurePort (oADB1, "B", 	"analog in", "analog in",¬
															"digital out", "digital in")

The message box should respond with 0 indicating that no errors occurred.

Reading from inputs

We will cover the remaining handlers in the order they occur in the parent script. Let's try reading channel 4 of port B, a digital input.

put getChannelDigital (oADB1, "B", 4)

Providing the jumper on this channel is set to pull-up and you use a push-to-make switch connected to ground, getChannelDigital returns 1 with the switch open and 0 with the switch closed.

Next we try reading an analog input, channel 1 of port B.

put getChannelAnalog (oADB1, 1)

getChannelAnalog should return a value. Now turn the potentiometer connected to this channel and call getChannelAnalog again. Notice how the value of the channel changes. Now try getChannelAnalog with channel 2 of port B. It should behave in the same way.

The last call for reading inputs is GetPort. Let's call it for port B:

put GetPort (oADB1, "B")

Remember that GetPort stores the channel values of a port in either pChannelValuesAList or pChannelValuesBList. Let's have a look at the property pChannelValuesBList.

put the pChannelValuesBList of oADB1

The message box shows a list with four values which correspond with the four channels of port B. We can simply get at each entry in the list through the use of getAt.

put getAt (the pChannelValuesBList of oADB1, 1)

gives us the value of the analog input channel 1 of port B. Likewise,

put getAt (the pChannelValuesBList of oADB1, 2)

gives us the value of the analog input channel 2 of port B. And

put getAt (the pChannelValuesBList of oADB1, 4)

gives us the value of the digital input channel 4 of port B.

List access calls to pChannelValuesAList or pChannelValuesBList must be preceded by a call to getPort. If you do not call getPort first, the lists contain old values which may differ from the current values. Think of getPort and getAt as a combo.

The last of the message handlers for reading from the ADB I/O is getPortDigital. With the components we have got hooked up, we can't really try it out in a meaningful way which would be to convert the binary pattern on a port with four digital input channels to a decimal number. What we can do is show its use with the four digital output channels of port A. First set the port with the XCMD SetADBIO, then read it again with getPortDigital.

SetADBIO 1, "A", 15
put getPortDigital (oADB1, "A")

If you play about with these commands, you will find that you can read back from the port the value you just wrote to it. Note that if you use getPortDigital with a port that has one or more channels set to analog in, you will not get an error message. An analog input channel will have the value 0. While that is not useful in itself, it allows you to work out the values of any remaining digital channels.

Writing to outputs

Luckily, writing to channels and ports is a bit easier than reading from them. Let's write to channel 3 of port B, a digital output. Typing

put setChannelDigital (oADB1, "B", 3, 0)

turns off the LED.

put setChannelDigital (oADB1, "B", 3, 1)

turns on the LED. Try using setChannelDigital on port A.

Next, try setChannelDigitalDuration. Remember, this call only works on port A. Let's try it on channel 1 of port A.

put setChannelDigitalDuration (oADB1, 1, 1, 10)

This turns on the LED for 10 x 0.1 = 1 second after which the LED turns off.

Finally, there is setPort for setting a whole port in one go. Enter

put setPort (oADB1, "A", 15)

This turns on all LEDs on port A, since the binary equivalent of the decimal integer 15 is 1111.

Play around a bit more with all these handlers. Try forcing some errors. For example, try writing to an input channel or reading from an output channel. Error messages will inform you if you ask for the impossible.

Trouble Shooting

Strange bugs but no error messages? Or error messages which completely puzzle you? This section lists some possible causes.

Software

  1. Have you enclosed the parameters in brackets? All our message handlers are in fact functions, that is, they return values. Lingo expects functions to have their parameters enclosed in brackets.
  2. Do the values you pass into the handler match the handler's function prototype? If they do not, confusing error messages may result.
  3. Are all child objects of the ADB I/O parent script declared as globals? If you expect a child object to stay around in memory, while it is actually a local child object which goes out of scope at the end of the handler in which it was created, you can get very confusing errors. If in doubt try using put to print the value of the child object to the message box. If you get void you are half way to finding the bug.

Hardware

  1. Is your port A channel correctly equipped with a relay or an opto-isolator? If you need digital output on a port A channel you need to have a relay installed, if you need digital input you need an opto-isolator installed. Obviously, if you have a relay installed on a channel and try to configure it as a digital input, or have an opto-isolator installed and try to configure it as a digital output, that channel will not work correctly. But as the processor in the ADB I/O cannot distinguish between relays and opto-isolators ConfigureADBIO will not return an error string.
  2. Is your port B channel jumper correctly set to pull-up, pull-down or open circuit? If you need digital input or digital output on a channel of port B, you need to set the jumper to pull-up or pull-down. If you need analog input you need to disable the jumper (open circuit). If you try analog input with pull-up or pull-down you will get erroneous values, but you will not receive any error message.

Enhancements

To keep the number of files to a minimum I made the testADBI/OMovie.dir self-contained. You may find it convenient to put the parent script in an external cast, so that you can easily include it with different movies.

Another point which you may have noticed is that there is no analog output available on the ADB I/O. However, Application Note 6 on the Beehive Technologies website documents how to use both port A and B to drive four digital potentiometers to create four analog outputs. If you have a need for analog outputs you may wish to extend the ADB I/O parent script with a setChannelAnalog handler which considers each digital potentiometer to be a channel. The prototype would look like:

on setChannelAnalog me, iChannel, iChannelValue

Conclusions

In this article we used object-oriented Lingo to create a friendly and clean interface between the ADB I/O and your Director movie. Object-oriented Lingo allowed us to hide set-up details in the parent script's birthing handler and get rid of global variables through the use of property variables. If you had never done anything like this before, you don't know half how spoiled you are. Director and the ADB I/O form a truly Mac-worthy couple: this is data acquisition 'for the rest of us'. Have fun!

References


Tom spent a large part of his life trying to work out which letter groupings minimize confusion when spelling out his name on the phone. Now that he has found that Djaj-adi-nin-grat leads to significantly better results than Dja-ja-di-nin-grat, he can fully concentrate on his research on interaction design at Delft University. Since he does not own any cats, you won't find any pictures of them on http://www.io.tudelft.nl/research/vormtheorie/djajadiningrat.html.

 
AAPL
$105.01
Apple Inc.
+0.18
MSFT
$45.45
Microsoft Corpora
+0.43
GOOG
$539.20
Google Inc.
-4.79

MacTech Search:
Community Search:

Software Updates via MacUpdate

BusyCal 2.6.3 - Powerful calendar app wi...
BusyCal is an award-winning desktop calendar that combines personal productivity features for individuals with powerful calendar sharing capabilities for families and workgroups. BusyCal's unique... Read more
calibre 2.7 - Complete e-library managem...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital... Read more
Skitch 2.7.3 - Take screenshots, annotat...
With Skitch, taking, annotating, and sharing screenshots or images is as fun as it is simple.Communicate and collaborate with images using Skitch and its intuitive, engaging drawing and annotating... Read more
Delicious Library 3.3.2 - Import, browse...
Delicious Library allows you to import, browse, and share all your books, movies, music, and video games with Delicious Library. Run your very own library from your home or office using our... Read more
Art Text 2.4.8 - Create high quality hea...
Art Text is an OS X application for creating high quality textual graphics, headings, logos, icons, Web site elements, and buttons. Thanks to multi-layer support, creating complex graphics is no... Read more
Live Interior 3D Pro 2.9.6 - Powerful an...
Live Interior 3D Pro is a powerful yet very intuitive interior designing application. View Video Tutorials It has every feature of Live Interior 3D Standard, plus some exclusive ones: Create multi... Read more
The Hit List 1.1.7 - Advanced reminder a...
The Hit List manages the daily chaos of your modern life. It's easy to learn - it's as easy as making lists. And it's powerful enough to let you plan, then forget, then act when the time is right.... Read more
jAlbum Pro 12.2.4 - Organize your digita...
jAlbum Pro has all the features you love in jAlbum, but comes with a commercial license. With jAlbum, you can create gorgeous custom photo galleries for the Web without writing a line of code!... Read more
jAlbum 12.2.4 - Create custom photo gall...
With jAlbum, you can create gorgeous custom photo galleries for the Web without writing a line of code! Beginner-friendly, with pro results Simply drag and drop photos into groups, choose a design... Read more
ExpanDrive 4.1.7 - Access remote files o...
ExpanDrive builds cloud storage in every application, acts just like a USB drive plugged into your Mac. With ExpanDrive, you can securely access any remote file server directly from the Finder or... Read more

Latest Forum Discussions

See All

nPlayer looks to be the ultimate choice...
Developed by Newin Inc, nPlayer may seem like your standard video player – but is aiming to be the best in its field by providing high quality video play performance and support for a huge number of video formats and codecs. User reviews include... | Read more »
Fighting Fantasy: Caverns of the Snow Wi...
Fighting Fantasy: Caverns of the Snow Witch Review By Jennifer Allen on October 24th, 2014 Our Rating: :: CLASSY STORYTELLINGUniversal App - Designed for iPhone and iPad Fighting Fantasy: Caverns of the Snow Witch is a sterling... | Read more »
A Few Days Left (Games)
A Few Days Left 1.01 Device: iOS Universal Category: Games Price: $3.99, Version: 1.01 (iTunes) Description: Screenshots are in compliance to App Store's 4+ age rating! Please see App Preview for real game play! **Important: Make... | Read more »
Toca Boo (Education)
Toca Boo 1.0.2 Device: iOS Universal Category: Education Price: $2.99, Version: 1.0.2 (iTunes) Description: BOO! Did I scare you!? My name is Bonnie and my family loves to spook! Do you want to scare them back? Follow me and I'll... | Read more »
Intuon (Games)
Intuon 1.1 Device: iOS Universal Category: Games Price: $.99, Version: 1.1 (iTunes) Description: Join the battle with your intuition in a new hardcore game Intuon! How well do you trust your intuition? Can you find a needle in a... | Read more »
Ravenous Rampage (Games)
Ravenous Rampage 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: | Read more »
Partia 2 (Games)
Partia 2 1.0.1 Device: iOS Universal Category: Games Price: $5.99, Version: 1.0.1 (iTunes) Description: Partia 2 is a SRPG (Strategy Role-playing) video game inspired by Fire Emblem and Tear Ring Saga series. In a high fantasy... | Read more »
Puzzle to the Center of the Earth Review
Puzzle to the Center of the Earth Review By Campbell Bird on October 23rd, 2014 Our Rating: :: SPELUNKING PUZZLESUniversal App - Designed for iPhone and iPad Do some puzzles to make some platforms in this smart and fun free-to-play... | Read more »
Puzzle to the Center of the Earth – Tips...
Dig this: Would you like to know what we thought of all this puzzling-around the deep recesses of the planet? Check out our Puzzle to the Center of the Earth review! Puzzle to the Center of the Earth is a surprisingly deep and challenging puzzle... | Read more »
Sleep Attack TD Review
Sleep Attack TD Review By Jennifer Allen on October 23rd, 2014 Our Rating: :: A TRUE TWISTUniversal App - Designed for iPhone and iPad Sleep Attack TD is a tower defense game with a difference – you can rotate the layout – and it’s... | Read more »

Price Scanner via MacPrices.net

13-inch 2.5GHz MacBook Pro on sale for $949,...
Best Buy has the 13″ 2.5GHz MacBook Pro available for $949.99 on their online store. Choose free shipping or free instant local store pickup (if available). Their price is $150 off MSRP. Price is... Read more
Save up to $125 on Retina MacBook Pros
B&H Photo has the new 2014 13″ and 15″ Retina MacBook Pros on sale for up to $125 off MSRP. Shipping is free, and B&H charges NY sales tax only. They’ll also include free copies of Parallels... Read more
Apple refurbished Time Capsules available sta...
The Apple Store has certified refurbished Time Capsules available for up to $60 off MSRP. Apple’s one-year warranty is included with each Time Capsule, and shipping is free: - 2TB Time Capsule: $255... Read more
Textilus New Word, Notes and PDF Processor fo...
Textilus is new word-crunching, notes, and PDF processor designed exclusively for the iPad. I haven’t had time to thoroughly check it out yet, but it looks great and early reviews are positive.... Read more
WD My Passport Pro Bus-Powered Thunderbolt RA...
WD’s My Passport Pro RAID solution is powered by an integrated Thunderbolt cable for true portability and speeds as high as 233 MB/s. HighlightsOverviewSpecifications Transfer, Back Up And Edit In... Read more
Save with Best Buy’s College Student Deals
Take an additional $50 off all MacBooks and iMacs at Best Buy Online with their College Students Deals Savings, valid through November 1st. Anyone with a valid .EDU email address can take advantage... Read more
iPad Air 2 & iPad mini 3 Best Tablets Yet...
The new iPads turned out to be pretty much everything I’d been hoping for and more than I’d expected.”More” particularly in terms of a drinking-from-a-firehose choice of models and configurations,... Read more
Drafts 4 Reinvents iOS Productivity App
N Richland Hills, Texas based Agile Tortoise has announced the release of Drafts 4 for iPhone and iPad. Drafts is a quick capture note taking app with flexible output actions. Drafts 4 scales from... Read more
AT&T accepting preorders for new iPads fo...
AT&T Wireless is accepting preorders for the new iPad Air 2 and iPad mini 3, cellular models, for $100 off MSRP with a 2-year service agreement: - 16GB iPad Air 2 WiFi + Cellular: $529.99 - 64GB... Read more
Apple offering refurbished Mac Pros for up to...
The Apple Store is offering Apple Certified Refurbished 2013 Mac Pros for up to $600 off the cost of new models. An Apple one-year warranty is included with each Mac Pro, and shipping is free. The... Read more

Jobs Board

*Apple* Solutions Consultant - Apple Inc. (U...
…important role that the ASC serves is that of providing an excellent Apple Customer Experience. Responsibilities include: * Promoting Apple products and solutions Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Project Manager / Business Analyst, WW *Appl...
…a senior project manager / business analyst to work within our Worldwide Apple Fulfillment Operations and the Business Process Re-engineering team. This role will work Read more
*Apple* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.