Raspberry Pi - Basic ESC - I2C

Hello! I am trying to use a Raspberry Pi 3 to communicate with the Basic ESC via I2C; my main goal is to read the RPM’s from it, for the purpose of controlling a quadcopter.

Background: I have 4 regular off-the-shelf Afro ESC’s which I purchased from HobbyKing. I have the Afro USB Programming tool that can flash them. I can set their motor speed using the Adafruit PCA9685 PWM board; however, the quadcopter wobbles significantly for a given constant applied PWM pulse width from the PCA9685. I measured the RPM’s during this wobbling using Sunfounder IR sensors and a strip of white-out on the motors. It’s crude, but it confirmed that the RPM’s were indeed changing significantly (+/- 50 or more for a given constant PWM pulse width, which is enough to tip the copter.) I am writing the PID control loop for the quadcopter myself that the Raspberry Pi will execute, and thus it is important that I be able to read the RPM’s accurately, so I can control them if they go out of whack for whatever reason. I purchased the BlueRobotics Basic ESC because the I2C documentation for the Afro ESC’s online consists of a single vague PDF document.

I have purchased and installed your off-the-shelf Basic ESC on the quadcopter, and I have successfully sent an integer (from 0 to 255) from the Pi to the ESC via the smbus write_byte() command, to no particular register at the default 0x29 address, and can thus set the speed. The write_byte_data() command sent to the 0x00 register gets the motors “just” spinning without getting anywhere close to max speed. I’d like to “properly” use the write_byte_data() method if I can.

Most importantly, I am having trouble reading any information from the ESC over I2C - whether I use read_byte(), read_byte_data(), read_block_data(), what I read is either zero, or some constant value that doesn’t seem affected by the motor speed, which I know is changing. This same result occurs whether I read from no particular register, or from registers 0x02 or 0x03.

I’m using the Adafruit BSS138 logic level converter (5V high side corresponding to the ESC’s I2C pins, and 3.3V low side corresponding to the Pi’s I2C pins), and I am able to communicate with other I2C devices using the Raspberry Pi, so I don’t think there are any issues there.

Aside from needing to remove the shrink wrap to get access to the I2C pins, are there other fundamental differences between the Basic ESC and the BlueESC? Does the I2C documentation for the BlueESC apply to the Basic ESC as well? If not, can I make it so? I have the tool to flash the Basic ESC.

Sorry for the wall of text.

Hi Austin,

Here are some links that I think are relevant to your questions:

I like your idea, and I appreciate the in-depth explanation of the problem you are trying to tackle. I have some questions and some thoughts that I think you should consider.

Are you going to use a gyroscope/accelerometer on your quadcopter?

What should matter is not the absolute speed of the rotors, but the lift that it is actually producing, and the lift that the rotors are producing relative to each other. 4 rotors, each spinning at a constant exact 5000 RPM is probably not going to keep your quadcopter level. Each rotor will produce slightly different amount of lift based on factors that vary locally at each rotor like imperfections of the propeller, shaft alignment, turbulence, air pressure, and vibration.

Multirotors generally work by measuring their attitude, or orientation, and adjusting the error between the desired and measured attitude by telling the different motors to ‘speed up’ or ‘slow down’, in relative terms. I can’t think of any advantage you would gain in telling a motor that you know is spinning at 5000RPM to speed up by telling it to spin at 5250RPM, rather than just saying spin a little bit faster than you are currently spinning. One advantage that knowing the current RPM would provide is that you would be able to detect if a motor has stopped spinning and be able to crash more gracefully (or less violently :wink: ).

A digital accelerometer/gyroscope can be interrogated very quickly and give you an accurate measurement of the copter’s current attitude. The way the RPM counter on the BlueESC works is by counting rotations since the last time you interrogated it. The faster you interrogate it, the less accurate and less useful this information becomes. If you interrogate the RPM on the BlueESC once per minute, the BlueESC may return 5000 RPM, and you can be pretty confident that it is in fact spinning at 5000RPM, on average. However, if you interrogate it at 100Hz, then it may return 42 rotations for one 10ms interval, and 54 the next. On average, the rotor is spinning at 5000 RPM, but when you sample in very small intervals you will probably get noisy results, which won’t be useful for what you want to do.

Beyond that, +/- 50 RPM seems like a very good tolerance to me when the rotor is spinning at many thousand RPM.

Some code that should help you figure out how to get the motor speed (I don’t know what language you are using). You should also take a look at the Arduino example code on the Blue Robotics github:

uint8_t lowBits;
uint8_t highBits;
highBits = read_byte_data(0x29, 0x02);
lowBits = read_byte_data(0x29, 0x03);
uint16_t pulseCount = (highBits << 8) | lowBits;

(RPM = pulseCount/dt*60/motor_pole_count)

Some code that should help you write the throttle value:

int16_t throttle = 12345;
uint8_t throttleLow = throttle &amp; 0x00FF;
uint8_t throttleHigh = throttle &amp; 0xFF00;
write_byte_data(0x29, 0x01, throttleLow);
write_byte_data(0x29, 0x00, throttleHigh);

It would be better to read/write a word at a time rather than a byte at a time, but there are some nuances relating to smbus and the way that the BlueESC registers are arranged that I won’t get into for now.


Hi Jacob, sorry for my late reply, and thank you for responding.

