Xanthus Remote Software Page

    We are releasing to the public domain some of the low-level software written to interface with the Xanthus Remote hardware.  The CMUCam is connected via a standard serial port.  The I2c sonar modules are connected via the USB I2C/IO interface, which is connected via USB port.  The USB I2C/IO is accessed via a DLL API provided by DevaSys.

    The motor control circuit is simply accessed via parallel port, so we will not bother providing source code examples for supporting the below circuit unless specifically requested.  Documentation of the sonar modules and USB I2C/IO are included on this page for reference.

    All example source code is written in Borland Delphi (Object Pascal).

Example source code


unit CMUTypes;

//Copyright 2003,2004 Artificial Ingenuity, LLC, all rights reserved

interface

type
  TCPacket = packed record
    Tag        : byte;  //always 255 to show start of message
    C          : char;
    x1,y1,x2,y2: byte;
    pixels     : byte;
    confidence : byte;
    end;
  TFPacket = packed record
    newframe: byte;  //always = 1
    lines   :packed array [1..144] of packed record
      newcol  : byte;  //always = 2
      columns: packed array [1..176] of packed record
        r,g,b: byte;
        end;
      end;
    endframe: byte;  //always = 3
    end;
  TMPacket = packed record
    Tag        : byte;  //always 255 to show start of message
    M          : char;
    mx,my      : byte;  //middle of mass
    x1,y1,x2,y2: byte;
    pixels     : byte;
    confidence : byte;
    end;
  TNPacket = packed record
    Tag        : byte;  //always 255 to show start of message
    N          : char;
    spos       : byte;  //servo position
    mx,my      : byte;  //middle of mass
    x1,y1,x2,y2: byte;
    pixels     : byte;
    confidence : byte;
    end;
  TSPacket = packed record
    Tag              : byte;  //always 255 to show start of message
    S                : char;
    Rmean,Gmean,Bmean: byte;
    Rdevi,Gdevi,Bdevi: byte;
    end;

implementation

end.

unit CMUCamU;

//Copyright 2003,2004 Artificial Ingenuity, LLC, all rights reserved

interface

uses
  CMUTypes;

const
  ActThreshold: integer = 50;
  TrackThreshold: integer = 26;

procedure ResetCMUCam;

procedure ClearInputBuffer;

procedure SetWindow(spec: string);

procedure SetPollMode;

procedure SetRawMode;

function ReadSPacket(var SPacket: TSPacket): boolean;

function ReadCPacket(var CPacket: TCPacket): boolean;

function ReadMPacket(var MPacket: TMPacket): boolean;

function ReadNPacket(var NPacket: TNPacket): boolean;

function ReadFPacket(var FPacket: TFPacket): boolean;

function StartTracking: byte;  //returns confidence of lock-on

type
  CMU_Action = (CA_Nothing, CA_Backward, CA_Forward, CA_Left, CA_Right);

function ActionToTrack: CMU_Action;

function TrackColor(var MPacket: TMPacket): byte;  //returns confidence level

procedure SendToCMUCam(s: string);

implementation

uses
  SysUtils, Forms, OoMisc, AdPort;

var
  ComPort: TApdComPort;

Const
  DayVal      = 1.0;
  HourVal     = DayVal / 24.0;
  MinVal      = HourVal / 60.0;
  SecVal      = MinVal / 60.0;
  MilliSecVal = SecVal / 1000.0;

procedure DoDelay(ms: integer);
var
  targetTime: TDateTime;
begin
  targetTime := Now + (ms * MilliSecVal);
  repeat
    Application.ProcessMessages;
    until Now > targetTime;
end;

procedure ResetCMUCam;
begin
  SendToCMUCam('RS'+char(13));
  DoDelay(200);
  ClearInputBuffer;
end;

procedure SetWindow(spec: string);
var
  s: string;
begin
  s := 'SW';
  if length(trim(spec)) > 0 then
    s := s + ' ' + trim(spec);
  SendToCMUCam(s+char(13));
end;

procedure SetPollMode;
begin
  SendToCMUCam('PM 1'+char(13));
end;

procedure SetRawMode;
begin
  SendToCMUCam('RM 3'+char(13));
end;

procedure SendToCMUCam(s: string);
begin
  ComPort.PutString(char(13));  //do this to return to command mode
  DoDelay(50);                 //give it a few millisecs to go into command mode
  ClearInputBuffer;             //get rid of any result
  ComPort.PutString(s);
