Spread spectrum integration

I’ve been developing a new bi-directional spread spectrum radio to command and control the mjbots quad robot.  Here I’ll describe my first integration of the protocol into the robot.

To complete that integration, I took the library I had designed for the nrfusb, and ported it to run on the auxiliary controller of the pi3 hat.  This controller also controls the IMU and an auxiliary CAN-FD bus.  It is connected to one of the SPI buses on the raspberry pi.  Here, it was just a matter of exposing an appropriate SPI protocol that would allow the raspberry pi to receive and transmit packets.

Slightly unfortunately, this version of the pi3hat does not have interrupt lines for any of the stm32s.  Thus, I created a multiplexed status register that the rpi can use to check which of the CAN, IMU, or RF has data pending.  Then I slapped together a few registers which allowed configuring the ID and reading and writing slots and their priorities.

Then I refactored things around on the raspberry pi side so that one core would keep busy polling for one of those things to become available.  So far, for the things which access SPI, I’ve been putting them locked to an isolcpu cpu to get improved SPI timing.  Eventually, once I have interrupt lines, I might consolidate all of these down to a single core.  That, plus defining an initial mapping between the controls and slots resulted in:

Finally, I created a very simple GL gui application which connects to an nrfusb and a joystick.  It uses Dear ImGui to render a few widgets and glfw to window and read the joystick.


While I was at it, I finally updated my joystick UI to make gait selection a bit faster, and got the robot to do a better job of switching out of the walk gait.  Thus the following video showing all of that hooked together.

Spread spectrum implementation

With a protocol design in hand, the next step was to go and implement it.  My goal was to produce a library which would work on the nrfusb, and also on the auxiliary stm32g4 on the mjbots pi3 hat.  In this first implementation pass however, I only worked with the nrfusb as both transmitter and receiver.

While developing this, I had more than my share of “huh” moments working from the datasheet and with the components.  To begin with, the initial nrf24l01+ modules I got were all Chinese clone ones.  While I was having problems getting auto acknowledgement to work, I discovered that the clones at a minimum were not compatible with genuine Nordic devices.  Thus I reworked genuine parts into the modules I had:


A Nordic clone about to be removed

That didn’t solve any of my immediate problems, but the subsequent modules I got all had genuine chips so it was useful that they all were compatible.

The other more annoying problems are somewhat obvious in hindsight.  For a transmitter to be able to successfully receive an automatic acknowledgment from a receiver, not only does the ID need to be configured in the appropriate RX_ADDR register, but EN_RXADDR also needs to have the correct bit set.  I had assumed that was only required for slave devices as there was no mention of it in any of the Enhanced Shockburst flow charts or setup procedures for transmitters or auto acknowledgment.

The second annoyance, was that when in receiver mode, switching channels seems to kinda work a little bit for some channels even with CE held high, but to be reliable you have to pull CE low and put the unit in standby mode while changing channels.

With those problems (and some others) resolved, I have a reliable bidirectional link that is ultimately tweakable.  Next I’ll integrate this into the quad A1 to actually control the robot and monitor its telemetry.


Spread spectrum protocol design

Last time I discussed the rationale for building a custom control and telemetry solution.  Here I’ll describe the protocol design a little bit, before discussing the implementation in a future post.

Frame design and frequency hopping

The basic idea is that the transmitter sends a frame to the receiver every 20ms, and each frame is sent on a different radio frequency.  A set of frequencies and their order is generated pseudo-randomly based on a “key” that the transmitter and receiver each share ahead of time.  The receiver replies on the same frequency with its telemetry.  Then the transmitter and receiver each switch to the next frequency in the list to get ready for the next frame.

Frequency selection uses a similar scheme as to DSMX.  The “key” is a 32 bit transmitter ID.  From it a simple pseudo-random number generator is used to generate candidate channels from 125 of the nrf24l01+’s available frequencies.  Channels are discarded if they repeat and they are also discarded to maintain roughly equal distribution across the available frequencies of the radio.  That is accomplished by grouping the frequencies into 4 separate “bands”, and enforcing a limit of how many frequencies can be chosen from each band.

The end result is that given an ID, the transmitter and receiver agree on ordered set of 23 frequencies to cycle between which do not repeat and cover most of the available spectrum.

Transmitter and receiver implementation

The transmitters job in this scheme is relatively straightforward.  It transmits one frame, waits a millisecond to see if a reply comes in, then switches frequencies and waits until the next 20ms boundary to send the next frame.

The receiver is slightly more complicated.  During an initial “synchronization” phase, it sits on one frequency for 20 periods waiting to receive a message.  If no message is received, then it advances to the next channel.  This ensures that even if one channel is completely unusable, eventually the transmitter and receiver will connect.

