Update of /cvsroot/playerstage/code/player/server/drivers/mixed/irobot/create
In directory 
sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32201/server/drivers/mixed/irobot/create

Added Files:
      Tag: release-2-0-patches
        .cvsignore Makefile.am create_comms.c create_comms.h 
        create_driver.cc 
Log Message:
backported create driver from HEAD

--- NEW FILE: .cvsignore ---
Makefile
Makefile.in
.deps
*.la
.libs
*.lo

--- NEW FILE: create_comms.c ---
/*
 *  Player - One Hell of a Robot Server
 *  Copyright (C) 2006 -
 *     Brian Gerkey
 *                      
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <termios.h>
#include <math.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>

#ifdef HAVE_CONFIG_H
  #include "config.h"
#endif

#include <libplayercore/playercommon.h>
#include <libplayercore/playerconfig.h>
#include <replace/replace.h>
//#include <sys/poll.h>

#include "create_comms.h"

#define DTOR(d) ((d) * M_PI / 180)

create_comm_t*
create_create(const char* serial_port)
{
  create_comm_t* r;

  r = calloc(1,sizeof(create_comm_t));
  assert(r);
  r->fd = -1;
  r->mode = CREATE_MODE_OFF;
  r->oa = r->ox = r->oy = 0;

  strncpy(r->serial_port,serial_port,sizeof(r->serial_port)-1);
  return(r);
}

void
create_destroy(create_comm_t* r)
{
  free(r);
}

int
create_open(create_comm_t* r, unsigned char fullcontrol)
{
  struct termios term;
  int flags;

  if(r->fd >= 0)
  {
    puts("create connection already open!");
    return(-1);
  }

  printf("Opening connection to Create on %s...", r->serial_port);
  fflush(stdout);

  // Open it.  non-blocking at first, in case there's no create
  if((r->fd = open(r->serial_port, 
                    O_RDWR | O_NONBLOCK, S_IRUSR | S_IWUSR )) < 0 )
  {
    perror("create_open():open():");
    return(-1);
  }

  if(tcflush(r->fd, TCIFLUSH) < 0 )
  {
    perror("create_open():tcflush():");
    close(r->fd);
    r->fd = -1;
    return(-1);
  }
  if(tcgetattr(r->fd, &term) < 0 )
  {
    perror("create_open():tcgetattr():");
    close(r->fd);
    r->fd = -1;
    return(-1);
  }
  
  cfmakeraw(&term);
  cfsetispeed(&term, B57600);
  cfsetospeed(&term, B57600);
  
  if(tcsetattr(r->fd, TCSAFLUSH, &term) < 0 )
  {
    perror("create_open():tcsetattr():");
    close(r->fd);
    r->fd = -1;
    return(-1);
  }

  if(create_init(r, fullcontrol) < 0)
  {
    puts("failed to initialize connection");
    close(r->fd);
    r->fd = -1;
    return(-1);
  }


  if(create_get_sensors(r, 10000) < 0)
  {
    puts("create_open():failed to get data");
    close(r->fd);
    r->fd = -1;
    return(-1);
  }

  r->oa = r->ox = r->oy = 0;

  // We know the robot is there; switch to blocking 
  // ok, we got data, so now set NONBLOCK, and continue
  if((flags = fcntl(r->fd, F_GETFL)) < 0)
  {
    perror("create_open():fcntl():");
    close(r->fd);
    r->fd = -1;
    return(-1);
  }
  if(fcntl(r->fd, F_SETFL, flags ^ O_NONBLOCK) < 0)
  {
    perror("create_open():fcntl()");
    close(r->fd);
    r->fd = -1;
    return(-1);
  }

  puts("Done.");

  return(0);
}

int
create_init(create_comm_t* r, unsigned char fullcontrol)
{
  unsigned char cmdbuf[1];

  //usleep(CREATE_DELAY_MODECHANGE_MS * 1e3);

  cmdbuf[0] = CREATE_OPCODE_START;
  if(write(r->fd, cmdbuf, 1) < 0)
  {
    perror("create_init():write():");
    return(-1);
  }
  r->mode = CREATE_MODE_PASSIVE;

  usleep(CREATE_DELAY_MODECHANGE_MS * 1e3);
  
  if (fullcontrol)
  {
    cmdbuf[0] = CREATE_OPCODE_FULL;
    if(write(r->fd, cmdbuf, 1) < 0)
    {
      perror("create_init():write():");
      return(-1);
    }
    r->mode = CREATE_MODE_FULL;
  }
  else
  {
    cmdbuf[0] = CREATE_OPCODE_SAFE;
    if(write(r->fd, cmdbuf, 1) < 0)
    {
      perror("create_init():write():");
      return(-1);
    }
    r->mode = CREATE_MODE_SAFE;

  }

  usleep(100000);

  return(0);
}

int
create_close(create_comm_t* r)
{
  //unsigned char cmdbuf[1];

  create_set_speeds(r, 0.0, 0.0);

  usleep(CREATE_DELAY_MODECHANGE_MS * 1e3);

  if(close(r->fd) < 0)
  {
    perror("create_close():close():");
    return(-1);
  }
  else
    return(0);
}

int
create_set_speeds(create_comm_t* r, double tv, double rv)
{
  unsigned char cmdbuf[5];
  int16_t tv_mm, rad_mm;

  //printf("tv: %.3lf rv: %.3lf\n", tv, rv);

  tv_mm = (int16_t)rint(tv * 1e3);
  tv_mm = MAX(tv_mm, -CREATE_TVEL_MAX_MM_S);
  tv_mm = MIN(tv_mm, CREATE_TVEL_MAX_MM_S);

  if(rv == 0)
  {
    // Special case: drive straight
    rad_mm = 0x8000;
  }
  else if(tv == 0)
  {
    // Special cases: turn in place
    if(rv > 0)
      rad_mm = 1;
    else
      rad_mm = -1;

    tv_mm = (int16_t)rint(CREATE_AXLE_LENGTH * fabs(rv) * 1e3);
  }
  else
  {
    // General case: convert rv to turn radius
    rad_mm = (int16_t)rint(tv_mm / rv);
    // The robot seems to turn very slowly with the above 
    rad_mm /= 2; 
    //printf("real rad_mm: %d\n", rad_mm);
    rad_mm = MAX(rad_mm, -CREATE_RADIUS_MAX_MM);
    rad_mm = MIN(rad_mm, CREATE_RADIUS_MAX_MM);
  }

  //printf("tv_mm: %d rad_mm: %d\n", tv_mm, rad_mm);

  cmdbuf[0] = CREATE_OPCODE_DRIVE;
  cmdbuf[1] = (unsigned char)(tv_mm >> 8);
  cmdbuf[2] = (unsigned char)(tv_mm & 0xFF);
  cmdbuf[3] = (unsigned char)(rad_mm >> 8);
  cmdbuf[4] = (unsigned char)(rad_mm & 0xFF);

  //printf("set_speeds: %X %X %X %X %X\n",
         //cmdbuf[0], cmdbuf[1], cmdbuf[2], cmdbuf[3], cmdbuf[4]);

  if(write(r->fd, cmdbuf, 5) < 0)
  {
    perror("create_set_speeds():write():");
    return(-1);
  }
  else
    return(0);
}

int
create_get_sensors(create_comm_t* r, int timeout)
{
  struct pollfd ufd[1];
  unsigned char cmdbuf[2];
  unsigned char databuf[CREATE_SENSOR_PACKET_SIZE];
  int retval;
  int numread;
  int totalnumread;
  //int i;

  cmdbuf[0] = CREATE_OPCODE_SENSORS;
  /* Zero to get all sensor data */
  cmdbuf[1] = 0;

  if(write(r->fd, cmdbuf, 2) < 0)
  {
    perror("create_get_sensors():write():");
    return(-1);
  }

  ufd[0].fd = r->fd;
  ufd[0].events = POLLIN;

  totalnumread = 0;
  while(totalnumread < sizeof(databuf))
  {
    retval = poll(ufd,1,timeout);

    if(retval < 0)
    {
      if(errno == EINTR)
        continue;
      else
      {
        perror("create_get_sensors():poll():");
        return(-1);
      }
    }
    else if(retval == 0)
    {
      printf("create_get_sensors: poll timeout\n");
      return(-1);
    }
    else
    {
      if((numread = 
read(r->fd,databuf+totalnumread,sizeof(databuf)-totalnumread)) < 0)
      {
        perror("create_get_sensors():read()");
        return(-1);
      }
      else
      {
        totalnumread += numread;
        /*printf("read %d bytes; buffer so far:\n", numread);
        for(i=0;i<totalnumread;i++)
          printf("%x ", databuf[i]);
        puts("");
        */
      }
    }
  }
  create_parse_sensor_packet(r, databuf, (size_t)totalnumread);
  return(0);
}

