CS23 Winter'09 - Team project technical information

Information available from here:

Please report any errors that you find!


Using Subversion (SVN) to manage your project

An authenticated network-accessible SVN repository has been created for each team. It is hosted on one of the Computer Science servers and you will need to use your DND account name and password to make your initial connection with your team's repository.

The standard reference book Version Control with Subversion (v1.5) has been available online by its authors, or you may like to download your own copy. The book also provides a High-Speed Tutorial.

When reading the documentation, there is no need to focus on material describing how to initially create a repository, and this has been done for you. Instead, you should just focus on the very common commands that you'll require to get started - checkout, import, update, and commit.

Each repository has a URL from which it can be initially checked out; once that's done, the URL is remembered for future operations. Your team's URL will be named something like:

https://svn.cs.dartmouth.edu/classes/cs23pink

The following is a suggested list of commands to commence. The lines commencing with the '#' symbol are just comments; other lines are commands you'll enter:

#
# Create a new directory for CS23 - you probably already have one
#
mkdir ~/cs23
#
# Change into this directory
#
cd ~/cs23
#
# Make your first 'connection' with your team's SVN repository.
# Your exact DND name and password provide the authentication.
# Note that you may need single quotes to enter any spaces and periods in your name.
#
svn checkout https://svn.cs.dartmouth.edu/classes/cs23pink --username 'Full D.N.D. Name'
#
# The above command will create a new directory for you. Change into this directory.
#
cd cs23pink
#
# You may now create and edit your project's files in this directory -
# but before you commence your team should design the working layout, e.g. :
#
mkdir, create, edit, copy, ....
#
# Once you have your directory layout (partially) created, import it into
# your repository.  Note the -m switch to provide a log message explaining your actions:
#
svn import . https://svn.cs.dartmouth.edu/classes/cs23pink -m "initial import"
#
# If all has gone well, your initial files will be copied to the repository.
# (Bravely) you may now delete them, and recover an "official" copy of them from svn:
#
svn checkout https://svn.cs.dartmouth.edu/classes/cs23pink/trunk .
#
# Throughout the project, after you've made changes to your files, update your
# your copy of the project to ensure that your changes don't conflict with those of others: 
#
svn update
#
# And finally commit your changes (and any merged changes with others) into the repository:
#
svn commit -m "removed the final bug"


Team websites

Each team has their own website below the CS23 Linux account. Each team's URL is of the form:

http://www.cs.dartmouth.edu/~cs23/W09/cs23pink/

This URL corresponds to the Linux directory /net/class/cs23/public_html/W09/cs23pink. Each website's starting page should be named index.html, and that file should use relative links to other HTML files, images, whatever, that your team wishes to be kept in that directory and presented below that URL.

Each team also has a specific Linux group, whose membership is (obviously) the team members.

As you add and modify files that you wish to viewed via the web, you'll need to ensure that other group members may modify them, and that the "rest of the world" can see them. We can use the Linux chmod command to ensure the correct permissions are in place:

#
# Use a shell environment variable to save typing:
#
set WWW /net/class/cs23/public_html/W09/cs23pink
#
# Create a new directory within your team's website:
#
mkdir  $WWW/newdir
chgrp  cs23pink  $WWW/newdir
chmod  771  $WWW/newdir
#
# Add a new file to your team's website:
#
cp  newfile.html  $WWW
chgrp  cs23pink  $WWW/newfile.html
chmod  664  $WWW/newfile.html

To save typing, and to "automate" this for all team members likely to contribute to your webpages, create a target in your project's top-level Makefile, so that the command make publish will perform all of the necessary commands.


Starting materials

From the following two directories, one for the Garcia Robot Controller and one for the on-board Garcia software, you will only need to take copies of the files listed in red. You are, of course, welcome to look at and copy any other files, though that shouldn't be necessary to get started:

/net/class/cs23/W09project/grc
                             |-- Makefile
                             |-- abort.gif
                             |-- grc.c
                             |-- grc.h
                             |-- grc.tcl
                             |-- landscape.gif
                             |-- libgrc.a
                             `-- src/

/net/class/cs23/W09project/garcia
                             |-- Makefile
                             |-- brainstem/
                             |-- brainstem.tgz
                             |-- cs23garcia.h
                             |-- libcs23garcia.a
                             |-- src/
                             `-- testprogram.c


Compiling your Garcia Robot Controller on Linux

Each team will develop a C99 program, running on a Linux machine (or possibly an Apple Mac), that controls a remote Garcia robot. This program, which we'll name the Garcia Robot Controller, grc, runs on a traditional Linux (or Mac) platform, and provides a simple GUI written in Tcl/Tk. As such, we may use our well-understood Linux commands to compile and link grc in the manner of our homework exercises.

