Log in

View Full Version : Using an FRC with the RCX?


Astronouth7303
06-04-2004, 14:56
I was lieing in bed this morning when it hit me: Since the prog port is RS-232, and so is a computer, there is a chance you could connect LEGO's old COMM tower with the prog port on the RC or the EDU. You would probably need to make a custom cable, but it might work.

Basically, to control the RCX, you send it IR messages via the IR tower. From there it's a simple matter of programming the two.
The LUGNET thread for sending messages in VB can be found here (http://www.lugnet.com/robotics/?n=15719).

Here's some FRC code to do it:

#define SendByte(Byte) {TXREG = Byte; Wait4TXEmpty();}
void SendRCXMessage (char Message)
{
//Header
SendByte(0x55);
SendByte(0xFF);
SendByte(!0xFF);
//Opcode for Message
SendByte(0xF7);
SendByte(!0xF7);
//Message Byte
SendByte(Message);
SendByte(!Message);
//Checksum
SendByte((0xF7 + Message) & 0xFF);
SendByte(!((0xF7 + Message) & 0xFF));
}

The RCX, unfortunately, only has a 1 byte buffer for IR, so I thought of a 1 byte setup to set each motor to a certain speed.
The RCX has outputs A, B, and C. They can be Forward, Backward, or stopped, and have a speed of 1-8. This can be expressed as -8 to 8.

If you just want 2 motors, you can set both in the same byte in the range of -7 to 8. Use the first 4 bits for A and the second 4 for B. In NQC (http://bricxcc.sourceforge.net/nqc/):

// A Basic RC Skid-Steer Program (Recieve)

#define mLeft OUT_A
#define mRight OUT_C
#define nOffset 7
#define nBit 16

int Left = 0;
int Right = 0;

task main()
{
start GetMessage;
start RunLeft;
start RunRight;
}

task GetMessage()
{
Left = Message() / nBit - nOffset;
Right = Message() % nBit - nOffset;
}


task RunLeft()
{
while (true)
{
SetPower (mLeft, abs(Left));
if (Left > 0) OnFwd(mLeft);
if (Left < 0) OnRev(mLeft);
if (Left == 0) Off(mLeft);
}
}

task RunRight ()
{
while (true)
{
SetPower(mRight, abs(Right));
if (Right > 0) OnFwd(mRight);
if (Right < 0) OnRev(mRight);
if (Right == 0) Off(mRight);
}
}(And yes, the RCX does support primitive multi-threading)

In the FRC:

void Set2RCXMotors(signed char Left, signed char Right)
{
char Temp = 0;
//Check Left value
if (Left > 8)
Left = 8;
if (Left < -7)
Left = -7;
//Check Right value
if (Right > 8)
Left = 8;
if (Right < -7)
Left = -7;

Temp = (Left+7) << 4;
Temp |= Right+7;
SendRCXMessage(Temp);
}



If you want to control all 3 outs directly, then you'll have to specify which one. The first 2 bits are what out, 3 is 0, 4 is direction, 5-7 are power-1, and 8 is "Is on?". So, in a nutshell: MM0D PPPOn.
On the RCX:

// A Basic RC 3 Motor control

int Left = 0;
int Center = 0;
int Right = 0;

task main()
{
start GetMessage;
start RunLeft;
start RunRight;
start RunCenter;
}

task GetMessage()
{
int Mess;
Mess = Message;
//Left
if ((Mess/64) == 1)
{
if (Mess & 0x10)
{
Left = ((Mess/2) & 8) & ((Mess & 1) != 0) ;
}
else
{
Left = -(((Mess/2) & 8) & ((Mess & 1) != 0));
}
}

//Center
if ((Mess/64) == 2)
{
if (Mess & 0x10)
{
Center = ((Mess/2) & 8) & ((Mess & 1) != 0);
}
else
{
Center = -(((Mess/2) & 8) & ((Mess & 1) != 0));
}
}

//Right
if ((Mess/64) == 3)
{
if (Mess & 0x10)
{
Right = ((Mess/2) & 8) & ((Mess & 1) != 0);
}
else
{
Right = -(((Mess/2) & 8) & ((Mess & 1) != 0)));
}
}
}


task RunLeft()
{
while (true)
{
SetPower (OUT_A, abs(Left));
if (Left > 0) OnFwd(OUT_A);
if (Left < 0) OnRev(OUT_A);
if (Left == 0) Off(OUT_A);
}
}

task RunRight()
{
while (true)
{
SetPower(OUT_C, abs(Right));
if (Right > 0) OnFwd(OUT_C);
if (Right < 0) OnRev(OUT_C);
if (Right == 0) Off(OUT_C);
}
}

task RunCenter()
{
while (true)
{
SetPower (OUT_B, abs(Center));
if (Left > 0) OnFwd(OUT_B);
if (Left < 0) OnRev(OUT_B);
if (Left == 0) Off(OUT_B);
}
}

And on the FRC side:

void Set3RCXMotors(signed char Left, signed char Center, signed char Right)
{
char Temp = 0;
//Check Left value
if (Left > 8)
Left = 8;
if (Left < -8)
Left = -8;
//Check Center value
if (Center > 8)
Left = 8;
if (Center < -8)
Left = -8;
//Check Right value
if (Right > 8)
Left = 8;
if (Right < -8)
Left = -8;

//Motor
Temp = 0x40;
//Direction
Temp |= ((Left<0)? 0 : 0x10); //Ooooh! A Ternary!
//Power
Temp |= absdif(Left, 1) << 1;
//Is On?
Temp |= (Left != 0) & 1;
SendRCXMessage(Temp);

//Again for the center
//Motor
Temp = 0x80;
//Direction
Temp |= ((Center<0)? 0 : 0x10); //Ooooh! Another Ternary!
//Power
Temp |= absdif(Center, 1) << 1;
//Is On?
Temp |= (Center != 0) & 1;
SendRCXMessage(Temp);

//Again for the right
//Motor
Temp = 0xC0;
//Direction
Temp |= ((Right<0)? 0 : 0x10); //Ooooh! Another Ternary!
//Power
Temp |= absdif(Right, 1) << 1;
//Is On?
Temp |= (Right != 0) & 1;
SendRCXMessage(Temp);
}

There are obviously a few bugs to work out (I wrote this on the fly!), but I think you can get the idea. (My favorite NQC IDE, BrixCC, can be found Here (http://bricxcc.sourceforge.net/).) I'll give every one a few hours to chug through that.

Ryan M.
06-04-2004, 15:37
That'd probably work. It would be interesting, you could say, have the RCX in charge of controlling drive motors, leaving the RC free to determine where you want to go, etc. I have a Mindstorms set (plus all of the expansions (a few of them twice :))), so I might find this and interesting thing to look into.

--EDIT--
Oh, yeah. NQC is good, but BrickOS (former LegOS) is better. :) BrickOS is also C/C++ programming, but everything is precompiled, not interpruted like with NQC and the default LEGo firmware.

Astronouth7303
06-04-2004, 16:27
Plus, if you have an RCX v1, there is a 9-volt AC adaptor plug in the back (made for the train adaptors). So, connect the 12v Exide to a 12vdc-9vdc transformer, hook that up to the RCX, and you don't need to use 6 AAs!

Anyway, The connecting cable won't be standard: the tower is a Male with just enough space for the cable that came with the kit (No screws). The FRC prog is female. Both the FRC prog and the LEGO IR Tower (LIRT) are made to connect to a computer, so there are going to be problems if you just connect them directly. (Some good info on RS-232 can be found here (http://www.beyondlogic.org/serial/serial.htm).)
Using the jargon, we are trying to connect 2 DCE devices. Unfortunately, I only have pins for DTEs which is (DB9):
DTE
Pin Purpose
0 - Gnd (Shell)
1 < DCD (Data Carrier Detect)
2 < Rx
3 > Tx
4 > DTR (Data Terminal Ready)
5 - Gnd (signal Ground)
6 < DSR (Data Send Ready)
7 > RTS (Request To Send)
8 < CTS (Clear To Send)
9 < RI (Ring Indicator)
^Direction (< = DCE to DTE, > = DTE to DCE, - = None)

So, to connect DCE to DCE (think of the FRC as the DTE):
FRC LIRT
Gnd Gnd
Tx Rx
Rx Tx
DSR DTR
DTR DSR
I'm not sure about the rest of it (RI, DCD, RTS, CTS). The problem is that there are 3 lines for DTE>DCE but there are 5 for DCE>DTE. And I don't want to fry my FRC or LIRT. Don't forget, the labels are relative to the DTE. If someone figures it out, tell us!
(The standard is RS-232.)

Ryan M.
06-04-2004, 17:02
Plus, if you have an RCX v1, there is a 9-volt AC adaptor plug in the back (made for the train adaptors).I want to know why they got rid of it on v1.5 and v2.0. :(

I'm going to look up some info on the RC to computer protocol.

Astronouth7303
06-04-2004, 19:12
Got it!
Thank you B&B Electronics! (http://www.bb-europe.com/tech_articles/faq_rs232_connections_work.asp)

FRC LIRT
Gnd - Gnd
Tx < Rx
Rx > Tx
DTR < DSR
DSR > DTR
RTS < CTS
CTS > RTS
DCD X DCD
RI X RI
Don't connect DCD or RI to anything.
The pins are as follows (DB9):
Shell Shell (optional)
5 5
3 2
2 3
4 6
6 4
7 8
8 7
1 and 9 don't connect to anything.

So if anyone fries something doing this, please tell us before we fry something!

Ryan M.
06-04-2004, 20:27
So if anyone fries something doing this, please tell us before we fry something!
LOL! ;)

You'll have to wait until tomorrow for the info. Sorry. :)

Grommit
07-04-2004, 01:48
Two questions:

1) What kind of hardware would I need on the receiving end to make this work?

2) Has anyone tried to accomplish this successfully?

Thanks, I would have never thought of trying something like this!

Ryan M.
07-04-2004, 06:04
Two questions:

1) What kind of hardware would I need on the receiving end to make this work?

