- A middle layer between programs and the Brainstem board.
- Download it from here.
- untar the tar ball.
- Check your
disk quota (quota -v)
3.1 Build basic library of Brainstem in Debug mode.
fedora $ cd brainstem
fedora $ ./build_basic.sh
|
example 3: building the basis of brainstem, good time for PIZZA break
3.2 Build the first program for the robot, understand the Brainstem Makefile structure
cd to aGarcia/
fedora $ cd aGarcia/
fedora $ ./make_cs23_debug.sh
|
example 4: Build the cs23Garcia project.
3.3 Here let's try.
- Goto brainstem/aDebug/aUnix/ARM/,
- Copy (scp) the cs23Garcia binary to the
/home/acroname/aBinary/ directory on your robot
- Run it using the run script! It takes executable
name as
first arg and arguments to executable as subsequent args.
3.4 Understand the Project Makfile
#!/bin/sh
# This is a fast tool to launch the building of a Garcia App..
make -f makeCS23.Linux CC=/net/wbc/src/gumstix/gumstix-oe/tmp/cross/bin/arm-angstrom-linux-gnueabi-gcc CCP=/net/wbc/src/gumstix/gumstix-oe/tmp/cross/bin/arm-angstrom-linux-gnueabi-g++ ARCH=ARM debug
|
example 5: Code for make_cs23_debug.sh
# makefile: host/aGarcia/makeapp.Linux
SHELL := /bin/sh
CC := gcc
CCP := g++
ROOT := ../
PROGNAME := cs23Garcia
INCLUDES := -IaCommon \
-IaUnix \
-IaGarcia \
-IaGarciaApp \
-I../aIO/aCommon \
-I../aIO/aUnix \
-I../aUI/aCommon \
-I../aUI/aUnix \
-I../aStem/aCommon \
-I../aStem/aUnix \
-I../aTEAvm/aCommon \
-I../aTEAvm/aUnix \
-I../aCommon \
-I../aDebug/aUnix \
-I../aDebug/aSystem \
-I../aUnix
#don't include X11 libraries for ARM
ifeq ($(ARCH),ARM)
LIBRARIES := -laIO \
-laUI \
-laStem \
-laGarcia \
-lpthread \
-lm
else
LIBRARIES := -laIO \
-laUI \
-laStem \
-laGarcia \
-lpthread \
-lm \
-L/usr/X11R6/lib \
-lX11
endif
VPATH := aCommon:aUnix:cs23Garcia:../aCommon:../aTEAvm/aCommon:cs23Garcia/vidcat:cs23Garcia/sf
SOURCES := aAssert.c \
aMemHandleDebug.c \
aUtil.c \
aGarciaGeom.c \
v4l.c \
simplevideo.c \
main.c \
common.c \
socketclient.c
CPPSOURCES := acpException.cpp \
garcia.cpp
include $(ROOT)make_program_cpp.Linux
|
example 6: Code for makeCS23.Linux
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include "garcia.h"
#include "./vidcat/simplevideo.h"
#include "./sf/sf_util.h"
int main(int argc, char** argv) {
init(NULL);
int i = GarciaTurn(45 / 180.0f * 3.1415926);
destroy();
}
|
example 7: main.c
4 Introduce to the ROBOT side API
4.1 Brainstem side.
You can actually modify garcia.cpp for your own usage. For example you could bypass the acroname
API and talk directly to the motors. The following example code shows some more sophisticated interactions with the robot using
the API, it is located in main2.c (make sure to change the makefile to use main2.c rather than main.c).
#include "garcia.h"
...
...
// Move around with blocking API
float distance = 0.2;
int i = GarciaWalk(distance); // Walking forward for 20 centimeters
int j = GarciaTurn(20 / 180 * 3.14159); // Turn right for 20 degrees
if ((!i)&&(!j))
printf("Succeeded");
// Move around with non blocking API
int i = GarciaWalkN(distance); // Walking forward for 20 centimeters, non blocking
int j = GarciaTurnN(20 / 180 * 3.14159); // Turn right for 20 degrees non blocking
GarciaStop();
// Read in sensor data.
float d = GarciaRangeData(BATTERY);
// Read in battery voltage, BATTERY could be
// FRONT_RANGE_LEFT, FRONT_RANGE_RIGHT...... see garcia.h for details
(Extra Stuff!!!!!!!!!!!!!!!!!!!!!!!! Hack garcia.cpp)
|
example 8: garcia brainstem API
4.1.0 Garcia API.
Following is the
Garcia API containing the exposed interfaces of the Garcia robots.
#ifndef GARCIA_UTIL_H
#define GARCIA_UTIL_H
/* This header file defines public interface to control the robot. */
/* The robot is controlled through acpGarcia class provided by */
/* acroname inside the Brainstem API. Here we use pure C function to */
/* take the place of the complicated acpGarcia class to do the same */
/* function. */
/* To use the Garcia robot, user should call init() first in the start */
/* of his code. When user's program is about to quit, user should call destroy() */
/* function. */
/* Any question related to Garcia.cpp and garcia.h should be reported to
pway@cs.dartmouth.edu(Wei Pan) */
#ifdef __cplusplus
extern "C" {
#endif
// This is a blocking function, which means that the function
// will not return before the robot finish the walk.
// The float is the distance the user want to robots to move.
// The unit of the distance is ADJUSTABLE, currently it is centimeter.
//! param f the distance to go, negative number for backward.
//! return a status value, 0 means success, otherwise failure(may be robots are blocked by wall so they can't move)
int GarciaWalk(float);
// This is also a blocking function. The robot will turn acording to
// the angle value the user pass in as a parameter. The current unit is
// rad. So PI means turn 180 degrees.
// param f the angel to turn, negative number for counter-clockwise
// return a status value, 0 means success, otherwise failure(may be robots are blocked by wall so they can't move)
int GarciaTurn(float);
// The following two functions is the non-blocking version of the up two
// functions, which means they will return immediately after you call them.
// The robot will execute the movement concurrently.
// If you call them multiple times, the robot will queue each movement and
// execute them in FIFO order.
int GarciaTurnN(float);
int GarciaWalkN(float);
// This function is to stop all movements of the robot and clear the movement
// queue. The robot stop immediately when you call it.
void GarciaStop();
// NOTICE: The upper two function will return a value indicating the status
// of the robots. 0 means works fine. Otherwise there is a problem.
// The return value and problems has a relation:
/*
Please refer to
brainstem/aCommon/aErr.h
for full list of error codes meanings.
*/
// Before the program, please call init(), and pass in the
// laptop server address, so there is a heart beat thread which will
// connect to the server address every certain time interval.
// The two following constant defines the properties of the heart beat thread: // the HEARTBEAT_PORT is the port for the server to receive the heart beat.
#define HEARTBEAT_INTERVAL 60
#define HEARTBEAT_PORT 5656
//! param c the ip address of the server side code,
// ( this IP address will be used to send back a HEARTBEAT signal.)
void init(char*);
// Before your program quit, please call destroy()
void destroy();
// parameters for GarciaRangeData()
#define FRONT_RANGE_LEFT 1
#define FRONT_RANGE_RIGHT 2
#define SIDE_RANGE_LEFT 3
#define SIDE_RANGE_RIGHT 4
#define REAR_RANGE_LEFT 5
#define REAR_RANGE_RIGHT 6
#define BATTERY 7
// Garcia has 8 rangers, however, we here only use 6 of them. Pass in which ranger
// you want to set type to REAR_RANGE_LEFT or SIDE_RANGE_LEFT, it will return the range
// information.
//! param type see #define
//! return a reading from sensors
float GarciaRangeData (int type);
#ifdef __cplusplus
}
#endif
#endif
|
example 8: garcia brainstem API
4.2 Camera side
The camera API is just a modified wrapper for vidcat, a utility
to capture a picture/image from the webcam. Google to learn more about it if you want.
The video architecture for the Gumstix system is known as video4linux,
and vidcat is a utility of video4linux. More information can be
found at link.
One important issue is that *YOU DON'T NEED TO* free() the returned
pointer once you call this function and finish processing the result.
You can just keep calling it again and again to update your picture.
The code is at brainstem/aGarcia/cs23Garcia/vidcat/
One
major issue we have is that the transimission of video feedback is too
slow(300k per frame). It will be great if you guys would like do
an extra credit on video compression using some open source compression
code.
v2.0 extra
ppm format and debug with ppm code.
================================>example of a ppm file
P3
320 240
255
RRR GGG BBB RRR GGG BBB RRR GGG BBB RRR GGG BBB
.......
================================>example of a ppm file
See an
example.
#include "vidcat/simplevideo.h"
.....
......
//! param device usually is "/dev/video0"
//! param palette usually is VIDEO_PALETTE_YUV420
// char* OpenDeviceGetImageAndClose(char* device, int palette);
char* data = OpenDeviceGetImageAndClose("/dev/video0" , VIDEO_PALETTE_YUV420P);
|
example 9-1: Video API
#include "vidcat/simplevideo.h"
int cam() {
char* data;
FILE *fp;
int i,j=0,m=0;
data=OpenDeviceGetImageAndClose("/dev/video0",VIDEO_PALETTE_YUV420P);
fp=fopen("test.ppm","w");
fputs("P3\n",fp);
fputs("320 240\n",fp);
fputs("255\n",fp);
for(i=0; i<320*240*3;i+=3) {
fprintf(fp,"%03d %03d %03d ",data[i+2], data[i+1],data[i]);
j++;
if(j >= 18) {
fprintf(fp,"\n");
j=0;
}
}
printf("done!\n");
fclose(fp);
return 0;
}
|
example 9-2:Capture a Picture to a PPM File API
4.3 Sensor side (Serial Forwarder).
The wireless sensor network research has been a hot topic for a
very long time. People built sensors like the tMote all over the world,
and hope these small, low power consumption sensing devices will be a
important component in human activities. These devices do not only
sense data (humanity, movement...), they also have built-in CPU and a
very short range wireless communication interface.
The wireless sensor is connected to a computer through a USB
interface. In detail, the USB interface is treated by the operating
system as an additional serial port. A commonly used program in
wireless sensor research is called "Serial Forwarder", which connects
to a sensor through this USB interface. Also, a client program could
connect to a serial forwarder through TCP/IP sockets. The "Forwarder"'s
job is to send everything it receive from the sensor to the clients
connected to it through sockets; and send everything clients send to it
to the sensor.
All these sensors on the robots play a role as an antenna, they pick
what every in the radio and send it to the serial forwarder, and to its
clients. Vice versa, they receive packets from serial forwarder and
broadcast them over the radio.
The API we will provide on the robot side is a wrapped version of
the serial forwarder a.k.a asf. Download here for asf.
This
program, asf, should be run as a daemon (a process runs in the
background). Currently the sensor is mounted to /dev/ttyUSB0, the
baudrate is 57600, and the platform is telos.
To run asf as a background daemon, simply type
garcia $ ./asf [PORT] /dev/ttyUSB0 57600 > asf.log 2>&1 &
- Since asf
will print stuff out, it will be better to ask asf to print everything
to a.log or any other file rather than print on the screen.
- Choose
your favorite port (larger than 5000), and when you write client code,
don't forget to connect to the port you specify here.
- You can actually listen to the packet use sflisten program.
To do so,
fedora $ make
fedora $ sflisten [ROBOT'S IP] [PORT]
|
example 10: Serial Forwarder Initializer
The basic component to go over the radio for wireless sensors are packets. A packet is a small size array of raw data.
06 01 08 8b ff ff ff ff 04 7d fc 00 fc 00 17 00
=====
= [key][key] [magic]
changes always 4 |
example 11: A Treasure Key Packet
06 01 08 8b ff ff ff ff 04 7d fc 00 **** 17 00
=====
= [key][any] [magic]
changes always 4 |
example 12: A Treasure Key Packet With the Previous Key
5 Introduce to the CLIENT side API
5.1 Sensor packet reader.
It will be ok if you could implement your own code to connect to
the serial forwarder on the robot side, but you could also use the
following API, the source code for this API is at here link.
You
are supposed to create two threads, one for sending data and one for
receiving data from the asf program running on the robot side. Here are
examples for these two threads:
#include "sfsource.h"
...
...
//! \brief thread to keep listening to the robot sensor reading.
void* ReadSensorData(void* ptr) {
int fd = open_sf_source(robot_ip, SENSOR_PORT); // open a connection to the serial forwarder with the robot_ip and SENSOR_PORT
while (fd < 0) {
LOG("Couldn't open serial forwarder\n");
sleep(5);
fd = open_sf_source(robot_ip, SENSOR_PORT);
}
int i;
while(1) { //sniff for ever
int key = HearKey(fd);
if (key == -1) {
//! we lost connection
LOG("Error in reading sensor data!\n");
break;
}
if (key > 0) {
//! That's our treasure key.
}
}
close(fd); // never reaches here
}
//! brief broadcast your packet to the sensor in a single thread
void* WriteSensorData(void* ptr) {
sleep(2);
int fd = open_sf_source(robot_ip, SENSOR_PORT);
while (fd < 0) {
LOG("Couldn't open serial forwarder\n");
sleep(5);
fd = open_sf_source(robot_ip, SENSOR_PORT);
}
for (;;) {
BroadcastKey(fd, 10); // 10 is the key to send out
sleep(5);
}
}
|
//These are actually the interface for client side sf api.
// This is a modified version from serial forwarder shipped with TinyOS 1.X
int open_sf_source(const char *host, int port);
/* Returns: file descriptor for serial forwarder at host:port, or
sf -1 for failure
(see init_sf_source for description of platform handling)
*/
int init_sf_source(int fd);
/* Effects: Checks that fd is following the serial forwarder protocol.
Use this if you obtain your file descriptor from some other source
than open_sf_source (e.g., you're a server)
Sends 'platform' for protocol version '!', and sets 'platform' to
the received platform value.
Modifies: platform
Returns: 0 if it is, -1 otherwise
*/
void *read_sf_packet(int fd, int *len);
/* Effects: reads packet from serial forwarder on file descriptor fd
Returns: the packet read (in newly allocated memory), and *len is
set to the packet length
*/
int write_sf_packet(int fd, const void *packet, int len);
/* Effects: writes len byte packet to serial forwarder on file descriptor
fd
Returns: 0 if packet successfully written, -1 otherwise
*/
// This is a wrapper for sending packet. It actually calls write_sf_packet().
// param fd the socket opened by open_sf_source
// param key the key to send out
// return 1 if successful, 0 otherwise
int BroadcastKey(int fd, int key);
// This is a wrapper for receiving packet. It actually call read_sf_packet().
// param fd the socket opened by open_sf_source
// return the key( >0 ) if the program hears a key, -1 means network error
int HearKey(int fd);
|
example 13: Serial Forwarder Client Side API
5.2 Treasure Key
The game, and everything.,,
5.3 GTK+,Glade-2
See Jonghoon's tutorial on GTK here.
There is another demo from last year on GTK+ and Glade-2 here.
This makefile information below is old and may not be necessary if you use pkg-config like Jonghoon says to in his tutorial above.
It may be hard to use auto-conf and auto-make. You can write a make file to compile a GTK program as follows:
# Simple makefile with GTK support
CC=gcc
CFLAGS=-g -ggdb -Wall -DPNG_NO_MMX_CODE -I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/freetype2 -I/usr/include/libpng12 -lc -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangocairo-1.0 -lfontconfig -lXext -lXrender -lXinerama -lXi -lXrandr -lXcursor -lXcomposite -lXdamage -lpango-1.0 -lcairo -lX11 -lXfixes -lgobject-2.0 -lgmodule-2.0 -ldl -lglib-2.0 -lpthread
SOURCES=src/interface.c src/support.c src/main.c src/callbacks.c src/interface.c
gtk:$(SOURCES)
$(CC) $(CFLAGS) $(SOURCES) -o $@
|
example 14: Manually Compile GTK+ Program with Makefile
********
YOU MAY FAIL
TO COMPILE YOUR PROGRAM WHEN USING YOUR OWN MAKEFILE, THE PROBLEM IS
THAT YOU NEED TO COMMENT OUT A LINE IN MAIN.C OF YOUR GUI CODE, THAT
LINE LOOKS LIKE:
add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
Appendix
Two sensors (treasures) are placed in LAB 001, The key to open the first one (closer to the door) is 10, the
key you should get from it is 5; the other one 5 and 12. Once each sensor receives the key, it will
keep sending out packets for 5 seconds. These are indentical to sensors
we will use in the DEMO session two weeks later.