int
create_parse_sensor_packet(create_comm_t* r, unsigned char* buf, size_t buflen)
{
  unsigned char flag;
  int16_t signed_int;
  uint16_t unsigned_int;
  double dist, angle;
  int idx;

  if(buflen != CREATE_SENSOR_PACKET_SIZE)
  {
    puts("create_parse_sensor_packet():packet is wrong size");
    return(-1);
  }

  idx = 0;

  /* Bumps, wheeldrops */
  flag = buf[idx++];
  r->bumper_right = (flag >> 0) & 0x01;
  r->bumper_left = (flag >> 1) & 0x01;
  r->wheeldrop_right = (flag >> 2) & 0x01;
  r->wheeldrop_left = (flag >> 3) & 0x01;
  r->wheeldrop_caster = (flag >> 4) & 0x01;

  r->wall = buf[idx++] & 0x01;
  r->cliff_left = buf[idx++] & 0x01;
  r->cliff_frontleft = buf[idx++] & 0x01;
  r->cliff_frontright = buf[idx++] & 0x01;
  r->cliff_right = buf[idx++] & 0x01;
  r->virtual_wall = buf[idx++] & 0x01;

  flag = buf[idx++];
  r->overcurrent_sidebrush = (flag >> 0) & 0x01;
  r->overcurrent_vacuum = (flag >> 1) & 0x01;
  r->overcurrent_mainbrush = (flag >> 2) & 0x01;
  r->overcurrent_driveright = (flag >> 3) & 0x01;
  r->overcurrent_driveleft = (flag >> 4) & 0x01;

  // The next two bytes are unused (used to be dirt detector left and right)
  idx += 2;

  r->remote_opcode = buf[idx++];

  flag = buf[idx++];
  r->button_max = (flag >> 0) & 0x01;
  r->button_clean = (flag >> 1) & 0x01;
  r->button_spot = (flag >> 2) & 0x01;
  r->button_power = (flag >> 3) & 0x01;

  memcpy(&signed_int, buf+idx, 2);
  idx += 2;
  signed_int = (int16_t)ntohs((uint16_t)signed_int);
  dist = signed_int / 1e3;

  memcpy(&signed_int, buf+idx, 2);
  idx += 2;
  signed_int = (int16_t)ntohs((uint16_t)signed_int);
  angle = DTOR(signed_int);

  /* First-order odometric integration */
  r->oa = NORMALIZE(r->oa + angle);
  r->ox += dist * cos(r->oa);
  r->oy += dist * sin(r->oa);

  r->charging_state = buf[idx++];

  memcpy(&unsigned_int, buf+idx, 2);
  idx += 2;
  unsigned_int = ntohs(unsigned_int);
  r->voltage = unsigned_int / 1e3;

  memcpy(&signed_int, buf+idx, 2);
  idx += 2;
  signed_int = (int16_t)ntohs((uint16_t)signed_int);
  r->current = signed_int / 1e3;

  r->temperature = (char)(buf[idx++]);

  memcpy(&unsigned_int, buf+idx, 2);
  idx += 2;
  unsigned_int = ntohs(unsigned_int);
  r->charge = unsigned_int / 1e3;

  memcpy(&unsigned_int, buf+idx, 2);
  idx += 2;
  unsigned_int = ntohs(unsigned_int);
  r->capacity = unsigned_int / 1e3;

  return(0);
}


