A real "toy" application
Charles holds a B.S. from Miami University. Douglas is a professor of systems analysis at Miami University in Oxford, Ohio. They can be contacted at [email protected] usa.net and [email protected], respectively.
The Lego Dacta Control Lab
In addition to the familiar building sets seen on toy shelves, Lego Dacta provides building sets for a range of machines used in science and technology education. At the low end are the Lego technic sets; at the high end is the Lego Dacta Control Lab, which enables construction of computer-controlled machines. Among the type of control systems you can build with the Control Lab are greenhouses that automatically regulate their temperature using a ventilation system, a vending machine, robotic arms, a PC plotter, and an automobile on a dynamometer (see Figure 1). Sophisticated simulations of real-world systems have also been created, such as an automobile manufacturing production line (http://www.gang.umass.edu/user/shen/lego/index.html) and a variety of robots (http://www.pycckuu.umd.edu/robots/ index.html). The Fischertechnik Corp. produces a product similar to the Legos that have also been used in teaching environments (http://www.cs.utah.edu/~cs451/).
In addition to the usual set of Lego blocks, the Control Lab includes pieces from the technic sets for building machines (gears, pneumatics, pulleys, and so on) and computer-controllable devices -- lamps, motors, sound elements, touch sensors, temperature sensors, light sensors, and angle sensors. The devices are connected to a controller, which in turn interfaces with a serial port on a personal computer to permit software control of the devices. The controller is capable of controlling eight input and eight output devices. The PC and the controller communicate via a protocol developed by Lego.
The Lego devices (motors, sensors, and the like) can be combined to build sophisticated machines, such as the automobile on a dynamometer (Figure 1) or the simulated manufacturing line. Software, written on a PC or Macintosh, controls the interaction of the Lego devices.
Lego ships the Control Lab with a unique version of the Logo programming language for writing the control software for a Lego system. Although satisfactory for illustrating programming concepts, more sophisticated users may wish to use other programming languages for control purposes. (For more information on Logo, see "Lego/Logo and Electronic Bricks: Creating a Scienceland for Children," by Fred Martin and Mitchel Resnick, Advanced Educational Technologies for Mathematics and Science, edited by David L. Ferguson, Springer-Verlag, 1993.)
We initially selected the Control Lab for use as a teaching environment for introductory object-oriented programming classes at Miami University. Students could write software objects corresponding to the real Lego devices, then combine these objects to control a system built with Legos.
Although object-oriented programming is well suited for creating software objects that correspond to the Lego devices, considerable programming is required to support the Lego controller's communication protocol. The protocol operates over the serial port of a PC, requiring the software to send messages to the Lego controller to turn devices on or off, control speeds, and receive messages from the controller indicating changes in the status of sensors. A further complication of the protocol is the requirement that "alive" messages must be sent to the controller at established intervals.
To illustrate the concepts of object-oriented programming, we did not want students to become bogged down in the details of the communication protocol. As a first attempt at hiding the details of the protocol, we developed a class library that supported the transmission of messages between the PC and the controller, and handling the alive messages. The usefulness of the resulting software is limited insofar:
- It is written using 16-bit Microsoft Visual C++ serial I/O routines that have been superseded by Microsoft's 32-bit operating systems and compilers.
- It requires programmers to use Visual C++.
- It requires the use of a skeleton application with code to handle windows and communication-related events. Without this skeleton, you must recreate much of the serial communication code.
Given these shortcomings, we decided to create a software component that would allow programmers to control the Lego system, but would also completely encapsulate the serial communication routines and allow the use of a variety of host programming languages.
Lego Interface Protocol
The Lego controller in Figure 1 is the interface between the PC's serial port and the Lego devices. The controller has eight output ports (labeled A through H) and eight input ports (labeled 1 through 8) for connecting Lego devices to the system.
There are three kinds of devices that can be connected to the eight output ports -- a motor, lamp, and sound device. Commands can be sent to these devices by writing a two-byte command to the Lego controller via the serial port. The first byte is a binary command code (see Table 1). The second byte specifies to which output port the command applies. If the least significant bit is 1, then the command applies to port A, if the most significant bit is 1, then it applies to port H, and so on. Table 1 shows the commands available for the output ports and the effects they have on each kind of output device.
There are four kinds of devices that can be connected to the input ports -- an on/off switch, thermometer, angle sensor, and light sensor. Input from the eight input ports comes in the form of 19-byte frames constantly being sent several times per second from the controller into the serial port. Table 2 shows the contents of each frame.
The sum of the values of the 19 bytes must add up to 0xFF; otherwise, an error has occurred in communication. The interpretation of the two-byte status code corresponding to a port in each frame depends upon the kind of device connected to the port. It is impossible to know, from the input, exactly what kind of device is attached to a certain port. Thus, the software must process the data from each port for each kind of input device. The interpretation of the two-byte status codes for each device is:
- For the touch sensor, if the first byte is 0x2E, then the switch is pressed in; otherwise it is not.
- For the light sensor, the rightmost 12 bits are a value that describes the intensity of light received by the device.
- For the thermometer, the rightmost 12 bits can be converted into an approximation of the Fahrenheit temperature by using the formula T=(760-value)/4.4+32.
- For the angle sensor, the third bit from the right is a Boolean value specifying the direction of rotation of the sensor. The rightmost two bytes specify the amount of change in the angle since the last frame. This value, divided by 16, gives the fraction of a circle that the sensor rotated through since the last update. By remembering the last angle and using the direction and rate of change, it is possible to track the angle of rotation.
To start communication with the Lego controller, the software must send the string p\0###Do you byte, when I knock?$$$, then wait until it receives the reply string ###Just a bit off the block!$$$.
To maintain the connection with the controller, whenever commands are not being sent, the software must send the value 0x02 every two seconds.
The ActiveX Control
The Lego ActiveX control is implemented using two threads of execution. Multiple threads were used so that processing of method calls and reading from the serial port could be performed concurrently.
The primary thread executes the control's standard message-handling program loop. It receives messages from the host language and executes the appropriate handler functions. From this thread, all write operations to the Lego controller are performed. This thread also establishes a timer function called by the operating system every two seconds to write the "alive" message to the controller.
The primary thread starts the second thread when the connection is made with the controller. It is used to read the frames of information from the controller and dispatch messages to the primary thread when the states of the Lego devices change. It should be noted that, because of the way ActiveX controls interface with their containers, it is not possible for this second thread to fire events to the host language directly (see "FIREEV.EXE Fires Events from a Second Thread," Knowledgebase Article ID: Q157437, Microsoft Corp. 1997). Instead, the secondary thread must send a message to the primary thread, which can then fire a Windows event in the host program.
Figure 2 illustrates the design of the control and its relationship to the host language.
The following methods were implemented in the control and can be called from a host language.
- Boolean StartLegos(). Opens the serial port and connects with the controller. Return Value: True if the function succeeded, otherwise False.
- Boolean StopLegos(). Disconnects from the controller and closes the serial port. Return Value: True if the function succeeded, otherwise False.
- Boolean TurnOn(Integer port). Sends the on command to the device connected to the specified port. The valid values for port are 1-8, corresponding to output ports A-H. Return Value: True if the function succeeded, otherwise False.
- Boolean TurnOff(Integer port). Sends the off command to the device connected to the specified port. The valid values for port are 1-8, corresponding to output ports A-H. Return Value: True if the function succeeded, otherwise False.
- Boolean Reverse(Integer port). Sends the reverse direction command to the device connected to the specified port. The valid values for port are 1-8, corresponding to output ports A-H. Return Value: True if the function succeeded, otherwise False.
- Boolean SetDirection(Integer port, Boolean direction). Sets the direction of the device connected to the specified port to the specified direction. The valid values for port are 1-8, corresponding to output ports A-H. Return Value: True if the function succeeded, otherwise False.
- Boolean SetPower(Integer port, Integer power). Sets the power of the device connected to the specified port to the specified power. The valid values for port are 1-8, corresponding to output ports A-H. The valid values for power are 0-8. Return Value: True if the function succeeded, otherwise False.
- Integer GetAngle(Integer port). Returns the value received by the angle sensor connected to the specified port. The valid values for port are 1-8 corresponding to input ports 1-8.
- Double GetTemperature(Integer port). Returns the value received by the thermometer or light sensor connected to the specified port. The valid values for port are 1-8 corresponding to input ports 1-8.
- Boolean IsPressed(Integer port). Returns the state of the switch connected to the specified port. The valid values for port are 1-8 corresponding to input ports 1-8.
The control has one property that can be accessed and changed by a host language: A string named SerialPort. SerialPort's value specifies the COM port (1 or 2) to which the Lego controller is connected. This value is stored in the Windows 95/NT system registry.
Events are fired by the control to the host language (Figure 3 shows the availability of the events using Visual Basic 4.0) when a change in the value is received from an input port. The following events are generated by the Lego control.
- OnAngleChange(Integer port, Integer angle). Called when the value from an angle sensor changes. The parameter port will be a value 1-8 corresponding to the output port the angle sensor is connected to. The value of the parameter angle will be a value 0-15 indicating the current position of the angle sensor, in 1/16th of a circle.
- OnPress(Integer port). Called when a switch connected to the port specified by the parameter port is pressed. The parameter port will be a value 1-8 corresponding to the output port to which the switch is connected.
- OnRelease(Integer port). Called when a switch connected to the port specified by the parameter port is released. The parameter port will be a value 1-8 corresponding to the output port to which the switch is connected.
- OnTemperatureChange(Integer port, Double temperature). Called when the temperature sensed by a thermometer or light sensor connected to the port specified by the parameter port changes. The parameter port will be a value 1-8 corresponding to the output port to which the device is connected. The value of temperature will be the temperature sensed by the thermometer or the intensity sensed by the light sensor.
We developed the control using Microsoft Visual C++ 4.0. The OLE Control Wizard was used to generate the skeleton control and ClassWizard was then used to add the methods, properties, and events. We then added code to make the control multithreaded and the timer function necessary to send "alive" messages.
Figure 4 shows an application written in Visual Basic 4.0 that demonstrates all the features of the Lego ActiveX control. The procedures in Listing One are used when buttons on the form are pressed to call methods of the Lego control.
COM and ActiveX provide an easy way to create modular programs in which the details of a complex task are hidden from application programmers. This is a powerful ability and will lead to more software comprised of components combined in specific ways to accomplish certain tasks. In the case of the Lego project, programmers using the control need not know how to open a serial port, create threads for reading, or send "alive" messages every two seconds. In fact, they don't even need to know that those things are going on behind the scenes. Instead, all they need to worry about is their specific application's requirements.
Much of the Lego serial protocol is based on research by Andrew Carol ([email protected]). This project builds upon work completed at Miami University in 1996 by a group comprised of Brian Franke, Shelly Kerschner, Julie Lawrence, Afshin Nasirian, and Charlie Huddleston. This group wrote the object-oriented framework and skeleton program that would allow programmers to write programs that interface with the Lego Controller.
For More Information
Lego Systems Inc.
555 Taylor Road
Enfield, CT 06083-1600
Private Sub StartButton_Click() Call Lego1.StartLegos End Sub Private Sub StopButton_Click() Call Lego1.StopLegos End Sub Private Sub OnButton_Click() Call Lego1.TurnOn(port) End Sub Private Sub OffButton_Click() Call Lego1.TurnOff(port) End Sub Private Sub DirectionButton_Click() Dim d As Boolean If Direction = 1 Then d = True Else d = False Call Lego1.SetDirection(port, d) End Sub Private Sub ReverseButton_Click() Call Lego1.Reverse(port) End Sub Private Sub PowerButton_Click() Call Lego1.SetPower(port, Power) End Sub The following procedures handle events fired by the Lego Control: Private Sub Lego1_OnPress(ByVal port As Integer) Press(port).Value = 1 End Sub Private Sub Lego1_OnRelease(ByVal port As Integer) Press(port).Value = 0 End Sub Private Sub Lego1_OnTemperatureChange(ByVal port As Integer, ByVal temperature As Double) Temp(port) = temperature End Sub Private Sub Lego1_OnAngleChange(ByVal port As Integer, ByVal angle As Integer) AngleBox(port) = angle End Sub
Copyright © 1998, Dr. Dobb's Journal