Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

DynamixelProtocol.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_DynamixelProtocol_h_
00003 #define INCLUDED_DynamixelProtocol_h_
00004 
00005 #include "IPC/Thread.h"
00006 #include "Shared/debuget.h"
00007 #include <cstddef>
00008 #include <iostream>
00009 #include <map>
00010 #include <string>
00011 
00012 #define IS_MXEX(m) (m==MODEL_MX28 || m==MODEL_MX64 || m==MODEL_MX106 || m==MODEL_EX106P)
00013 
00014 //! Contains structures which define the layout of the binary communication with Dynamixel servos
00015 namespace DynamixelProtocol {
00016   
00017   const unsigned int MAX_ID=0xFD;
00018   const unsigned int BROADCAST_ID=0xFE;
00019   const unsigned int INVALID_ID=0xFF;
00020   const unsigned int MARKER_VALUE=0xFF;
00021   
00022   extern const char* MODEL_UNKNOWN_NAME;
00023   enum ModelID_t {
00024     MODEL_DX113=113,
00025     MODEL_DX116=116,
00026     MODEL_DX117=117,
00027     MODEL_AX12=12,
00028     MODEL_AX18=18,
00029     MODEL_AXS1=13,
00030     MODEL_RX10=10,
00031     MODEL_RX24=24,
00032     MODEL_RX28=28,
00033     MODEL_RX64=64,
00034     MODEL_MX28=29,
00035     MODEL_MX64=310,
00036     MODEL_MX106=320,
00037     MODEL_EX106P=107,
00038     MODEL_UNKNOWN=static_cast<size_t>(1<<16)
00039   };
00040   //! maps model numbers (e.g. from a ServoInfoResponse) to human-readable names
00041   extern const std::map<DynamixelProtocol::ModelID_t, const std::string> dynamixelModels;
00042   
00043   //! symbol names for status response level settings, see SetStatusResponseLevelCmd
00044   enum StatusResponseLevel {
00045     RESPOND_NONE=0, //!< don't respond to anything
00046     RESPOND_READ=1, //!< only respond to read commands (see ReadCmd and GenericResponseHeader subclasses)
00047     RESPOND_ALL=2 //!< send a response packet for every command (see WriteResponse)
00048   };
00049   
00050   //! Compute a bitwise-negated checksum for a region of memory, with an optional offset
00051   /*! You can add checksums from multiple calls to this function, then bitwise-negate at the
00052   *  end to obtain final checksum value */
00053   inline unsigned char nchecksum(const unsigned char* p, size_t len, size_t off=0) {
00054     const unsigned char* end=p+len;
00055     p+=off;
00056     unsigned char c=0;
00057     while(p!=end) {
00058       //debuget::charhexout(*p); cout << ' '; debuget::charhexout(c+*p); cout << endl;
00059       c+=*p++;
00060     }
00061     return c;
00062   }
00063   //! 'specialization' of nchecksum for the GenericCmdHeader, which should skip marker fields
00064   inline unsigned char nchecksum(const struct GenericCmdHeader& p, size_t len);
00065   //! 'specialization' of nchecksum for the GenericResponseHeader, which should skip marker fields
00066   inline unsigned char nchecksum(const struct GenericResponseHeader& p, size_t len);
00067   //! updates the checksum field of the specified structure, call this for a structure after you modify its fields
00068   template<class T> void updateChecksum(T& cmd) { cmd.checksum=~nchecksum(cmd,sizeof(cmd)-1); }
00069 
00070   //! returns true if markers and checksum are valid
00071   template<class T> bool validate(const T& msg) {
00072     const unsigned char MARKER=(unsigned char)-1U;
00073     if(msg.markerA!=MARKER || msg.markerB!=MARKER) return false;
00074     if(msg.resplen!=sizeof(msg)-4) return false;
00075     typeof(msg.checksum) chk = ~nchecksum(msg,sizeof(msg)-1);
00076     return chk == msg.checksum;
00077   }
00078   
00079   struct GenericCmdHeader {
00080     GenericCmdHeader(unsigned char bytelen, unsigned char instruction)
00081     : markerA(MARKER_VALUE), markerB(MARKER_VALUE), servoid(BROADCAST_ID), cmdlen(bytelen),  cmdid(instruction) {}
00082     operator const char*() const { return reinterpret_cast<const char*>(&markerA); }
00083     operator const unsigned char*() const { return &markerA; }
00084     unsigned char markerA;
00085     unsigned char markerB;
00086     unsigned char servoid;
00087     unsigned char cmdlen;
00088     unsigned char cmdid;
00089   };
00090   inline unsigned char nchecksum(const struct GenericCmdHeader& p, size_t len) { return nchecksum(p,len,2); }
00091   
00092   
00093   //! Allows you to do a "synchronized write", where all recipients commit the value(s) at the same time, and do not send any response packets
00094   /*! To do a synchronized write, first send a SyncWriteHeader indicating the type of write command
00095    *  being sent (T for example below) and the number of entries.
00096    *  Then send the corresponing entries themselves, followed by a checksum:
00097    * - unsigned char checksum = 0;
00098    * - write( os, SyncWriteHeader<T>(n), checksum );
00099    * - for(int i=0; i<n; ++i) { write( os, t[i], checksum ); }
00100    * - os.put(~checksum); // note bitwise-not!
00101    */
00102   template<class T>
00103   struct SyncWriteHeader : public GenericCmdHeader {
00104     SyncWriteHeader(unsigned char len)
00105     : GenericCmdHeader(sizeof(T)*len+4,0x83), addr(T::ADDRESS), writelen(sizeof(T)-1) {}
00106     unsigned char addr;
00107     unsigned char writelen;
00108   };
00109   struct SyncWriteEntry {
00110     SyncWriteEntry() : servoid() {}
00111     SyncWriteEntry(unsigned char sid) : servoid(sid) {}
00112     operator const char*() const { return reinterpret_cast<const char*>(&servoid); }
00113     operator const unsigned char*() const { return &servoid; }
00114     unsigned char servoid;
00115   };
00116   //! An element that follows a SyncWriteHeader, controls position and speed
00117   struct SyncWritePosSpeedEntry : public SyncWriteEntry {
00118     SyncWritePosSpeedEntry() : SyncWriteEntry(), posl(), posh(), speedl(), speedh() {}
00119     SyncWritePosSpeedEntry(unsigned char sid, unsigned short pos, unsigned short speed)
00120     : SyncWriteEntry(sid), posl(pos), posh(pos>>8), speedl(speed), speedh(speed>>8) {}
00121     static const unsigned char ADDRESS=0x1E;
00122     unsigned char posl;
00123     unsigned char posh;
00124     unsigned char speedl;
00125     unsigned char speedh;
00126   };
00127   //! An element that follows a SyncWriteHeader, enables or disables continuous rotation mode
00128   struct SyncWriteContinuousRotationEntry : public SyncWriteEntry {
00129     SyncWriteContinuousRotationEntry() : SyncWriteEntry(), ccwlimitl(), ccwlimith() {}
00130     SyncWriteContinuousRotationEntry(unsigned char sid, bool enable, ModelID_t model) :
00131       SyncWriteEntry(sid), ccwlimitl(enable?0:0xFF), 
00132       ccwlimith(IS_MXEX(model) ? (enable?0:0x0F) : (enable?0:0x03) ) {}
00133                 static const unsigned char ADDRESS=0x8;
00134     unsigned char ccwlimitl;
00135     unsigned char ccwlimith;
00136   };
00137   //! An element that follows a SyncWriteHeader, toggles LED value
00138   struct SyncWriteLEDEntry : public SyncWriteEntry {
00139     SyncWriteLEDEntry() : SyncWriteEntry(), led() {}
00140     SyncWriteLEDEntry(unsigned char sid, bool enable) : SyncWriteEntry(sid), led(enable?1:0) {}
00141     static const unsigned char ADDRESS=0x19;
00142     unsigned char led;
00143   };
00144   struct SyncWriteComplianceEntry : public SyncWriteEntry {
00145     SyncWriteComplianceEntry() : SyncWriteEntry(), cwmargin(), ccwmargin(), cwslope(), ccwslope() {}
00146     SyncWriteComplianceEntry(unsigned char sid, unsigned char margin, unsigned char slope) : SyncWriteEntry(sid), cwmargin(margin), ccwmargin(margin), cwslope(slope), ccwslope(slope) {}
00147     static const unsigned char ADDRESS=0x1A;
00148     unsigned char cwmargin;
00149     unsigned char ccwmargin;
00150     unsigned char cwslope;
00151     unsigned char ccwslope;
00152   };
00153   struct SyncWritePunchEntry : public SyncWriteEntry {
00154     SyncWritePunchEntry() : SyncWriteEntry(), punchl(), punchh() {}
00155     SyncWritePunchEntry(unsigned char sid, unsigned short punch) : SyncWriteEntry(sid), punchl(punch), punchh(punch>>8) {}
00156     static const unsigned char ADDRESS=0x30;
00157     unsigned char punchl;
00158     unsigned char punchh;
00159   };
00160   struct SyncWriteTorqueEntry : public SyncWriteEntry {
00161     SyncWriteTorqueEntry() : SyncWriteEntry(), maxTorqueL(), maxTorqueH() {}
00162     SyncWriteTorqueEntry(unsigned char sid, unsigned short max) : SyncWriteEntry(sid), maxTorqueL(max), maxTorqueH(max>>8) {}
00163     static const unsigned char ADDRESS=0x22;
00164     unsigned char maxTorqueL;
00165     unsigned char maxTorqueH;
00166   };
00167   //! clears the sound data max hold field of an AX-S1
00168   struct SyncWriteSoundHoldEntry : public SyncWriteEntry {
00169     SyncWriteSoundHoldEntry() : SyncWriteEntry(), sndMaxHold(0) {}
00170     SyncWriteSoundHoldEntry(unsigned char sid) : SyncWriteEntry(sid), sndMaxHold(0) {}
00171     static const unsigned char ADDRESS=0x24;
00172     unsigned char sndMaxHold; //!< the maximum microphone value from microphone (requires reset)
00173   };
00174   //! clears the sound detected count field of an AX-S1
00175   /* This can be sent after the count becomes non-zero to clear it so you can detect another count of the same magnitude.
00176    *  In other words, the AX-S1 count stays at zero until a time after the last increment, when the total count is posted to the register.
00177    *  You don't receive updates for individual increments to the counter — you receive only the total count at the end.
00178    *  So once you have read the total count, you should reset it. */
00179   struct SyncWriteSoundCountEntry : public SyncWriteEntry {
00180     SyncWriteSoundCountEntry() : SyncWriteEntry(), sndCount(0) {}
00181     SyncWriteSoundCountEntry(unsigned char sid) : SyncWriteEntry(sid), sndCount(0) {}
00182     static const unsigned char ADDRESS=0x25;
00183     unsigned char sndCount; //!< number of times the microphone reading has exceeded a threshold
00184   };
00185   //! clears the sound data max hold and sound detected count fields of an AX-S1 at the same time, see ClearSoundHold and ClearSoundCount
00186   struct SyncWriteSoundHoldAndCountEntry : public SyncWriteEntry {
00187     SyncWriteSoundHoldAndCountEntry() : SyncWriteEntry(), sndMaxHold(0), sndCount(0) {}
00188     SyncWriteSoundHoldAndCountEntry(unsigned char sid) : SyncWriteEntry(sid), sndMaxHold(0), sndCount(0) {}
00189     static const unsigned char ADDRESS=0x24;
00190     unsigned char sndMaxHold; //!< the maximum microphone value from microphone (requires reset)
00191     unsigned char sndCount; //!< number of times the microphone reading has exceeded a threshold
00192   };
00193   
00194   
00195   struct WriteHeader : public GenericCmdHeader {
00196     WriteHeader(unsigned char address, unsigned char len)
00197     : GenericCmdHeader(len+3,0x3), addr(address) {}
00198     unsigned char addr;
00199   };
00200   //! Broadcasts a 'torque enable' (or disable) command to all servos using a write command to the appropriate address
00201   struct BroadcastTorqueCmd : public WriteHeader {
00202     //! constructor, pass true to enable torque, false to cut power
00203     BroadcastTorqueCmd(bool enable)
00204     : WriteHeader(0x18,1), torqueEnable(enable?1:0), checksum() { updateChecksum(*this); }
00205     
00206     unsigned char torqueEnable; //!< non-zero to enable torque of the servo
00207     char checksum;  //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00208   };
00209   //! Broadcasts a command to set controller parameters to 0, also disabling torque (see also BroadcastNoPunchCmd)
00210   struct BroadcastFullComplianceCmd : public WriteHeader {
00211     BroadcastFullComplianceCmd()
00212     : WriteHeader(0x1A,4), cwmargin(0), ccwmargin(0), cwslope(0), ccwslope(0), checksum() { updateChecksum(*this); }
00213     unsigned char cwmargin;
00214     unsigned char ccwmargin;
00215     unsigned char cwslope;
00216     unsigned char ccwslope;
00217     char checksum;  //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00218   };
00219   //! Sets 'punch' parameter to 0, eliminating torque jump (see also BroadcastFullComplianceCmd)
00220   struct BroadcastNoPunchCmd : public WriteHeader {
00221     BroadcastNoPunchCmd()
00222     : WriteHeader(0x30,2), punchl(0), punchh(0), checksum() { updateChecksum(*this); }
00223     unsigned char punchl;
00224     unsigned char punchh;
00225     char checksum;  //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00226   };
00227   //! Sets 'speed' parameter to 0, ensuring no motion will occur from leftover 'free spin' commands
00228   struct BroadcastZeroSpeedCmd : public WriteHeader {
00229     BroadcastZeroSpeedCmd()
00230     : WriteHeader(0x20,2), speedl(0), speedh(0), checksum() { updateChecksum(*this); }
00231     unsigned char speedl;
00232     unsigned char speedh;
00233     char checksum;  //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00234   };
00235   //! Sets 'baud rate' parameter to the specified value, where resulting bits per second will be 2M / (baud+1)
00236   struct BroadcastBaudCmd : public WriteHeader {
00237     BroadcastBaudCmd(unsigned char divisor) : WriteHeader(0x04,1), baudDivisor(divisor), checksum() { updateChecksum(*this); }
00238     unsigned char baudDivisor;
00239     char checksum;  //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00240   };
00241   //! Sets 'return delay time' parameter to the specified value, where actual delay time is 2µs * value
00242   struct SetReturnDelayTimeCmd : public WriteHeader {
00243     SetReturnDelayTimeCmd() : WriteHeader(0x05,1), delayTime(0), checksum() { updateChecksum(*this); }
00244     SetReturnDelayTimeCmd(unsigned char delay) : WriteHeader(0x05,1), delayTime(delay), checksum() { updateChecksum(*this); }
00245     SetReturnDelayTimeCmd(unsigned char sid, unsigned char delay) : WriteHeader(0x05,1), delayTime(delay), checksum() { servoid=sid; updateChecksum(*this); }
00246     unsigned char delayTime; //!< amount of time to delay before responding, in units of 2µs
00247     char checksum;  //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00248   };  
00249   //! Sets 'status response level' parameter to the specified value, see StatusResponseLevel
00250   struct SetStatusResponseLevelCmd : public WriteHeader {
00251     SetStatusResponseLevelCmd() : WriteHeader(0x10,1), responseLevel(0), checksum() { updateChecksum(*this); }
00252     SetStatusResponseLevelCmd(StatusResponseLevel level) : WriteHeader(0x05,1), responseLevel(level), checksum() { updateChecksum(*this); }
00253     SetStatusResponseLevelCmd(unsigned char sid, StatusResponseLevel level) : WriteHeader(0x05,1), responseLevel(level), checksum() { servoid=sid; updateChecksum(*this); }
00254     unsigned char responseLevel; //!< see StatusResponseLevel
00255     char checksum;  //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00256   };  
00257   //! Sends a command to change a servo's ID value (use carefully!)
00258   struct SetServoIDCmd : public WriteHeader {
00259     SetServoIDCmd(unsigned char tgtsid, unsigned char newsid)
00260     : WriteHeader(0x03, 1), newservoid(newsid), checksum() { servoid=tgtsid; updateChecksum(*this); }
00261     unsigned char newservoid;
00262     char checksum;  //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00263   };
00264   //! Sets the position and speed for a single servo
00265   struct SetPosSpeedCmd : public WriteHeader {
00266     SetPosSpeedCmd() : WriteHeader(0x1E,4), posl(), posh(), speedl(), speedh(), checksum() { updateChecksum(*this); }
00267     SetPosSpeedCmd(unsigned char sid, unsigned short pos, unsigned short speed)
00268     : WriteHeader(0x1E,4), posl(pos), posh(pos>>8), speedl(speed), speedh(speed>>8), checksum() { servoid=sid; updateChecksum(*this); }
00269     unsigned char posl;
00270     unsigned char posh;
00271     unsigned char speedl;
00272     unsigned char speedh;
00273     char checksum;  //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00274   };
00275   //! clears the sound data max hold field of an AX-S1
00276   struct ClearSoundHoldCmd : public WriteHeader {
00277     ClearSoundHoldCmd() : WriteHeader(0x24,1), sndMaxHold(0), checksum() { updateChecksum(*this); }
00278     ClearSoundHoldCmd(unsigned char sid) : WriteHeader(0x24,1), sndMaxHold(0), checksum() { servoid=sid; updateChecksum(*this); }
00279     unsigned char sndMaxHold; //!< the maximum microphone value from microphone (requires reset)
00280     char checksum;  //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00281   };
00282   //! clears the sound detected count field of an AX-S1
00283   /* This can be sent after the count becomes non-zero to clear it so you can detect another count of the same magnitude.
00284    *  In other words, the AX-S1 count stays at zero until a time after the last increment, when the total count is posted to the register.
00285    *  You don't receive updates for individual increments to the counter — you receive only the total count at the end.
00286    *  So once you have read the total count, you should reset it. */
00287   struct ClearSoundCountCmd : public WriteHeader {
00288     ClearSoundCountCmd() : WriteHeader(0x25,1), sndCount(0), checksum() { updateChecksum(*this); }
00289     ClearSoundCountCmd(unsigned char sid) : WriteHeader(0x25,1), sndCount(0), checksum() { servoid=sid; updateChecksum(*this); }
00290     unsigned char sndCount; //!< number of times the microphone reading has exceeded a threshold
00291     char checksum;  //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00292   };
00293   //! clears the sound data max hold and sound detected count fields of an AX-S1 at the same time, see ClearSoundHold and ClearSoundCount
00294   struct ClearSoundHoldAndCountCmd : public WriteHeader {
00295     ClearSoundHoldAndCountCmd() : WriteHeader(0x24,2), sndMaxHold(0), sndCount(0), checksum() { updateChecksum(*this); }
00296     ClearSoundHoldAndCountCmd(unsigned char sid) : WriteHeader(0x24,2), sndMaxHold(0), sndCount(0), checksum() { servoid=sid; updateChecksum(*this); }
00297     unsigned char sndMaxHold; //!< the maximum microphone value from microphone (requires reset)
00298     unsigned char sndCount; //!< number of times the microphone reading has exceeded a threshold
00299     char checksum;  //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00300   };
00301   
00302   
00303   
00304   //! Provides bitmasks for checking various error conditions in GenericResponseHeader::error
00305   enum ResponseError_t {
00306     VOLTAGE_ERROR=1, ANGLE_ERROR=2, HEAT_ERROR=4, RANGE_ERROR=8, CHECKSUM_ERROR=16, LOAD_ERROR=32, INSTRUCTION_ERROR=64
00307   };
00308   extern const char* ResponseErrorNames[9];
00309   
00310   //! contains fields global to all response packets
00311   struct GenericResponseHeader {
00312     GenericResponseHeader() : markerA(0), markerB(0), servoid(0), resplen(0), error(0) {}
00313     operator char*() { return reinterpret_cast<char*>(&markerA); } //!< easy serialization
00314     operator const char*() const { return reinterpret_cast<const char*>(&markerA); } //!< easy serialization
00315     operator const unsigned char*() const { return &markerA; } //!< easy serialization
00316     unsigned char markerA;  //!< should always be 0xFF
00317     unsigned char markerB;  //!< should always be 0xFF
00318     unsigned char servoid;  //!< the ID of the servo who made the response
00319     unsigned char resplen;  //!< number of data bytes which follow, including error field and final checksum
00320     unsigned char error;    //!< error bitmask, see ResponseError_t
00321   };
00322   inline unsigned char nchecksum(const struct GenericResponseHeader& p, size_t len) { return nchecksum(p,len,2); }
00323   
00324   const unsigned char RESPONSE_HEADER_LEN = sizeof(GenericResponseHeader)+1;
00325   
00326   //! Expected response from a Write command (when sent to non-broadcast address)
00327   struct WriteResponse : public GenericResponseHeader {
00328     unsigned char checksum;
00329   };
00330   //! Expected response from a ReadServoSensorsCmd, common across AX, RX, and EX series at least
00331   struct ServoSensorsResponse : public GenericResponseHeader {
00332     //! constructor
00333     ServoSensorsResponse() : GenericResponseHeader(), posl(), posh(), speedl(), speedh(), loadl(), loadh(), voltage(), temp(), checksum() {}
00334     
00335     //! returns current position in 'tics', 10 bit resolution
00336     unsigned short getPosition() const { return (static_cast<unsigned short>(posh)<<8) + posl; }
00337     //! returns current speed as 'tics' per second, 11 bit signed resolution
00338     short getSpeed() const { short x = (static_cast<short>(speedh&0x3)<<8) | speedl; return (speedh&4) ? -x : x; }
00339     //! returns an indication of how hard the servo is working, 11 bit signed resolution; note we flip the sign from what the hardware provides
00340     short getLoad() const { short x = (static_cast<short>(loadh&0x3)<<8) | loadl; return (loadh&4) ? x : -x; }
00341     
00342     unsigned char posl;       //!< low byte of position
00343     unsigned char posh;       //!< high byte of position
00344     unsigned char speedl;       //!< low byte of speed
00345     unsigned char speedh;       //!< high byte of speed
00346     unsigned char loadl;      //!< low byte of load
00347     unsigned char loadh;      //!< high byte of load
00348     unsigned char voltage;      //!< voltage times 10
00349     unsigned char temp;       //!< temperature (Celsius)
00350     unsigned char checksum;       //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00351   };
00352   //! Expected response from a ReadAXS1SensorsCmd
00353   struct AXS1SensorsResponse : public GenericResponseHeader {
00354     //! constructor
00355     AXS1SensorsResponse() : GenericResponseHeader(),
00356       leftIR(), centerIR(), rightIR(), leftLum(), centerLum(), rightLum(),
00357       obsFlag(), lumFlag(), _robotisReserved(), sndData(), sndMaxHold(), sndCount(),
00358       /*sndTimeLow(), sndTimeHigh(), buzzerIndex(), buzzerTime(), voltage(), temp(), */
00359       checksum()
00360     {}
00361     
00362     /*
00363     //! timestamp of last #detCount increment, 16MHz timing: advances by 16000 per ms
00364     unsigned short getDetectedTime() {
00365       return (static_cast<unsigned short>(sndTimeHigh)<<8) + sndTimeLow;
00366     }about:startpage
00367      */
00368 
00369     unsigned char leftIR; //!< reflectance from the left
00370     unsigned char centerIR; //!< reflectance from the center
00371     unsigned char rightIR; //!< reflectance from the right
00372     unsigned char leftLum; //!< external illumination from the left
00373     unsigned char centerLum; //!< external illumination from the center
00374     unsigned char rightLum; //!< external illumination from the right
00375     unsigned char obsFlag; //!< bitset indicating whether #leftIR, #centerIR, or #rightIR have exceeded a threshold (unused, requires reset)
00376     unsigned char lumFlag; //!< bitset indicating whether #leftLum, #centerLum, or #rightLum have exceeded a threshold (unused, requires reset)
00377     unsigned char _robotisReserved; //!< unused
00378     unsigned char sndData; //!< current pressure reading from microphone (unused, not sampled frequently enough to be useful)
00379     unsigned char sndMaxHold; //!< the maximum microphone value from microphone (requires reset)
00380     unsigned char sndCount; //!< number of times the microphone reading has exceeded a threshold
00381     /*
00382     unsigned char sndTimeLow; //!< timestamp of last #detCount increment, 16MHz timing: advances by 16000 per ms (low order byte)
00383     unsigned char sndTimeHigh; //!< timestamp of last #detCount increment, 16MHz timing: advances by 16000 per ms (high order byte)
00384     unsigned char buzzerIndex; //!< the note to play, 0-51 inclusive (command value); if #buzzerTime is set to 255, then 0-26 inclusive to select a score (command value)
00385     unsigned char buzzerTime; //!< time to play the note for in tenths of a second, 0-50; 254 plays continuously, 255 plays scores via #buzzerIndex (command value)
00386     unsigned char voltage; //!< voltage times 10
00387     unsigned char temp; //!< temperature (Celsius)
00388      */
00389     unsigned char checksum; //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00390   };
00391   //! Expected response from a ReadTorqueCmd
00392   struct TorqueResponse : public GenericResponseHeader {
00393     unsigned char torqueEnable;  //!< if non-zero, torque is enabled on the servo (false indicates an error may have occurred)
00394     unsigned char checksum;  //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00395   };
00396   //! Expected response from a ReadModelCmd
00397   struct ServoInfoResponse : public GenericResponseHeader {
00398     ServoInfoResponse() : GenericResponseHeader(), modell(0), modelh(0), version(0), checksum() {}
00399     unsigned short getModelNumber() const { return (static_cast<unsigned short>(modelh)<<8) | modell; }
00400     const char* getModelString() const {
00401       typedef typeof(dynamixelModels) modelmap_t;
00402       modelmap_t::const_iterator it = dynamixelModels.find((ModelID_t)getModelNumber());
00403       if(it==dynamixelModels.end()) {
00404         return (getModelNumber()==0 && version==0) ? "INVALID RESPONSE" : MODEL_UNKNOWN_NAME;
00405       } else {
00406         return it->second.c_str();
00407       }
00408     }
00409     unsigned char modell;  //!< indicates low byte of model number
00410     unsigned char modelh;  //!< indicates high byte of model number
00411     unsigned char version;  //!< firmware version
00412     unsigned char checksum;  //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00413   };
00414   
00415   
00416   
00417   //! Contains fields which are global to all read commands, which can read a block of parameters from the unit
00418   struct ReadCmd : public GenericCmdHeader {
00419     //! constructor, pass the unit ID, the starting address, and the number of bytes to read
00420     ReadCmd(unsigned char servoID, unsigned char address, unsigned char len)
00421       : GenericCmdHeader(4,0x2), addr(address), readlen(len), checksum() { servoid=servoID; updateChecksum(*this); }
00422     unsigned char addr;  //!< address to start reading from on the unit
00423     unsigned char readlen;  //!< number of bytes to read
00424     unsigned char checksum;  //!< checksum value (bitwise negated sum of all non-marker fields), see nchecksum()
00425   };
00426   //! Requests a block of servo sensor values be sent, servo should respond with a ServoSensorsResponse
00427   struct ReadServoSensorsCmd : public ReadCmd {
00428     //! constructor, pass the ID of the servo you want to query
00429     ReadServoSensorsCmd(unsigned char servoID) : ReadCmd(servoID,0x24,sizeof(ServoSensorsResponse)-RESPONSE_HEADER_LEN) {}
00430   };
00431   //! Requests a block of AX-S1 sensor values be sent, module should respond with a AXS1SensorsResponse
00432   struct ReadAXS1SensorsCmd : public ReadCmd {
00433     //! constructor, pass the ID of the servo you want to query
00434     ReadAXS1SensorsCmd(unsigned char servoID) : ReadCmd(servoID,0x1A,sizeof(AXS1SensorsResponse)-RESPONSE_HEADER_LEN) {}
00435   };
00436   //! Requests the 'torque enable' status be sent, servo should respond with a TorqueResponse
00437   struct ReadTorqueCmd : public ReadCmd {
00438     //! constructor, pass the ID of the servo you want to query
00439     ReadTorqueCmd(unsigned char servoID) : ReadCmd(servoID,0x18,sizeof(TorqueResponse)-RESPONSE_HEADER_LEN) {}
00440   };
00441   //! Requests the 'model number' be sent, servo should respond with a ServoInfoResponse
00442   struct ReadModelCmd : public ReadCmd {
00443     //! constructor, pass the ID of the servo you want to query
00444     ReadModelCmd(unsigned char servoID) : ReadCmd(servoID,0x00,sizeof(ServoInfoResponse)-RESPONSE_HEADER_LEN) {}
00445   };
00446   
00447   
00448   
00449   void reportErrors(unsigned int servoID, unsigned int offset, unsigned char err);
00450   
00451   //! reads a response from an input stream, attempts to handle line noise before response
00452   /*! returns false if an invalid response type is received */
00453   template<class R> bool readResponse(std::istream& is, R& response, unsigned int offset) {
00454     //std::cout << "reading markers..." << std::endl;
00455     is.read((char*)&response.markerA,sizeof(response.markerA)*2);
00456     if(!is || is.gcount()!=sizeof(response.markerA)*2) {
00457       Thread::testCurrentCancel();
00458       std::cerr << "Dynamixel protocol bad read! 1" << std::endl;
00459       is.sync();
00460       is.clear();
00461       return false;
00462     }
00463     size_t noiseCnt=0;
00464     //std::cout << "got markers " << (int)response.markerA << ' ' << (int)response.markerB << std::endl;
00465     while(response.markerA!=DynamixelProtocol::MARKER_VALUE || response.markerB!=DynamixelProtocol::MARKER_VALUE) {
00466       //std::cout << "re-reading markers " << (int)response.markerA << ' ' << (int)response.markerB << std::endl;
00467       ++noiseCnt;
00468       response.markerA=response.markerB;
00469       try {
00470         is.read((char*)&response.markerB,sizeof(response.markerB));
00471         if(!is || is.gcount()!=sizeof(response.markerB)) {
00472           Thread::testCurrentCancel();
00473           std::cerr << "Dynamixel protocol bad read! 2" << std::endl;
00474           is.sync();
00475           is.clear();
00476         }
00477       } catch(...) {
00478         std::cerr << "Dynamixel protocol couldn't find packet start, skipped " << noiseCnt << " bytes of line noise." << std::endl;
00479         throw;
00480       }
00481     }
00482     if(noiseCnt!=0)
00483       std::cerr << "Dynamixel protocol skipping " << noiseCnt << " bytes of line noise" << std::endl;
00484     const size_t HEADER_SIZE = sizeof(response.servoid)+sizeof(response.resplen)+sizeof(response.error);
00485     //std::cout << "reading header " << HEADER_SIZE << std::endl;
00486     is.read((char*)&response.servoid,HEADER_SIZE);
00487     if(!is || (size_t)is.gcount()!=HEADER_SIZE) {
00488       Thread::testCurrentCancel();
00489       std::cerr << "Dynamixel protocol bad read! 3" << std::endl;
00490       is.sync();
00491       is.clear();
00492       return false;
00493     }
00494     if(response.resplen<2) {
00495       std::cerr << "Dynamixel protocol got bad packet, too short! (" << (int)response.resplen << ")" << std::endl;
00496       return false;
00497     }
00498     //std::cout << "got header from " << (int)response.servoid << std::endl;
00499     if(response.resplen != sizeof(R)-sizeof(GenericResponseHeader)+1) {
00500       unsigned char tmpbuf[256];
00501       memcpy(tmpbuf,&response.servoid,HEADER_SIZE);
00502       is.read((char*)tmpbuf+HEADER_SIZE,response.resplen-sizeof(response.error));
00503       if(!is || (size_t)is.gcount()!=response.resplen-sizeof(response.error)) {
00504         Thread::testCurrentCancel();
00505         std::cerr << "Dynamixel protocol bad read! 4" << std::endl;
00506         is.sync();
00507         is.clear();
00508         return false;
00509       }
00510       unsigned char rchksum = tmpbuf[HEADER_SIZE+response.resplen-sizeof(response.error)-1];
00511       unsigned char lchksum = ~nchecksum(tmpbuf,HEADER_SIZE+response.resplen-sizeof(response.error)-1,0);
00512       if( lchksum == rchksum) {
00513         if(response.error==0)
00514           std::cerr << "Dynamixel protocol: invalid response (expected " << (sizeof(R)-sizeof(GenericResponseHeader)+1) << ", length=" << (int)response.resplen << ")" << std::endl;
00515         else
00516           reportErrors(response.servoid,offset,response.error);
00517       } else {
00518         std::cerr << "Dynamixel line noise: sensor checksum failed" << std::endl;
00519         is.sync();
00520       }
00521       return false;
00522     }
00523     
00524     //std::cout << "Header complete, reading payload size " << response.resplen-sizeof(response.error) << std::endl;
00525     is.read((char*)(&response.error+1),response.resplen-sizeof(response.error));
00526     if(!is || (size_t)is.gcount()!=response.resplen-sizeof(response.error)) {
00527       Thread::testCurrentCancel();
00528       std::cerr << "Dynamixel protocol bad read! 5" << std::endl;
00529       is.sync();
00530       is.clear();
00531       return false;
00532     } else if(!validate(response)) {
00533       /*std::cerr << "Sensor checksum failed, response was: " << std::flush; 
00534       debuget::hexout(response,sizeof(response));
00535       std::cerr << "checksum: " << std::flush;
00536       debuget::charhexout(~nchecksum(response,response.resplen+3)); std::cout.flush();
00537       std::cerr << std::endl;*/
00538       std::cerr << "Dynamixel line noise: sensor checksum failed" << std::endl;
00539       is.sync();
00540       return false;
00541     }
00542     
00543     if(response.error!=0)
00544       reportErrors(response.servoid,offset,response.error);
00545     return true;
00546   }
00547   
00548   //! Attempts to ping the specified servo by reading its model number, and if successful, current sensor values
00549   /*! The run() call returns with the model string if successful, or NULL if the ping timed out */
00550   class PingThread : public Thread {
00551   public:
00552     //! constructor, pass input and output streams, the servo id, and optional sensor response to be filled in
00553     PingThread(std::istream& is, std::ostream& os, unsigned char servoid, unsigned int outputOffset, ServoSensorsResponse* servoinfo=NULL, AXS1SensorsResponse* servoinfoS1=NULL)
00554       : Thread(), response(), icomm(is), ocomm(os), sid(servoid), output(outputOffset), info(servoinfo), infoS1(servoinfoS1), unknownModelName() { start(); }
00555     //! destructor, stop and join thread
00556     ~PingThread() { if(isStarted()) stop().join(); }
00557     
00558     static long getTimeout() { return timeout; } //!< get #timeout
00559     static void setTimeout(long t) { timeout = t; } //!< set #timeout
00560     
00561     ServoInfoResponse response;
00562     
00563   protected:
00564     virtual void * run();
00565     virtual void cancelled();
00566     static long timeout; //!< time to wait for a response before giving up (in milliseconds)
00567     std::istream& icomm;  //!< input stream
00568     std::ostream& ocomm;  //!< output stream
00569     unsigned char sid;  //!< servo id value
00570     unsigned int output; //!< output offset
00571     ServoSensorsResponse* info;  //!< response to be filled in if successful and the servo is servo following common Dynamixel servo register layout.
00572     AXS1SensorsResponse* infoS1;  //!< response to be filled in if successful and the servo is an AX-S1 model.
00573     std::string unknownModelName; //!< temporary storage to append the model number to 
00574 
00575   private:
00576     PingThread(const PingThread&); //!< dummy
00577     PingThread& operator=(const PingThread&); //!< dummy
00578   };
00579   
00580   // this is a bad idea because it lets you encode 'pure' headers, where the actual data is just junk values following the header
00581   //inline std::ostream& operator<<(std::ostream& os, const GenericCmdHeader& cmd) { return os.write(cmd,cmd.cmdlen+4); }
00582   
00583   //! writes a command into a stream, returning the stream for convenient ostream::flush call.
00584   template<class T> std::ostream& write(std::ostream& os, const T& cmd) { return os.write(cmd,sizeof(cmd)); }
00585   //! writes a command into a stream and incrementing a checksum, returning the stream for convenient ostream::flush call.  Remember to bitwise-not (~) the checksum before transmission!
00586   template<class T> std::ostream& write(std::ostream& os, const T& cmd, unsigned char& checksum) { checksum+=nchecksum(cmd,sizeof(cmd)); return os.write(cmd,sizeof(cmd)); }
00587 };
00588 
00589 /*! @file
00590  * @brief 
00591  * @author Ethan Tira-Thompson (ejt) (Creator)
00592  */
00593 
00594 #endif

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Sat May 4 06:37:21 2013 by Doxygen 1.6.3