khepera_serial.cc

00001 #include "khepera_serial.h"
00002 
00003 #include <sys/types.h>
00004 #include <sys/stat.h>
00005 #include <sys/time.h>
00006 #include <fcntl.h>
00007 #include <termios.h>
00008 #include <stdio.h>
00009 #include <strings.h>
00010 #include <unistd.h>
00011 #include <stdlib.h>
00012 #include <errno.h>
00013 #include <string.h>
00014 
00015 
00016 KheperaSerial::KheperaSerial(char * port, int rate)
00017 {
00018         fd = -1;
00019         
00020         pthread_mutex_init(&lock,NULL);
00021                 
00022         // open the serial port
00023         fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
00024         if ( fd<0 )
00025         {
00026                 fprintf(stderr, "Could not open serial device %s\n",port);
00027                 return;
00028         }
00029         //fcntl(fd,F_SETFL, O_NONBLOCK);
00030         
00031         // save the current io settings
00032         tcgetattr(fd, &oldtio);
00033 
00034         // rtv - CBAUD is pre-POSIX and doesn't exist on OS X
00035         // should replace this with ispeed and ospeed instead.
00036         
00037         // set up new settings
00038         struct termios newtio;
00039         memset(&newtio, 0,sizeof(newtio));
00040         newtio.c_cflag = /*(rate & CBAUD) |*/ CS8 | CLOCAL | CREAD;
00041         newtio.c_iflag = IGNPAR;
00042         newtio.c_oflag = 0;
00043         newtio.c_lflag = ICANON;
00044         
00045         // activate new settings
00046         tcflush(fd, TCIFLUSH);
00047         if (cfsetispeed(&newtio, rate) < 0 ||   cfsetospeed(&newtio, rate) < 0)
00048         {
00049                 fprintf(stderr,"Failed to set serial baud rate: %d\n", rate);
00050                 tcsetattr(fd, TCSANOW, &oldtio);        
00051                 close(fd);
00052                 fd = -1;                                
00053                 return;
00054         }
00055         tcsetattr(fd, TCSANOW, &newtio);
00056         tcflush(fd, TCIOFLUSH);
00057         
00058         // clear the input buffer in case junk data is on the port
00059         usleep(100000);
00060         write(fd,"\r\n",2);
00061         usleep(100000);
00062         tcflush(fd, TCIFLUSH);
00063         
00064         // try a test command
00065         int Values[2];
00066         if (KheperaCommand('E',0,NULL,2,Values) < 0)
00067         {
00068                 fprintf(stderr,"Failed to initialise the Khepera Serial Port %s\n",port);
00069                 close(fd);
00070                 fd = -1;
00071         }               
00072 }
00073 
00074 
00075 KheperaSerial::~KheperaSerial()
00076 {
00077         // restore old port settings
00078         if (fd > 0)
00079         {
00080                 tcsetattr(fd, TCSANOW, &oldtio);
00081                 close(fd);      
00082         }
00083 }
00084 
00085 void KheperaSerial::Lock()
00086 {
00087         pthread_mutex_lock(&lock);
00088 }
00089 
00090 void KheperaSerial::Unlock()
00091 {
00092         pthread_mutex_unlock(&lock);
00093 }
00094 
00095 int KheperaSerial::WriteInts(char command, int Count, int * Values)
00096 {
00097 
00098         if (fd < 0)
00099                 return -1;
00100                 
00101         buffer[0] = command;
00102         buffer[1] = '\0';
00103         int length;
00104         for (int i = 0; i < Count; ++ i)
00105         {
00106                 length = strlen(buffer);
00107                 snprintf(&buffer[length],KHEPERA_BUFFER_LEN - length - 2,",%d",Values[i]);
00108         }
00109         length = strlen(buffer);
00110         snprintf(&buffer[length],KHEPERA_BUFFER_LEN - length ,"\n");
00111         length = strlen(buffer);
00112         
00113         // ugly error handling, if write fails then shut down unit
00114         tcflush(fd, TCIFLUSH);
00115         if(write(fd, buffer, length) < length)
00116         {
00117                 fprintf(stderr,"Error writing to Khepera, disabling\n");
00118                 tcsetattr(fd, TCSANOW, &oldtio);        
00119                 fd = -1;
00120                 return -1;
00121         }
00122         return 0;
00123 }
00124 
00125 
00126 // read ints from khepera
00127 int KheperaSerial::ReadInts(char Header, int Count, int * Values)
00128 {
00129         if (fd < 0)
00130                 return -1;
00131         struct timeval Start,Now;
00132         int TimePassed = 0;
00133         gettimeofday(&Start,NULL);
00134         int length = 0; 
00135         do
00136         {
00137                 pthread_testcancel();
00138                 length += read(fd, &buffer[length], KHEPERA_BUFFER_LEN - length);
00139                 gettimeofday(&Now,NULL);
00140                 TimePassed = (Now.tv_sec - Start.tv_sec)*1000000 + Now.tv_usec - Start.tv_usec;
00141         } while (buffer[length-1] != '\n' && TimePassed < KHEPERA_SERIAL_TIMEOUT_USECS);
00142 
00143         if (TimePassed >= KHEPERA_SERIAL_TIMEOUT_USECS)
00144         {
00145                 fprintf(stderr,"Time out while reading khepera reply\n");
00146                 return -1;
00147         }
00148         
00149         if (buffer[0] != Header)
00150                 return -1;
00151                 
00152         char * pos = &buffer[2];
00153         for (int i = 0; i < Count; ++i)
00154         {
00155                 Values[i] = strtol(pos,&pos,10);
00156                 pos++;
00157         }
00158         return 0;               
00159 }
00160 
00161 int KheperaSerial::KheperaCommand(char command, int InCount, int * InValues, int OutCount, int * OutValues)
00162 {
00163         //printf("Serial command: %c %d %d\n",command,InCount,OutCount);
00164         Lock();
00165         int ret1 = WriteInts(command,InCount,InValues);
00166         usleep(10000); // delay to give the khepera time to process the command before reading the result
00167         int ret2 = ReadInts(command + 32,OutCount,OutValues);
00168         Unlock();
00169         return ret1 < ret2 ? ret1 : ret2;
00170 }

Last updated 12 September 2005 21:38:45