end;

procedure ClearInputBuffer;
var
  c: char;
begin
  while ComPort.CharReady do
    c := ComPort.GetChar;
end;

function AdvanceToPacket: boolean;
var
  c: char;
begin
  result := true;
  while ComPort.CharReady do begin
    if ComPort.PeekChar(1) = char(255) then
      exit;
    c := ComPort.GetChar;
    end;
  if not ComPort.CharReady then
    DoDelay(100);
  result := ComPort.CharReady;
end;

function ReadSPacket(var SPacket: TSPacket): boolean;
begin
  FillChar(SPacket, SizeOf(SPacket), 0);
  if AdvanceToPacket then
    ComPort.GetBlock(SPacket, SizeOf(SPacket));
  result := (SPacket.Tag = 255) and (SPacket.S = 'S')
end;

function ReadCPacket(var CPacket: TCPacket): boolean;
begin
  FillChar(CPacket, SizeOf(CPacket), 0);
  if AdvanceToPacket then
    ComPort.GetBlock(CPacket, SizeOf(CPacket));
  result := (CPacket.Tag = 255) and (CPacket.C = 'C')
end;

function ReadMPacket(var MPacket: TMPacket): boolean;
begin
  FillChar(MPacket, SizeOf(MPacket), 0);
  if AdvanceToPacket then
    ComPort.GetBlock(MPacket, SizeOf(MPacket));
  result := (MPacket.Tag = 255) and (MPacket.M = 'M')
end;

function ReadNPacket(var NPacket: TNPacket): boolean;
begin
  FillChar(NPacket, SizeOf(NPacket), 0);
  if AdvanceToPacket then
    ComPort.GetBlock(NPacket, SizeOf(NPacket));
  result := (NPacket.Tag = 255) and (NPacket.N = 'N')
end;

function ReadFPacket(var FPacket: TFPacket): boolean;
begin
  FillChar(FPacket, SizeOf(FPacket), 0);
  result := false;
end;

function StartTracking: byte;  //returns confidence of lock-on
var
  MPak: TMPacket;
begin
  SendToCMUCam('TW'+char(13));
  DoDelay(100);
  ClearInputBuffer;
  result := TrackColor(MPak);
end;

function TrackColor(var MPacket: TMPacket): byte;  //returns confidence level
begin
  SendToCMUCam('TC'+char(13));
  DoDelay(100);
  if ReadMPacket(MPacket) then
    result := MPacket.confidence
  else
    result := 0;
end;

function ActionToTrack: CMU_Action;
var
  MPacket: TMPacket;
begin
  result := CA_Nothing;
  if TrackColor(MPacket) < TrackThreshold then exit;
  if (MPacket.pixels > 250) and (MPacket.confidence > ActThreshold) then begin
    result := CA_Backward;
    exit;
    end;
  if MPacket.mx > 55 then begin
    result := CA_Right;
    exit;
    end;
  if MPacket.mx < 35 then begin
    result := CA_Left;
    exit;
    end;
  if MPacket.pixels < 150 then begin
    result := CA_Forward;
    exit;
    end;
end;

initialization
  ComPort := TApdComPort.Create(Application);
  ComPort.ComNumber     := 1;
  ComPort.Baud          := 115200;
  ComPort.Parity        := pNone;
  ComPort.DataBits      := 8;
  ComPort.StopBits      := 1;
  ComPort.InSize        := 30000;
  ComPort.OutSize       := 30000;
  ComPort.AutoOpen      := false;
  ComPort.HWFlowOptions := [];
  ComPort.SWFlowOptions := swfNone;
  ComPort.InitPort;
  ComPort.Open          := true;
  ResetCMUCam;
  SetRawMode;
  SetPollMode;
  ClearInputBuffer;
  SendToCMUCam('CR 18 44 19 33'+char(13));
  DoDelay(5000);  //allow camera to adjust
  ClearInputBuffer;
finalization
//  ResetCMUCam;  //leave it at default state
//  ComPort.Open := false;
//  ComPort.Free;
end.

unit USBI2cTypes;

//Copyright 2003,2004 Artificial Ingenuity, LLC, all rights reserved

interface

type
  Sonars = (S_Front, S_Back, S_Left, S_Right, S_All);
  TSonarData = packed record
    SoftRev: byte;
    Light  : byte;
    Ranges : packed array [1..17] of word;
    end;

