LegOS Research

Ryan Simmons

CIS 690, Kansas State University

Dr. Neilsen

Spring 2002


Introduction

My research has primary involved legOS, an “operating system” for Lego Mindstorms computers to use. It replaces the firmware given by Lego and allows for C-like programs to be cross-compiled to the Lego brick’s Hitachi chip. The homepage for this project is at: http://legos.sourceforge.net/. This software allows for a high degree of control of what the Lego computer, or commonly referred to as the RCX, does. Threads, priority scheduling, and networking are some of the interesting features you get from this powerful addition.

In the spring of 2002, my research included several different areas and findings related to legOS version 0.2.5. My findings will be presented in four different sections:

1. A fix for the generic Robotics Invention System (RIS) software

2. How to get legOS to work with a USB Infrared tower in Linux

3. Some documentation and specification information I have compiled relating to the Lego network protocol (LNP)

4. Some sample code and discussion of LNP program implementations (including some multi-thread programs)

Fix for Robotics Invention System Software

The Lego software provided in called the Robotics Invention System (RIS). It has a problem where the RIS launcher will load after every reboot of the system. To remedy this I called Lego technical support. I was informed to do the following:

1. Use Microsoft’s regedit and find the key HKEY_LOCAL_MACHINE -> SOFTWARE -> Microsoft -> Windows -> Current Version -> Run.

2. Under the section labeled “Run,” find RIS2PostReboot. Under RIS2PostReboot remove its contents. This includes the path and executable.

How to get legOS to work with the USB IR Tower in Linux

LegOS Installation notes

This installation has been tested on Redhat Linux 7.2 with the default kernel and 2.4.18. It is not an extremely easy process and it is not as fast the serial version of the tower. I believe it is because these drivers are a kind of “hack” to get it to work. Also, the software to send Lego network protocol messages does not currently work with the USB version of the tower.

These instructions allow you to get a Lego USB IR tower to work with Bret Thaeler's patch and drivers. If you have any problems, feel free to email me at . More information about testing legOS can be found at the legOS homepage at http://legos.sourceforge.net and the legOS newsgroup at http://www.lugnet.com/robotics/rcx/legos/.

I have done most this as root, but there are some cases where you would not need this access. To be safe, it is easier to be root for everything because of the way the legOS software accesses hardware.

Installation Instructions

Note: Commands that should be executed are in italics.

(1) Files and packages needed

Cross Compiler Resources:

http://legos.sourceforge.net/files/linux/redhat/

· rcx-binutils-2.9.5.0.22-1.i386.rpm

· rcx-egcs-1.1.2-1.i386.rpm

· rcx-egcs-c++-1.1.2-1.i386.rpm

legOS source (get version 0.2.5):

http://legos.sourceforge.net/

USB Patch:

http://members.aol.com/bthaeler/linux_driver_v02.driver.tar.gz

(2) Install the RPMs

To install be sure to put them all on one line because of dependencies. Like this:

rpm -ivh rcx-binutils-2.9.5.0.22-1.i386.rpm rcx-egcs-1.1.2-1.i386.rpm

rcx-egcs-c++-1.1.2-1.i386.rpm

(3) Install legOS

I recommend installing in /usr/local and I will refer to installations this directory

as [base] throughout the rest of the file in case you decide otherwise.

(5) Patching legOS (for USB support)

From your [base] directory:

1. untar the linux_driver

2. run "patch -p1 < linux_driver/legOS.0.2.5.patch"

(6) Fixing the Makefiles, etc.

In your [base] directory:

Edit Makefile.common -- under linux/solaris section change

TOOL_PREFIX = .... to TOOL_PREFIX = /usr/bin/h8300-hitachi-hms-

In [base]/boot:

Edit .depend -- change occurences of /home/louie/project/legOS/kernel to [base]/legOS

In Emacs this is easy to do: hit ESC then %; type the expression being replaced;

type the expression being inserted; finally hit ! to replace all occurrences

Or you can use a regular expression.

(7) Driver Compilation

Note: If you are using the default Redhat 7.2 kernel, you can skip parts A and B.

Also: If you are using an older kernel than specified

(such as the default Red Hat 7.2 kernel);

You will need to comment out or remove the last line in usb_lego_driver.c.

This line says MODULE_LICENSE("GPL"); . It will not compile on some kernels

with this in. The file is located in linux_driver\usb_lego.

A. Make sure you have USB support compiled as a module for your kernel.

B. This links your kernel source to your current kernel.

Otherwise the usb_lego module will not properly compile.

Replace /usr/src/linux with your own directory if you store your source elsewhere.

Go into /usr/include rm -rf linux

Go into /usr/asm rm -rf asm