grc requires functions from the Tcl and Tk libraries, for its GUI, and a single function to connect to the robot. Communication with the robot will employ WiFi networking, through WiFi access points in Sudikoff. Because networking is such an integral part of modern operating systems, the necessary operating system system calls are provided in the C libraries provided by these operating systems (don't confuse this, however - the networking function are not part of the C99 standard library, but modern operating systems bundle many additional functions into their huge libc.a libraries).

We thus require information in our Makefile to specify which header files and libraries are required. An example is:

INCLUDES = -I/net/class/cs23/W09project/grc
LIBDIRS  = -L/net/class/cs23/W09project/grc
LIBS     = -lgrc -ltcl -ltk -lm

The W09project/grc directory is "open", and you are welcome to read or copy any of the files therein. However, it is recommended that you compile and link against the files in that directory (rather than your own copies) - if during the project any bugs are found, updates will be made to these "official" copies.

To connect to the Garcia robot, your grc should call GRCconnect, which receives the robot's hostname or Internet Protocol (IP) address, the TCP/IP communication port on which the robot should be listening, and a maximum timeout (in seconds):

#include "grc.h"

    FILE *fp;

    fp = GRCconnect( "129.170.210.z", GARCIA_PORT, 5);
    if(fp == NULL) {
        fprintf(stderr, "could not connect to robot within 5 seconds\n");
        exit(EXIT_FAILURE);
    }
    //  ... communicate with robot

    GRCdisconnect(fp);

Once the connection has been established, we may use standard C functions, such as fprintf(), fgets(), fread(), fwrite(), and fclose() to perform bi-directional communication with the robot. Note that when writing to the robot you should call fflush() after each write operation to prevent the data being buffered inside grc. Note, however, that unlike disk-based I/O using FILE *, network-based I/O is more likely to fail (the robot may move out of range), and so we need to be dilligent about checking the return values of I/O functions.


Communicating with your robot

This section outlines the necessary steps to rebooting and connecting to your robot.

  1. login to a Linux workstation in S001.

  2. ensure that you will have write access to the special serial device /dev/ttyS0:
        ls -l /dev/ttyS0
        crw-------  1  yourname  uucp 4, 64  Aug  4  2008  /dev/ttyS0
    

  3. connect your robot's serial cable to the serial connector on the Startgate board (in the lid of your robot), and then the other end to the Linux workstation's serial connector (at the rear).

  4. from a Linux shell window, run the program /usr/bin/minicom. It will clear the window and say "Initializing modem..."

  5. turn on the robot's power (a small ON/OFF switch near the top of the robot). You should see lots of Linux diagnosic messages in your minicom window as the robot reboots.

  6. when the rebooting sequence has finished, you should see a standard "garcia login:" banner. Login as root (being very careful not to accidently delete any wrong files!).

  7. you can now run some standard Linux commands (as on a workstation).

  8. to date, you've been communicating with the robot over the serial cable. We now wish to connect to it over the Dartmouth Registered WiFi network.
    Run the shellscript   /root/changewifi2manage.sh   It may ask you to first remove   /etc/dhcpc/dhcpcd-wlan0.pid   - do so, and again run /root/changewifi2manage.sh.
    Then run the command   ifconfig wlan0   to determine the IP address assigned to your robot.

  9. now, from the Linux workstation or your laptop, you should be able to login to the robot over WiFi:
        ssh  root@ROBOTS-IP-ADDRESS
    

  10. it's now safe to terminate the minicom program and remove the serial cable.

You're now ready to copy your on-board programs to the robot using scp.

Team color Ad-Hoc address Registered address
Orange 192.168.3.x 129.170.210.228
Red 192.168.3.x 129.170.210.231
Blue 192.168.3.x 129.170.210.x
Black 192.168.3.x 129.170.210.229
Yellow 192.168.3.42 129.170.210.230


Compiling and installing software to execute on the Garcia robot

Developing software to execute on the Garcia robot is much more difficult, and probably something quite new for many. The processor on the robot is not a standard Intel Pentium, like our workstations and laptops - it's an ARM processor, used in many embedded devices, even the iPhone. In addition, we do not compile programs on the robots themselves, as their capapcity is too limited to host all necessary compiler programs and files.

Instead we compile programs for the robots on Pentium-based machines, informing the compiler and linker that the target device uses a different architecture. This activity, term cross-compiling, may be performed in a few different ways. The simplest is use a completely different compiler and linker, running on the Pentium, which has knowledge of the target architecture and has local copies of the necessary library files.

So, to compile a simple program for execution on the robots, we require a command such as:

# Cross-compile a single C file:
#
/usr/local/arm/3.3.2/bin/arm-linux-gcc -o helloworld helloworld.c
#
# OK, what did we just produce?
#
file helloworld
helloworld: ELF 32-bit LSB executable, ARM, version 1, dynamically
linked (uses shared libs), for GNU/Linux 2.0.0, not stripped

The compilation and linking of programs that will execute on the robot, on our Linux platform, is rather messy. Not only do our C files need to be cross-compiled (as described above), but we to include many Garcia-specific header files and link against many Garcia-specific libraries, that scattered around a number of directories.

To simplify the process, the Makefile in the directory /net/class/cs23/W09project/garcia should be used (at least as a starting point).

The directory /net/class/cs23/W09project/garcia also contains the cs23garcia.h header file, which declares the constants and functions forming the robot's API for our project. The header file should be included by all of your on-robot programs.

Once we have cross-compiled and linked our ARM-based executable, we need to copy it to the robot. As is typical on embedded Linux platforms, only a single user is supported on the robots, that user being the traditional administrator account, or root account. While we work on our workstations and laptops as "ourselves", we work as root on the robots.

We are able to connect to the robots using the ssh command, just as we do to connect between standard Unix machines, and using the related scp command to copy files.

# Copy our executable to the robot
#
scp  helloworld  root@ROBOTS-IP-ADDRESS:/tmp
enter root's password for the robot
#
# Login to the robot
#
ssh  root@ROBOTS-IP-ADDRESS
enter root's password for the robot
cd  /tmp
./helloworld
output of helloworld appears
exit

Of course typing in these repetitive commands becomes tedious, and so we prefer to develop a Makefile so that we may just type make robot and make push (or similiar) to perform common tasks.

When you commence developing on-board programs that make use of the cs23garcia library, you will need a more complicated command on the robots just to run your programs. It is necessary to inform Linux where it can locate additional libraries, at runtime. We can do this with the following shellscript:

#!/bin/sh                                                                       
                                                                                
if [ ! -r garcia.config ]                                                       
then                                                                            
    cat << EOF > garcia.config                                                  
# Configuration file for Garcia robot                                           
# Define the serial port of the Stargate attached to the Stem                   
portname=tts/1                                                                  
# Define the serial baud rate                                                   
baudrate = 38400                                                                
EOF                                                                             
fi                                                                              
                                                                                
LD_LIBRARY_PATH="/usr/local/acroname/brainstem/aBinary" $@    

Copy this shellscript to /root/runit on your robot, make the shellscript executable, and then execute your program with  ./runit ./yourprogram [arguments]


Capturing and displaying images from the camera

The function garciaSnapshot() takes a snapshot using the robot's on-board camera and, on success, returns a pointer to the captured data. All images will be the same size, 320 pixels wide, 240 pixels high, with 24 bits of color information per pixel. Thus the size of each captured image is (320 * 240 * 3) = 230400bytes (225KBytes).

The captured image has very little structure - it is just a vector of unsigned bytes, with each byte's value (0..255) representing the blue, green, or red intensity of each pixel. For example:

    data[0]  is the  blue intensity of pixel[0][0];
    data[1]  is the green intensity of pixel[0][0];
    data[2]  is the   red intensity of pixel[0][0];
    data[3]  is the  blue intensity of pixel[0][1];
      ....

This 24-bit data format cannot be displayed by our Garcia Robot Controller GUI.
Instead, we'll need to convert the representation to either the GIF format or the
Portable Pixel Map format with one of these functions:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>

#include <gd.h>

bool raw24bit_to_gif(FILE *outfp, uint8_t *raw, int width, int height)
{
//  ENSURE THAT WE RECEIVED REASONABLE PARAMETERS
    if(outfp == NULL || raw == NULL || width < 1 || height < 1)
	return false;

//  CREATE THE IN-MEMORY IMAGE
    gdImage	*im	= gdImageCreateTrueColor(width, height);

    if(im == NULL)
	return false;

    int p = 0;
    for(int h=0 ; h<height ; h++)
	for(int w=0 ; w<width ; w++, p+=3)
	    gdImageSetPixel(im, w, h, 
			gdImageColorResolve(im, raw[p+2], raw[p+1], raw[p]));

//  WRITE THE IMAGE TO DISK AS A GIF FILE
    gdImageGif(im, outfp);

//  DEALLOCATE SPACE ALLOCATED HERE
    gdImageDestroy(im);
    return true;
}


bool raw24bit_to_ppm(FILE *outfp, uint8_t *raw, int width, int height)
{
//  ENSURE THAT WE RECEIVED REASONABLE PARAMETERS
    if(outfp == NULL || raw == NULL || width < 1 || height < 1)
	return false;

    fprintf(outfp, "P6\n%d %d\n255\n", width, height);
    for(int p=0 ; p<width*height*3 ; p+=3)
	fprintf(outfp, "%c%c%c", raw[p+2], raw[p+1], raw[p]);

    return true;
}