For the RC, you need a cable to attach to the LEGO IR tower. For the RCX, nothing. It has it's own built in IR.


2) Has anyone tried to accomplish this successfully?I don't know of anyone. :)

Ryan M.
07-04-2004, 07:09
Here are a few links:

Packet description: (includes example of sending IR message)
http://graphics.stanford.edu/~kekoa/rcx/protocol.html
From this website, I think that sending:
55 ff 00 f7 08 M ~M C ~C
Where C is the checksum. Sends the messageM. One thing I'm confused on is the checksum. It doesn't seem to work the way I read it in his description. Even trying to figure the checksum using his example I didn't come up with the same thing he did. It may be I just don't understand it. If you kow, tell me how to compute it.

Some more stuff on the packets: (not as good)
http://graphics.stanford.edu/~kekoa/rcx/#Protocol

Do you need to know how to send that out, or do you already have that figured out?

--EDIT--
Here is a site with examples of using it in C++. http://www.generation5.org/content/2001/rob08.asp.
Reading that, it's a great site. :)

Astronouth7303
07-04-2004, 11:34
Two questions:

1) What kind of hardware would I need on the receiving end to make this work?
LEGO's RCX. That's all (You may need the official firmware to get the message packets to work). At transmission, however, you need an old COM tower, not a new USB.
2) Has anyone tried to accomplish this successfully?
I don't think so. But it hasn't been very long. The hardest part would be the cable
Thanks, I would have never thought of trying something like this!
Neither would I until yesterday!

