How I Developed the Scout Flight Controller, Part 3: Receiving Control Inputs via an RC Receiver

Tim Hanewich
5 min readAug 7, 2023

--

This article is part of a series documenting the development of a custom quadcopter flight controller from scratch. For a list of the other related articles, please refer to the introductory article found here.

In the previous chapter on gyroscopes we learned that a quadcopter flight controller achieves stable flight by comparing the actual rate of attitude change against the pilot’s desired rate of atttitude change. In then determines the precise thrust to apply to each of its four motors to achieve (comply with) the aforementioned desired rate of attitude change.

In the last chapter, I explained how an inexpensive MPU-6050 module is onboard my Scout quadcopter and how this serves as the gyroscope which allows the Scout Flight Controller to determine its roll, pitch, and yaw rate of change; this is the actual rate of change data. Next, we need to collect the desired rate of change data from the pilot. We will do this via an RC receiver.

Beside the Raspberry Pi Pico (microcontroller that runs the Scout Flight Controller) and the MPU-6050, a FlySky FS-iA6B Receiver is mounted directly to the frame. This module allows the Scout Flight Controller to receive control inputs from the pilot over a 2.4 GHz radio frequency.

The FS-iA6B receiver module can be seen below:

The pilot will be using a FlySky FS-i6 Transmitter to transmit control commands to the drone.

Reading from the FS-iA6B Receiver

I won’t go into heavy detail about how the FS-iA6B works (PWM, PPM, etc.). Scout reads incoming data from the FS-iA6B receiver module via its ibus protocol functionality. Ibus is a protocol that uses a UART serial interface to transmit the value of each channel from the receiver. In other words, by connecting our FS-iA6B to one of the Raspberry Pi Pico’s UART-capable pins, we can perform some code that will allow us to read each channel (inputs from the pilot).

Fortunately, the community has our back. I found this fantastic video by the YouTube channel Penguin Tutor that describes, in detail, the ibus protocol, how we can leverage it, and even provides sample code for doing so.

You can find the sample code that Penguin Tutor provided on his website here. I will provide the class definition that he wrote below as well. This is the class that we will use to read (and parse) channel values from the FS-iA6B receiver:

ibus.py

Interpreting the Data

The read method of the IBus class, for a 6-channel transmitter like the one I am using (FS-i6), will return a list of seven integer values. For example:

[1, 1499, 1498, 1002, 1500, 1000, 1000]
  • Index 0 (1 in this case) is the status of this returned list. 1 means this is a valid data packet (new values).
  • Index 1 (1499 in this case) is the horizontal axis of the right stick. We'll use this to control the roll rate. The value ranges from 1000 (full left) to 2000 (full right). At approximately 1500, the stick is in the middle position, untouched.
  • Index 2 (1498 in this case) is the vertical axis of the right stick. We'll use this to control the pitch rate. The value ranges from 1000 (full up) to 2000 (full down). At approximately 1500, the stick is in the middle position, untouched.
  • Index 3 (10002 in this case) is the vertical axis of the left stick. We'll use this to control average thrust (throttle) of all four motors, controlling if the quadcopter is ascending or descending. The value ranges from 1000 (full down) to 2000 (full up). At approximately 1000, the stick is at its lowest position.
  • Index 4 (1500 in this case) is the horizontal axis on the left stick. We'll use this to control yaw rate. The value ranges from 1000 (full left) to 2000 (full right). At approximately 1500, the stick is in the middle position, untouched.
  • I customized index 5 (1000 in this case) to read the values of the SWA switch on the transmitter. This is the small switch at the top left part of the controller (see picture above). 1,000 is the off position (up) while 2,000 is the on position (down). You must configure your FS-I6 RC controller to provide the value of this switch in the setting menu of the transmitter.
  • I customized index 6 (1000 in this case) to read the values of the SWB switch on the transmitter. This is the large switch at the top left part of the controller, directly to the right of SWA. 1,000 is the off position (up) while 2,000 is the on position (down). You must configure your FS-I6 RC controller to provide the value of this switch in the setting menu of the transmitter. We will not be using the value of this switch in this version of the Scout Flight Controller.

Normalizing Input Values

Instead of working with these values that range from 1,000 to 2,000, I preferred to normalize each within the range of -1.0 to 1.0 for the roll, pitch, and yaw, and between 0.0 and 1.0 for the throttle. This makes the values easier to understand and work with. I wrote the following simple function in Python to handle the normalization process:

def normalize(value:float, original_min:float, original_max:float, new_min:float, new_max:float) -> float:
"""Normalizes (scales) a value to within a specific range."""
return new_min + ((new_max - new_min) * ((value - original_min) / (original_max - original_min)))

The method above takes the value you provide and scales it to a new value between the new_min and new_max values you provide, proportionally to the original_min and original_max values. We will use this in the Scout Flight Controller code to normalize the roll, pitch, yaw, and throttle inputs.

Using the Control Data

Now with both the actual attitude rate of change values from the gyroscope (see the last chapter) and the desired attitude rate of change values form the RC receiver (covered above), we now have the full set of telemetry required to calculate precisely how much thrust to apply to each of the four motors to achieve the desired attitude rates.

In the next chapter, we’ll learn how a system called a PID Controller is implemented in quadcopter flight controllers that determine the optimal level of thrust to apply at any moment to achieve the desired attitude rates.

--

--