Once the receiver receives a packet and replies, it then enters the “locked” mode.  In this state, after each reception, the channel is changed awaiting the next frame.  If a frame is 10ms overdue, then it is assumed lost and the channel is switched anyway.  If some number of frames are dropped in a row, then the receiver re-enters the “synchronization” phase.

Mapping onto the nrf24l01+

The following parameters are used with the nrf24l01+:

  • 5 byte address field (the address is derived from the ID)
  • dynamic payload length
  • 2 byte CRC
  • no automatic retransmission
  • auto acknowledgment
  • data rate is configurable

Replies are handled using the nrf24l01+’s “Enhanced Shockburst” “auto acknowledgment” feature.  This ensures that the receiver replies to the transmitter within a few microseconds of receiving the transmitter’s packet, relieving the microcontroller of that timing requirement.

“slot” data scheduling

The final piece of this is how to package up the data.  I decided on letting each of the transmitter and receiver define up to 15 slots.  Each slot contains up to 15 bytes of data and can be configured, via a bitmask termed “priority”, to be sent in every frame, every other frame, or some more interesting division.  On the wire, the 32 byte RF packet is filled with slots based on the current frame.  Each slot gets a 1 byte header, with 4 bits denoting which slot is next, and 4 bits denoting the size.  The host is responsible for selecting the bitmasks and sizes such that no frame is over-allocated.

So, if the transmitter has 4 slots that look like:

Slot Size “Priority”
0 (data denoted as “A”) 8
1 (data denoted as “B”) 4
2 (data denoted as “C”) 5
3 (data denoted as “D”) 6

Then it would incorporate those slots into frames as follows.  Each frame is written in hex, with ABCD corresponding to the data from slot 0123 respectively:


It is up to the transmitter and receive to agree upon how data is allocated to slots and the semantics of data inside each slot.  For the quad A1 application, that is hard-coded ahead of time.

Comparison and references

Aside from bidirectional data, the RF portion is pretty similar to DSMX with only a few differences.  First, DSMX uses a Cypress CYRF6936, where I’m using a slightly more available nrf24l01+.  Second, the frequency selection is the same in theory, but has slightly different parameters in implementation.  Third, bidirectional transmission is achieved using a single RF IC and amplifier.

The slot data scheduler is unique, as DSMX only allows one type of data to be transmitted in the control direction – “transmitter channels”.

Next I’ll demonstrate my implementation by itself before any integration!


Spread spectrum RF control and telemetry

Now that I have both sides of the nrf24l01+ link covered, it was time to design a protocol to take advantage of them.

Design space

To recap, what I needed was a reliable means of commanding the robot and receiving telemetry, even in congested radio environments.  At competitions or events like Maker Faire, Robogames and such, the wireless environment is often totally trashed.  Hundreds of devices are operating in close proximity, across all spectrum bands, including plenty of things that probably aren’t licensed to be transmitting in the first place.  When we first built Super Mega Microbot, we used a custom protocol with a 5 GHz wifi transmitter as the physical layer and selected USB based dongles which allowed control over the physical layer.  USB proved problematic, and with national RF regulations, it is extremely challenging to find wifi devices which provide that level of control at the RF layer.  Also, even with full physical layer control, wifi is difficult to make work in a reliable manner as there is so much congestion in both the 2.4GHz and 5GHz bands and the channels are so wide.

What does usually work at these events, despite the extreme congestion, are standard hobby RC transmitters.  DSMX from Spektrum, is one of the more popular varieties.  It uses an off the shelf 2.4GHz RF IC, and then hops between frequencies every transmission based on a pseudorandom key shared between transmitter and receiver.  This enables many transmitters to share the same RF environment, and renders them extremely resistant to interference.

The biggest problem with DSMX for this application (and basically every other RC protocol), is that they are unidirectional.  At best, bidirectional solutions involve sticking two independent transmitter and receiver pairs in opposite directions.  This is despite the fact that most of the low level RF ICs actually support bidirectional communications natively.  Even then, the only supported telemetry forms are things specific to RC models.  Joint position feedback would need to be encoded in propeller RPM for instance!

Proposed design goals

The target features I’m looking to achieve with this protocol are:

  • Resistant to heavy RF interference
  • Bidirectional communication of arbitrary data
  • 50Hz update rate or higher
  • Multiple transmitters and receivers can operate in the same area without interfering or explicitly coordinating
  • Support data that is transmitted at different rates (i.e. voltage telemetry update can be low rate, but movement commands are high rate)
  • Control over source to add new features over time

Things I’m not necessarily trying to accomplish (yet):

  • Long range
  • A “binding” mode that uses RF to share the common psuedorandom key
  • A “stable” over the air protocol