Execute ln -s /usr/src/linux/include/linux linux

Execute ln -s /usr/src/linux/include/asm asm

C. In [base]/legOS/linux_driver/usb_lego:

Edit Makefile -- change MODULE_DIR to point to your module directory.

This is likely /lib/modules/[kernel version]/kernel/drivers/usb

D. As root: run mknod --mode=a=rw /dev/usb/lego0 c 180 200

This creates a location to the device.

E. Finally, from [base]/legOS/linux_driver/usb_lego:

run make; make install

When you run make install, make sure the modprobe is successful.

Running a Test program

You can't use the default firmdl3 (firmware downloader) or dll (dynamic linker) to coordinate with the

USB tower. You have to go into their individual directories and compile them.

You have to edit the Makefile.common in [base]/legOS/util:

Change LINUX_USB = ... to LINUX_USB = [base]/legOS/linux_driver/usb_lego

Then, you can run the Makefile in the [base]/legOS/util/firmdl to compile firmdl.

Make sure you have cleared the memory in the RCX block (lego robot computer). Removing one battery and then replacing it can do this. But, sometimes you have to let it sit without a battery for a while to clear it.

Once you have cleared the RCX, you can "flash" legOS onto it.

To be sure the legOS tower is properly installed either reboot or pull the USB connection out and put it back in. If the green light goes on, it should work.

From [base]/legOS/util/firmdl:

execute ./firmdl3 ../../boot/legOS.srec

You should see the legOS firmware test fast downloading and then it will install the firmware in a few seconds. The USB tower should have a green light go on. If this works you should have the USB successfully working. Sometimes the connection will not work right or the firmware will not download properly. In these cases, unplug the tower and plug it back in.

Also, make sure the modules are loaded after you reboot. On my machine, sometimes I receive a notice from Red Hat that the module is not fulfilling all dependencies. This is an error you can ignore because the module is not perfectly written, but it works. If you are having problems, you might run “lsmod” to make sure the USB IR tower driver is loaded. If not, run “insmod” as directed above.

Documentation on Lego Network Protocol

The Lego network protocol (LNP) specification provides powerful features for communication between RCXs, RCXs to PC, and PC to RCXs. On the PC side the LNP runs as a daemon for interpreting and sending messages. legOS has LNP features built in, the only requirement is that the LNP libraries must be loaded in the RCX code. Like many networking protocols, LNP was designed with a layering model. This table shows the layers implemented by the LNP:

Table from: http://www.informatik.uni-kiel.de/inf/von-Hanxleden/teaching/WS2001-02/Verteilte_Echtzeitsysteme/Lectures/Lecture_14.pdf

Layers 0 and 1 are handled at low-level RCX and assembly language code. Layers 2, integrity, and 3, addressing, are the ones developers will be interested in. RCX and PC code can directly interface with these layers at a high level. This is typically done with C code.

The integrity layer sends generic messages without identifying ports or the receiver. It is simpler than the addressing layer and it requires 40% less overhead than addressing [2]. This is what the packet format for the integrity layer looks like:

Figure from: http://www.efd.lth.se/~d94tn/exjobb/rapport.pdf

F0 is a packet header for integrity packets, LEN gives the length of the packet data, IDATA is the actual data desired to be transmitted, and the checksum tries to give the protocol some reliability [1]. Reliability is not guaranteed with LNP, erroneous packets are discarded [2].

In the addressing layer LNP code can be directed to specific ports and receivers can be identified. This is what the packet format for the integrity layer looks like:

Figure from: http://www.efd.lth.se/~d94tn/exjobb/rapport.pdf

F1 is a packet header for addressing packets, LEN gives the length of the data, DEST gives the receiver’s address, and SRC gives the sender’s address. The addressing layer is especially useful for coordinating multiple LNP clients. The ability to specific receiver and know who the sender is allows for more flexibility.

This is a basic overview of the structure and specification of the LNP. In the next section, I will give some examples of code and templates that could be used in implementing LNP applications.

Creating LNP Applications

In this section I will present several code examples and explain the reasoning and implementation behind them. This section only works with the SERIAL version of the Lego IR tower. Also, I run all of these programs as “root” to work with the lnpd code. Some users will not need to be “root”; I have read that this depends on your motherboard’s chipset. I was unable to patch the LNP PC code for USB, but I believe the LNP RCX code should work. Attempting to patch the USB code proved to be very difficult. I will focus primarily on PC to RCX communication since RCX to PC is very similar and easier. Most of my ideas for this were found at the legOS HOWTO for LNP at: http://legos.sourceforge.net/HOWTO/x405.html.