Yes, I had initially used two separate sensors (MPU6050 and HMC5883L) to get the pitch, roll, and yaw data, but switched to the Adafruit BNO055 sensor, as those three orientation readings are coupled, and the BNO055 has a feature which does the calculations automatically. I agree, all sorts of effects can influence the attitude of the quadcopter. My hope is that the PID control loop I am writing will be robust enough to handle disturbances from the environment.

My issue right now is that, using the PCA9685 PWM board to generate the PWM pulse widths to send to the ESC’s, the motor RPM will fluctuate enough to make the quadcopter wobble significantly in its fixture (a pair of low friction bearings) for a given steady supplied pulse width. I am working under the assumption that the PCA9685 board is producing a steady PWM pulse and that it isn’t fluctuating. If that’s the case, then my plan is to try to use a control loop to help keep the RPMs constant. To do that, I would like to be able to read the RPM, even if its not that often, as you have advised. My hope is that the Basic ESC offered here (the Afro ESC with custom firmware), as opposed to the BlueESC, is capable of giving me a relatively accurate RPM reading over I2C.

To date, I’ve looked at the links your provided, but the Python code given there hasn’t helped me yet. I’ll keep tinkering; if I get it working, I’ll share whatever knowledge I can.

Hello to anyone who may read this! I have seemingly resolved the issue, which was that the firmware that the Basic ESC was shipped installed with needed to be replaced, as per the following zip file linked in this forum conversation. I have also attached that zip file to this post.


I used the Afro Programming Tool with the KKMulticopterTool software to update the firmware on the ESC, and can now control the speed via the write_word_data(address,0x00,integer) command, and read what appears to be the expected RPM data used the read_word_data(address,0x02) command. Happy day!

afro_nfet_firmware_2015-08-16_d190d7f-2 (160 KB)

To clarify my previous post, I was not successful in reading from any other registers besides 0x02 on the Basic ESC via I2C. In addition, the reading given from register 0x02 was nonzero, and it varied with the speed of the motor, but whether or not that value was a function of the RPM was not determined at the time, and is still unproven, as I am no longer working on this endeavor. Sorry for any confusion that may have caused.

Hi Austin,
Thanks for your quick reply on my email and the additional information.

Today I was made some reengineering on the BasicESC from BlueRobotics and now I can confirm your guess, that the BasicESC is equal to the Afro 30A Muti-Rotor ESC OPTO from HobbyKing (Link). I create a schematic from the board, to be one hundred percent sure that the software don’t will burn the ESC, by switching the wrong pin’s.

Here is the schematic:

And here are two images of bottom and top side of the board and the component placement:

So because the BasicESC and the Afro ESC has the same pinout there is no problem to use the “afro_nfet_firmware …” with this board.

I try to flash the ESC with the KKMulticopterTool software and an Arduino Pro Min but without success. The KKMulticopterTool software already failed by flashing the Arduino with the “ArduinoUSBLinker” software. So I make this step manually with the Arduino IDE and this project: GitHub - bluerobotics/ArduinoUSBLinker: Arduino based USB Linker protocol
Then I moved on and flash the ESC with the “afro_nfet_firmware …” software and avrdude.
The command I used is shown in the next picture.

The connection of the Arduino to the ESC was quite easy. I only need to connect Pin2 to the signal wire and Gnd to the ESC’s ground.

After successfully flashing the ESC I wrote a small test program in mbed for the NUCLEO-L432KC from STM to check if the new firmware is working right.

#include "mbed.h"
#define ESC-ADDRESS 0x29

Serial pc(USBTX, USBRX);
I2C i2c(PB_7,PB_6); // SDA (D4)/ SCL (D5)

int main() {
char cmd[8];
char addr=ESC_ADDRESS<<1; // shift because mbed want a 8 Bit address

int speed=0;
while (1) {
    cmd[0]=0;               // Register: 0x00
    cmd[1]=0;               // Byte 0: throttle_h
    cmd[2]=speed;           // Byte 1: throttle_l
    speed++;                // increase Speed slowly
    i2c.write(addr, cmd, 3);// write to Register 0         
    cmd[0] = 0x02;          // Start at Register 0x02
    i2c.write(addr, cmd, 1);// Write start address         
    i2c.read(addr, cmd, 8); // Read 8 Bytes
    // Print receive Bytes
    printf("id:%d : %d ; %d ; %d ; %d ; %d ; %d ; %d ; %d \n",addr, cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7]);
    wait(0.1);              // Wait for 100 ms


The picture above show my test setup.
I read the transmitted data via HTerm and I think the result is ok so far.

pulse_count_raw = 7

The real voltage was 12.7 Volt and when I calculate the voltage from the reading I get 25856 * 0.0004921 V = 12,72 V.
Also the temperature seems to be correct, 45056 convert by the formula shown here, I get 28.2 °C and for the speed I get 300 RPM (for spinning at low speed I think it could be correct but I will make some comparative measurements to confirm this). The current is zero because on the board there is no chip for measuring the current, so this is also ok.

The only weird thing on the BlueESC software is that I can only use one communication interface at the time, either I use I2C or PWM. I was hoping it would be possible to read the speed information via I2C and control the speed via PWM at the same time but this seems to not working.

@rjehangir Maybe the is a setting in the .asm file of the firmware where I can change this behaviour.

But again thanks for your help and maybe the above information will help somebody to get his project working.

Best regards