void
create_print(create_comm_t* r)
{
  printf("mode: %d\n", r->mode);
  printf("position: %.3lf %.3lf %.3lf\n", r->ox, r->oy, r->oa);
  printf("bumpers: l:%d r:%d\n", r->bumper_left, r->bumper_right);
  printf("wall: %d virtual wall: %d\n", r->wall, r->virtual_wall);
  printf("wheeldrops: c:%d l:%d r:%d\n", 
         r->wheeldrop_caster, r->wheeldrop_left, r->wheeldrop_right);
  printf("cliff: l:%d fl:%d fr:%d r:%d\n",
         r->cliff_left, r->cliff_frontleft, r->cliff_frontright, 
r->cliff_right);
  printf("overcurrent: dl:%d dr:%d mb:%d sb:%d v:%d\n",
         r->overcurrent_driveleft, r->overcurrent_driveright,
         r->overcurrent_mainbrush, r->overcurrent_sidebrush, 
r->overcurrent_vacuum);
  printf("dirt: l:%d r:%d\n", r->dirtdetector_left, r->dirtdetector_right);
  printf("remote opcode: %d\n", r->remote_opcode);
  printf("buttons: p:%d s:%d c:%d m:%d\n",
         r->button_power, r->button_spot, r->button_clean, r->button_max);
  printf("charging state: %d\n", r->charging_state);
  printf("battery: voltage:%.3lf current:%.3lf temp:%.3lf charge:%.3lf 
capacity:%.3f\n", 
         r->voltage, r->current, r->temperature, r->charge, r->capacity);

}

int create_set_song(create_comm_t* r, unsigned char songNumber, unsigned char 
songLength, unsigned char *notes, unsigned char *noteLengths)
{
  int size = 2*songLength+3;
  unsigned char cmdbuf[size];
  unsigned char i;

  cmdbuf[0] = CREATE_OPCODE_SONG;
  cmdbuf[1] = songNumber;
  cmdbuf[2] = songLength;

  for (i=0; i < songLength; i++)
  {
    cmdbuf[3+(2*i)] = notes[i];
    cmdbuf[3+(2*i)+1] = noteLengths[i];
  }

  if(write(r->fd, cmdbuf, size) < 0)
  {
    perror("create_set_song():write():");
    return(-1);
  }
  else
    return(0);
}

int 
create_play_song(create_comm_t *r, unsigned char songNumber)
{
  unsigned char cmdbuf[2];

  cmdbuf[0] = CREATE_OPCODE_PLAY;
  cmdbuf[1] = songNumber;

  if(write(r->fd, cmdbuf, 2) < 0)
  {
    perror("create_set_song():write():");
    return(-1);
  }
  else
    return(0);
}

int
create_vacuum(create_comm_t *r, int state)
{
  unsigned char cmdbuf[2];

  cmdbuf[0] = CREATE_OPCODE_MOTORS;
  cmdbuf[1] = state;

  if (write(r->fd, cmdbuf, 2) < 0)
  {
    perror("create_vacuum():write():");
    return -1;
  }

  return 0;
}