Astronouth7303
07-04-2004, 11:51
Here are a few links:

Packet description: (includes example of sending IR message)
http://graphics.stanford.edu/~kekoa/rcx/protocol.html
From this website, I think that sending:
55 ff 00 f7 08 M ~M C ~C
Where C is the checksum. Sends the messageM. One thing I'm confused on is the checksum. It doesn't seem to work the way I read it in his description. Even trying to figure the checksum using his example I didn't come up with the same thing he did. It may be I just don't understand it. If you kow, tell me how to compute it.

Some more stuff on the packets: (not as good)
http://graphics.stanford.edu/~kekoa/rcx/#Protocol

Do you need to know how to send that out, or do you already have that figured out?

--EDIT--
Here is a site with examples of using it in C++. http://www.generation5.org/content/2001/rob08.asp.
Reading that, it's a great site. :)

The example from LUGNET was made for MSCOMM and VB. I just took the array and wrote the functions for the controller.

A message packet is only one additional info section.
Header (3 bytes)
OpCode (2)
Message (Info, 2)
Checksum (2)

The OpCode for Message is 0xF7. It has one argument: the message value. Every data byte is followed by it's not. So you get:
Header = 55FF00
OpCode = F708
Message = M ~M
CheckSum = (F7+M) & FF

I think we're finally getting it!

The second link is extremely helpful. And I'm glad to know about the Null-modem cable thing: The LIRT is CTE, not a CSE (it is a male, like your computer), so maybe a standard cable works!

Ryan M.
07-04-2004, 13:19
You said the prog port earlier, but how do we access that? Or, rather, can we get enough control to do what we want? You can send text, but I haven't studied the printf() access of the port. Maybe that port will work or maybe something else is necessary. Correct me if you know the way. :)

Astronouth7303
07-04-2004, 21:17
You said the prog port earlier, but how do we access that? Or, rather, can we get enough control to do what we want? You can send text, but I haven't studied the printf() access of the port. Maybe that port will work or maybe something else is necessary. Correct me if you know the way. :)

