The Communication Protocol
Remote Controller Protocol design is the core part of the DIY Remote Controller Project, which can also be the most difficult part if you are aiming for a sophisticated design. I have had similar design experience in my past project designing communication protocol.
The protocol I am talking about is the format of a series of values we send from the remote controller to the client, each value is one byte packet sent using Serial.write(). I also call this series of values “command” and it looks something like this:
1 122 93 28 19 1 0 1 | type | value1 | value2 | value3 | value4 | value5 | value6 | value7 |
“Type” is the type of protocol. I am going to design various type of protocols, each type uses different control values and precisions. The “Type” number tells the client how many and what kind of value we are expecting, and how to use these values.
This is a demo video using the protocol, controlling a quadruped robot.
A Trick To Encode Button/Toggle States
Since the buttons and toggles states only have values of 0 or 1 (boolean value), so it would not be very wise to transfer each of the input using a full byte. What I have done is to treat each button/toggle as 1 bit, and I have 8 of them so it’s just enough to make 1 byte (8 bits). That way I can save 7 bytes each time I send a command! I call this process of converting Button States “Button States Encoding”. When it reaches the client side, we have to do the reverse process to convert 1 byte value into 8 boolean values, and I call it “Button States Decoding”.
Table of Contents
Button States Encoding
This is an example on how it works. Imagine we align the inputs like this
| toggle1 | toggle2 | toggle3 | toggle4 | button1 | button2 | button3 | button4 |
(1) If Button3 is pressed and not any others, we have 0000 0010 in binary, which is 2 in decimal, and that’s the number we are going to send using this Serial.write(2);
(2) Another example if Toggle2 is on, and button2 is pressed and not others, we have 01000100, which is 2^6 + 2^2 = 64 + 4 = 68.
Notice the maths (+ additions and ^ powers) we have to do when doing the button encodings, this is quite computationally expensive. To improve this, we can manipulate what we call “Bit Shift Operator“. It can help reduce calculation time.
For example (1), we can now do button3 << 1, which is 2
For example (2), toggle2 << 6 + button2 << 2 = 68
[sourcecode language=”cpp”]
// convert 4 toggle and 4 buttons state into binary, and then convert binary to byte number for transmission
// expect bit1 – bit8 are zeros or ones
byte EncodeButton(bool bit1, bool bit2, bool bit3, bool bit4, bool bit5, bool bit6, bool bit7, bool bit8){
byte sum = 0;
sum += bit1 << 7;
sum += bit2 << 6;
sum += bit3 << 5;
sum += bit4 << 4;
sum += bit5 << 3;
sum += bit6 << 2;
sum += bit7 << 1;
sum += bit8 << 0;
return sum;
}
[/sourcecode]
Button States Decoding
To do the reverse at the client side, we need to test each bit of the received byte value to see if they are 0 or 1 and assign it to a variable that represents each button. Or you can also have a switch case statement to look it up, it’s up to you. From above examples:
(1) If we received 2, we need to check each bit start from most significant bit. Assuming Toggle1 is on, we should have received a value 10000000 = 2^7 = 128. But 2/128 < 1 thus Toggle1 = 0. Just like this, we work all the way down to Button3.
Assume Button3 is pressed, we should have 00000010 = 2, 2/2 >= 1, thus button3 = 1 is true!
Assume Button4 is pressed, we should have 00000001 = 1, (2-2)/2 < 1, thus button4 = 0!
So, all variables are 0’s, except button3.
Again, like Button Encoding, we can explode the “Bit Shift Operator” trick. From the above example, we need to test each bit starting from the most significant bit.
We received 2, 2 >> 7 = 0, thus toggle1 = 0; And work your way down.
2 >> 1 >= 1, so button3 = 1, (2-2) >> 0 < 1, thus button4 = 0;
[sourcecode language=”cpp”]
void DecodeButton(byte byte1, bool *bit1, bool *bit2, bool *bit3, bool *bit4, bool *bit5, bool *bit6, bool *bit7, bool *bit8){
// Usage: DecodeButton(buttonByte, &toggle1, &toggle2, &toggle3, &toggle4, &button3, &button4, &button5, &button6);
byte sum = 0;
*bit1 = byte1 >> 7;
sum += (*bit1)*128;
*bit2 = (byte1-sum) >> 6;
sum += (*bit2)*64;
*bit3 = byte1-sum >> 5;
sum += (*bit3)*32;
*bit4 = byte1-sum >> 4;
sum += (*bit4)*16;
*bit5 = byte1-sum >> 3;
sum += (*bit5)*8;
*bit6 = byte1-sum >> 2;
sum += (*bit6)*4;
*bit7 = byte1-sum >> 1;
sum += (*bit7)*2;
*bit8 = byte1-sum >> 0;
}
[/sourcecode]
Types of Remote Controller Protocols
As I have discussed in the last post, we are going to have multiple protocols that transmit data in different format and level of accuracy, so users are backed by these choices of different protocols depend on the situation. Sometimes you might want the smallest latency protocol (less accurate but faster transmission), and sometimes you might prefer high resolution commands (high accuracy but slower transmission). At the moment I have implemented these protocols, these examples give you an idea what the commands look loke.
1. All Controls, Short Version
All the values are made up as an example. Each value is a byte which has a max value of 255. Same applies to all four examples.
0 122 93 28 19 60 55 199 250 50 | type | pot1 | pot2| pot3 | pot4 | js1x | js1y| js2x | js2y | buttons |
2. All Controls, Long Version (full accuracy)
0 2 93 0 19 3 | type | pot1(high) | pot1(low) | pot2(high) | pot2(low) | pot3(high) 0 155 1 155 3 0 | pot3(low) | pot4(high) | pot4(low) | js1x(high) | js1x(low) | js1y(high) 3 51 1 12 25 21 | js1y(low) | js2x(high) | js2x(low) | js2y(high) | js2y(low) | buttons |
3. Selected Controls, Short Version
For this example, I selected a potentiomete, a joystick and all buttons and toggles.
0 129 93 0 19 | type | pot1 | js1x | js1y | buttons |
4. Selected Controls, Long Version (full accuracy)
For this example, I selected a potentiomete, a joystick and all buttons and toggles.
0 2 93 0 19 3 20 0 |type| pot1(high)| pot1(low)| js1x(h)| js1x(l)| js1y(h)| js1y(l)| buttons
Channel
The term “channel” is used quite a lot when it comes to commercial RC transmitters. Channel can mean 2 totally different things:
1. the number of “things” you can control, for example for a joystick you need at least 2 channel, one for left right, one for up down (1 Degree of freedom each channel).
2. the number of different transmission frequency you can use to avoid conflicts with other remote controllers. (pretty much like Radio channels)
Fortunately none of these would be relevant to my remote controller. For (1), because the way I send data, I can choose to send input data from each control one by one if I want, so no matter how many thing I need to control, it would be do-able (although the more control means more data to transmit thus takes longer). For (2), I can use something called “controller identifier” in the command I send to differentiate different controllers, so the commands will only be picked up at the client side with the pre-defined identifier. This is only an idea but totally do-able.
Calibration
Update – I have not done this yet, probably will just leave it there.
Joystick and potentiometers are analogue inputs. They might have slight different start and end value, changes might not be linear and for joystick the resting mid value might not be the exact mid value.
Therefore analogue inputs sometimes need to be calibrated before use. Badly calibrated analogue controls can sometimes affect accuracy and controllability.
I have written a script for testing, this script only needs to be run once
Improvement 1 – Analogue Input Buffer
1
9 comments
HI, do you have Improvement 2 or more?
Thanks
Adam
Please give me some information about frequency hopping and how to implement in this project. I really need it. also please tell me more about version 2. full accuracy mode. waiting for your reply. thank u very very very much.
Hii
Dear
I want to know that how will be wiring becouse i m doing this first time ….can you please expain the wirng and parts voltageand i also want to know that transmitter and reciver work for quardcopter.
can u please provide a source code for building a fpv quad that can be controlled using this remote controller. i have searched a lot but not able to find it. please help
Hey Oscar! First off, this blog is AMAZING! I greatly appreciate all this work you’ve done here.
I actually would like to bug you about a question I have for a project I would like to start. I am going to start a 180 quad project and would like to use the Ciseco XRF module in a variation of the remote you’ve built (arduino based). I’m having a problem figuring out the best flight control to use in this case though. I was looking at the naze32, but is this FC capable of connecting to the XRF chip for Rx? Would it be better to go arduino base FC? Or maybe you have a better idea all together for Tx/Rx between an arduino based transmitter and a 180 quadcopter.
Thanks Chris. that should be fine, you probably need a micro controller like the Arduino to decode the signal from the XRF, and encode it into PWM or PPM signal and feed it to the Naze32. Using Arduino based FC means lots of code changing in the Multiwii source code, just equally difficult i think. Sorry i am not being very helpful as i havne’t done this before.
hi dear:
we are planning to design an robot tank which can be controlled via remote control .this robot has an arm aswell
now the question is here that how can we design a rc radio transmitter that be able to control both arm and robot tank together .
please if you have a sample project or a tutorial send to my email.
best regards
mujtaba hatef
what I have done here should be capable of what you described. You can adapt my project if you want.
Thank you for sharing your info. I truly appreciate your efforts and I am waiting for your next write ups thank you once again.