вторник, 19 декабря 2017 г.

I think there’s a bug in HerkuleX DRS-0101 smart servo’s firmware. Workaround

First of all – I love these servos. There’s a great user manual, small sized, an enormous number of features, etc. etc.
But there was one issue I’ve finally solved! Maybe two. The second one is that there is not much information about this servo in the Net. Some people have been faced the same problem. So I decided to share my humble experience.

But - in order… The first quest was to find a library to use it in my code. I found one dead Python project, some more or less alive Arduino libraries and one C++ Windows library (with dll and with no source code). My plan was to control HerkuleX servos in C++ ROS node, so Python was my plan “B”, Windows library was useless in Ubuntu, and the only suitable way was to edit the open source Arduino library. Of course, I could write my own library from scratch, but that’s the warrior’s way.
I think I had chosen one of the most popular Arduino library and the only thing I changed in it was the implementation of serial port communication since it is very “Arduino oriented”. When the library was completed, I included it in my code and all functions I’d used were working fine except one… GetAngle() was unstable. Sometimes it worked correctly, sometimes it returned -166.72°.
I tried three servos HerkuleX DRS-0101 with the same result. It was definitely a software issue.
I dived deep into the servo’s manual and attentively checked the code of GetAngle() function. It seemed everything is fine with the function. So I wrote a Python script to send data packets to the servo and receive response packets from it. Finally, I noticed that sometimes packets with the servo’s position have wrong size byte.
Here is a “good” packet:
FF FF 0D FD 44 B2 4C 3A 02 1F 62 00 42
And this is a broken one:
FF FF 0A FD 44 B2 4C 3A 02 1F 62 00 42
  • First two bytes are the header.
  • 0D(13) is the length of the packet. 13 – exactly the size of this sequence of bytes. And 0A(10) – is the core of my problem.
  • FD is the servo’s unique identifier.
  • 44 identifies the response on Read RAM command.
  • B2 – checksum1 (bold text).
  • 4C – checksum2 (bold text).
  • 3A – address in the RAM Registry we are reading (address of the current calibrated position).
  • 02 – number of bytes we are reading.
  • 1F 62 – the servo’s angle (I’ll explain these numbers in a moment).
  • 00 – status error (0 is a good sign).
  • 42 – status detail.
Just a short off-topic about 1F 62 value. These two bytes are a flipped 2-byte integer 0x621F. Significant bits for calibrated servo position is 10 lowest bits, so the final value is 0x021F. To get the angle in degrees we can use the table in the manual or just calculate this expression:
(0x021F - 512) * 0.325 = (543 - 512) * 0.325 = 10.075°.
So when the servo sends a packet with the wrong length byte, the GetAndle() function fails while testing the packet’s checksums. The returned value -166.72 is a consequence of this fail.
The thing is that if we change the length byte from 0A to 0D, the checksums will be correct! Note that checksums in both correct and broken packets are the same. I believe that there is a bug in servo’s firmware that this byte with the packet’s length value sometimes is not set and it is filled with garbage. That’s why sometimes GetAngle() works, sometimes doesn’t.
I checked the firmware version in my servos: 090. Unfortunately, it’s the latest available firmware. It is dated May 10, 2012. What is the manufacturer waiting for? Today it is December 2017.
And now the worst thing. GetAngle() is a high-level function from Arduino library that reads two bytes from RAM Register starting with address 0x3A. I mean that GetAngle() uses a low-level firmware function that reads data from RAM Register. So I assume that reads of any other RAM Register addresses don’t work properly either.
That’s the time for good news. Here is the workaround I promised in the title. We can change the byte with the packet length in the received data packet. Only one single byte. After that, both checksums will become valid.
Here is the original Arduino library I’ve used. User Rambo made a GitHub copy and some improvements in it and I made a fork just to fix the firmware bug. See the changes in my GitHub commit.
By the way. There’s another issue with this library while running on Arduino Uno. It uses SoftwareSerial, thereby I noticed the communication is unstable on 115200 baud rate and should be reduced to 57600. But that’s another story.