Various projects need remote control, like remote control planes, cars, or hexapods. You can make your remote control with an Arduino and NRF24, but sometimes you might prefer a pre-made solution. This was the case with my RC Tank project. While the ultimate goal was to create a custom controller, I opted for a ready-made controller for the project’s initial phase to streamline and increase the likelihood of success. This article discusses how to use the FlySky Ibus protocol with an Arduino.
What is IBUS?
IBUS is a protocol developed by FlySky. It is a two-way protocol (this article will focus only on the control aspects – none of the telemetry). Depending on your particular transmitter and receiver setup, the iBus protocol enables the transmission of as many as 14 RC channels to your Arduino. 14 Channels should be more than enough for most robotic applications.
iBus protocol Description
iBus is a serial-based protocol, meaning a UART/USART port is required on your microcontroller. iBus is transmitted at 115,200Bits/s – so obviously, your Arduino will need to be configured to receive this. Fortunately, unlike SBUS, iBus is non-inverted, which means no external circuitry is required! The iBus signal is encoded with 8 data bits, even parity and 2 stop bits – fortunately, this is the default Serial/UART configuration for Arduinos.
The iBus protocol encoded the RC channel data into a single packet. This packet is formed of 32 bytes which contains 2 header bytes, 28 bytes of RC channels (in which 14 RC channels are encoded) and finally 2 checksum bytes. This data packet is transmitted every 7ms.
Packet Structure
Each IBus packet is comprised of 32 bytes. Each packet contains:
- 2 header bytes (0x20 and 0x40)
- 14 channels, encoded on 28 bytes
- 2 byte checksum
Decoding the Packet
We can identify the start of a packet by looking for the first header byte. Once we have identified the start of a packet, the remaining bytes in the message can be recorded to into an array of bytes.
To ascertain the numeric value of the channels, we then need to perform some bitwise operations on the middle 28 bytes. We know the value of each channel is split across two bytes. We also know the first byte of each pair is the low side byte and then the second byte of each pair is the high side byte.
To calculate the 16 bit channel value, the high side byte needs to shift to the left 8 positions and then be “OR’d” with the low side byte.
Each channel is therefore encoded as follows in the iBus Packet:
- Channel 1 = byte[3]< < 8 | byte[2]
- Channel 2 = byte[5]< < 8 | byte[4]
- Channel 3 = byte[7]< < 8 | byte[6]
- Channel 4 = byte[9]< < 8 | byte[8]
- Channel 5 = byte[11]< < 8 | byte[10]
- Channel 6 = byte[13]< < 8 | byte[12]
- Channel 7 = byte[15]< < 8 | byte[14]
- Channel 8 = byte[17]< < 8 | byte[16]
- Channel 9 = byte[19]< < 8 | byte[18]
- Channel 10 = byte[21]< < 8 | byte[20]
- Channel 11 = byte[23]< < 8 | byte[22]
- Channel 12 = byte[25]< < 8 | byte[24]
- Channel 13 = byte[27]< < 8 | byte[26]
- Channel 14 = byte[29]< < 8 | byte[28]
Using iBUS with an Arduino
What do I need?
To use iBus on an Arduino, you need three things:
- Arduino – It’s possible to use the SoftwareSerial library to receive the iBus data however, this is not recommended. I recommend you use an Arduino board with multiple hardware serials. An Arduino Mega is a good choice.
- iBus Receiver – Be careful which receiver you pick – not all FlySky receivers support the iBus protocol. I recommend the following: FS-X6B, FS-IA6B and FS-A8S.
- Transmitter – You will need an RC Transmitter that works with the FlySky receivers. I personally use the Multiprotocol version of the Radio Master TX16S but I would recommend sticking to one of the official FlySky FS-i6X transmitter.
Electrical Connections
- VCC to +5V
- GND to GND
- I-BUS to RX1
Arduino Software Implementation
For all but the smallest projects, it is best practice to split your code into multiple files, such that different functional areas can be contained. The code shown below follows these principles. The IBUS decoding has been separated into its own header (.h) file and class (.cpp).
#ifndef ibus_H_
#define ibus_H_
#include <Arduino.h>
#define noChannels 14
#define messageLength 32
#define startByte 0x20
class IBUS {
public:
IBUS(HardwareSerial* uart, unsigned long baud) {_ibusRX = uart; uart->begin(baud, SERIAL_8N1);buffer[0] = 0x20;};
int readBuffer();
int processChannels();
int update();
uint16_t channels[14];
uint16_t getChannel(uint8_t i) { return channels[i]; }
private:
HardwareSerial* _ibusRX;
uint8_t buffer[32];
};
#endif
#include <ibus.h>
#include <stdint.h>
int IBUS::update()
{
readBuffer();
processChannels();
return 1;
}
int IBUS::readBuffer()
{
if (_ibusRX->available() >= 32)
{
if (_ibusRX->read() == 0x20)
{
uint8_t i;
for (i = 1; i < 32; i++)
{
buffer[i] = _ibusRX->read();
}
return 1;
} else {
return 0;
}
}
else {
return 0;
}
}
int IBUS::processChannels()
{
for (int channel_index = 0, byte_position = 2; channel_index < 14; channel_index++, byte_position += 2)
{
channels[channel_index] = buffer[byte_position + 1] << 8 | buffer[byte_position];
}
return 1;
}
As mentioned, an ibus packet is sent every 7ms, therefore the arduino must be programmed such that the serial buffer is cleared every 7ms. This ensures that no ibus packets are missed and that the latency between control input and control effect is minimised.
#include "ibus.h"
IBUS ibus(&Serial1, 115200);
void setup(){
}
void loop(){
ibus.update();
Serial.println(ibus.channels[2]);
}
Conclusion
Using a pre-made RC transmitter can make a project significantly easier – allowing you to focus on the development of one piece of hardware. Whilst focussing on one piece of hardware can make the project significantly simpler, it is still important to understand the technology you are using. In this article, we have discussed what iBus is, the protocol itself and how it can be decoded. We have also discussed the electrical and software implementations required to receive and decode the iBus signals.