Next up, a design which hopefully achieves these goals!

Resurrected quadruped simulator

Thankfully, I’m now at the point where I’m fixing actual dynamics problems on the robot.  Doubly thankfully I have a robot which is pretty robust and keeps working!  That said, it is still, shall we say, “non-ideal”, to be testing code for the first time ever on a real robot.

Back with my HerkuleX based Super Mega MicroBot, I had a working DART based simulation which was decently accurate.  However, the actuators for that machine were so limited that it didn’t really make sense to do any work in simulation.  The only way to be effective with that machine was to tweak and tweak on the real platform and rely on exactly the right amount of bouncing and wiggling that would get it moving smoothly.

Now that I can accurately control force at 400Hz and beyond, that isn’t a problem anymore, so I’m working to resurrect the bitrotted simulator.  In the end though, it turned out to be a complete re-write as basically nothing of the original made sense to use.

Here’s a video of the very first time it moved around in sim (which means there are still many problems left!)

Power distribution board r3

While I was able to make the r2 power distribution board work, it did require quite a bit more than my usual number of blue wires and careful trace cutting.


Thus I spun a new revision r3, basically just to fix all the blue wires so that I could have some spares without having to worry about the robustness of my hot glue.  While I was at it, I updated the logo:


As seems to be the way of things, a few days after I sent this board off to be manufactured, I realized that the CAN port needed to actually be isolated, since when the switches are off, the ground is disconnected from the rest of the system.  Sigh.  Guess that will wait for r4.

Here is r3 all wired up into the chassis:



Ground truth torque testing for qdd100

First, a limited number of qdd100 servos are available for sale to beta testers!  Check them out at shop.mjbots.com.

After building up the first set of qdd100 servos, I wanted to empirically measure their performance parameters.  Some astute commenters uncovered in my terrible juggling video, that I didn’t actually have any ground truth measure of torque with these actuators.  Given that the ultimate torque is a pretty useful performance metric, it’s a good thing to have a solid understanding of.

To measure this, I built a simple test fixture (which is also the qdd100 beta development kit), consisting of two brackets.  The first lets the servo be bolted to a table, and the second mounts to the output and has set screws to hold a 1″ diameter pipe.  I used this to insert a 1 meter pipe which then can press against a digital scale.

1m is pretty long for my workshop!

Then I created a simple C++ application which emitted torque commands in response to joystick input and reported back telemetry from the servo: qdd100_test

Using these I was able to generate a plot of actual torque vs motor phase current:

qdd100 torque vs phase current

There are a couple of interesting things here, one is that the torque constant at low phase currents is slightly lower than I had estimated based on the motor’s Kv rating.  Second, the torque constant drops off faster at higher currents than I had anticipated, and third, the motor Kv rating was lower than I had predicted.  Those things combined result in a peak torque of between 12.5 and 15Nm depending upon the servo.  That’s still enough torque to do some serious jumping, but exploring those discrepancies is now on my backlog.

Here’s a video showing how this testing (and max speed testing) was done:


In order to bring up the final piece of the raspberry pi 3 hat, the nrf24l01+, I wanted a desktop development platform that would allow for system bringup and also be useful as a PC side transmitter.  Thus, the nrfusb:


Similar to the fdcanusb, it is just an STM32G474 on the USB bus, although this has a pin header for a common nrf24l01+ form factor daughterboard.

The next steps here are to get this working at all, then implement a spread spectrum bidirectional protocol for control and telemetry.

Simple walking gait on the quad A1

After I restructured my control laws to take advantage of high rate force feedback for the pronking experiments, I haven’t actually managed to port the walking gait yet.  Now that I have a brand new robot, it seemed like a good time!

This gait is basically the same thing as I ran on the quad A0 in principle.  The opposing feet are picked up according to a rigid schedule, and moved to a point opposite their “idle” position based on the current movement speed.  Any feet that are completely placed on the ground just move with the inverse of the robot’s velocity.

What differs now is that the leg positions and forces are controlled in 3D at a high rate, 400Hz for now.  At each time step, the position and velocity of all 12 joints is measured.  The gait algorithm calculates a desired 3D position, velocity, and force.  Feedforward force is currently only used to control the weight supporting legs.  Then, those 3D parameters are transformed into a joint position, velocity, and force based on the current joint position, and the command is sent out.

While not conceptually too different, just controlling the system in 3D at a high rate gives significantly improved results for a range of walking parameters.  There is still a lot left to do, but it is a good start!

(also, a limited number of the qdd100 servos are now open for sale to beta testers at shop.mjbots.comhttps://shop.mjbots.com/product/qdd100-beta-developer-kit/)