Mostrando entradas con la etiqueta C#. Mostrar todas las entradas
Mostrando entradas con la etiqueta C#. Mostrar todas las entradas

miércoles, 20 de noviembre de 2013

(I) Reading sensors connected to Robotis CM-510

cm-510 sensorsUsing 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)


[sourcecode language="cpp"]
enum AX12Address //and functions implemented in the CM-510 program, like
{
ReadCM510SensorRaw = 1,
Beep = 8,
ReadCM510SensorFiltered = 4,
SetSensorValuesToFilter = 5,
...
}
[/sourcecode]

[sourcecode language="cpp"]
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);
}
[/sourcecode]

Querying sensor:

[sourcecode language="cpp"]
void doQuerySensor()
{
int sensorPort=getSensorPort();
int value=mySystem.dynamixelCommunication.readSensorValue (200,ReadCM510SensorRaw, sensorPort);
cout << "the sensor reads: [" << value << "] " << endl << endl << endl;
}
[/sourcecode]

These command and query are processed in the CM-510:

Getting the sensor value:

[sourcecode language="c"]

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;
}

[/sourcecode]

 

[sourcecode language="c"]

void beep()
{
buzzOn(100);
buzzOff();
}
[/sourcecode]

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:

[sourcecode language="c"]
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;
}
...
[/sourcecode]

We also can take values from multiple sensors with one query, but It will be explained in the next post...

domingo, 17 de marzo de 2013

C++, Bioloid and Raspberry Pi (v0.2)

[V.02 updates: AX C++ architecture, core classes diagram and HexaWheels scanning video]

Why C++, Bioloid and Raspberry Pi?


[caption id="attachment_1254" align="alignleft" width="150"]C++ Stroustrup's book C++ Stroustrup's book[/caption]

C++, specially with the great improvements of the last C++11 standard, joins together a great efficiency in performance and a low memory footprint with advanced high level language features, making C++ a great tool for embedding, robotics, programming.

If you want to know how to use C++ very efficiently these two guides will help you:

- The JSF air vehicle C++ coding standards ( F-35 fighter aircraft)

- ISO C++ committee's report on performance

.

.

 

[caption id="attachment_1221" align="alignleft" width="300"]Bioloid Premium Bioloid Premium[/caption]

Bioloid Premium is a wonderful kit for creating legged and wheeled robots, including (here full parts list):

- 18 powerful and versatile AX-12 servos

- an ATMega 2561 (CM-510) or, recently, an ARM STM32F103RE 32bits (CM-530), based controller. Also you can control the AX-12 with the USB2Dynamixel straight from your USB with a FTDI driver.

- And a lot of parts to create the structure of the robot

.

[caption id="attachment_1257" align="alignleft" width="216"]RaspberryPi RaspberryPi[/caption]

Raspberry Pi is the cheaper and more brilliant conceived SBC (more specifications here):

- Broadcom BCM2835 SoC full HD multimedia applications processor

- 700 MHz Low Power ARM1176JZ-F Applications Processor

- Dual Core VideoCore IV® Multimedia Co-Processor

- 256/512 MB SDRAM

