Konrads Guide to CS450

Part I

Talking to the Robot

Version 1.0 (06-Dec-1997)
 Konrads Guide to CS450 Part I


Intro

Two words: Start Early! Talking to the robot is not a difficult task, once you know what to do. However, from prior experience, leaving the program to the end of the semester will give you sleepless nights, and bad headache. Second suggestion, MAKE A BACKUP of your work periodically. Your code might have some side-effects, which may cause crashes, hence you might loose any unsaved work. Having said that, lets go onto the actual robot.


Major Considerations

The hardware available to you in the Real Time lab is listed on the  Real Time Lab Web Page   . The hardware we're concerned with here is the Mitsubishi RM-501 Move Master robotic manipulator arm. The robotic arm has 6 individual joint movements, which can be controlled by sending a control string to the drive unit, which in return tells the arm to perform desired movements.

The six individual joint movements are as follows:

The robot may perform one of the six joint movements in sequence, or simultaneously.

The drive unit contains various interfaces, two of which are of importance to us. The first one, an RS-232 serial port, and second, the CN-5 Centronics connector. The RS-232 serial port is not functioning at this point, mainly due to the fact that the RS-232 port is a non-standard 24-pin connector, and the lack of pin layout information. A cable was built for the CN-5 Centronics connector which allows us to interface the drive unit with a standard parallel port on any hardware. Hence you are limited to how you're going to interface the hardware with the robot to the parallel cable.

You do require at least one machine containing a parallel port, however, whether you talk directly to the parallel port, or not is up to you. If you decide to do your work on a machine which does not contain a parallel port of its own, you can connect the drive unit to a machine that does, and simply do a network connection via socket() operations, or connect the two machines via a NULL Modem (which will not be discussed here). Refer to  Konrads Guide to CS450 Part II for information how a socket() connection can be achieved. But basically you would load a server program on machine which has the parallel port, which would wait for calls from a client on your architecture. After receiving the call from the client, the server would translate the received information to a code that the robot could understand, and send the necessary commands. In the end, however, one of the computers has to write to a parallel port. Refer to the Move Master II Instructional Manual located in the Real Time Lab for information pertaining to the control instructions for the Move Master robot.


What To Use/How To Connect/How To Compile

First of all you need to determine what machines you will be running on which will help you select a proper compiler. The AIX, SunOS, Lynx, and QNX machines all have a standard cc compiler installed. Please refer to proper pages for information how to use these compilers. Most of the time you would use the following syntax:

#cc <source_file_name.c> -o <executable_name> Next you need to determine the type of connection you want to obtain. If you want a serial connection between two machines, you must make sure that both of the machines have an RS-232 serial port. Next, you need to make sure that you are using a NULL modem to connect these two machines because a simple serial cable will not work. A NULL modem will look somewhat like the cable shown in the figure below (most of the time it will have a NULL etched out somewhere on the connector).

Finally, you need to determine how to hook up the cable to the computer and the other piece of hardware. If you're not sure what a 25-pin serial port looks like, just grab your NULL modem and play around the back of the computer until the cable fits.  What you are looking for is a receptor-port with 25 little steel pins sticking out of it in two rows.  The "shorter" row should have about 12 pins, and the "longer" row should have 13 (total of 25).  Simply place your NULL modem against the receptor port matching it row-for-row and push the NULL modem gently towards the PC until it won't go in any further (both the NULL modem and the receptor port will have two rows that look alike, except the NULL modem has "holes" instead of pins).  Please refer to the machine users manual for specific information.


Movement Commands

The most important piece of information necessary to get you going is the format of commands that you need to send to the drive unit. YOU ARE SENDING A STRING to the drive unit, the only difference between sending this string to any other machine, and the drive unit is the terminating character!

A sample of the string you'd send to the robot:

char *buf = "MI 400,0,0,0,0,0"; Again, remember that this string is terminated with a '\0', and when you write it to the port, the default terminating character is '\n'. Because the robot was built to operate on a DOS machine, it requires a special terminating command. Again, the default terminating character on most systems is "\n", which needs to be replaced by a "\r\f". Refer to the Move Master II Instructional Manual located in the Real Time Lab for information pertaining to the control instructions and format for the Move Master robot.


Talking to the Robot

Now that you have the string you'd like to send to the robot, you need to actually send it to the robot. However, first you need to get the file descriptor of the device that you'll be sending this data to. In UNIX, everything is a FILE! What this means is the fact that when Unix programs do any sort of I/O, they do it by reading or writing to a file descriptor. A file descriptor is simply an integer associated with an open file. But that file can be a network connection, a FIFO, a pipe, a terminal, a real on-the-disk file, or just about anything else. Everything in Unix is a file! So when you want to communicate with another program over the Internet you're gonna do it through a file descriptor.

Getting the file descriptor is easy, however, here we need to make few considerations. Are we going to reinvent the wheel (perhaps greater control over the device would be achieved here), or are we going to use the facilities provided to us?

The two methods are shown here, I'll explain the difference once you see what the code looks like:

int bytes_written, bytes_read; Read further on as to the functionality of these two variables, however, their descriptive names should provide some insight as to what they're for. int fd0; This will be the file descriptor through which we will communicate. fd0=open("/dev/ptr0",2); We open the device, in this case the parallel port. The "2" means its read or write. bytes_written = write(fd0,buf,sizeof(buf)); Here we're writing to the file descriptor that the open() returned, the buf is the string we'd like to send. We can send ANY type of data, in this case I chose a string. Note that bytes_written is assigned the return value of write(). Write returns the number of bytes it wrote to the port in this pass.

And finally, we close the file once we're through with it.

close(fd0); Similarly, the reading from the port requires you to declare a file descriptor through which we will be receiving the data.
        
        int fd1;
And again, we need to get the file descriptor of the device we'd like to talk to. fd1=open("/dev/ptr1",2); Now that we have the file descriptor, we can read from it. bytes_read = read(fd1,buf,sizeof(buf)); Again, we're assigning the return value of read() to bytes_read. This allows us to make sure that we've read as much data as we have sent.

And finally, we close the file once we're through with it.

close(fd1); Just like in the Wheel method, we need the file descriptor through which we will communicate FILE* parprint;

parprint=popen("lp -P ps ","w");

Now we get the file descriptor returned by the popen() call. The popen() function opens a pipe to a "device", and in this case, it’s the printer program. Take note that I'm assuming that the "printer" (or the device) that you want to print to is installed, and that "ps" or any other designation for the printer device is correct and an existing one. fprintf(parprint,"%s\n",buf); As you can see, we're using standard fprintf() and fscanf() function calls to write and read from his file descriptor. If you really dwell deeper into the two methods, you'll see that they're pretty much the same, requiring just as much coding on both parts. However, the benefits of using the easy method is that you don't have to determine which port to write to, but instead you let the "pipe" which you just opened determine where to send the data.

Finally, we close the pipe once we're through using it.

pclose(parprint);

Sample Code

Now that you have seen the two different methods, let me show you an example of the "Wheel Method". In this case you will have to run the executable created by compiling the listener.c program prior to running the executable created by the sender.c program. You must also make sure that the device (in this case "ser0") is an actual device that you want to use. Look through the /dev directory for the device list. This is why I called this method "Wheel Method". You have no clue as to which one of the 50+ device names located in the /dev directory you want to use. Note that this code has been tested on a SunOS machines, as well as an AIX machine using the pre-installed cc compiler. You should be able to compile this source code on almost any machine, however take care not to misplace your header files, which could be located in subdirectories on different machines. The code here is mono-directional. It either only-sends or only-reads, but not both. I leave that up to you to figure out.

 Here is the Sender source code:

#include <errno.h> /* Needed for error tracking */
#include <io.h>
#include <stdio.h>
 
char *test_string = "Hello World!"; /* Some arbitrary string */
 
main()
{
  int fd0; /* File descriptor through which we will be talking */
 
  fd0=open("/dev/ser0",2); /* Open the device for communication */
  printf("Open Error: %d\n",errno); /* Always a good idea to see if open was successful */
 
  write(fd0,move_string,sizeof(test_string)); /* Write the string to the device */
  printf("Write Error: %d\n",errno); /* Again, always a good idea to know if error occurs */
}
 Here is the Listener source code:
#include <errno.h> /* Needed for error tracking */
#include <io.h>
#include <stdio.h>
 
char response_string[100]; /* I'm assuming the other end will send a string */
int num_bytes = -1; /* Keeps track of how much data was received */
 
main()
{
  int fd0; /* File descriptor through which we will be talking */
 
  fd0=open("/dev/ser0",2); /* Open the device for communication */
  printf("Open Error: %d\n",errno); /* Always a good idea to see if open was successful */
 
  while(num_bytes <= 0) {
        num_bytes = read(fd0, response_string, sizeof(buf));
  } /* Loop until we read at least 1 byte of information */
 
  response_string[num_bytes] = '\0';
  printf("Received: %s\n",response_string);
}

More Sample Code

For completion I have decided to include this piece of source code so you can see how you would do device "writes" the easy way. Note again that I'm assuming that the "ps" is an actual printer on your machine, and that "lp" is the program which controls the printing. Compile this source code on a QNX operating system using its cc compiler. Again, if you have any questions on how to use the cc compiler, refer to the user manuals.

 Here is a sample Print program:

#include<stdio.h> /* Always a good idea to include the basics */
#include<stdlib.h>
#include<string.h>
 
void main()
{
FILE* pipeptr; /* Again, a file descriptor through which we will talk to the device */
char test_string = "Hello World!"; /* Sample test string */
 
pipeptr=popen("lp -P ps ","w"); /* Opening a pipe to an external process */
fprintf(pipeptr,"%s\n",test_string); /* Printing the test string to the device */
 
pclose(pipeptr); /* Always close your files when you're done with them */
}
Note that reading from the device would work similarly, except in this case you would use fscanf() to read from the device. Take note that here we're printing to the parallel port which may or may not be capable of sending information back. Finally, the source code provided here is mono-directional, ie: you can't send data back and forth simultaneously.


Help on Picking Correct Device

As I mentioned, picking the correct device from the /dev directory is a difficult task, simply because of the overwhelming number of devices in that directory.  Here is a short list of what you are looking at:
 

There are many more devices in the /dev directory.  Some of them you can guess at as to what they're responsible for, some of them you'll have to look up in the manual corresponding the the machine.  Whatever way you go about picking your device, please make sure you make a backup of your work!