In my ongoing quest to make CmdMessenger the best PC to Arduino communication library, I have released a new version with some significant updates. This article will give an overview of these changes: the speed optimization for the fast ARM based boards, better compatibility in data formats between the PC and Arduino, and a unit testing framework.
The library, along with many examples and documentation can be downloaded here:
Or at the Github repository:
An overview of all articles on CmdMessenger can be found here:
Paul Stoffregen graciously sent me two of his Teensy 3.0 boards, and I bought a Teensy 3.1 as soon as it came out, which has rapidly become my favorite prototyping board.
One of the main reasons for this is the niceUSB performance. I started with seeing if I could reproduce Paul’s benchmarks. Indeed, my Teensy 3 (the starred entry) performed similarly:
Paul did not benchmark the Arduino Uno or Arduino Nano. On Both boards the serial baudrate is limited to 115200 baud, but I was still curious to see how that translates out in the benchmarks. I added them because, while they are architecturally very different to the Teensy, they are similar in price. As expected, the transfer speeds are quite different: both 16 bit boards are capable of receiving +/-11500 bytes/sec, which is reasonably close to 115200 bits/sec / (8 bits + 1 stopbit), whereas the Teensy comes close to 1 MB/sec.
An interesting find by Paul was that Windows 7 performs much worse than Linux and OS-X for small data-blocks. I was curious to see if that had changed with Windows 7.1 and Windows 8. As you can see in the chart below, indeed it has, but not all for the better:
The Windows 7.1 results are slightly different, but that can be due to the hardware (HP 8560w: i7 2820QM, 2.3 GHz ) or compilers (PC: gcc-4.7.1, Arduino: Arduino 1.0.5) used. Under the same conditions Windows 8 performs better than Windows 7 for smaller data-blocks, although still not nearly as good as Linux and Mac. Strangely enough, though, for larger data-chunks, Windows 8 performs significantly worse than Windows 7.1!
Speed improvements and benchmarks
This served as a prelude to measuring the speed of CmdMessenger. Per Paul’s suggestion I had already implemented a serial read buffer using readBytes() on the Arduino using to improve performance. Looking at the raw receive benchmarks, I was expecting pretty solid results. This made my first measurements a bit disappointing: the receiving speed was only twice that of an the Arduino Uno.
It occurred to me that while the Teensy was now able to receive and process many bytes at once, the PC side of CmdMessenger still send only very small messages, typically around 10 bytes. which
This means that we might improve performance by sending longer messages. Because the PC CmdMessenger has a nice send queue for asynchronously sending commands, I could combine multiple messages on the queue. This results in a single long string of multiple commands, which makes sending it much more efficient. As it turns out, this makes a world of difference, as you can see in the chart below. Compare the buffered and unbuffered send benchmark:
Obviously, it is still not as fast as sending raw data, but it is now 30 times faster than the Arduino Uno / Nano. I think that is a pretty excellent result! Note that sending & receiving commands is more that just sending out bytes: it is packing commands, queuing and combining them, sending them, and unpacking and parsing them.
If you want to reproduce these, go right ahead. They are part of the unit testing project. You can play adjust the buffer size, by adding it to the PC based CmdMessenger constructor.
CmdMessenger(ITransport transport, int sendBufferMaxLength)
Be careful, though: the Arduino serial buffer size is 64 bytes. For my 16 bit boards I’ve seen that sending command strings larger than 64 bytes will sometimes choke the board.
An issue I had with sending commands from .Net to my Arduino is that the definitions of some basic variables differ. This grew further with the advent of the ARM based Arduino boards. Consider the following variables and their size on the different platforms.
It is very important to use the same data formats on both side of the line. If we use the different formats , this can result in different problems: for text based variables truncation and loss of accuracy may occur, and for binary variables a mismatch will almost certainly result in completely wrong values.
This has been solved by doing two things:
1.In the library as well as the examples I switched from Int, Long & Short to the unambiguous Int16 and Int32 descriptions.
2. For floating point values the library uses the Float and Double description, but I removed all Single references. Float was already unambiguously 32 bits on all platforms, but in order to process Doubles correctly on all platforms, I modified the library: you can now set on the PC side whether the receiving embedded board is 32 bits or 16 bits:
_cmdMessenger.BoardType = BoardType.Bit32
In the former case, unaltered 64 bit values are sent and received, but In the latter case the CmdMessenger library will automatically convert values. That is, double variables are automatically converted from 32 bit to 16 when being sent to the board, and converted from 16 bit to 32 bit when being send to the PC. While this may result in reduced accuracy and truncation of values, both text based and binary transfers will be compatible and sending variables will be straight forward :
Please look at the examples to see how these data formats are being used
When I started with CmdMessenger 3.0 It was still a quite straight-forward library, but it has grown, both on Arduino and PC side to include new features such as: binary sending of data, asynchronous send and receive queues, adaptive throttling, Queue strategies and much more. As a result, the code has become more complex, and requires a lot of testing with every release. Because of this I started to add unit tests, to perform the test I would normally do by hand. At this point they do not test every scenario I can think of, but they cover all mayor functionality. It runs the following sets of tests:
- Test all plain text formats
- Test all binary formats
- Test sending multiple arguments
- Test send and receive speed
As a matter of fact, making and running these tests revealed multiple bugs and shortcomings, ranging from small inconveniences to hidden and serious bugs. These have been fixed in this release, and I believe this CmdMessenger 3.6 is the most robust release so far!
The tests have not been written to function as examples, so they are not as extensively documented, but they may be interesting nonetheless. For example, it performs send- and receive benchmarks that you might like to run. Please try it for your own boards, but be sure to create your own board description. I supplied one for my Arduino Mini and Teens 3.1. Below is the definition of the latter
var teensy31 = new systemSettings()
Description = @"Teensy 3.1",
MinReceiveSpeed = 2000000, // Bits per second
MinSendSpeed = 1250000, // Bits per second
MinDirectSendSpeed = 47500, // Bits per second
BoardType = BoardType.Bit32, // 32 architecture, needed from binary value conversion
sendBufferMaxLength = 512, // Maximum send buffer size
Transport = new SerialTransport
CurrentSerialSettings = new SerialSettings()
PortName = "COM15", // Can be different!
BaudRate = 115200, // Bits per second
DataBits = 8, // Data bits
Parity = Parity.None, // Bit parity
DtrEnable = false, // Some boards need to send this to enabled
Using the testing framework, I learned that the Arduino is only capable of printing clear-text floats(and doubles) in the range of from -2,147,483,648 to 2,147,483,647. This is strange because the internal representation of a float value has a range of -3.4028235E+38 to -3.4028235E+38. We can solve this by sending floats and doubles in binary format. However, some users prefer clear-text values, so I’ve added a print function that will send clear-text values in scientific format that span the full range:
cmdMessenger.printSci (arg, n)
This release features many fixes, some came from the unit tests, but also came from users of the library. Thanks for that!
- [Arduino] Bugfix: approx 1 in 1000 commands failed, when multiple binary parameters are sent over
- [Arduino] Bugfix: Binary sending of non-number would give compile time error
- [Arduino] feature: Posibility to send command without argument
- [Arduino] feature: Posibility to send floats with scientific notation, to get full float range
- [.Net/.Mono] Added more multi-threading robustness, this fixes, amongst others, that the receive thread is starved while the main program is waiting for a receive acknowledgement