The LNP packages must be downloaded from http://legos.sourceforge.net/files/linux/LNPD/ and get the lnpd+liblnp.tgz package. Untar the package by typing “tar xvzf lnpd+liblnp.tgz” in /usr/local/ or a directory of your choice.

Applications developed for transmitting and receiving LNP messages on the PC side will reside in the /usr/local/lnpd+liblnp/applications directory. I will refer to the sample program as pc_lnp.c. Edit the Makefile to include the name of your new program for PC to RCX communication. Add this program to the Makefile by adding (changes are in bold):

TARGET2 = pc_lnp

SRCS2 = pc_lnp.c

OBJS2=$(SRCS2:.c=.o)

AND

clean:

rm -f *.o *~ *.bak $(TARGET1) $(TARGET2)

depend:

$(MAKEDEPEND) $(SRCS1) $(SRCS2) > .depend

AND

$(TARGET2): $(OBJS2)

gcc -o $(TARGET2) $(OBJS2) $(LDFLAGS) $(LIBS)

These changes should follow the same form as the original Makefile. For example, insert the $(TARGET2) section after the $(TARGET1) section.

Applications developed for RCX LNP code will reside in your legOS home directory (probably /usr/local). Create a subdirectory called pgrms (or something else) in your legOS home directory. Copy the Makefile from /usr/local/legOS/demo to /usr/local/legOS/pgrms. I will refer to the sample program as rcx_lnp.c. Edit the Makefile to include the name of your new file by changing the section “PROGRAMS=” to equal the name of your program: rcx-lnp.c.

I have developed a template for sending string messages from the PC to the RCX. And I have developed another one for the RCX to interpret these messages. I am presenting these examples using the integrity layer for clarity and simplicity. I will provide some sample information on how these templates can be converted into addressing mode. Also, I am providing the information for sending strings only. I feel that strings will be the most common interface for sending data to the RCX, but additional information can be found at the legOS HOWTO on the LNP: http://legos.sourceforge.net/HOWTO/x405.html on how to work with other data types like integers.

My example is based on the legOS HOWTO’s template, but I clarify sections and correct some of their errors. This is an efficient multi-threaded solution on the RCX side, so I will also explain how to implement threads in legOS.

These templates provide the foundation for allowing the PC to remotely control the RCX. Reliability features can be added by having the RCX send verification to the PC that it received a command. LNP features for the RCX to send to the PC are the same as the PC to the RCX, but the lnp_init command is not needed for RCX’s to send messages.

Template for PC Implementation

This is the source code for pc_lnp.c:

#include "liblnp.h"

/**

* Function to send the string to the RCX

* from the PC.

*/

void printString(char *s)

{

int len, result;

char *buffer;

len = strlen(s) + 1;

buffer = malloc(len + 2);

buffer[0] = 's'; //first char 's' denotes a string datatype

memcpy(buffer + 1, s, len + 1);

result = lnp_integrity_write(buffer,len + 2);

free(buffer);

}

int main(int argc, char **argv) {

if ( lnp_init(0,0,0,0,0) ) { //Initialize the PC for LNP

perror("lnp_init");

exit(1);

}

printString("Data you wish to send");

}

This code sets the PC up for transmitting a message to the RCX. The ‘s’ char at the beginning denotes that the datatype is a string. The string is then written in integrity mode. For addressing mode you might want to make definitions like:

#define DEST_HOST 0x8

#define DEST_PORT 0x7

#define DEST_PORT_2 0x8

#define DEST_ADDR ( DEST_HOST << 4 | DEST_PORT )

#define DEST_ADDR_2 ( DEST_HOST << 4 | DEST_PORT_2 )

to define what ports and hosts you want to use. On the RCX side you would want similar definitions, but you would want to “flip” the destinations and port numbers.

Template for RCX Implementation

This is the source code for rcx_lnp.c:

#include <conio.h>

#include <unistd.h>

#include <lnp.h>

/**

* The template for the RCX to receive LNP data.

*/

int gNewData = 0; //flag for new data

char gMessagingData[256]; //data to send

int gDataLength; //length of data to sen

/**

* Handles the incoming packets.

*/

void packet_handler(const unsigned char* data,unsigned char length,

unsigned char src) {

if(gNewData == 0) {

memcpy(gMessagingData, data, length);

gDataLength = length;

gNewData = 1;

}

}

/**

* When the thread awakens, return that there is new data

* available.

*/

wakeup_t WaitForData(wakeup_t data) {

return gNewData;

}

/**

* Thread to handle incoming packets.

*/

int PacketWatcher(int argc, char **argv) {

while(1) {

wait_event(&WaitForData, 0); //wait for a message

switch(gMessagingData[0]) { //check the first char for message type 's'=string 'i'=int