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

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Mon May 9 05:01:38 2016 by Doxygen 1.6.3