type
  TTransPtr = ^TI2C_Trans;
  TI2C_Trans = packed record
    TransType : byte;  //No Addr = 0, 8 bit Addr = 1, 16bit addr = 2
    SlvDevAddr: byte;
    MemoryAddr: word;
    Count     : word;
    Data      : packed array [0..255] of byte;
    end;

implementation

end.

unit USBI2c;

//Copyright 2003,2004 Artificial Ingenuity, LLC, all rights reserved

interface

uses
  USBI2cTypes;

function Ping(Sonar: Sonars): boolean;

function ReadSonar(Sonar: Sonars; var Data: TSonarData): boolean;

function GetDLLVersion: word;

function GetDeviceCount: byte;

procedure OpenDeviceInstance;

procedure CloseDeviceInstance;


function ReadI2c(Trans: TTransPtr): integer;

function WriteI2c(Trans: TTransPtr): integer;


implementation

uses
  Windows, classes, sysutils, dialogs;

function Ping(Sonar: Sonars): boolean;
var
  trans: TI2C_Trans;
  wrote : integer;
begin
  if Sonar = S_All then begin
    result := Ping(S_Front) and Ping(S_Back) and Ping(S_Left) and Ping(S_Right);
    exit;
    end;
  fillchar(trans, sizeof(trans), 0);
  trans.TransType  := 1;
  case Sonar of
    S_Front: trans.SlvDevAddr := $E2;
    S_Back : trans.SlvDevAddr := $E0;
    S_Left : trans.SlvDevAddr := $E4;
    S_Right: trans.SlvDevAddr := $E6;
  else
    end;
  trans.MemoryAddr := 0;
  trans.Count      := 1;
  trans.Data[0]    := $51;
  wrote := WriteI2c(@trans);
  result := wrote = 1;
end;

function ReadSonar(Sonar: Sonars; var Data: TSonarData): boolean;
var
  trans  : TI2C_Trans;
  read   : integer;
  w      : word;
  al     : packed record
    blo,bhi: byte;
    end absolute w;
  i      : integer;
begin
  result := true;
  fillchar(trans, sizeof(trans), 0);
  trans.TransType  := 1;
  case Sonar of
    S_Front: trans.SlvDevAddr := $E2;
    S_Back : trans.SlvDevAddr := $E0;
    S_Left : trans.SlvDevAddr := $E4;
    S_Right: trans.SlvDevAddr := $E6;
  else
    //multiple read not supported
    end;
  trans.MemoryAddr := $00;
  trans.Count      := 36;
  read := ReadI2c(@trans);
  if read <> 36 then begin
    result := false;
    exit
    end;
  Data.SoftRev := trans.Data[0];
  Data.Light   := trans.Data[1];
  for i := 1 to 17 do begin
    al.bhi := trans.Data[i*2];
    al.blo := trans.Data[(i*2)+1];
    Data.Ranges[i] := w
    end;
end;

var
  Handle : THandle;
  DHandle: integer;

type
  TGetDLLVersion = function(): word; stdcall;
var
  PGetDLLVersion: TGetDLLVersion;
type
  TGetDeviceCount = function(DevName: PChar): byte;  stdcall;
var
  PGetDeviceCount: TGetDeviceCount;
type
  TOpenDeviceInstance = function (DevName: PChar; ByInstance: byte): integer;  stdcall;
var
  POpenDeviceInstance: TOpenDeviceInstance;
type
  TCloseDeviceInstance = function (Handle: integer): boolean;  stdcall;
var
  PCloseDeviceInstance: TCloseDeviceInstance;
type
  TReadI2c = function (Handle: integer;  Trans: TTransPtr): integer;  stdcall;
var
  PReadI2c: TReadI2c;
type
  TWriteI2c = function (Handle: integer;  Trans: TTransPtr): integer;  stdcall;
var
  PWriteI2c: TWriteI2c;

function WriteI2c(Trans: TTransPtr): integer;
begin
  result := 0;
  if @PWriteI2c = nil then begin
    ShowMessage('Write I2c DLL Method Not Found!');
    exit
    end;
  result := PWriteI2c(DHandle, Trans);
end;

function ReadI2c(Trans: TTransPtr): integer;
begin
  result := 0;
  if @PReadI2c = nil then begin
    ShowMessage('Read I2c DLL Method Not Found!');
    exit
    end;
  result := PReadI2c(DHandle, Trans);
end;