int
create_set_leds(create_comm_t *r, uint8_t dirt_detect, uint8_t max, uint8_t 
clean, uint8_t spot, uint8_t status, uint8_t power_color, uint8_t 
power_intensity )
{
  unsigned char cmdbuf[5];
  cmdbuf[0] = CREATE_OPCODE_LEDS;
  cmdbuf[1] = dirt_detect | max<<1 | clean<<2 | spot<<3 | status<<4;
  cmdbuf[2] = power_color;
  cmdbuf[3] = power_intensity;

  printf("Set LEDS[%d][%d][%d]\n",cmdbuf[1], cmdbuf[2], cmdbuf[3]);
  if (write(r->fd, cmdbuf, 4) < 0)
  {
    perror("create_set_leds():write():");
    return -1;
  }

  return 0;
}

int
create_run_demo(create_comm_t *r, uint8_t num)
{
  unsigned char cmdbuf[2];
  cmdbuf[0] = CREATE_OPCODE_DEMO;
  cmdbuf[1] = num;

  if (write(r->fd, cmdbuf, 2) < 0)
  {
    perror("create_run_demo():write():");
    return -1;
  }
}

--- NEW FILE: create_comms.h ---
/*
 *  Player - One Hell of a Robot Server
 *  Copyright (C) 2006 -
 *     Brian Gerkey
 *                      
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifndef CREATE_COMMS_H
#define CREATE_COMMS_H

#ifdef __cplusplus
extern "C" {
#endif

#include <limits.h>

/* command opcodes */
#define CREATE_OPCODE_START            128
#define CREATE_OPCODE_BAUD             129
#define CREATE_OPCODE_SAFE             131
#define CREATE_OPCODE_FULL             132
#define CREATE_OPCODE_SPOT             134
#define CREATE_OPCODE_COVER            135
#define CREATE_OPCODE_DEMO             136
#define CREATE_OPCODE_DRIVE            137
#define CREATE_OPCODE_MOTORS           138
#define CREATE_OPCODE_LEDS             139
#define CREATE_OPCODE_SONG             140
#define CREATE_OPCODE_PLAY             141
#define CREATE_OPCODE_SENSORS          142
#define CREATE_OPCODE_COVERDOCK        143
#define CREATE_OPCODE_PWM_MOTORS       144
#define CREATE_OPCODE_DRIVE_WHEELS     145
#define CREATE_OPCODE_DIGITAL_OUTPUTS  147
#define CREATE_OPCODE_STREAM           148
#define CREATE_OPCODE_QUERY_LIST       149
#define CREATE_OPCODE_DO_STREAM        150
#define CREATE_OPCODE_SEND_IR_CHAR     151
#define CREATE_OPCODE_SCRIPT           152
#define CREATE_OPCODE_PLAY_SCRIPT      153
#define CREATE_OPCODE_SHOW_SCRIPT      154
#define CREATE_OPCODE_WAIT_TIME        155
#define CREATE_OPCODE_WAIT_DISTANCE    156
#define CREATE_OPCODE_WAIT_ANGLE       157
#define CREATE_OPCODE_WAIT_EVENT       158


#define CREATE_DELAY_MODECHANGE_MS      20

#define CREATE_MODE_OFF                  0
#define CREATE_MODE_PASSIVE              1
#define CREATE_MODE_SAFE                 2
#define CREATE_MODE_FULL                 3

#define CREATE_TVEL_MAX_MM_S           500     
#define CREATE_RADIUS_MAX_MM          2000

#define CREATE_SENSOR_PACKET_SIZE       26

#define CREATE_CHARGING_NOT              0
#define CREATE_CHARGING_RECOVERY         1
#define CREATE_CHARGING_CHARGING         2
#define CREATE_CHARGING_TRICKLE          3
#define CREATE_CHARGING_WAITING          4
#define CREATE_CHARGING_ERROR            5

#define CREATE_AXLE_LENGTH            0.258

#define CREATE_DIAMETER 0.33

#define CREATE_BUMPER_XOFFSET 0.05

#ifndef MIN
  #define MIN(a,b) ((a < b) ? (a) : (b))
#endif
#ifndef MAX
  #define MAX(a,b) ((a > b) ? (a) : (b))
#endif
#ifndef NORMALIZE
  #define NORMALIZE(z) atan2(sin(z), cos(z))
#endif

typedef struct
{
  /* Serial port to which the robot is connected */
  char serial_port[PATH_MAX];
  /* File descriptor associated with serial connection (-1 if no valid
   * connection) */
  int fd;
  /* Current operation mode; one of CREATE_MODE_* */
  unsigned char mode;
  /* Integrated odometric position [m m rad] */
  double ox, oy, oa;

  /* Various Boolean flags */
  int bumper_left, bumper_right;
  unsigned char wheeldrop_caster, wheeldrop_left, wheeldrop_right;
  unsigned char wall;
  unsigned char cliff_left, cliff_frontleft, cliff_frontright, cliff_right;
  unsigned char virtual_wall;
  unsigned char overcurrent_driveleft, overcurrent_driveright;
  unsigned char overcurrent_mainbrush, overcurrent_sidebrush;
  unsigned char overcurrent_vacuum;
  unsigned char dirtdetector_right, dirtdetector_left;
  unsigned char remote_opcode;
  unsigned char button_power, button_spot, button_clean, button_max;

  /* One of CREATE_CHARGING_* */
  unsigned char charging_state;
  /* Volts */
  double voltage;
  /* Amps */
  double current;
  /* degrees C */
  double temperature;
  /* Ah */
  double charge;
  /* Capacity */
  double capacity;
} create_comm_t;

