CM-510 firmware and post updated
Source code (I should clean it …)
Create robots requires a lot of different skills and, depending on how sophisticated is the behavior or the tasks that must be performed, can be really complex and extremely difficult.
The three pillars of robotics are electronics, mechanics and programming; there are others, but only with these three you can already start experimenting.
In addition to applicable knowledge you also need some others items such as motors (or other actuators), sensors, a small computer to avoid weight and power consumption (typically a SBC or microcontroller ) and a power source (battery to be autonomous) and some parts that sustain and hold together all these elements and their movements.
These elements can be acquired (some even manufactured) separately, or more easily, and probably cheaper as a kit.
.
Mainly, you need wish to enjoy creating and learning.
* Acquire a minimum and basic knowledge of:
+ Electronics or electricity (if only to distinguish voltage and current),
+ Mechanics (the minimum would be screwing and unscrewing or connect Lego type pieces)
+ Computer (at least to run a program on a computer)
* Get at least a microcontroller (Arduino, for example), a pair of motors, a distance sensor, a battery, cables, and a structure to support it. The basic set or kit.
A clear advantage of starting with a kit is that you start NOW, spending your time on where is your interest (electronic, mechanical or computer) , because the others areas are already solved, but anytime you can get to work in any of them. When you buy the kit with all the necessary set of elements together, the price is also often cheaper.
From the more expensive and complex to the cheapest and easiest
Bioloid : 18 powerful servo motors, 4 sensors. Ideal for humanoid, quadrupeds, hexapods and even vehicles; software to create them and behave like living beings. Includes programming software RoboPlus Tasks, you can also program in C, and create motion sequences with RoboPlus Motion. Using other controller, like Raspberry Pi you can use any language and programming tools which generate code to it.
It costs around 1000 euros. Yes, it is somewhat expensive, but if you have enough money and programming skills or willingness to learn, I think it deserves the price.
Mindstorm EV3: 2 servo motors, 1 motor, 4 sensors. Ideal for mechanisms and vehicles. Very easy to use, but it also allows you to create complex robots. Includes programming software NXT-G, NXT is also possible to program in Java with Far and C / C + + with Osek, not yet available for EV3 or very early versions.
It costs around 300 euros, although it may seem expensive compared to other options, its enormous potential and flexibility in all aspects (construction, programming and electronics) make it tremendously interesting.
Mindstorms EV3 is the latest version, released in August 2013.
Vehicle based on Arduino: at least 2 servomotors and all the sensors you want to start. Is easy and cheap, for around 40€/50$ – 70€/97$ you can have the a robot. Ideal for deepening in electronics.
It can be programmed with the friendly Arduino programming environment .
Is the cheapest option and you can go further by buying more components as you go. It not offers as much flexibility and ability to build as Mindstorms vehicles or Bioloid articulated robots, but you can really learn a lot more than it may seem.
.
.
.
For less money, for starters, you can get an Arduino microcontroller or its clones, which cost just over 20 euros/dollars.
Or, completely free of charge, you can start learning to program in C or C + +, which will come very handy to program robots.
Free resources for learning C programming:
But there are a lot…
C Language Tutorial (html)
How C Programming Works (html)
Several C programming tutorials (html)
… and more from Google
And here you can find free resources for learning C++ programming.
In the next post I will write about a fast comparative between Bioloid, Mindstorms and Arduino based robots and about Arduino programming.
Using the Dynamixel SDK instead of RoboPlus Tasks is not possible to query sensors connected to the CM-5xx controller. But, of course, using standard programming languages, like C++, and tools, line QT Creator or Eclipse, with its full featured IDEs and debuggers is a hugue gain.
So I created a firmware for the CM-510 which can be queried and receive commands to itself, including its sensors, or to Dynamyxel devices.
The idea is very simple:
This program running in the CM-510 receives:
– commands or queries to IDs of Dynamixel devices. These are not processed, only redirected to the Dynamixel bus only if it was received by serial port ( serial cable or zigbee). If it was received from the Dynamixel bus nothing is done.
– commands or queries to the CM-510 ID (I chose ID=200), like beep, or to the sensors connected to it. This commands and queries are processed in the CM-510, basically querying the sensors.
In both cases the answer is sent to the connection from which the query or command was received.
After power on the CM-510, you can select the mode with the 4 cursor keys as showed in a terminal connected to its serial port:
“For ‘Toss mode’ press (Up), for ‘Device mode’ (Down), for ‘Device debug mode’ (Left),to start press (Right)”
In the Device mode:
all the receptions and sends are through the Dynamixel bus, the CM-510 is simply another device.
In the Toss mode:
– what is received from the serial connection is sent to the Dynamixel bus or processed in the CM-510 (If sent to its ID)
-what is received from the Dynamixel bus is sent to the serial connection
Finally, the Debug mode:
is like the Device mode, but all the debug messages included in the CM-510 are sent to the serial connection.
A complete sequence with code snippets from the CM-510 program and from the code running in the other computer:
Some C++ code snippets from this example: (C# in the next post)
enum AX12Address //and functions implemented in the CM-510 program, like { ReadCM510SensorRaw = 1, Beep = 8, ReadCM510SensorFiltered = 4, SetSensorValuesToFilter = 5, ... }
void doBeep() { cout << "Beep" << endl; mySystem.dynamixelCommunication.sendOrder(100, AXS1_Buzzer, (byte) DO, (short) 500); usleep (200000); mySystem.dynamixelCommunication.sendOrder(200,MyCommunications::Beep short)5); }
Querying sensor:
void doQuerySensor() { int sensorPort=getSensorPort(); int value=mySystem.dynamixelCommunication.readSensorValue (200,ReadCM510SensorRaw, sensorPort); cout << "the sensor reads: [" << value << "] " << endl << endl << endl; }
These command and query are processed in the CM-510:
Getting the sensor value:
int executeCM510Function() { ... case F_GET_SENSOR_VALUE_RAW: values[1] = getSensorValueRaw(parameters[1]); break; case F_GET_SENSOR_VALUE_FILTERED: values[1] = getSensorValueFiltered(parameters[1], sensorValuesToFilterDefined); break; case F_GET_TWO_DMS_SENSOR_VALUES: parametersReceived=3; getTwoDMSSensorsValues(); break; case F_GET_MULTIPLE_SENSOR_VALUE: getMultipleSensorsValues(); break; case F_DO_SENSOR_SCAN: values[1]= sensorScan(parameters[1]); break; case F_SET_VALUE_DMS1 : //set default value DMS1 DMS1=parameters[1]; break; case F_SET_VALUE_DMS2 : //set default value DMS1 DMS2=parameters[1]; break; case F_BEEP: if (debugMode) printf("executeCM510Function beep\n"); beep(); break; case F_SET_SENSOR_VALUES_TO_FILTER: sensorValuesToFilterDefined=parameters[1]; break; } return function; } } ... int getSensorValueRaw(unsigned char portId) { ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); setPort(portId); //PORTA &= ~0x80; //PORTA &= ~0x20; //_delay_us(12); // Short Delay for rising sensor signal _delay_us(24); ADCSRA |= (1 << ADIF); // AD-Conversion Interrupt Flag Clear ADCSRA |= (1 << ADSC); // AD-Conversion Start while( !(ADCSRA & (1 << ADIF)) ); // Wait until AD-Conversion complete PORTA = 0xFC; // IR-LED Off //printf( "%d\r\n", ADC); // Print Value on USART //_delay_ms(50); _delay_ms(ReadSensorDelayMS); return ADC; }
void beep() { buzzOn(100); buzzOff(); }
But we can do a little more with the CM-510 processor, we can do some filtering to the sensor values.
The readings from the DMS are usually somewhat erratic, so we can simply:
– discard the minimum and maximum values:
– if we take 5 more than measures, then return the average if the are more than 3, if 3 or less it
Previously we should set how many readings should be done, if not, the default number of readings are 5:
int getSensorValueFiltered(unsigned char portId, int times) { ... switch(function) { case F_GET_SENSOR_VALUE_RAW: values[1] = getSensorValueRaw(parameters[1]); break; case F_GET_SENSOR_VALUE_FILTERED: values[1] = getSensorValueFiltered(parameters[1], sensorValuesToFilterDefined); break; case F_GET_TWO_DMS_SENSOR_VALUES: parametersReceived=3; getTwoDMSSensorsValues(); break; case F_GET_MULTIPLE_SENSOR_VALUE: getMultipleSensorsValues(); break; case F_DO_SENSOR_SCAN: values[1]= sensorScan(parameters[1]); break; case F_SET_VALUE_DMS1 : //set default value DMS1 DMS1=parameters[1]; break; case F_SET_VALUE_DMS2 : //set default value DMS1 DMS2=parameters[1]; break; case F_BEEP: if (debugMode) printf("executeCM510Function beep\n"); beep(); break; case F_SET_SENSOR_VALUES_TO_FILTER: sensorValuesToFilterDefined=parameters[1]; break; } ...
We also can take values from multiple sensors with one query, but It will be explained in the next post…
Connections and operating system symbolic names
A serial port is a communication physical interface through which information transfers in or out one bit at a time (in contrast to a parallel port) being, more or less compliant, with the RS-232 standard.
But serial port communications aren’t only useful for wired DE-9 connectors. it also allows us to use it with USB (ftdi), Bluetooth (serial profile) and Zigbee using virtual serial ports.
·
Serial and virtual serial ports appear as COMx in Windows operating systems (COM1, COM2, …) andin UNIX/Linux as ttySx or ttyUSBx or even ttyACMx (ttyS0, ttyS1, ttyUSB0, ttyUSB1, ttyACM0, ttyACM1,…).
For programming purposes we usually want to communicate computers with others computers, microcontrollers or other devices like GPS, LED or LCD displays.
·
Serial port programming in C/C++, Windows and Linux
Using the serial port is a lot easier, but sometimes tricky. The basic commands are to open a connection, read and write over this connection and, finally, tom close it, better if using the C++ RAII idiom.
Windows commands:
Here you can find a complete C++ for Windows example.
With these next two definitions (among others needed):
HANDLEserialPortHandle
wchar_t
* device
serialPortHandle = CreateFile(device, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, NULL, NULL);
if (serialPortHandle!=INVALID_HANDLE_VALUE)
ReadFile(serialPortHandle, buffer, len, &read_nbr, NULL);
…
if (serialPortHandle!=INVALID_HANDLE_VALUE)
WriteFile(serialPortHandle, buffer, len, &result, NULL);
CloseHandle(serialPortHandle);
Unix/Linux commands:
Here you can find a complete C++ for Linux example.
With these two definitions:
·
int
fileDescriptor;
char
*device;
struct termios terminalAttributes;
fileDescriptor = open(device, O_RDWR | O_NOCTTY | O_NDELAY | O_FSYNC );
// clear terminalAttributes data
memset(&terminalAttributes, 0, sizeof(struct termios));
terminalAttributes.c_cflag = B57600 | CS8 | CLOCAL | CREAD;
terminalAttributes.c_iflag = IGNPAR | ONLCR;
terminalAttributes.c_oflag = OPOST;
terminalAttributes.c_cc[VTIME] = 0;
terminalAttributes.c_cc[VMIN] = 1;
tcsetattr(fileDescriptor, TCSANOW, &terminalAttributes);
int
n=read(fileDescriptor, buffer, len);
…
int
n=write(fileDescriptor, buffer, len);
close(fileDescriptor);
More information:
https://en.wikipedia.org/wiki/Serial_communication
http://en.wikipedia.org/wiki/Serial_port
http://www.lvr.com/serport.htm
http://digilander.libero.it/robang/rubrica/serial.htm
http://en.wikibooks.org/wiki/Serial_Programming
Another little pearl from www.dx.com is the Arduino compatible LCD 1602 (16 characters each of the 2 rows) display:
It’s really cheap, 6$/4.5€, works very fine and it’s easy to use! I will use as a handy debug display and little dashboard (it has 6 buttons at the bottom) while on field robots debugging, but this wioll be a near post with the sourcecode (that also will be a order receiver to program a Rapsberry Pi to control a robot using and Arduino Funduino Duemilanove.)
Two easy examples from the Arduino IDE wondeful examples, it’s important that you notice that the initialization sentence should be:
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
The hello world
/* LiquidCrystal Library - Hello World Demonstrates the use a 16x2 LCD display. The LiquidCrystal library works with all LCD displays that are compatible with the Hitachi HD44780 driver. There are many of them out there, and you can usually tell them by the 16-pin interface. This sketch prints "Hello World!" to the LCD and shows the time. The circuit: * LCD RS pin to digital pin 12 * LCD Enable pin to digital pin 11 * LCD D4 pin to digital pin 5 * LCD D5 pin to digital pin 4 * LCD D6 pin to digital pin 3 * LCD D7 pin to digital pin 2 * LCD R/W pin to ground * 10K resistor: * ends to +5V and ground * wiper to LCD VO pin (pin 3) Library originally added 18 Apr 2008 by David A. Mellis library modified 5 Jul 2009 by Limor Fried (http://www.ladyada.net) example added 9 Jul 2009 by Tom Igoe modified 22 Nov 2010 by Tom Igoe This example code is in the public domain. http://www.arduino.cc/en/Tutorial/LiquidCrystal */ // include the library code: #include <LiquidCrystal.h> // initialize the library with the numbers of the interface pins //LiquidCrystal lcd(12, 11, 5, 4, 3, 2); LiquidCrystal lcd(8, 9, 4, 5, 6, 7); void setup() { // set up the LCD's number of columns and rows: lcd.begin(16, 2); // Print a message to the LCD. lcd.print("Te, amo, Nuriitaa!"); } void loop() { // set the cursor to column 0, line 1 // (note: line 1 is the second row, since counting begins with 0): lcd.setCursor(0, 1); // print the number of seconds since reset: lcd.print(millis()/1000); }
Testing buttons
//Sample using LiquidCrystal library #include <LiquidCrystal.h> /******************************************************* This program will test the LCD panel and the buttons Mark Bramwell, July 2010 ********************************************************/ // select the pins used on the LCD panel LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // define some values used by the panel and buttons int lcd_key = 0; int adc_key_in = 0; #define btnRIGHT 0 #define btnUP 1 #define btnDOWN 2 #define btnLEFT 3 #define btnSELECT 4 #define btnNONE 5 // read the buttons int read_LCD_buttons() { adc_key_in = analogRead(0); // read the value from the sensor // my buttons when read are centered at these valies: 0, 144, 329, 504, 741 // we add approx 50 to those values and check to see if we are close if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result if (adc_key_in < 50) return btnRIGHT; if (adc_key_in < 195) return btnUP; if (adc_key_in < 380) return btnDOWN; if (adc_key_in < 555) return btnLEFT; if (adc_key_in < 790) return btnSELECT; return btnNONE; // when all others fail, return this... } void setup() { lcd.begin(16, 2); // start the library lcd.setCursor(0,0); lcd.print("Push the buttons"); // print a simple message } void loop() { lcd.setCursor(9,1); // move cursor to second line "1" and 9 spaces over lcd.print(millis()/1000); // display seconds elapsed since power-up lcd.setCursor(0,1); // move to the begining of the second line lcd_key = read_LCD_buttons(); // read the buttons switch (lcd_key) // depending on which button was pushed, we perform an action { case btnRIGHT: { lcd.print("RIGHT "); break; } case btnLEFT: { lcd.print("LEFT "); break; } case btnUP: { lcd.print("UP "); break; } case btnDOWN: { lcd.print("DOWN "); break; } case btnSELECT: { lcd.print("SELECT"); break; } case btnNONE: { lcd.print("NONE "); break; } } }
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.