Using LSS adaptor board with serial communication software

Sounds good! I quite like screen if I need to do some quick serial port stuff. It has many interesting features, too! Go check the man page! :smiley:

Even if it is not really your last one, I don’t mind either way… :wink:
Lets break this down:

Well, it is 115200 bits per second, or 11520 characters per second when using 8n1 settings (1 start bit, 8 data bits, no parity, 1 stop bits = 10 bits per character). Therefore, one character takes 1/11520 seconds to transfer, or ~86.81 µs. In my case it was ~84 µs:

Therefore, sending something like #3QD\r would take 5 x 86.81 µs or roughly 434 µs. Of course, raw transfer speeds are not enough. You need to also include processing time (on the LSS) before a reply is sent back. Here’s a screenshot from my logic analyzer software of a LSS @ 115.2 kbaud responding to a QD command:


As you can see in the image, the total duration of the transaction from when the last character ( \r ) is received in the LSS (from the PC) to when the response is fully transferred for the QD command was about 827 µs (purple). The processing time for the QD command inside the LSS was roughly 316 µs (green) and the response time (i.e.: outputting the values physically on the bus) took just a bit over half a ms, or ~510 µs (orange), yielding us a length per character of ~86 µs which falls right in the expected range for communications at 115.2 kbaud rate.

Now, if this is your bottleneck, the best you can do is increase the baud rate. The LSS and LSS Adapter Board were tested at up to 500 kbps with 36 servos on the bus at the same time and the bus completely saturated at 100% with 0 packet loss. Of course, running at 500 kbps will reduce transmission time of one character by a factor of about 4.34, which is quite significant. In the example above, the orange section would take about 118 µs instead of 510 µs. As you probably figured out, though, there’s no real way to reduce the processing time on the LSS, so that ~316 µs will stick around no matter the baud rate.

Now that we’ve established the communication speeds, lets dive into the real reason(s) why the transfers with the LSS might actually much slower (or seem to be):

Your main issue here will be FIFO buffer & driver latency. Most USB interfaces will use a FIFO to allow for efficient communication at high speed. The alternative would be to generate an interrupt for every single character; doing so would lead to your CPU doing effectively nothing at all due to interrupt processing overhead while communications are on going. This gets worse the faster the communication speed (in the case of UART, the baud rate).

To get around this issue, a FIFO buffer is used. The characters are received in the interface and sent to a FIFO buffer, typically a simple circular buffer. Once that buffer reaches a certain per-determined limit (often half full, or 3/4 or full; where full is a number of bytes. That size depends on hardware and drivers) an interrupt is triggered for the drivers. These will read the buffer’s content and pass them along to the OS’ handling routine which will eventually trigger an event in the library/code/etc. that you are using to handle serial port communications.

As you can imagine, all of this is quite a lot of abstractions and not exactly lighting fast. Also, since this is a regular OS and not a real-time one, there are no hard limits on total duration of those operations and how much jitter (variable delays) can happen from other processes (a modern OS runs dozens if not hundreds/thousands of threads at once).

For example, when I run my code in a profiler against a LSS bus running at 115200 baud rate, I often get delays of ~15-20 ms before replies are sent back to my code running in a .NET app. But, sometimes, other stuff is schedule at the same time and the response is receive nearly a full 100 ms later. And, unfortunately, there is nothing I can do about it! Neither plan for it or prevent it…

The point that is important to mention is that a FIFO buffer in UART hardware will have a size and a certain trigger limit (half, full, etc.) for when to generate an interrupt. If your communication doesn’t fill that buffer, such as my reply above of *3QD2\r which is only 6 characters, the hardware will not trigger an interrupt until the trigger size is met or the bus is idle long enough to timeout. Depending on the hardware interface (ex: FT232R) and the drivers installed and other configurations, this can be quite long if it is optimized for large transfers (very typical, default use case) instead of small, numerous quick packets (what we do with the LSS).

Also, whatever library you use (and programming framework, compiler/interpreter and its optimizations) will affect how fast you can process those messages coming from the OS telling you that data is available.

Hopefully this gives you a better idea of why communications could be much slower than expected, such as 60 ms to read one value vs 827 µs for the actually processing and transmission of the response.

A good option to alleviate these issues if you are stuck on a non-real-time OS (such as Windows) is to package your queries to one(1) servo together so the servo can respond to them all at once. This has two benefits:

  1. The servo will process all the queries quickly and you won’t have to wait for each response to send the next query, therefore limiting how much you wait for each data piece.
  2. This will most likely cause the FIFO buffer to be full quickly, giving you access to the data sooner instead of waiting for an internal timeout on the UART hardware.

Here’s a quick example of gathering some telemetry from a servo:
Instead of sending #3QD\r, wait for a reply then send #3QV\r, etc. you could construct a string and send it all at once, such as:
#3QD\r#3QV\r#3QT\r#3QC\r#3Q\r.
By doing this your servo will receive all of this in one large burst and respond as fast as possible. This will allow you to dodge all those delays due to hardware timeouts and interrupt delays down to just one for each! Much improved, right? :slight_smile:

Try this out and let me know if you see any improvements!

Sincerely,

(1): If you need to query two or more servos, you’ll still need to wait for each one to respond before querying the next one. If you don’t you will most likely get bus contention, where two servos try to use the bus at the same time. That’s A Bad Idea ™! :smiley: