The program that runs in the Scouts allows motors, light and sounds to be controlled remotely - and produces sensor returns from the two sensor inputs and the built-in light sensor. All of the 'intelligence' in the robot will run in the RCX.
The code at the RCX end is very simple - and complete programs to drive the scouts is provided below.
A message consists of only a single byte (8 bits):
7 6 5 4 3 2 1 0 [IDENT] [ DATA ]The top two bits is the destination identifier, the bottom 6 bits remains for data.
This permits up to four computers to be addressed. One RCX and up to three Scouts. The RCX is computer ZERO.
The data part of the message has different meanings for Scout and RCX.
7 6 5 4 3 2 1 0 0 0 X X S3H S3L S2 S1Since the scout can only read touch-sensors (switches), we only need one bit for each of SENSOR_1 and SENSOR_2 S3H and S3L are the SENSOR_3 return - which can be:
S3H S3L 0 1 - Low 1 0 - Medium 1 1 - HighThe value will never be zero because that could result in message 0x00 being sent to the RCX - which is "discouraged".
Bits 4 and 5 are unused - don't rely on them being Zero.
To avoid two scouts transmitting at the same instant and corrupting each others' data - thus confusing the RCX, they obey the "Only speak when you are spoken to" rule. Hence the RCX must talk to each Scout in turn in order to get data back from them.
Bits 4/5 of the data therefore contains a command code that is:
0 0 - Command the Scout's motors. 0 1 - Play a sound. 1 0 - Set Motor Power. 1 1 - Set Light threshold, etc.
0 0 - Off. 0 1 - OnFwd. 1 0 - OnRev. 1 1 - Float.OUT_A is bits 0 and 1, OUT_B is bits 2 and 3.
/* Compile using: nqc -S/dev/{port} -TRCX2 rcx.nqc Where: {port} - Whichever serial port you use. */ #define TIMEOUT 50 /* 500ms */ int sensor [ 9 ] ; int motor [ 6 ] ; int motor_power [ 6 ] ; task scout_comms () { int i ; int msg ; while ( true ) { for ( i = 0 ; i < 3 ; i++ ) { while ( true ) { ClearMessage () ; SetTimer ( 0, 0 ) ; SendMessage ( (i+1) * 64 + 0 * 16 + motor[i*2] + motor[i*2+1] * 4 ) ; do { msg = Message () ; } while ( FastTimer ( 0 ) < TIMEOUT && msg == 0 ) ; if ( msg != 0 ) break ; PlaySound ( SOUND_DOUBLE_BEEP ) ; } sensor[i*3 ] = msg & 0x01 ; sensor[i*3+1] = (msg/2) & 0x01 ; sensor[i*3+2] = ((msg/4) & 0x03) - 1 ; } } } task main () { int i ; for ( i = 0 ; i < 3 ; i++ ) { sensor [ i*3 + 0 ] = 0 ; sensor [ i*3 + 1 ] = 0 ; sensor [ i*3 + 2 ] = 0 ; motor [ i*2 + 0 ] = 0 ; motor [ i*2 + 1 ] = 0 ; } start scout_comms ; Wait ( 100 ) ; /* Everything from here on down is your application code. */ .....whatever..... }Then, in your application code, you can set the motors in the scouts running just by assigning to the 'motor' array which has six entries - corresponding to the six Scout motor outputs. Set the array entry to zero to stop the motor, 1 to run it forwards, 2 to run it backwards or 3 to 'float' it.
motor [ 4 ] = 1 ; /* Turn on motor number 4 */Similarly, you can find the most recently known value of each of the Scout's sensors by reading the 'sensor' array which has nine entries.
/* Compile using: nqc -S/dev/{port} -TScout -DMY_IDENT={id} scout.nqc Where: {port} - Whichever serial port you use. {id} - 1, 2 or 3 depending on which Scout you are setting up. */ task main () { ClearMessage () ; while ( true ) { int msg, bits ; msg = Message () ; if ( msg != 0 ) ClearMessage () ; /* No need to check for message zero because bits would be zero - which won't match MY_IDENT */ bits = (msg / 64) & 0x03 ; msg = msg & 0x3F ; if ( bits == MY_IDENT ) { SendMessage ( SENSOR_1 + SENSOR_2 * 2 + (SENSOR_3+1) * 4 ) ; bits = msg / 16 ; /* 0 0 - Command the Scout's motors. 0 1 - Play a sound. 1 0 - Set Motor Power. 1 1 - Set Light threshold, etc. */ switch ( bits ) { case 0 : /* Motor */ /* 0 0 - Off. 0 1 - OnFwd. 1 0 - OnRev. 1 1 - Float. */ bits = msg & 0x03 ; switch ( bits ) { case 0 : Off ( OUT_A ) ; break ; case 1 : OnFwd ( OUT_A ) ; break ; case 2 : OnRev ( OUT_A ) ; break ; case 3 : Float ( OUT_A ) ; break ; } bits = ( msg / 4 ) & 0x03 ; switch ( bits ) { case 0 : Off ( OUT_B ) ; break ; case 1 : OnFwd ( OUT_B ) ; break ; case 2 : OnRev ( OUT_B ) ; break ; case 3 : Float ( OUT_B ) ; break ; } break ; case 1 : /* Sound */ break ; case 2 : /* Motor Power */ SetPower ( OUT_A, (msg & 0x03) * 2 ) ; SetPower ( OUT_B, (msg & 0x0C) / 2 ) ; break ; case 3 : /* Light Threshold */ break ; } } } }Notice that you have to compile and download for each slave computer in turn, changing 'MY_IDENT' to 1, 2 or 3 each time:
nqc -S/dev/{port} -TScout -DMY_IDENT={id} scout.nqc