BlitzMax & Lego Mindstorms NXT

BlitzMax Forums/BlitzMax Programming/BlitzMax & Lego Mindstorms NXT

Winni(Posted 2006) [#1]
Hi folks,

Anybody here building robots with the new Lego NXT brick and using BlitzMax to send commands to it via BlueTooth?

I'm on OS X and have a working BlueTooth connection to the NXT with a bluetooth serial port named NXT_1 assigned to it. It appears in /dev as tty.NXT_1.

This port works with iCommand, the Open Source Java environment for the NXT.

However, when I try this is BlitzMax, which is supposed to play a tone, nothing happens and nothing is returned from the brick:


NXTWrite:TStream = WriteStream( "/dev/tty.NXT_1" )

If Not NXTWrite
	Print "Failed to open write stream."
	End
End If

NXTRead:TStream = ReadStream( "/dev/tty.NXT_1" )


If Not NXTRead
	Print "Failed to open read stream."
	End
End If

WriteByte( NXTWrite, $00 )
WriteByte( NXTWrite, $03 )
WriteShort( NXTWrite, 8000 )
WriteShort( NXTWrite, 60 )

FlushStream( NXTWrite )

Print "Now:" + StreamSize( NXTRead )


For n:Int = 0 To 6
	Print ReadByte( NXTRead )
Next

CloseStream( NXTWrite )
CloseStream( NXTRead )



Anybody got an idea what I'm doing wrong?

Thanks!


skidracer(Posted 2006) [#2]
You may need an endian conversion on your stream as WriteShort is endian sensitive.


Winni(Posted 2006) [#3]
Thanks for the hint, skidracer. However, it does not seem to be the problem. The NXT still does not react at all.


Winni(Posted 2006) [#4]
iCommand initializes the serial port like this:


...

	static final int SERIAL_TIMEOUT = 2000;
	/** Baud rate to transmit using Bluetooth. */
	static final int BLUETOOTH_BAUD = 460800;
	
	// Ensures no one tries to make instance of NXTComm
	private NXTComm() {	}
	
	/**
	* Connects to the COM port specified in NXTCOMM environment variable.
	* For more information on configuring Bluetooth, visit www.lejos.org. {link to document]
	*/
	static void open() throws Exception
    {
		String portName = System.getenv("NXTCOMM");
		System.out.println("NXTCOMM = " + portName); // DELETE ME
        if(portName == null)
        	throw new Exception("NXTCOMM is not defined as a system Environment Variable");
		CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
        if(portIdentifier.isCurrentlyOwned()) {
            System.out.println("Error: Port is currently in use");
            throw new PortInUseException();
        } else {
            commPort = portIdentifier.open("Bluetooth " + portName,SERIAL_TIMEOUT);
            
            if (commPort instanceof SerialPort) {
                SerialPort serialPort = (SerialPort) commPort;


-->> Here it comes:

                serialPort.setSerialPortParams(BLUETOOTH_BAUD,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);
                serialPort.setRTS(true); 
                serialPort.setDTR(true); // This allows NXT programs to be run without resetting NXT each time.


                in = serialPort.getInputStream();
                out = serialPort.getOutputStream();


...



The above Java-Code uses RXTX, which basically is a JNI-wrapper for OS-specific API calls. Is there a way to do something similiar in BlitzMax without having to go down to the system level?


Diordna(Posted 2006) [#5]
I would LOVE for someone to make a module for this. It would be _amazing_. I'd take it upon myself to write some kind of NQC-like thing.


Winni(Posted 2006) [#6]
Hi Diordna,

I intend to release whatever comes out of this as a module for everyone. The main problem at the moment is the communication with the brick, which I simply cannot get to work.

With the new NXT brick, there are two ways to program it. It can either be "remote controlled", which is what iCommand is doing, or you can compile a program to the VM of the brick and its byte code language. The latter is what you would want to do with an own NQC-like implementation.

Like the guys from the leJOS project, I wanted to start with the seemingly simpler thing, the "remote control" option. It only has a few and quite simple commands and is not much of a "black magic voodoo" thing -- once you get past the communication issue.

But initializing and using the BlueTooth serial port in the right way is giving me headaches. And I am listening to my girl friend's advice: "Instead of wasting your time thinking about how to program in Java, you should concentrate on drinking some Java." I never got a grip on that strange curley braces language, so I guess she's right. ;-)


Who was John Galt?(Posted 2006) [#7]
"Is there a way to do something similiar in BlitzMax without having to go down to the system level?"
Not unless someone writes a BlitzMax wrapper I guess. Have you tried running the iCommand initialisation, then running your Blitz program? What do you get then?


Winni(Posted 2006) [#8]
Hi Nomen luni,

I still need to test this, but I won't get around to it until later tonight. I'll post an update here once it is done.


Who was John Galt?(Posted 2006) [#9]
OK - I'm interested to see what you get because the guy I sit next to at work has a set of mindstorms.


Winni(Posted 2006) [#10]
Sounds cool. Especially when you consider that with the new NXT brick you can connect and synchronize four (!) bricks via BlueTooth. :)

Coming back to the problem at hand, I presume that in the end that wrapper needs to be written. I'm already trying to find usable documentation for that in the Apple developer network, but I have not looked in the right place, yet. I suppose it must be somewhere in the Posix/BSD part of it.

The other option, using the code from the RXTX project, is a bit problematic, because it was written with JNI in mind - and for quite a bunch of operating systems. The code is stuffed with ifdefs, and extracting the useful parts out of it probably is more work than writing a wrapper from scratch.


skidracer(Posted 2006) [#11]
I think the header file to find is called termios.h, just looking for it now. For the typically unfathomable apple docs:

http://developer.apple.com/documentation/Darwin/Reference/ManPages/man4/termios.4.html

or a slightly easier to read intro:

http://www.lafn.org/~dave/linux/terminalIO.html

Will see if I can help this weekend if noone else picks up the ball, even have a bluetooth -> pocketpc serial port connection I can test with.


Winni(Posted 2006) [#12]
Thanks for the hint, skidracer!

Spotlight says that it is in /usr/include/sys (OS X 10.4.8, Intel).

Problem is, I won't get around to looking at the stuff before Saturday, but then I will definitely take some time for it.


Winni(Posted 2006) [#13]
An update and a partial success:

I launched a small icommand Java program to initialize the serial port and play a tone. Instead of quitting the application, I let it wait for a key press:

import icommand.platform.nxt.*;
import java.io.File;
import icommand.nxtcomm.*;

	public class Melody {
		
		public static void main(String [] args) throws Exception {

			NXTCommand.playTone( 4000, 600);

System.in.read();

			NXTCommand.close();
		}
}


Now, I let this BlitzMax program run while the Java app is still waiting for a keypress:

NXTWrite:TStream = WriteStream( "/dev/tty.NXT_1" )

If Not NXTWrite
	Print "Failed to open write stream."
	End
End If

NXTRead:TStream = ReadStream( "/dev/tty.NXT_1" )


If Not NXTRead
	Print "Failed to open read stream."
	End
End If

WriteShort( NXTWrite, $06 )
WriteByte( NXTWrite, $00 )
WriteByte( NXTWrite, $03 )
WriteShort( NXTWrite, 4000 )
WriteShort( NXTWrite, 600 )

FlushStream( NXTWrite )

Print "Now:" + StreamSize( NXTRead )


For n:Int = 0 To 6
	Print ReadByte( NXTRead )
Next

CloseStream( NXTWrite )
CloseStream( NXTRead )



And guess what? It -DOES- play the same sound on the brick! :-))))

So the problem -is- the initialization of the BlueTooth serial port.

Well, I cannot do more tonight, the thing has to wait until the weekend, I'm afraid. But at least there is some progress. :)


Winni(Posted 2006) [#14]
I have to confess that I am too dumb to get the C-code right. My disgust for C probably is not helping much either.

Anyway, I grabbed together a bunch of sample code of which I think it is going into the right direction, but still, it does not work:

#include <stdio.h> 
#include <string.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <sys/ioctl.h> 
#include <errno.h> 
#include <paths.h> 
#include <termios.h> 
#include <sysexits.h> 
#include <sys/param.h> 
#include <sys/select.h> 
#include <sys/time.h> 
#include <time.h> 
#include <CoreFoundation/CoreFoundation.h> 
#include <IOKit/IOKitLib.h> 
#include <IOKit/serial/IOSerialKeys.h> 
#include<IOKit/IOBSD.h>
#define  B460800  0010004

int MyOpenSerialPort() 
{ 
	int fd = -1; 
	struct termios options; 

	fd = open( "/dev/tty.NXT_1" , O_RDWR | O_NOCTTY | O_NONBLOCK); 
	if (fd == -1) 
		{
			return -1;
		} 
	tcgetattr(fd, &options);

	options.c_iflag = INPCK;
	options.c_lflag = 0;
	options.c_oflag = 0;
	options.c_cc[VMIN] = 1; 
	options.c_cc[VTIME] = 2000; 
	options.c_cflag |= CS8 ; 

	cfsetspeed(&options, B460800);  

	tcsetattr( fd, TCSANOW, &options );

	ioctl(fd, TIOCSDTR);

	int handshake = TIOCM_DTR | TIOCM_RTS | TIOCM_CTS | TIOCM_DSR;
	ioctl(fd, TIOCMSET, &handshake);
	
	return fd; 
} 


int MyCloseSerialPort( int fd ) {
	return close( fd );
}




In Max, I am doing this:


Import "term.c"

Extern
	Function MyOpenSerialPort:Int( cPort:String )
	Function MyCloseSerialPort:Int( fd:Int )
End Extern


Print MyOpenSerialPort( "/dev/tty.NXT_1" )


NXTWrite:TStream = WriteStream( "/dev/tty.NXT_1" )

If Not NXTWrite
	Print "Failed to open write stream."
	End
End If

NXTRead:TStream = ReadStream( "/dev/tty.NXT_1" )


If Not NXTRead
	Print "Failed to open read stream."
	End
End If

WriteShort( NXTWrite, $06 )
WriteByte( NXTWrite, $00 )
WriteByte( NXTWrite, $03 )
WriteShort( NXTWrite, 4000 )
WriteShort( NXTWrite, 600 )

FlushStream( NXTWrite )



skidracer(Posted 2006) [#15]
Hi Winni,
I've made a start on a freeport module that I have talking to a USB serial port on the Mac. If you're on PowerPC you should be able to syncmods with the axe modules and will install automatically. There is a test program in axe.mod/freeport.mod/doc/comtest.bmx which will enumerate the serial ports on your machine then open a hardcoded name at 115200, change the name yourself to one of the devices listed in the enumeration after you have run it once. If you need to change the baud, you'll need to edit the .macos.c file manually for now (then rebuild mods).

if you're on intel, here's a zip

http://www.gamecodenow.com/nitrologic/freeport.mod.zip


TeaVirus(Posted 2006) [#16]
Cool! Skidracer, does this allow non-blocking serial port I/O?


Winni(Posted 2006) [#17]
Hi skidracer,

Thanks for your great support!!!

I only got around to looking at it this weekend. I made some changes to the C-code to change the baud rate to 460800 and I also removed the "exclusive" flag - it is easier to use a stream to send the instructions to the brick, and when I use the Java app to initialize the serial port, this works quite nice, so I assume that using freeport instead should do the same.

Still, there must be a difference between what your code does and what the Java-rxtx is doing: The brick remains silent and does not respond.

Lego requires these settings:
460.8 Baud
8 Bits
No parity bits
One stop bit
RTS & CTS

I'm not sure if your code sets the stop bit. I found a CSTOPB flag, but this is used to set 2 stop bits. Does leaving this out then set one stop bit implicitly?

When I understand your code right, you do set all the other options (except for the baud rate).

Any ideas what the problem could be?

Thanks a lot,


skidracer(Posted 2006) [#18]
This code asserts and then clears DTR, I would try changing the second bit to assert RTS (so it matches the setup your java snippet performs) on a pc at the moment but i think thats means TIOCCDTR->TIOCSRTS

    if (ioctl(fileDescriptor, TIOCSDTR) == kMyErrReturn) 
    // Assert Data Terminal Ready (DTR)
    {
        printf("Error asserting DTR %s - %s(%d).\n",
            deviceFilePath, strerror(errno), errno);
    }
 
    if (ioctl(fileDescriptor, TIOCCDTR) == kMyErrReturn) 
    // Clear Data Terminal Ready (DTR)
    {
        printf("Error clearing DTR %s - %s(%d).\n",
            deviceFilePath, strerror(errno), errno);
    }



Winni(Posted 2006) [#19]
No, that won't work. ttycom.h does not define TIOCSRTS, it only defines TIOCSBRK and TIOCSDTR. There is a TIOCM_RTS, and that is used here:

	   handshake = TIOCM_DTR | TIOCM_RTS | TIOCM_CTS | TIOCM_DSR;
    // Set the modem lines depending on the bits set in handshake.
    if (ioctl(fileDescriptor, TIOCMSET, &handshake) == kMyErrReturn)
    {
        printf("Error setting handshake lines %s - %s(%d).\n",
            deviceFilePath, strerror(errno), errno);
    }



skidracer(Posted 2006) [#20]
OK, well at least remove the clear DTR section, so it is configured as set. Hmmm, i wish apple had better documented that code, I would also try modifying the "modem" handshake to just include the DTR/CTS bits.
Will do some more experimenting tonight but without the hardware in question might be a bit stuck.


Winni(Posted 2006) [#21]
I tried it, but it didn't help.

I snooped around in the rxtx code, and I think this is what they are doing to set RTS:

JNIEXPORT void JNICALL RXTXPort(setRTS)( JNIEnv *env,
	jobject jobj, jboolean state )
{
	unsigned int result = 0;
	int fd = get_java_var( env, jobj,"fd","I" );
	char message[80];

	ENTER( "RXTXPort:setRTS" );
	ioctl( fd, TIOCMGET, &result );
	if( state == JNI_TRUE ) result |= TIOCM_RTS;
	else result &= ~TIOCM_RTS;
	ioctl( fd, TIOCMSET, &result );
	sprintf( message, "setRTS( %i )\n", state );
	report( message );
	LEAVE( "RXTXPort:setRTS" );
	return;
}



I tried to put the ioctl statements in the freeport code, but it still didn't help.
I guess I give up for tonight. Tomorrow's another day...


Winni(Posted 2006) [#22]
Ok, I just couldn't leave it be and did some more research and read several lines of POSIX C-code. From what I have learned, this -should- work, but it still doesn't:

int openserial(const char *deviceFilePath,int baudrate){
    int         fileDescriptor = -1;
    int         status = 0;
    struct termios  options;
 
    fileDescriptor = open(deviceFilePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
    tcgetattr(fileDescriptor, &options);
 
	options.c_iflag &= ~(INPCK | PARMRK | BRKINT | INLCR | ICRNL | IXANY);			// disable input processing
	options.c_lflag = 0;					// no local flags
 	options.c_oflag &= ~OPOST;				// Output: Raw mode
	options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;					// 8 Bits
	options.c_cflag &= ~PARENB;				// No parity
	options.c_cflag &= ~CSTOPB;				// 1 Stop Bit
	options.c_cflag |= CRTSCTS;				// flow ctrl
	options.c_cc[VMIN] = 0;					// read() returns immediately
    options.c_cc[VTIME] = 0;				// with the data in buffer

    tcsetattr(fileDescriptor, TCSANOW, &options);

	// Set speed to 460.8K
    cfsetispeed(&options, 460800);
    cfsetospeed(&options, 460800);
  
	// Set RTS
    ioctl(fileDescriptor, TIOCMGET, &status);
	status |= TIOCM_RTS;
    ioctl(fileDescriptor, TIOCMSET, &status);

	// Set DTR
    ioctl(fileDescriptor, TIOCMGET, &status);
    status |= TIOCM_DTR;
    ioctl(fileDescriptor, TIOCMSET, &status);



    return fileDescriptor;
}




skidracer(Posted 2006) [#23]
tcsetattr needs to be called after the baudrate lines


Winni(Posted 2006) [#24]
Yep, you're right. I changed it, but it still doesn't work.


Winni(Posted 2006) [#25]
I just discovered that we can forget the whole serial port programming thing!!

The info was here:
http://juju.org/articles/2006/08/01/mindstorms-nxt-bluetooth-on-osx

All I need to do on the console to properly initalize the bluetooth port is this:

cat /dev/tty.NXT_1

The important part is -not- to close the terminal window or abort the command with ctrl-c. It needs to stay open for as long as the connection to the brick is required.

After "opening" the port with the cat command, I can simply send commands from BlitzMax through a stream, and the brick receives and accepts them.

The challenge now is to get BlitzMax to do all of that. The dirty approach would be to launch a background process that performs the cat command, and then proceed with the BlitzMax code. Not beautiful, but it'd probably work.


Diordna(Posted 2006) [#26]
Yeah, CreateProcess would probably take care of it.


Difference(Posted 2010) [#27]
Did anybody ever get BlitzMax and NXT talking together on OS X ?


Brucey(Posted 2010) [#28]
If I had one, I definitely would have :-)


Difference(Posted 2011) [#29]
Hey! I just got a motor running, using the Terminal trick and this code:




Difference(Posted 2011) [#30]
I'm no longer using the "terminal trick".
I simply open the correct /dev device, in my case "/dev/tty.NXT-DevB" and it works.

I can reboot and turn off NXT, and when I come back , My Blitzmax program will just connects if the NXT is on.

It pretty stable, and working well.

I would like to use USB instead, for greater communication speed, because reading motor positions is a little slow ( without measuring I'd say approximately 0.5 secs ), and I'd like to have a little better precision.

Last edited 2011