procedure CloseDeviceInstance;
begin
  if @PCloseDeviceInstance = nil then begin
    ShowMessage('Close Device Instance DLL Method Not Found!');
    exit
    end;
  PCloseDeviceInstance(DHandle);
end;

procedure OpenDeviceInstance;
var
  pc: PChar;
begin
  if @POpenDeviceInstance = nil then begin
    ShowMessage('Open Device Instance DLL Method Not Found!');
    exit
    end;
  pc := 'UsbI2cIo';
  DHandle := POpenDeviceInstance(pc, 0);
end;

function GetDeviceCount: byte;
var
  pc: PChar;
begin
  result := 0;
  if @PGetDeviceCount = nil then begin
    ShowMessage('Get Device Count DLL Method Not Found!');
    exit
    end;
  pc := 'UsbI2cIo';
  result := PGetDeviceCount(pc);
end;

function GetDLLVersion: word;
begin
  result := 0;
  if @PGetDLLVersion <> nil then
    result := PGetDLLVersion
  else
    ShowMessage('Get Version DLL Method Not Found!');
end;

Initialization
  Handle := LoadLibrary('USBI2cIo.dll');
  @PGetDLLVersion       := GetProcAddress(Handle, 'DAPI_GetDllVersion');
  @PGetDeviceCount      := GetProcAddress(Handle, 'DAPI_GetDeviceCount');
  @POpenDeviceInstance  := GetProcAddress(Handle, 'DAPI_OpenDeviceInstance');
  @PCloseDeviceInstance := GetProcAddress(Handle, 'DAPI_CloseDeviceInstance');
  @PReadI2c             := GetProcAddress(Handle, 'DAPI_ReadI2c');
  @PWriteI2c            := GetProcAddress(Handle, 'DAPI_WriteI2c');
Finalization
  FreeLibrary(Handle);
end.

Motor controller circuit (without PWM modules)



SRF08 Ultra sonic range finder

Technical Specification

Communication with the SRF08 ultrasonic rangefinder is via the I2C bus. This is available on popular controllers such as the OOPic and Stamp BS2p, as well as a wide variety of micro-controllers. To the programmer the SRF08 behaves in the same way as the ubiquitous 24xx series eeprom's, except that the I2C address is different. The default shipped address of the SRF08 is 0xE0. It can be changed by the user to any of 16 addresses E0, E2, E4, E6, E8, EA, EC, EE, F0, F2, F4, F6, F8, FA, FC or FE, therefore up to 16 sonar's can be used. In addition to the above addresses, all sonar's on the I2C bus will respond to address 0 - the General Broadcast address. This means that writing a ranging command to I2C address 0 (0x00) will start all sonar's ranging at the same time. This should be useful in ANN Mode (See below). The results must be read individually from each sonar's real address. We have examples of using the SRF08 module with a wide range of popular controllers.


Sonar data

http://www.robot-electronics.co.uk/htm/srf08tech.shtml

schematic

http://www.robot-electronics.co.uk/images/srf08schematic.gif


USB I2C/IO P.C.B. (Rev. B)


Product Summary:
The USB I2C/IO interface board provides a simple, "drop-in", solution for customers that need to connect hardware to a P.C. The board provides 20 bits of user-configurable digital I/O, a 90Kbps I2C interface, and an 8 bit wide data bus for fast FIFO transfers.

Key Features
12Mbps USB interface to host P.C. 
Cypress AN2131QC micro-controller. 
20 bits of user configurable, digital I/O, via a commonly available, 34 pin connector. 
90Kbps I2C interface, onboard 16KB I2C eeprom, 5 pin connector for attaching external I2C hardware. 
USB Status LED, lights on enumeration, blinks to indicate USB traffic, off when suspended. 
Break LED, useful when developing new firmware, also will be controllable via our API software 
Small form factor (3.0" X 2.25") with 0.125" mounting holes in corners. 
Downloadable board firmware, simplifies updates and allows for code customization. 
Included API (applications programming interface) software gets your application up and running fast! 

Applications
USB to I2C bridge for interfacing to a wide variety of I2C components. 
USB to digital I/O bridge for interfacing to switches, LEDs, and other hardware. 
USB to FIFO bridge for interfacing to FPGA's and other hardware. 
Low cost USB development platform for AN2131QC (note: no address bus availability). 
Test fixture interface. 
Rapid prototyping interface. 
Data Acquisition. 