create_comm_t* create_create(const char* serial_port);
void create_destroy(create_comm_t* r);
int create_open(create_comm_t* r, unsigned char fullcontrol);
int create_init(create_comm_t* r, unsigned char fullcontrol);
int create_close(create_comm_t* r);
int create_set_speeds(create_comm_t* r, double tv, double rv);
int create_parse_sensor_packet(create_comm_t* r, 
                               unsigned char* buf, size_t buflen);
int create_get_sensors(create_comm_t* r, int timeout);
void create_print(create_comm_t* r);

int create_set_song(create_comm_t* r, unsigned char songNumber, 
                    unsigned char songLength, unsigned char *notes, 
                    unsigned char *noteLengths);
int create_play_song(create_comm_t *r, unsigned char songNumber);

int create_vacuum(create_comm_t *r, int state);
int create_set_leds(create_comm_t *r, uint8_t dirt_detect, uint8_t max, 
                    uint8_t clean, uint8_t spot, uint8_t status, 
                    uint8_t power_color, uint8_t power_intensity );

int create_run_demo(create_comm_t *r, uint8_t num);

#ifdef __cplusplus
}
#endif

#endif


--- NEW FILE: Makefile.am ---
noinst_LTLIBRARIES = 
if INCLUDE_CREATE
noinst_LTLIBRARIES += libcreate.la
endif

AM_CPPFLAGS = -Wall -I$(top_srcdir)

libcreate_la_SOURCES = create_comms.h create_comms.c create_driver.cc

--- NEW FILE: create_driver.cc ---
/*
 *  Player - One Hell of a Robot Server
 *  Copyright (C) 2006 -
 *     Brian Gerkey
 *                      
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/** @ingroup drivers */
/** @{ */
/** @defgroup driver_create create
 @brief iRobot Create

Newer versions of the iRobot Create vaccum robot can be controlled by an
external computer over a serial line.  This driver supports control of
these robots.  

Note that the serial port on top of the Create operates at 5V, not the
RS232 standard of 12V.  This means that you cannot just plug a plain
old serial cable between the Create and your PC's serial port.  You need
to put a level-shifter in between them.  Or you if have a computer that
exposes serial lines at "logic level," (e.g., the Gumstix), you can use
them directly.  Check out <a href="http://www.irobot.com/hacker";>iRobot's
hacker site</a> for more information, including the pinout on the Create's
serial port.  The <a href="http://create.pbwiki.com";>Create Wiki</a>
has a howto on building an appropriate serial cable.

@par Compile-time dependencies

- none

@par Provides

The create driver provides the following device interfaces:

- @ref interface_position2d
  - This interface returns odometry data (PLAYER_POSITION2D_DATA_STATE), 
    and accepts velocity commands (PLAYER_POSITION2D_CMD_VEL).

- @ref interface_power
  - This interface returns battery levels (PLAYER_POWER_DATA_STATE).

- @ref interface_bumper
  - This interface returns bumper data (PLAYER_BUMPER_DATA_STATE).

- @ref interface_opaque
  - This driver supports programming song, playing songs, setting the LEDs,
    and running demo scripts. 
  - Play song data format in bytes: [0][song_number]
  - Program song data format in bytes: 
[1][song_number][length(n)][note_1][length_note_1]...[note_n][length_note_n]. 
  - Set LEDS format in bytes: 
[2][dirt_dectect(0/1)][max_bool(0/1)][clean(0/1)][spot(0/1)][status(0=off,1=red,2=green,3=amber)][power_color(0-255)][power_intensity(0-255)]
  - Run a demo script in bytes: [3][demo number]

@par Supported configuration requests

- PLAYER_POSITION2D_REQ_GET_GEOM
- PLAYER_BUMPER_GET_GEOM

@par Configuration file options

- port (string)
  - Default: "/dev/ttyS0"
  - Serial port used to communicate with the robot.
- safe (integer)
  - Default: 1
  - Nonzero to keep the robot in "safe" mode (the robot will stop if
    the wheeldrop or cliff sensors are triggered), zero for "full" mode

@par Example

@verbatim
driver
(
  name "create"
  provides ["position2d:0" "power:0" "bumper:0" "ir:0" "opaque:0"]
  port "/dev/ttyS2"
  safe 1
)
@endverbatim


@todo
- Add support for IRs, vacuum motors, etc.
- Recover from a cliff/wheeldrop sensor being triggered in safe mode;
the robot goes into passive mode when this happens, which right now
requires Player to be restarted

@author Brian Gerkey
*/
/** @} */


#include <unistd.h>
#include <stdlib.h>

#include <libplayercore/playercore.h>

#include "create_comms.h"

#define CYCLE_TIME_US 200000

class Create : public Driver
{
  public:
    Create(ConfigFile* cf, int section);
    ~Create();

    int Setup();
    int Shutdown();

    // MessageHandler
    int ProcessMessage(MessageQueue * resp_queue,
                       player_msghdr * hdr, 
                       void * data);

