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

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Tue Jan 31 04:35:31 2012 by Doxygen 1.6.3