Source code (I should clean it …)
Category: Programming CM-9.xx
(II) C++ crash examples tutorial using Robotis CM-900 board and IDE (II)
In this post I will try to show one important C++ feature, virtual classes as interfaces, defining a rol that several classes can implement. Also
Here you can download the file with the source code.
// C++ crash tutorial with Robotis CM-900. // First example: http://softwaresouls.com/softwaresouls/ /* Classes inheritance and interface creation in C++ */ // Abstract class as Interface for communication device classes http://www.cplusplus.com/doc/tutorial/polymorphism/ class CommunicationDevice { public: virtual void setup(int baudrate=0)=0; // baudrate=0, default parameter in case is not passed any parameter. //...)=0; is a pure virtual method that convert the class in abstract http://www.cplusplus.com/doc/tutorial/polymorphism/ // because is not straight usable , a derived classimpllementing the pure virtual methods must be created virtual void send(char *message)=0; // default parameter in case is not passed any parameter. virtual void send(int i)=0; //Method overloading http://www.cplusplus.com/doc/tutorial/functions2/ Same method name with different parameter types }; class Zigbee : public CommunicationDevice //inherits the interface a should define the methos to be able to create objects. { public: void setup(int baudrate=0); void send (char *message); void send(int i); }; class USB : public CommunicationDevice //inherits the interface a should define the methos to be able to create objects. { public: void setup(int baudrate=0); void send (char *message); void send(int i); }; class Debug { CommunicationDevice &commDevice; public: Debug(CommunicationDevice &aCommDevice) : commDevice(aCommDevice) {}; // member object commDevice is instantiated by reference to avoid object copy void show(char *message) { commDevice.send(message); // the "commDevice" method called depends on the object passed to the constructor } void show(int i) { commDevice.send(i); } }; void Zigbee::setup(int baudrate) { if (baudrate==0) baudrate=57600; //only changes local copy, not the variable passed as value parameter http://www.cplusplus.com/doc/tutorial/functions2/ Serial2.begin(baudrate); } void Zigbee::send(char *message) { Serial2.println(message); } void Zigbee::send(int i) { Serial2.println(i); } void USB::setup(int baudrate) { SerialUSB.begin(); } void USB::send(char *message) { SerialUSB.println(message); } void USB::send(int i) { SerialUSB.println(i); } Zigbee zigbee; USB usb; const byte NumberOfCommsDevices=2; CommunicationDevice *commsDevices[]={&usb, &zigbee}; //={usb, zigbee}; void setup() { // Individually: // zigbee.setup(); // usb.setup(); // collectively. using the common Interface "CommunicationDevice" for (int i=0;i<NumberOfCommsDevices;i++) commsDevices[i]->setup(); delay(2000); // waiting for 2 seconds, let the user activate the monitor/console window (Control+Shift+M). } int counter=0; void loop() { ++counter; Debug debug1(usb); // Debug constructor accepts any class inhereting the Interface "CommunicationDevice", Debug(CommunicationDevice &aCommDevice) debug1.show("Hola"); //debug object will call the method "send" implemented in the object passed to the constructor. In this case "usb.send" //Showing the counter for each iteration until the 2000th iteration if (counter <=2000) debug1.show(counter); else { // Showing the counter every 100 iterations and waiting for 2 seconds if (counter%100==0) // Operator modulo http://www.cplusplus.com/doc/tutorial/operators/ (returns the remainder) { debug1.show(counter); delay(2000); } } }
Robotis CM-900 as a tosser for Dynamixel commands
CM-900 is really tiny and cheap, so is perfect to use as communication bridge between any computer (Raspberry Pi, Pandaboard, etc. included, of course) and the Dynamixel bus. Whatever it receives from the Serial USB (usually commands and queries) is sent to the Dynamixel bus, and what it receives from the Dynamixel bus is sent to the SerialUSB (usually answers)
Here is the source code of the little program for CM-900 IDE:
int counter; bool onlyOnceHappened; void blinkOnce() { digitalWrite(BOARD_LED_PIN, LOW); delay_us(100); digitalWrite(BOARD_LED_PIN, HIGH); } void setup() { pinMode(BOARD_LED_PIN, OUTPUT); onlyOnceHappened=false; counter=0; //USB Serial initialize SerialUSB.begin(); // SerialUSB.attachInterrupt(USBDataReceived); //DXL initialize Dxl.begin(1); } byte aByte=0; uint8 aUint8; void loop() { // SerialUSB.println (counter++); if (onlyOnceHappened==false) { blinkOnce(); onlyOnceHappened=true; delay (3000); //Some time to the user to activate the monitor/console SerialUSB.println ("v1.1.1 Orders receiver started"); } if (SerialUSB.available()) { aUint8=SerialUSB.read(); blinkOnce(); Dxl.writeRaw(aUint8); // delay(20); } if (Dxl.available()) { aByte=Dxl.readRaw(); blinkOnce(); SerialUSB.write(aByte); // delay(20); } }
In the next post I will include an improved version that could read sensors connected to the CM-900, “expanding” the Dynamixel protocol.
CM-900 Robotis IDE more programming examples
I’ve started to program CM-900 “translating” the example from the C programming tutorial for CM-510
You can get more information at robotsource.org CM-900 community circle and the CM-900 e-manual. There you can find:
– Arduino based IDE to program it very easily (examples included)
– Source code and instructions
I already have “translated” two examples: a simple “hello world” like where a servo is move to 2 different positions and another where the servo id and position is asked and after entered the data the servo is positionated in the typed position, validating that id and position are in range.
The “hello World ” example (here the zipped file):
#define P_GOAL_POSITION_L 30 #define P_MOVING 46 int counter; int onlyOnceHappened; void setup() { //USB Serial initialize onlyOnceHappened=0; counter=0; SerialUSB.begin(); Dxl.begin(1); delay (5000); SerialUSB.println("Setup"); } void loop() { delay (3000); SerialUSB.print("loop"); SerialUSB.println(counter); counter++; delay(1000); if (onlyOnceHappened==0) { onlyOnceHappened=1; SerialUSB.println("Hello, World"); } int id=8; SerialUSB.println ("Simple example #0"); SerialUSB.print ("Perform movement 1 with the AX-12:"); SerialUSB.println (id); byte bMoving = Dxl.readByte( id, P_MOVING); byte CommStatus = Dxl.getResult(); if( CommStatus == COMM_RXSUCCESS ) Dxl.writeWord( id, P_GOAL_POSITION_L, 511 ); else SerialUSB.println ("CommStatus IS NOT COMM_RXSUCCESS"); SerialUSB.println ("Pause half a second"); delay (500); // half-second pause SerialUSB.println ("Beep!"); //buzzOn (100); // beep SerialUSB.println ("Pause for a second"); delay (1000); // pause for 1 second SerialUSB.print ("Perform movement 2 with the AX-12: "); SerialUSB.println (id); Dxl.writeWord( id, P_GOAL_POSITION_L, 611 ); SerialUSB.println ("End"); }
Asking Id and Position example, (here the zipped file)
int counter; bool onlyOnceHappened; int id; int position; bool debugOutputOn; char charReaded; void debugOutputUSB(char *buffer, bool newlineAfterMessage) { if (debugOutputOn) { if (newlineAfterMessage) SerialUSB.println(buffer); else SerialUSB.print(buffer); } } void debugOutputUSB(int value, bool newlineAfterMessage) { if (debugOutputOn) { if (newlineAfterMessage) SerialUSB.println(value); else SerialUSB.print(value); } } void debugOutputPairUSB(char *buffer, int value) { if (debugOutputOn) { SerialUSB.println (""); SerialUSB.print(buffer); SerialUSB.println(value); } } void debugOutputPairUSB(char *buffer1, char *buffer2) { if (debugOutputOn) { SerialUSB.println (""); SerialUSB.print(buffer1); SerialUSB.println(buffer2); } } void setup() { //USB Serial initialize onlyOnceHappened=false; counter=0; debugOutputOn=false; SerialUSB.begin(); Dxl.begin(1); delay (3000); debugOutputUSB("Setuped", true); } void checkIfDebugOn() { SerialUSB.print("Put debug ON?, y/n"); charReaded=SerialUSB.read(); if (charReaded=='y' || charReaded=='Y') debugOutputOn=true; else debugOutputOn=false; debugOutputPairUSB ("DebugPut is:", debugOutputOn); } void loop() { delay (1500); SerialUSB.println ("Simple example #1"); if (onlyOnceHappened==false) { onlyOnceHappened=true; checkIfDebugOn(); } debugOutputPairUSB ("Loop: ",counter); counter++; delay(500); debugOutputUSB("Starting loop", true); while(true) // we'll repeat this loop forever { id=getId(); // get the ID of the AX-12 that we want to move position=getPosition(); // get the goal position Dxl.writeWord( id, P_GOAL_POSITION_L, position); // sent the command to the Dynamixel debugOutputUSB ("Dxl.writeWorded", true); } } // I have created a simple ayoi because I have problem compiling atoi int myatoi(char *buffer) { int len=strlen(buffer)-2; int value=0; int i=0; int base=0; int number=0; for (i=len;i>=0;i--) { number=int(buffer[i])-int('0'); debugOutputPairUSB ("buffer[i]: ", int(buffer[i])); if (i==len) { value=number; } else { value+=base * number; } debugOutputPairUSB ("i: ",i); debugOutputPairUSB ("number: ", number); debugOutputPairUSB("base: ", base); debugOutputPairUSB("valor: ", value); if (base==0) { base=10; } else base=base*10; } return value; } bool isValid(int value, int MinValue, int MaxValue) { return (value>=MinValue && value<=MaxValue); } void readString(char *bufferParameter) { int i=0; // We'll use this variable as index of the buffer where we will store data do { bufferParameter[i]=(char) SerialUSB.read(); // it store the read character in the i position of bufferParameter SerialUSB.print(bufferParameter[i]); // showing it if (bufferParameter[i]=='\b') // if Backspace was pressed i--; // it goes to the previous position, rewritting the previoulsy typed character else // i++; // it will write in the next position // SerialUSB.println(int(bufferParameter[i-1])); }while(bufferParameter[i-1]!=10); // while the last values is not INTRO. The symmbol ! represents the logical operator NOT bufferParameter[i]=0; // A NULL (0, zero) is necessary in the last position of any string } /* It read an string, it's converted to integer and returned */ int readInteger(char *bufferParameter) { // return SerialUSB.parseInt(); readString(bufferParameter); // int valor=int(bufferParameter[0]-int('0')); debugOutputPairUSB ("Leido: ", bufferParameter); int valor=myatoi(bufferParameter); debugOutputPairUSB ("Valor leido: ", valor); return valor; } int getId() { /* We define an array enough large, 256 bytes (characters). Probably it's enough with 4 bytes, but if we type more than the defined size we will get an error*/ char buffer[256]; /* And we define another integer variable, it's very advisable to asign a value in the definition, in this case we assign the minimun value, 1*/ int ax12Id=1; do { // starting the loop SerialUSB.println ("Enter the ID of the AX-12 that you wwant to move, between 1 y 18: "); SerialUSB.print("Id:"); ax12Id=readInteger(buffer); // this function will read from what we type in the keyboard //// exclamation (!) is the NOT logical operator. It will repeat the loop while the value is not valid }while(!isValid(ax12Id, 1, 18)); // Showing the typed value SerialUSB.println(" "); SerialUSB.print("AX12 ID:"); SerialUSB.println(ax12Id); return ax12Id; } int getPosition() { char buffer[256]; int position=0; do { SerialUSB.println ("Enter a value between 0 and 1023 as the goal position"); SerialUSB.print ("Position:"); position=readInteger(buffer); }while(!isValid(position, 0, 1023)); // Showing the typed value SerialUSB.println(" "); SerialUSB.print("Position:"); SerialUSB.println(position); return position; }