  private:
    // Main function for device thread.
    virtual void Main();

    // Serial port where the create is
    const char* serial_port;

    // full control or not
    bool safe;

    player_devaddr_t position_addr;
    player_devaddr_t power_addr;
    player_devaddr_t bumper_addr;
    player_devaddr_t ir_addr;
    player_devaddr_t opaque_addr;
    player_devaddr_t gripper_addr;

    player_opaque_data_t *cpdata;

    // The underlying create object
    create_comm_t* create_dev;
};

// a factory creation function
Driver* Create_Init(ConfigFile* cf, int section)
{
  return((Driver*)(new Create(cf, section)));
}

// a driver registration function
void Create_Register(DriverTable* table)
{
  table->AddDriver("create", Create_Init);
}

Create::Create(ConfigFile* cf, int section)
        : Driver(cf,section,true,PLAYER_MSGQUEUE_DEFAULT_MAXLEN)
{
  memset(&this->position_addr,0,sizeof(player_devaddr_t));
  memset(&this->power_addr,0,sizeof(player_devaddr_t));
  memset(&this->bumper_addr,0,sizeof(player_devaddr_t));
  memset(&this->ir_addr,0,sizeof(player_devaddr_t));
  memset(&this->opaque_addr,0,sizeof(player_devaddr_t));
  //memset(&this->gripper_addr,0,sizeof(player_devaddr_t));

  this->cpdata = (player_opaque_data_t*)malloc(sizeof(player_opaque_data_t));

  // Do we create a position interface?
  if(cf->ReadDeviceAddr(&(this->position_addr), section, "provides",
                        PLAYER_POSITION2D_CODE, -1, NULL) == 0)
  {
    if(this->AddInterface(this->position_addr) != 0)
    {
      this->SetError(-1);
      return;
    }
  }

  // Do we create a power interface?
  if(cf->ReadDeviceAddr(&(this->power_addr), section, "provides",
                        PLAYER_POWER_CODE, -1, NULL) == 0)
  {
    if(this->AddInterface(this->power_addr) != 0)
    {
      this->SetError(-1);
      return;
    }
  }

  // Do we create a bumper interface?
  if(cf->ReadDeviceAddr(&(this->bumper_addr), section, "provides",
                        PLAYER_BUMPER_CODE, -1, NULL) == 0)
  {
    if(this->AddInterface(this->bumper_addr) != 0)
    {
      this->SetError(-1);
      return;
    }
  }

  // Do we create a IR interface?
  if(cf->ReadDeviceAddr(&(this->ir_addr), section, "provides",
                        PLAYER_IR_CODE, -1, NULL) == 0)
  {
    if(this->AddInterface(this->ir_addr) != 0)
    {
      this->SetError(-1);
      return;
    }
  }

  // Do we create a Opaque interface?
  if(cf->ReadDeviceAddr(&(this->opaque_addr), section, "provides",
                        PLAYER_OPAQUE_CODE, -1, NULL) == 0)
  {
    if(this->AddInterface(this->opaque_addr) != 0)
    {
      this->SetError(-1);
      return;
    }
  }

  // Do we create a gripper interface?
  if(cf->ReadDeviceAddr(&(this->gripper_addr), section, "provides",
                        PLAYER_GRIPPER_CODE, -1, NULL) == 0)
  {
    if(this->AddInterface(this->gripper_addr) != 0)
    {
      this->SetError(-1);
      return;
    }
  }


  this->serial_port = cf->ReadString(section, "port", "/dev/ttyS0");
  this->safe = cf->ReadInt(section, "safe", 1);
  this->create_dev = NULL;
}

Create::~Create()
{
  free (this->cpdata);
}

int
Create::Setup()
{
  this->create_dev = create_create(this->serial_port);

  if(create_open(this->create_dev, !this->safe) < 0)
  {
    create_destroy(this->create_dev);
    this->create_dev = NULL;
    PLAYER_ERROR("failed to connect to create");
    return(-1);
  }

  this->StartThread();

  return(0);
}

int
Create::Shutdown()
{
  this->StopThread();

  if(create_close(this->create_dev))
  {
    PLAYER_ERROR("failed to close create connection");
  }
  create_destroy(this->create_dev);
  this->create_dev = NULL;
  return(0);
}

