My idea is to make a universal controller for Radio Control Toys. From my previous post, I described how to make the receiver and transmitter from a hacked RC car. Now I am going to decode the Radio Signals for the following RC Tank and use Nokia N2 or PC to control the tank.
http://www.henglongtoys.net/eng/ShowProduct.asp?id=568
Lets watch how I control the tank from Nokia N82.
And the source codes:
http://universalrccontroller.googlecode.com/files/rc_controller_v0_1.zip
If you want to know the details on the controlling, please see the explanation blew.
By measuring the width of the pulses , I got the following results (for the details, please see the figure from oscilloscope):
Start High pulse width: 1000us (microseconds)
Start Low pulse width: 600us (microseconds)
Long pulse width: 260us (microseconds)
Short pulse width: 140us (microseconds)
And the Logical Bit 0 and Bit 1 can be made from the following combination:
Bit 1 = short high pulse (140us) + long low pulse (260us)
Bit 0 = short high pulse (140us) + short low pulse (140us)
And Start High pulse and Start Low pulse have to be sent at the start of each command.
Connecting the required components (Transmitter, Bluetooth serial module, Arduino and Nokia N82), uploading programe for Arduino and N82, I can now control the RC Tank from my N82.
Each command have three bytes, but the size of the actual command is less than three bytes. It has 17 bits only. e.g. (00000000111101000). I use a variable called MASK_BITS to mask the active bits. In this way, I can use the Arduino to stimulate the signals as the factory transmitter do.
const int COMMAND_LENGTH = 3; const byte MASK_BITS[] = {B11111111, B11111111, B10000000}; // mask control the active bits // the commands const byte STOP[] = {B00000000, B11110100, B00000000}; // 00000000111101000 const byte TURBO[] = {B10110000, B01010100, B00000000}; // 10110000010101000 const byte FORWARD[] = {B10110000, B01010100, B00000000}; // 10110000010101000 const byte BACKWARD[] = {B10110000, B01010100, B00000000}; // 10110000010101000 const byte LEFT[] = {B00010000, B01100100, B00000000}; // 00010000011001000 const byte RIGHT[] = {B00010000, B10010100, B00000000}; // 00010000100101000 const byte RIGHT_FORWARD[] = {B00010000, B00010100, B00000000}; // 00010000000101000 const byte RIGHT_BACKWARD[] = {B00010000, B00100100, B00000000}; // 00010000001001000 const byte LEFT_FORWARD[] = {B00010000, B01000100, B00000000}; // 00010000010001000 const byte LEFT_BACKWARD[] = {B00010000, B10000100, B00000000}; // 00010000100001000 const byte CANNONRIGHT[] = {B00011000, B00000100, B00000000}; // 00011000000001000 const byte CANNONLEFT[] = {B00010100, B00000100, B00000000}; // 00010100000001000 const byte CANNONUP[] = {B00010001, B00000100, B00000000}; // 00010001000001000 const byte CANNONFIRE[] = {B00010010, B00000100, B00000000}; // 00010010000001000 |
For the PyS60, it senses the orientation (X,Yand Z axis) of the phone and use the following simple protocols to transfer commands.
The simple protocols sent from PyS60
msg = bytecmd(65) # "A" the sync byte msg +=bytecmd(mode) # mode [ 0: moving directing, 1: moving turret ] msg +=bytecmd(x_val) # x-axis [ -127 to 127 ] msg +=bytecmd(z_val) # z-axis [-127 to 127 ] |
// decode the protocols in the Arduino
loop until the byte matches the sync byte
int findPacketHead() { while(1) { while(Serial.available() == 0) select_command() ; if(Serial.read()=='A') { // "A" is the sync byte break; } } } |
if the value is grater than 127, it means it is a negative number
cmd[0] = receiveByte(); // mode cmd[1] = receiveByte(); // x-axis if (cmd[1] > 127) cmd[1] = cmd[1] -256; // if is it larger than 127, it is negative number cmd[2] = receiveByte(); // z-axis if (cmd[2] > 127) cmd[2] = cmd[2] -256; |
According to the following table, the select_command() will select the command. ? means don't care the number.
Mode cmd[0] | Z-axis cmd[2] | X-axis cmd[1] | Command |
0 | ? | >30 | Backward |
0 | ? | <-30 | Forward |
0 | 30 | ? | Right |
0 | <-30 | ? | Left |
0 | >30 | >30 | Forward_right |
0 | <-30 | >30 | Forward_left |
0 | <-30 | <-30 | Backward_right |
0 | >30 | <-30 | Backward_left |
1 | >30 | ? | Turret_right |
1 | <-30 | ? | Turret_left |
1 | ? | >30 | Cannon_up |
1 | ? | <-30 | Fire |
? | <30 | <-30 | Stop |
If you don't have the bluetooth serial module and Nokia N82, you can directly control the tank by just a PC.
The easy way is to read the command (byte) sent from keyboard and directly map the key to it's related command. Please see the following select_command() for details.
int select_command(){ delay_time=25; if(cmd[0] == '8') { // key 8 send_command(FORWARD); }else if(cmd[0] == '2') { // key 2 send_command(BACKWARD); }else if(cmd[0] == '4') { // key 4 send_command(LEFT); }else if(cmd[0] == '6') { // key 6 send_command(RIGHT); }else if(cmd[0] == '7') { // key 7 send_command(LEFT_FORWARD); }else if(cmd[0] == '9') { // key 9 send_command(RIGHT_FORWARD); }else if(cmd[0] == '1') { // key 1 send_command(LEFT_BACKWARD); }else if(cmd[0] == '3') { // key 3 send_command(RIGHT_BACKWARD); }else if(cmd[0] == '5') { // key 5 send_command(STOP); }else if(cmd[0] == 'q') { // key q send_command(CANNONLEFT); }else if(cmd[0] == 'w') { // key w send_command(CANNONRIGHT); }else if(cmd[0] == 'a') { // key a send_command(CANNONUP); }else if(cmd[0] == 's') { // key s send_command(CANNONFIRE); } } |
Find the best rc tank at Nitrotek. From Heng Long & Taigen 1/16 scale bb firing radio controlled tanks, to 1/32 entry level RC tanks, Nitrotek has the biggest selection at the best prices. All our RC tanks come with with free mainland UK delivery. We have an RC tank to suit every budget and skill level.
ReplyDelete