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)}