void
Create::Main()
{
  for(;;)
  {
     this->ProcessMessages();

     if(create_get_sensors(this->create_dev, -1) < 0)
     {
       PLAYER_ERROR("failed to get sensor data from create");
       create_close(this->create_dev);
       return;
     }

     ////////////////////////////
     // Update position2d data
     player_position2d_data_t posdata;
     memset(&posdata,0,sizeof(posdata));

     posdata.pos.px = this->create_dev->ox;
     posdata.pos.py = this->create_dev->oy;
     posdata.pos.pa = this->create_dev->oa;

     this->Publish(this->position_addr, NULL,
                   PLAYER_MSGTYPE_DATA, PLAYER_POSITION2D_DATA_STATE,
                   (void*)&posdata, sizeof(posdata), NULL);

     ////////////////////////////
     // Update power data
     player_power_data_t powerdata;
     memset(&powerdata,0,sizeof(powerdata));

     powerdata.volts = this->create_dev->voltage;
     powerdata.watts = this->create_dev->voltage * this->create_dev->current;
     powerdata.joules = this->create_dev->charge;
     powerdata.percent = 100.0 * 
             (this->create_dev->charge / this->create_dev->capacity);
     powerdata.charging = 
             (this->create_dev->charging_state == CREATE_CHARGING_NOT) ? 0 : 1;
     powerdata.valid = (PLAYER_POWER_MASK_VOLTS |
                        PLAYER_POWER_MASK_WATTS | 
                        PLAYER_POWER_MASK_JOULES | 
                        PLAYER_POWER_MASK_PERCENT |
                        PLAYER_POWER_MASK_CHARGING);

     this->Publish(this->power_addr, NULL,
                   PLAYER_MSGTYPE_DATA, PLAYER_POWER_DATA_STATE,
                   (void*)&powerdata, sizeof(powerdata), NULL);

     ////////////////////////////
     // Update bumper data
     player_bumper_data_t bumperdata;
     memset(&bumperdata,0,sizeof(bumperdata));

     bumperdata.bumpers_count = 2;
     bumperdata.bumpers[0] = this->create_dev->bumper_left;
     bumperdata.bumpers[1] = this->create_dev->bumper_right;

     this->Publish(this->bumper_addr, NULL,
                   PLAYER_MSGTYPE_DATA, PLAYER_BUMPER_DATA_STATE,
                   (void*)&bumperdata);

     ////////////////////////////
     // Update IR data
     player_ir_data_t irdata;
     memset(&irdata,0,sizeof(irdata));

     irdata.ranges_count = 11;
     irdata.ranges[0] = (float)this->create_dev->wall;
     irdata.ranges[1] = (float)this->create_dev->cliff_left;
     irdata.ranges[2] = (float)this->create_dev->cliff_frontleft;
     irdata.ranges[3] = (float)this->create_dev->cliff_frontright;
     irdata.ranges[4] = (float)this->create_dev->cliff_right;
     irdata.ranges[5] = (float)this->create_dev->virtual_wall;
     irdata.ranges[6] = (float)this->create_dev->dirtdetector_right;
     irdata.ranges[7] = (float)this->create_dev->dirtdetector_left;
     irdata.ranges[8] = (float)this->create_dev->wheeldrop_caster;
     irdata.ranges[9] = (float)this->create_dev->wheeldrop_left;
     irdata.ranges[10] = (float)this->create_dev->wheeldrop_right;

     this->Publish(this->ir_addr,NULL,
         PLAYER_MSGTYPE_DATA, PLAYER_IR_DATA_RANGES,
         (void*)&irdata);


     ////////////////////////////
     // Update Gripper data
     player_gripper_data_t gripperdata;
     memset(&gripperdata,0,sizeof(gripperdata));

     gripperdata.state=this->create_dev->overcurrent_vacuum;
     
gripperdata.beams=this->create_dev->dirtdetector_right+this->create_dev->dirtdetector_left;

     this->Publish(this->gripper_addr, NULL,
         PLAYER_MSGTYPE_DATA,
         PLAYER_GRIPPER_DATA_STATE,
         (void*) &gripperdata, sizeof(gripperdata), NULL);

     ////////////////////////////
     // Update Opaque-Control data
     memset(this->cpdata,0,sizeof(cpdata));

     this->cpdata->data_count=5;

     this->cpdata->data[0] = this->create_dev->button_max;
     this->cpdata->data[1] = this->create_dev->button_clean;
     this->cpdata->data[2] = this->create_dev->button_spot;
     this->cpdata->data[3] = this->create_dev->button_power;
     this->cpdata->data[4] = this->create_dev->remote_opcode;

     this->Publish(this->opaque_addr,NULL,
         PLAYER_MSGTYPE_DATA,PLAYER_OPAQUE_DATA_STATE,
         (void*)this->cpdata, sizeof(*this->cpdata), NULL);

     usleep(CYCLE_TIME_US);
  }
}

