It's probably best to start with the
build instructions,
which explain how to build and install the “default”
test agent for the Lego Mindstorms RCX,
bumper.soar
.
Once you've verified that this agent works as expected, you're
ready to write a new agent for the RCX. The steps to create a new
agent are as follows:
export
command to write a new version of the
agent.inc
file,
legosoar
target for the RCX.
legosoar
firmware to
the RCX using the LegOS firmdl3
command.
For example, say we wanted to install the brain-dead agent
implemented in
drive.soar
on the RCX. First, we start the tclsh
host environment
and load the productions by source
-ing the
drive.soar
file:
$ TCLLIBPATH=`pwd` tclsh % source ../tinysoar/tests/drive.soar Firing: Retracting: compiled: propose*drive compiled: implement*drive compiled: drive*reconsider %
Next, we export
this file, replacing
tinysoar/agent.inc
:
% export ../tinysoar/agent.inc %
Now we exit the host environment, and rebuild the
legosoar
target:
% exit $ cd ../h8300-hitachi-hms $ make legosoar ...Build output... $
And finally, we download the new firmware to the RCX:
$ /usr/src/legOS/util/firmdl3 --slow legosoar $
Note that you'll need to pull the batteries out of the RCX each time before downloading a new TinySoar agent. This is because a TinySoar agent is firmware, and isn't smart enough to notice that new firmware is trying to be downloaded on top of it. (I really ought to fix this.)
If all goes well, the motors should start to unconditionally run when you press the Run button on the RCX.
The TinySoar runtime for the Lego Mindstorms RCX allows a TinySoar
agent to interact with the RCX's sensors via the
^input-link
, and control the RCX's motors with the
^output-link
.
Specifically, each of the three sensors are sampled once per
elaboration. Each sensor's value is converted to an integer value
between 0 and 1023, and added to the top state's
^io.input-link
as the value of the
^sensor-1
, ^sensor-2
, or
^sensor-3
preference, as appropriate.
The touch sensors typically register values above 1,000 when not depressed, and values in the low 100's when depressed (the signals tend to drift, due to the fact that they are being converted from an analog voltage). A rule like the one that follows is probably sufficient to detect a touch sensor's depression:
sp {detect*sensor-a*depressed (state <s> ^superstate nil ^io.input-link.sensor-1 > 512) --> (<s> ^touch-sensor-a depressed)}
The light sensor tends to wobble between values of about 300 for bright light to 800 for darkness.
The sensors are currently run in “active mode” all the time; e.g., so that the light sensor's LED will turn on.
Similarly, the RCX runtime checks the ^io.output-link
for ^motor-a
, ^motor-b
, and
^motor-c
attributes once per elaboration. Each
attribute's value controls the corresponding motor: the symbolic
constant forward
will make the motor run
“forwards”; the symbolic constant reverse
will make the motor run “backwards”; the symbolic
constant brake
will lock the motor in position; and a
value of off
(or the absence of an attribute) will
turn off the motor, allowing it to “free-wheel”.
The following rule (from
drive.soar
)
runs motors A and C “forwards” when the
drive
operator is selected:
sp {implement*drive (state <s> ^operator <o> ^io <io>) (<o> ^name drive) (<io> ^output-link <out>) --> (<out> ^motor-a forward ^motor-c forward)}