Ok. The text is transmitted as bytes (I assumed ASCII, but I'm not sure). So basically, you replace the text with bytes. A string is an array of bytes terminated by a null characcter (0x00).

I looked in printf_lib.c I traced the flow of execution from printf(), and ended up at Write_Byte_To_Uart(). Which is:

/************************************************** *******
* SUBROUTINE NAME: Write_Byte_To_Uart
* PURPOSE: Writes a byte to the UART.
* Argument Type IO Description
* -------- ----------- -- -----------
* data int I data to transmit to the UART
* RETURNS: void
************************************************** *******/

static void Write_Byte_To_Uart(int data)
{
TXREG = data; /* a carriage return */
Wait4TXEmpty();
}
I simplified it to the macro:
#define SendByte(Byte) {TXREG = Byte; Wait4TXEmpty();}(And I don't actually know how to write macros!)

You call this repeatedly to write the whole packet.

Of course, You can't recieve: the LIRT only remains active for 3 seconds after you transmit something.
Also: this whole setup is unusual, so if it doesn't work, don't worry. We just may end up buying a pair of UART DTEs and making a double box! ;)

Ryan M.
08-04-2004, 06:28
I simplified it to the macro:
#define SendByte(Byte) {TXREG = Byte; Wait4TXEmpty();}(And I don't actually know how to write macros!)

You call this repeatedly to write the whole packet.
That's what I figured would work, but I wasn't sure. Don't do a lot of COMM port work. :)

And the macro looks fine. :)

Astronouth7303
08-04-2004, 12:53
Neither do I. I just read the file. It's aparently easy!

Astronouth7303
09-04-2004, 09:55
2 updates:
1. Found gray cable setup.
GRAY CABLE
Rx Tx
Tx Rx
DTR DTR
Gnd Gnd
RTS CTS
CTS RTS
Or am I misinterpreting?

From front:
1 2 3 4 X
X 5 6 X

1 2 4 3 X
X 6 5 X
X's are no connection, numbers mean it's connected to the same number.
Get the idea?


2. What's the TTL output? I was thinking of the possibility of connecting through that. The prog port is used for so much else!

Ryan M.
09-04-2004, 18:48
2 updates:
1. Found gray cable setup.
GRAY CABLE
Rx Tx
Tx Rx
DTR DTR
Gnd Gnd
RTS CTS
CTS RTS
Or am I misinterpreting?

From front:
1 2 3 4 X
X 5 6 X

1 2 4 3 X
X 6 5 X
X's are no connection, numbers mean it's connected to the same number.
Get the idea?Where'd you get the info? I'll see if I agree. ;)

2. What's the TTL output? I was thinking of the possibility of connecting through that. The prog port is used for so much else!I don't know. You're much better at harware stuff than me. :):)

Gabriel
09-04-2004, 20:52
I know that its probably more fun to reverse-engineer it, but have you guys thought about talking to LEGO for the specs? I know that they sponsor team 96 and I think their mindstorms engineers work with them.

Astronouth7303
09-04-2004, 21:41
Why? all the reverse-engineering and spec-finding has been done for us. The most reverse-engineering I've done yet is putting a continuity tester to the original gray cable! And RS-232 has been standardized to death, so it's the easiest protocal to use (at either end!).

Astronouth7303
09-04-2004, 22:25
The TTL port uses the same transmission format as RS-232 (low start bit, 8 data bits, high stop bit). So if you take a 3-pin cable and attach it the Tx, Rx, and Gnd pins on a DB9 female connector, you can use TTL port instead of the prog port. The macro would need to be changed slightly.

Odd, ifi_utilities.c and printf_lib.c both use 'TXREG', but ifi_picdefs.h defines 'TXREG1' and 'TXREG2'.

Ryan M.
10-04-2004, 07:20
The TTL port uses the same transmission format as RS-232 (low start bit, 8 data bits, high stop bit). So if you take a 3-pin cable and attach it the Tx, Rx, and Gnd pins on a DB9 female connector, you can use TTL port instead of the prog port. The macro would need to be changed slightly.

Odd, ifi_utilities.c and printf_lib.c both use 'TXREG', but ifi_picdefs.h defines 'TXREG1' and 'TXREG2'.If you think it'll work, go for it. :)

Astronouth7303
15-04-2004, 14:14
Use this cable for connecting to the LIRT:
LIRT TTL/Dig
DB9F 1x3 Connectors (2x)
Tx TTL.Rx
Rx TTL.Tx
Gnd TTL.Gnd
RTS Dig.Sig
CTS Dig.Gnd

In the LIRT, the RTS and CTS pins are jumpered. Use the Digital connector to find out if the tower is connected (high when it is, low when isn't. So FALSE is CONNECTED, and TRUE is NOT CONNECTED)
The Pin Equivalents are:
3 TTL.Red
2 TTL.White
5 TTL.Black
7 Dig.White
8 Dig.Black


This is relative to the Male connector, so double check. You need to use a FEMALE connector. I believe you just switch Tx and Rx (RTS and CTS doesn't matter in this case: a jumper's a jumper)

Specific info on what to send can be found in the Mindstorms SDK (mindstorms.lego.com/sdk/). (Use v1.5 I believe)