One simple example:
[youtube http://www.youtube.com/watch?v=Yhv43H5Omfc&w=480&h=360]

Learning C++



Starting:

C++ is a very powerful but complex programming language, so I think that the better approach is to start step by step, from the most easy features (yes, C++ could be used in an easy way) to the most advanced features it offers. What is C++? I will quote (I try to not explain anything that already is explained), Stroustrup, "his father", from his book The C++ programming language 3th Edition:
"C++ is a general-purpose programming language with a bias towards systems programming that
– is a better C,
– supports data abstraction,
– supports object-oriented programming, and
– supports generic programming."

And wikipedia:
C++ (pronounced "see plus plus") is a statically typed, free-form, multi-paradigm, compiled, general-purpose programming language. It is regarded as an intermediate-level language, as it comprises a combination of both high-level and low-level language features.[3] Developed by Bjarne Stroustrup starting in 1979 at Bell Labs, it adds object oriented features, such as classes, and other enhancements to the C programming language.

Web resources:

If you want more C++ links, these found at JUCE will help you.




[caption id="attachment_1385" align="alignright" width="140"]Programming -- Principles and Practice Using C++Programming -- Principles and Practice Using C++ Programming -- Principles and Practice Using C++[/caption]

Free books and documents:




- Maintain stability and compatibility with C++98 and possibly with C;
- Improve C++ to facilitate systems and library design, rather than to introduce new features useful only to specific applications;
- Increase type safety by providing safer alternatives to earlier unsafe techniques;
- Increase performance and the ability to work directly with hardware


Books:

Advancing:

In robotics, and embedded programming in general, we will need some advanced knowledge and practices to reach our goals.

Free books and documents:


  • Concurrent programming, threading Our robots we will need to do several actions simultaneously, like perceiving the world with several sensors, moving and deciding what to do to reach is objectives.



  • Communications, the serial port communications functions are used for wireless and wired connections, and we will need to communicate between controllers and with sensors and servos.


Books:

C++ robotics programming


Well, this is really the goal, robotics programming.

As this is a workshop it will follow the creation of the the walker and vehicle Hexapod showed above in the video. This is currently the core architecture and the HexaWheels module (namespace classes):

[caption id="attachment_1391" align="aligncenter" width="300"]AX C++ architecture v2 AX C++ architecture v2[/caption]

And these are the core classes:

todo_signatura

The workshop will include:

- Basics

Like communications with serial port and wireless, using Dynamixels, sensors, ... Language features for robotics, like asynchronous communications and threads and... delays!.

- Intermediate

Combination of basics features using sensors (like scanning) and servos (walking motions). For example, scanning with a DMS sensor:

As a simple example:

[youtube http://www.youtube.com/watch?v=UHKaYuaZi4A&w=480&h=360]

- Advanced

Advanced perception and behaviours

I think this could very funny, using an advanced sensor like Asus Xtion, to detect certain objects to interact, and create configurable and amusing behaviours.

CM-510 mirocontroller programming

- Tools:

PC, Raspberry Pi and Pandaboard, installation and configuration, tool and projects

- GNU C++, Boost
- Eclipse
- QT 5

The contents will come soon, very soon...

C. C++, C# robotics programming Workshop/tutorial with the cheapest robotic platform

[caption id="" align="alignleft" width="108"]My Smart Car construction My Smart Car construction[/caption]

[Update March 30, 2013: Added photo gallery]

I will start a C. C++, C# robotics programming Workshop/tutorial with the cheapest robotic platform I have found at dealextreme.com: (Arduino based) plus a Raspberry Pi (like this that use Bioloid as the hardware platform):

Ultrasonic Smart Car Kit

I have not still received the kit, but I will review the kit and start the workshop as soon as I receive it.

viernes, 1 de marzo de 2013

Workshop: USB, serial and remote communications with C#

[Previous post: Workshop: Dynamixel communications with C#]

[caption id="attachment_1117" align="alignright" width="150"]Bioloid SerialPort2Dynamixel C# Bioloid SerialPort2Dynamixel C#[/caption]

SerialPort2Dynamixel


Encapsulating  the SerialPort .Net class offers an easy way to use the serial port and receive Dynamixel Zig messages with the Dynamixel protocol.

.

.

Collaborator classes:


[caption id="attachment_1134" align="alignright" width="150"]Bioloid SerialPort2Dynamixel C# Collaborators Bioloid SerialPort2Dynamixel C# Collaborators[/caption]

- The SerialPort .Net class.

- RCDataReader class, which unpack the Dynamixel Zigbee sequence offering the clean data received.

Operations:


The public interface that others classes will use offers principally these operations:

public void setRemoteControlMode(bool on), which sets on or off the reception of data

[sourcecode language="csharp"]

public void setRemoteControlMode(bool on)
{
if (on)
setReceiveDataMethod(rdDataReader.rawRemoteDataReceived);
else
setReceiveDataMethod(null);
}

[/sourcecode]

public void setReceiveDataMethod(remoteControlDataReceived rcDataReceived), that sets the method that will be called when serial port data is received.

And some basics serial port data operations:

[caption id="attachment_1112" align="alignright" width="131"]Raspberry Pi - USB2Dynamixel - CM510 Raspberry Pi - USB2Dynamixel - CM510[/caption]

public bool open(String com, int speed), to open the serial port which name is in the com parameter. Wireless communications and USB ports, as used by Zig or USB2Dynaniel, are also serial ports  (COM1, COM2, ... or /ttyUSB0, ttyUSB1).

public void close(), it will do nothing if the port is already closed.

public byte[] query(byte[] buffer, int pos, int wait), send (write) a query and gets (read) the result.

public void rawWrite(byte[] buffer, int pos), well... it will write whatever contains the buffer in the first pos positions

public byte[] rawRead() , read and returns the data received.

Notes:


To avoid concurrency problems all the operations that use the Dynamixel bus are protected with a Mutex object that avoids that two or more concurrent objects use SerialPort2Dynamixel simultaneously entering the same operation or using the same resources, like variables, objects or the Dynamixel bus.

[caption id="attachment_1620" align="aligncenter" width="300"]Xevel USB2AX Xevel USB2AX[/caption]

[caption id="attachment_1622" align="aligncenter" width="300"]USB2AX over USB2DYNAMIXEL USB2AX over USB2DYNAMIXEL[/caption]


RCDataReader


[caption id="attachment_1155" align="alignright" width="150"]Bioloid RCDataReader C# Bioloid RCDataReader C#[/caption]

[caption id="attachment_1128" align="alignleft" width="112"]Remote communications RemoteCommunications[/caption]

Its responsability is to receive the Dynamixel Zig packets and extract the data.

Collaborator class:


- The ZigSequence enum, with the Dynamixels protocols data sections

[caption id="attachment_1171" align="aligncenter" width="300"]RC-100 packet RC-100 packet[/caption]

Operations:


[caption id="attachment_1146" align="alignleft" width="105"]Robotis RC-100 remote controller values Robotis RC-100 remote controller values[/caption]

public void rawRemoteDataReceived(byte[] rcData), receives the Zigbee data.

public int getValue(), returns the last value received

miércoles, 27 de febrero de 2013

Workshop: Dynamixel communications with C#

[Next post: Workshop: USB, serial and remote communications with C#]

As I wrote in the previous post, I am not using Robotis Dynamixel SDK USB2Dynamixelbecause it only works with the  USB2Dynamixel, and I need that it also should work with the serial port and with zigbee or bluetooth (really all 4 use the serial connection). Also I want to query sensors connected to the CM-510.

[caption id="attachment_1053" align="alignright" width="150"]Zigbee device Zigbee[/caption]

Using the CM-510 and computer serial port (or USB to serial) connection you are free to use any wired or wireless device. Really there are a lot of possibilities.

We will start connecting to the Dynamixel bus and sending commands and queries. These classes do the work:

- DynamixelCommunication

- SerialPort2Dynamixel

- RCDataReader

But there are other classes that offer to them some additional services, like Configuration, Utils, Hex and several enumeration types.

I will use the Class-Responsability-Collaboration template to present the classes.

DynamixelCommunicationBioloid DynamixelCommunication class C#


The main responsibility of this class is sending commands and queries to any Dynamixel device, including the sensors, sound and other capabilities of the CM-510 controller.

Collaborator classes:


SerialPort2Dynamixel,  that offers operations to use the serial port encapsulating .Net SerialPort class

- Three enums for easy use and avoid errors, using an specific type is safer that using simple integers.

    public enum AXS1_IRSensor { Left, Center, Right, None };
    public enum AXS1_SoundNote { LA, LA_, SI, DO, DO_, RE }; //Only the first six 
    public enum DynamixelFunction, with all the Dynamixel protocols codes and some that I added for the CM-510.

- Configuration class, that reads a file where are stored basic configuration parameters. like:

        private static string ParameterSerialPortName
        private static string ParameterSerialPortBaudRate
        private static string ParameterWaitTime_ms
        private static string ParameterWaitTimeForSensors_ms

Bioloid communications C#



Operations:


The public operations are the interface that other classes will use, like:

- short readValue(int id, DynamixelFunction address), reads the value of any AX-12 parameter (or other Dynamixels)


- bool sendOrder(int id, DynamixelFunction address, int value), send commands, like position, speed or torque.


And the private that do internal work supporting the public interface, like:

static int getReadWordCommand(byte[] buffer, byte id, DynamixelFunction address), create the Dynamixel hexadecimal sequence (FF FF 0F 05 03 1E CB 01 FE)


- static short getQueryResult(byte[] res), once the query or command is sent it gets the result.


Let's see readValue and two other called functions:

[sourcecode language="csharp"]

public short readValue(int id, DynamixelFunction address)
{
mutex.WaitOne();
short position = -1;

try
{
int size = getReadWordCommand(buffer, (byte)id, address);
byte[] res = serialPort.query(buffer, size, WaitTimeReadSensor);

position = getQueryResult(res);
if (position < 0)
Debug.show("DynamixelCommunication.readValue", position);

}
catch (Exception e)
{
Debug.show("DynamixelCommunication.readValue", e.Message);
}

mutex.ReleaseMutex();

return position;
}

private static int getReadWordCommand(byte[] buffer, byte id, DynamixelFunction address)
{
//OXFF 0XFF ID LENGTH INSTRUCTION PARAMETER1 …PARAMETER N CHECK SUM
int pos = 0;

buffer[pos++] = 0xff;
buffer[pos++] = 0xff;
buffer[pos++] = id;

// bodyLength = 4
buffer[pos++] = 4;

//the instruction, read => 2
buffer[pos++] = 2;

// AX12 register
buffer[pos++] = (byte)address;

//bytes to read
buffer[pos++] = 2;

byte checksum = Utils.checkSumatory(buffer, pos);
buffer[pos++] = checksum;

return pos;
}

private static short getQueryResult(byte[] res)
{
short value = -1;

if (res != null)
{
int length = res.Length;
if (res != null && length > 5 && res[4] == 0)
{
byte l = 0;
byte h = res[5];
if (length > 6)
{
l = res[6];
}

value = Hex.fromHexHLConversionToShort(h, l);
}
}
return value;
}

[/sourcecode]

Notes:


To avoid concurrency problems all the operations that use the Dynamixel bus are protected with a Mutex object that avoids that two or more concurrent objects use DynamixelCommunication simultaneously entering the same operation or using the same resources, like variables, objects or the Dynamixel bus.

All the operations use the same buffer, but being protected with the Mutex object I think that is the better option, although in a previous version I used a very different approach where there were AX12 objects with their own buffer.

[Next post: Workshop: USB, serial and remote communications with C#]

domingo, 24 de febrero de 2013

Workshop: Programming a Bioloid robot workbench using C# and C++

[Next post: Dynamixel communications with C#]

It would be a workshop using C# .Net and C++ with Qt 5. The code presented here is used in this two different robots and boards, a HP 214 Ipaq with Windows Mobile and a Raspberry Pi, using the Robotis CM-510 as the servo and sensors controller:

[youtube http://www.youtube.com/watch?v=mvaMTdlb48E&w=281&h=210][youtube http://www.youtube.com/watch?v=Yhv43H5Omfc&w=281&h=210]

These will be the first steps, using C# and .Net , here the code and the exe for the Workbench UI:

Bioloid Workbench


Using this enhaced Toss Mode that adds some new functions.  Some of them:


martes, 7 de febrero de 2012

Choose hardware, firmware and language

There are a lot of possible combinations of hardware, firmware and languages for programming Bioloid. I think that the table below show the the main combinations.

You can choose from the easy but limited Robotis own tool (Roboplus Task) and only your CM-5 or CM-510 to a SBC or "embedded" PC like Roboard and any language which can manage a serial port connection, like C, C++, Java, Python,...

Linux C++ Dynamixel reading and writing example

C# Dynamixel reading and writing example

Practical C++ programming tutorial for Bioloid

[caption id="attachment_256" align="aligncenter" width="617"]Programming Bioloid: choose hardware, firmware and languages Programming Bioloid: choose hardware, firmware and languages[/caption]

Robotis officially supports the programming solutions with the blue background:

  • The dark blue, RoboPlus Tasks, is the only one in which you can create the motions with RoboPlus Motion and execute it in CM-5/CM-510 with the program create with RoboPlus Tasks, after downloading the generated executable into the CM-5/CM-510, of course.



With these programming solutions you can use the Zigbee SDK to send and receive data between the CM5-/CM-510 and any computer, using Zig-110A or the new bluetooth BT-110, the Zig2Serial and the USB2Dynamixel. You can download example in Visual Basic .Net, C# and Visual C++

But there are more options!

Using a PC, SBC (Single Board Computer), PDA, Mobile or other light and battery powered computer

Using a serial port connection with your more beloved programming language:

  • USB2Dynamixel


If you have any device with a USB host and a FTDI driver you can use USB2Dynamixel to command programatically your Dynamixel servos using the Dynamixel protocol. You only will need your CM-5 or CM-510 to connect your Dynamixel to the battery.

  • Serial port cable


Same as the previous option but instead of using the USB2Dynamixel you only will need the serial cable and the "Toss Mode" launched with the 't' command from the "Manage Mode"


  • Wireless control


Instead of only sending and receiving data, with the previous wireless connections you can command remotely your robot using the standard firmware and the "Toss Mode" launched with the 't' command from the "Manage Mode". You will need to command it using the Dynamixel protocol.

With these options and the CM-510 you will find a little problem... there is no way to read your sensor values! Well, you can use this firmware that offers a "Toss Mode" that supports reading CM-510 ports.

Start learning!

If you want to start learning how to program your CM-5 or CM-510 controller you will find interesting this post "Start programming  CM-5/CM-510 in C". But may be you prefer to control your robot from a PC, SBC or other computer in C++ or C#

martes, 20 de diciembre de 2011

C# Dynamixel reading and writing example

Three C# classes for a simple example for reading and writing the position of a AX-12 Dynamixel servo. You can use it with the USB2Dynamixel or using only the serial port: in "Manage mode" send "T" to put firmware in "Toss Mode".

Here you can find several combinations of hardware, firmware and programming tools.

The main


.

[sourcecode language="csharp"]
class Program
{
static void Main(string[] args)
{
int error=0;
int idAX12=15;

SerialPort2Dynamixel serialPort = new SerialPort2Dynamixel();
Dynamixel dynamixel = new Dynamixel();

if (serialPort.open("COM1")==false) {
dynamixel.sendTossModeCommand(serialPort);

int pos=dynamixel.getPosition(serialPort, idAX12);

if (pos>250 && pos dynamixel.setPosition(serialPort, idAX12, pos-100);
else
Console.Out.WriteLine("nPosition under 250 or over 1023", pos);

serialPort.close();
}
else {
Console.Out.WriteLine("nCan't open serial port");
error=-1;
}
}
}
[/sourcecode]

Serial Port


[sourcecode language="csharp"]
public class SerialPort2Dynamixel
{
const int HeaderSize = 4;
const int PacketLengthByteInx = 3;
const int BufferSize = 1024;
const int MaximuTimesTrying = 250;
const int waitTime = 5;

private string portName = "COM3";

private byte[] buffer = new byte[BufferSize];

private SerialPort serialPort = new SerialPort();

public bool open(String com)
{
bool error = false;
portName = com;

serialPort.PortName = com;
serialPort.BaudRate = 57600;
serialPort.DataBits = 8;
serialPort.Parity = Parity.None;
serialPort.StopBits = StopBits.One;

try
{
if (serialPort.IsOpen)
Console.WriteLine("Serial port is already open");
else
serialPort.Open();
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
error = true;
}

return error;
}

public void close()
{
if (serialPort.IsOpen)
serialPort.Close();
Console.WriteLine("Conecction closed!");
}

private void cleanConnection()
{
serialPort.DiscardInBuffer();
}

public byte[] query(byte[] buffer, int pos)
{
byte[] outBuffer = null;
try
{
serialPort.Write(buffer, 0, pos);
System.Threading.Thread.Sleep(waitTime);
outBuffer = rawRead();
}
catch (Exception exc)
{
Console.Out.WriteLine(exc.Message);
}

return outBuffer;
}

public void rawWrite(byte[] buffer, int pos)
{
try
{
serialPort.Write(buffer, 0, pos);
}
catch (Exception exc)
{
Console.Out.WriteLine(exc.Message);
}
}

public byte[] rawRead()
{
byte[] localbuffer = null;
int n = serialPort.BytesToRead;
if (n != 0)
{
localbuffer = new byte[n];
try
{
serialPort.Read(localbuffer, 0, n);
}
catch (Exception exc)
{
Console.Out.WriteLine(exc.Message);
}
}
return localbuffer;
}
}<code>
[/sourcecode]

Dynamixel


[sourcecode language="csharp"]
class Dynamixel
{
protected static int MaxBufferSize = 1024;
protected byte[] buffer = new byte[MaxBufferSize];

private static byte checkSumatory(byte[] data, int length)
{
uint cs = 0;
for (int i = 2; i < length; i++)
{
cs += data[i];
}
cs = ~cs;
return (byte)(cs & 0x0FF);
}

public static void toHexHLConversion(int pos, out byte hexH, out byte hexL)
{
ushort uPos = (ushort)pos;
hexH = (byte)(uPos >> 8);
hexL = (byte)uPos;
}

public static ushort fromHexHLConversion(byte hexH, byte hexL)
{
return (ushort)((hexL << 8 ) + hexH);
}

public static short fromHexHLConversionToShort(byte hexH, byte hexL)
{
return (short)((hexL << 8 ) + hexH);
}

public static void toHexHLConversion(int pos, out string hexH, out string hexL)
{
string hex;
int lon, start;
hex = String.Format("{0:X4}", pos);
lon = hex.Length;
if (lon < 2)
{
hexL = hex;
hexH = "0";
}
else
{
start = lon - 2;// lon = 4, start = 2; lon=3, start=1
hexL = hex.Substring(start);
hexH = hex.Substring(0, start);
}
}

public void sendTossModeCommand(SerialPort2Dynamixel sp2d)
{
byte[] buffer = { (byte)'t', (byte)'r' };
sp2d.rawWrite(buffer, 2);
System.Threading.Thread.Sleep(100);
sp2d.rawRead();
}

private static int getReadPositionCommand(byte[] buffer, byte id)
{
//OXFF 0XFF ID LENGTH INSTRUCTION PARAMETER1 …PARAMETER N CHECK SUM
int pos = 0;
buffer[pos++] = 0xff;
buffer[pos++] = 0xff;
buffer[pos++] = id;
buffer[pos++] = 4; // bodyLength = 4
buffer[pos++] = 2; //the instruction, rawRead => 2

// pos registers 36 and 37
buffer[pos++] = 0x24;

//bytes to rawRead
buffer[pos++] = 2;

byte checksum = checkSumatory(buffer, pos);
buffer[pos++] = checksum;

return pos;
}

private static int getSetPositionCommand(byte[] buffer, byte id, short goal)
{
int pos = 0;
byte numberOfParameters = 0;
//OXFF 0XFF ID LENGTH INSTRUCTION PARAMETER1 …PARAMETER N CHECK SUM

buffer[pos++] = 0xff;
buffer[pos++] = 0xff;
buffer[pos++] = id;

// bodyLength
buffer[pos++] = 0; //place holder

//the instruction, query => 3
buffer[pos++] = 3;

// goal registers 30 and 31
buffer[pos++] = 0x1E;// 30;

//bytes to write
byte hexH = 0;
byte hexL = 0;
toHexHLConversion(goal, out hexH, out hexL);
buffer[pos++] = hexL;
numberOfParameters++;
buffer[pos++] = hexH;
numberOfParameters++;

// bodyLength
buffer[3] = (byte)(numberOfParameters + 3);

byte checksum = checkSumatory(buffer, pos);
buffer[pos++] = checksum;

return pos;
}

public short getPosition(SerialPort2Dynamixel sp2d, int id)
{
//byte[] localbuffer = new byte[MaxBufferSize];
int size = getReadPositionCommand(buffer, (byte)id);
byte[] res = sp2d.query(buffer, size);

short position = -1;

if (res != null)
{
int length = res.Length;
if (res != null && length > 4 && res[4] == 0)
{
byte l = 0;
byte h = res[5];
if (length > 6)
{
l = res[6];
}

position = fromHexHLConversionToShort(h, l);
}
}

return position;
}

public bool setPosition(SerialPort2Dynamixel sp2d, int id, int goal)
{
bool couldSet = false;

short position = (short)goal;
int size = getSetPositionCommand(buffer, (byte)id, (short)goal);
byte[] res = sp2d.query(buffer, size);

//ushort value = 1;
if (res != null && res.Length > 4 && res[4] == 0)
couldSet = true;

return couldSet;
}
}
[/sourcecode]

The zip with the full example for Visual Studio 2008