int
Create::ProcessMessage(MessageQueue * resp_queue,
                       player_msghdr * hdr, 
                       void * data)
{
  if(Message::MatchMessage(hdr,
                           PLAYER_MSGTYPE_CMD,
                           PLAYER_POSITION2D_CMD_VEL,
                           this->position_addr))
  {
    // get and send the latest motor command
    player_position2d_cmd_vel_t position_cmd;
    position_cmd = *(player_position2d_cmd_vel_t*)data;
    PLAYER_MSG2(2,"sending motor commands %f:%f", 
                position_cmd.vel.px,
                position_cmd.vel.pa);
    if(create_set_speeds(this->create_dev, 
                         position_cmd.vel.px, 
                         position_cmd.vel.pa) < 0)
    {
      PLAYER_ERROR("failed to set speeds to create");
    }
    return(0);
  }
  else if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ,
                                 PLAYER_POSITION2D_REQ_MOTOR_POWER,
                                 this->position_addr))
  {
    this->Publish(this->position_addr, resp_queue,
        PLAYER_MSGTYPE_RESP_ACK, PLAYER_POSITION2D_REQ_MOTOR_POWER);
    return 0;
  }
  else if(Message::MatchMessage(hdr,PLAYER_MSGTYPE_REQ,
                                PLAYER_POSITION2D_REQ_GET_GEOM,
                                this->position_addr))
  {
    /* Return the robot geometry. */
    player_position2d_geom_t geom={{0}};
    // Assume that it turns about its geometric center, so leave as zeros

    geom.size.sl = CREATE_DIAMETER;
    geom.size.sw = CREATE_DIAMETER;

    this->Publish(this->position_addr, resp_queue,
                  PLAYER_MSGTYPE_RESP_ACK,
                  PLAYER_POSITION2D_REQ_GET_GEOM,
                  (void*)&geom, sizeof(geom), NULL);
    return(0);
  }
  else if(Message::MatchMessage(hdr,PLAYER_MSGTYPE_REQ,
                                PLAYER_BUMPER_GET_GEOM,
                                this->bumper_addr))
  {
    player_bumper_geom_t geom;

    geom.bumper_def_count = 2;

    geom.bumper_def[0].pose.px = 0.0;
    geom.bumper_def[0].pose.py = 0.0;
    geom.bumper_def[0].pose.pa = 0.0;
    geom.bumper_def[0].length = 0.0;
    geom.bumper_def[0].radius = CREATE_DIAMETER/2.0;

    geom.bumper_def[1].pose.px = 0.0;
    geom.bumper_def[1].pose.py = 0.0;
    geom.bumper_def[1].pose.pa = 0.0;
    geom.bumper_def[1].length = 0.0;
    geom.bumper_def[1].radius = CREATE_DIAMETER/2.0;

    this->Publish(this->bumper_addr, resp_queue,
                  PLAYER_MSGTYPE_RESP_ACK,
                  PLAYER_BUMPER_GET_GEOM,
                  (void*)&geom);

    return(0);
  }
  else if(Message::MatchMessage(hdr,PLAYER_MSGTYPE_REQ,
                                    PLAYER_IR_POSE,
                                    this->ir_addr))
  {
    player_ir_pose poses;

    poses.poses_count = 11;

    // TODO: Fill in proper values
    for (int i=0; i<11; i++)
    {
      poses.poses[i].px = 0.0;
      poses.poses[i].py = 0.0;
      poses.poses[i].pa = 0.0;
    }

    this->Publish(this->ir_addr, resp_queue, 
                  PLAYER_MSGTYPE_RESP_ACK,
                  PLAYER_IR_POSE,
                  (void*)&poses);
    return(0);
  }
  else if(Message::MatchMessage(hdr,PLAYER_MSGTYPE_CMD,
                                    PLAYER_OPAQUE_CMD,
                                    this->opaque_addr))
  {
    player_opaque_data_t opaque_data;

    opaque_data = *(player_opaque_data_t*)data;

    // Play Command
    if (opaque_data.data[0] == 0 )
    {
      uint8_t song_index;

      song_index = opaque_data.data[1];

      create_play_song(this->create_dev, song_index);
    }
    // Program song command
    else if (opaque_data.data[0] == 1)
    {
      uint8_t index = opaque_data.data[1];
      uint8_t length = opaque_data.data[2];
      uint8_t notes[length];
      uint8_t note_lengths[length];

      for (unsigned int i=0; i<length; i++)
      {
        notes[i] = opaque_data.data[3+i*2];
        note_lengths[i] = opaque_data.data[4+i*2];
      }

      create_set_song(this->create_dev, index, length, 
          notes, note_lengths);
    }
    // Set the LEDs
    else if (opaque_data.data[0] == 2)
    {
      uint8_t dirt_detect = opaque_data.data[1] == 0 ? 0 : 1;
      uint8_t max = opaque_data.data[2] == 0 ? 0 : 1;
      uint8_t clean = opaque_data.data[3] == 0 ? 0 : 1;
      uint8_t spot = opaque_data.data[4] == 0 ? 0 : 1;
      uint8_t status = opaque_data.data[5];
      uint8_t power_color = opaque_data.data[6];
      uint8_t power_intensity = opaque_data.data[7];

      if (status > 3)
        status = 3;

      if (create_set_leds(this->create_dev, dirt_detect, max, clean, spot, 
            status, power_color, power_intensity) < 0)
      {
        PLAYER_ERROR("failed to set create leds");
        return -1;
      }
    }
    // Run a demo script
    else if (opaque_data.data[0] == 3)
    {
      uint8_t demo_num = opaque_data.data[1];
      if (create_run_demo(this->create_dev, demo_num)<0)
      {
        PLAYER_ERROR("failed to run create demo");
        return -1;
      }
    }

    return 0;
  }
  else if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_CMD,
                                 PLAYER_GRIPPER_CMD_STATE,
                                 this->gripper_addr))
  {
    player_gripper_cmd_t* gcmd = (player_gripper_cmd_t*)data;

    if(gcmd->cmd)
      create_vacuum(this->create_dev,7);
    else
      create_vacuum(this->create_dev,0);
    return 0;
  }
  else
  {
    return(-1);
  }
}


-------------------------------------------------------------------------
SF.Net email is sponsored by:
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Playerstage-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/playerstage-commit

Reply via email to