In this section, I will describe the functions used to control the basic
mechanical functions of the RCX. If you are reasonably skilled in C, then
check out the actual source code of the version of brickOS that we use at
/course/cs148/src/brickOS_cs148
(from a Linux Machine) Also, check out the
brickOS command reference page and the
Code Samples page
#include <dmotor.h>
motor_X_dir(enum MotorDirection)
: This function controls the
direction of motor travel. X is the letter of the output to which the motor is
connected (a, b, or c, lowercase only). MotorDirection is an enum- accepted
values are: fwd, rev, off, and brake. 'Brake' shorts the motor, which physically
prevents the motor from spinning, whereas 'off' allows it to spin freely. Be
aware. Shorting the motor using brake bleeds current and can drain your battery
quickly. If you need to use brake, use it only briefly and then make sure you
switch to off after an appropriate time period.
motor_X_speed(int speed)
: This function controls the speed of
the motor. As in motor_X_dir(), X is the letter of the
motor (a, b, or c). Speed is an integer value between MIN_SPEED and
MAX_SPEED, where each of those are defined in dmotor.h
as 0
and 255, respectively. Once that is applied, the motor's RPMs should be
reasonably linear.
#include <dsensor.h>
The light sensors are used to read the difference between light and dark areas. For our purposes, this can be used to detect dark objects against a light background, or follow a dark line on a bright floor. There is sufficient detail in the reading to allow your robot to choose between different colors, but be aware that you may need to strictly control light conditions and experiment with placement of the sensor to get consistent and usable values.
Remember: if you are having inexplicable problems with the light sensors (i.e., as far as you can tell not in your code) then remember to check and make sure that they are plugged in where your code expects them to be.
Measuring Light Intensity
The constant LIGHT_X (where X=1, 2, or 3) appropriate to the number of the input on the RCX) is set by the OS roughly every 1/4 ms. The scale of the value depends on the "mode," which I describe in the next section.
Setting the Light Sensor Mode
You will need to put the light sensor into active mode in order for it to be useful
To go into active mode: ds_active(&SENSOR_X) where X is 1, 2, or 3.
What does this mean? Well, as you can see by looking at the front of the light-sensor brick, there are two components in the brick. The first is the actual light detector, and the second is a small light source. The idea is that the light is turned on if you want to find something reasonably close which will have a big difference in reflectivity from the surrounding. This will amplify the difference between the light and dark (much like shining a flashlight on something.)
Light readings range between about 18 and 100.
When the sensor is particularly close to an object, LIGHT_X becomes less a measure of darkness/brightness than it does of reflectivity. Given sufficient separation the reflected light should give a reasonably consistent value.
Be aware that the response of the light sensor is not strictly linear (i.e. the graph of actual light intensity vs. the value read from the sensor.) Michael Gasperi has a graph of light response and discussion of the light sensor internals here.
#include <dsensor.h>
To access the touch sensors, legOS 0.2.x has TOUCH_X, which should return either a 1 (pressed) or a 0 (not pressed). Just use it as a variable, and it'll contain the proper value.
Be aware that the sensors don't spring back very well after having been touched. This allows them to be more sensitive, but also means that they tend to get stuck and remain pressed after the initial pressure is removed. Therefore, if you attach a Lego mechanism to the sensor, make sure that it is weighted such that it will spring back on its own if your design requires the sensor to be pressed and then released.
#include <dsensor.h>
brickOS includes the capability for the measurement of rotations using the Lego Group's angle sensors. These sensors are designed to indicate when an axle passing through the sensor rotates 1/16 of a rotation. In order to use the rotation sensors in brickOS, three functions must be used:
ds_active() must be called before you expect to use the sensors, just as with a light sensor.
ds_rotation_set( number, int position) allows you to set the value of the rotation sensor to an arbitrary position, which the OS will then increment or decrement as appropriate. If not called, the position defaults to zero. In both this and the next functions, sensor number is of the form SENSOR_X, where X is 1, 2, or 3.
ds_rotation_on(&sensor number) turns on the rotation sensor processing in the OS, which is necessary before the program starts running.
To access the rotation sensors, use ROTATION_X, where X is 1, 2,
or 3. The value returned should change every 1/16 of a turn. This value should
start at the position set by ds_rotation_set, and then
increment when turned in one direction and decrement when turned in the other.
The LCD
#include <dlcd.h>
#include <conio.h>
Writing to the LCD is pretty straightforward: just call one of the functions listed below. In legOS 0.2.x the screen is updated automatically by the OS every 100 ms.
lcd_int(x) writes the int x to the buffer.
lcd_show(enum lcd_segment) and lcd_hide(enum lcd_segment) show and hide, respectively, various lcd_segments to the screen. These include the man on the screen and the arrows next to each input/output. Read the API documentation for lcd_segment for the full list.
lcd_clear() clears the entire screen, which can be handy, since a character which has not been overwritten will stay on the screen. cls() does the same thing, except only for the characters and not for the various "process visualization" characters like the man.
cputs(char *s) displays a five character string. If the string is less than five characters long, only the given characters will be printed, and any characters already on screen will not be cleared.
#include <dbutton.h>
#include <dkey.h>
Buttons are accessed through dbutton.h and dkey.h. Button access is not arbitrary, however. On/Off and Program are fixed so that On/Off always turns the robot off and Program always stops the running program. Because of that linkage, only the view and run buttons are accessible all the time.
PRESSED(dbutton(), NAME)
or
RELEASED(dbutton(),NAME)
allow read access to the buttons.
Each function returns true if the button that has been named is in the
appropriate state (pressed or released) and false otherwise. NAME must be one of
the following: BUTTON_RUN or BUTTON_VIEW. This function is
not debounced: i.e., if you don't put msleep() statements into your code, and
have several PRESSED() statements in a row, they may all be triggered by a
single press! Under certain circumstances (usually while loops that repeatedly
look for button input) this can actually cause the program to freeze.
Alternately, you can just tell your loop to wait until the button is released
before accepting any other input or attempting to run any other code.
There is also another way to get get button presses. The function getchar(),
when called, will wait until a button is pressed, and then immediately return a
value signaling the first button that has been pressed. If VIEW is pressed, it
will return KEY_VIEW, and if RUN is pressed, it will return KEY_RUN.
Sound
#include <dsound.h>
brickOS has very thorough support for sound. The basic structure of the driver
is pretty straightforward. Music is defined as a series of note_t structs. Each
note_t contains a pitch and a duration. Once
you've defined an array of note_t's that you want to play, pass it to
dsound_play() and the robot will play the music.
The IR Unit
#include <lnp/lnp.h>
#include <lnp/lnp-reliable.h>
There is very robust support for IR networking between robots, and with Linux/Windows workstations.
brickOS uses the legOS Network Protocol (LNP) for all communication. The basics are similar to UDP networking. If you send a data packet, it is not guaranteed to arrive; but if it does arrive, it will arrive intact (through a checksumming algorithm)
The cs148 TAs have written an additional protocol on top of the LNP protocol which
is similar to TCP in that if a packet arrives, all packets sent before it are guaranteed
to have arrived as well.
Data Logging
#include <log.h>
In short, brickOS sucks for debugging. The cs148 TAs have hacked a data logger into the kernel. It's extremely hacky, and can cause brickOS to crash because it's not written correctly, so use it only if you absolutely have to.