Navigating the uncertain world of ESCs

July 29, 2022

As part of a course I’m teaching over the summer on building an RC drone, I’ve had to do some research myself into electronic speed controllers for brushless DC motors. Initially, I thought this would be as easy as plugging it into my microcontroller of choice, sending an out-of-the-box PWM signal, and being done with it…but it turns out things aren’t as clear-cut.

What is an ESC?

An electronic speed controller is a device that drives brushless DC motors (BLDCs). They exist because BLDCs are built such that their three phases need to be turned on and off at precisely timed intervals to maximize torque. They often use built-in Hall effect sensors to detect when a phase needs to be switched off and consequently another turned on.

In effect, they are motor drivers, much like the L298-based ones for simpler DC motors. The end goal is simple: send a throttle value (between 0% to 100%) to the motor, and the request will be matched by the physical motor.

Due to the wide variety of protocols they must support, the vast majority of ESCs come nowadays with an on-board SiLabs-based or Atmel-based microcontroller, as opposed to some purpose-specific strung together gates. However, an ESC alone cannot control a drone. It still needs to be told what to do, ie. when to spin and how fast to spin the motor. This is where a flight controller comes in, and where the main pain of my ESC journey begins.

ESC, meet flight controller

A flight controller is the main brains behind any RC plane, drone or quadcopter. It sends output signals to all the onboard ESCs at precisely timed points with a throttle value, usually the result of the demand input to some PID algorithm. Bar physical motor hysterisis, the ESC will then switch phases on/off such that the BLDC shaft spins at this requested throttle value.

Much like an older brother speaking to his younger counterpart, a flight controller and an ESC need a common language to share information, especially in a hobbyist field dominated by proprietary hardware/software from many vendors. In technical speak, this is called a protocol.

There are two types of possible communication protocols: analog and digital. Digital protocols encode information in bit sequences (a continuous string of 1s and 0s) while analog ones encode information in the length of a pulse. Analog protocols predate digital ones and while they are handier to produce from a flight controller, they are susceptible to electronic noise and provide no form of error correction.

Here’s a list of protocols I managed to come across:

  • DShot (with variants for higher data rates) – digital
  • Analog PWM (at fixed frequencies)
  • 1-2ms PWM
  • OneShot
  • MultiShot

The main thing I found out while doing all this research is that there is a surprising amount of vendor lock-in in the RC drone space. Consequently, a lot of documentation is obscure or even nonexistent. Everything seemed quite ad-hoc and scraped together on RC forums of the 2000s. Forget even trying to find a proper specification. This is what in part inspired me to write this blog post; to bring some semblance of order to this chaotic mess.

The baby of analog protocols was the simple PWM (pulse width modulation). I have another blog post on PWM that explains that in detail, but essentially a PWM signal has a high and low time held at a certain ratio (20% on, 80% off say), all within a given time period. By varying this ratio, the duty cycle, we can infer a throttle value and the ESC will perform the necessary adjustments to boost power to the motor if, for example, the duty ratio rises.

Then, there came along the 1-2ms PWM. Here, the frequency remains fixed and the length of the pulse (ON time) is used for throttle variation. Here, it become useful to set a PWM duty cycle in terms of time (eg. the MicroPython duty_ns function) instead of as a percentage. Newer protocols simply increased the data rate by shortening the pulse width and bumping up the frequency.

The major difference came with DShot, named after ‘digital’ and the previous protocols that all seemed to end in ‘shot’. The invisible specification states that 16 bits should be sent, where the first 11 bits are throttle, 1 indicates a telemetry request, and 4 bits of forward error checking. This makes the protocol quite robust (and eays to bitbang on the Rasberry Pi Pico’s PIO!).

So many protocols, ah!

The abundance of protocols might give you a headache, and indeed it would be impractical to sell ESCs that support only one protocol. Additionally, the existence of a microcontroller on nearly all ESCs means that custom software can be written that auto-detects the protocol being used. This isn’t perfect, but works for nearly all cases.

ESCs used to come (and perhaps still might do) with proprietary firmware but these were often limited in functionality and hard to hack on, so hobbyists wrote up some firmware that was open source and has since become the de-facto standard. The only one that survives to this day is called BLHeli, of which there are now two improved versions: BLHeli_s and BLHeli_32 (for 32-bit microcontrollers, also not open source). They are all written in assembly to ensure the time critical nature of quadcopter control is respected. The source code can be found here.

Programming the ESC

Another thing that took a while for me to grasp was how exactly to talk to the ESC..I have 30 GPIOs on the Pico but don’t know what to do with them! My ESC has one ground pin and one signal pin. The trick is that all of the above protocols are one pin only, so this setup is fine. Now how about programming it? Or flashing new software? The ESC only has one pin, so I can’t just plug it into my computer as so many YouTube tutorials show.

This is where there is a frustrating amount of documentation is lacking, once again a cause of centralization and concentration of software that is capable of doing this. It might also be perhaps that these drones are sold as kits that come with all the parts, including a flight controller that has been pre-programmed so there is rarely ever a need to learn the actual internals of communication. It is universally assumed that the flight controller will handle this functionality.

Often, ESCs have configuration available for us to tweak, like turning off all beeps. Flight controllers (on the market) have what’s called a pass-through mode that opens a UART interface that can easily be plugged into your laptop. How do we convert instructions sent over the serial interface (or indeed, what instructions should we send?) to instructions in the protocol we’ve chosen?

This is the task of the flight controller and from what I gathered by looking in the BLHeli Configurator (one of the tools out there for configuring ESCs with BLHeli) source code (which isn’t very beginner friendly..) a special protocol called MSP has been used for this. For my own Pico-based flight controller, I’ll have to reverse engineer this protocol by looking in the BLHeli Configurator and Betaflight (open source flight controller software) source code.

Writing a flight controller

So what now? All that’s left is to write a flight controller that can speak a protocol of choice. It really doesn’t have to be complicated. After all, all a flight controller does is drive the ESC according to some pre-defined logic. Here’s some starter code for a controller written in MicroPython:

from machine import Pin, PWM
import utime

pwm_pin = PWM(Pin(0, Pin.OUT))
pwm_pin.freq(50)
pwm_pin.duty_ns(1000)

I plan to write a PIO implementation of DShot, but that’s for another day and another post :-).

For me:

A previous attempt at implementing DShot with PIO