Hardware
The full-speed USB interface provides your application hardware with a "Hot Plug and Play", 12Mbps, industry standard connection to your P.C. host.

Utilizes Cypress Semiconductors AN2131QC USB micro-controller. 
Power configuration jumper allows board to be powered by USB or by external circuitry. 
I2C power configuration jumper allow I2C bus to be powered by variety of sources. 
Eeprom enable/disable jumper allows board to be used as an AN2131QC development platform for developing your own firmware. 
Eeprom Write Protect jumper provides protection from accidental onboard eeprom erasure. 



USBi2cIO data

http://www.devasys.com/usbi2cio.htm

schematic:

http://www.devasys.com/i2ciob/schematic.pdf


DC Motor Speed Controller Kit (KIT 67)


Control the speed of any common DC motor rated up to 100V (5A). Operates on 5V to 15V. Uses NE556 to pulse-width modulate a high current switching power transistor, TIP122. In this way motor torque is maintained. Adjustable speed control. Box mounted. 

Motor speed controllers:

http://www.circuitspecialists.com/prod.itml/icOid/3349

details:

http://st4.yahoo.com/lib/webtronics/kit67.pdf


CMUcam Kit


Developed by Carnegie Mellon University who selected Acroname to provide the kit under license agreement, the CMUcam is a new low-power sensor for mobile robots which can do several different kinds of on-board, real-time vision processing. The CMUcam uses a serial port and can be directly interfaced to other low-power processors such as PIC chips. Using a CMUcam, it is easy to make a robot head that swivels around to track an object. You can even build a robot that will chase a ball around.

The kit comes with everything you need to get started with the camera. The software for your PC or Linux machine is available for download on the CMU site and we pre-program the Scenix chip with the most current version of the firmware. This firmware can later be upgraded but requires a programmer for subsequent changes. This kit is shipping with the current version 1.12 of the firmware.

At 17 frames per second, the CMUcam can do the following:

track the position and size of a colorful or bright object
measure the RGB or YUV statistics of an image region
automatically acquire and track the first object it sees
physically track using a directly connected servo
dump complete image over the serial port
dump a bitmap showing the shape of the tracked object
The camera dimensions are 2.25" wide x 1.75" high x 2" deep. The camera kit uses a Omnivision OV6620 single-chip CMOS CIF color digital camera with a 4.0mm, F2.8 lens and IR filter.




CMUCam kit:

http://www.acroname.com/robotics/parts/R140-CMUCAM-KIT.html

schematic:

http://www-2.cs.cmu.edu/~cmucam/Downloads/CMUcamManual.pdf


CMPS03 - Robot Compass Module


This compass module has been specifically designed for use in robots as an aid to navigation. The aim was to produce a unique number to represent the direction the robot is facing. The compass uses the Philips KMZ51 magnetic field sensor, which is sensitive enough to detect the Earths magnetic field. The output from two of them mounted at right angles to each other is used to compute the direction of the horizontal component of the Earths magnetic field. We have examples of using the Compass module with a wide range of popular controllers. 

Connections to the compass module



The compass module requires a 5v power supply at a nominal 15mA. 
There are two ways of getting the bearing from the module. A PWM signal is available on pin 4, or an I2C interface is provided on pins 2,3.

The PWM signal is a pulse width modulated signal with the positive width of the pulse representing the angle. The pulse width varies from 1mS (0° ) to 36.99mS (359.9° ) – in other words 100uS/° with a +1mS offset. The signal goes low for 65mS between pulses, so the cycle time is 65mS + the pulse width - ie. 66ms-102ms. The pulse is generated by a 16 bit timer in the processor giving a 1uS resolution, however I would not recommend measuring this to anything better than 0.1° (10uS). Make sure you connect the I2C pins, SCL and SDA, to the 5v supply if you are using the PWM, as there are no pull-up resistors on these pins.

Pin 2,3 are an I2C interface and can be used to get a direct readout of the bearing. If the I2C interface is not used then these pins should be pulled high (to +5v) via a couple of resistors. Around 47k is ok, the values are not at all critical.


Compass module:

http://www.robot-electronics.co.uk/htm/cmps3doc.shtml


  Front above left


 

Return to Xanthus Research

Return to Research


Home


Contact info@artificialingenuity.com
Copyright © 2005 Artificial Ingenuity, LLC
Last modified: June 11, 2005
Initial design by Webinizer, LLC