< async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"">

Saturday, June 14, 2014

Controlling a 27mhz remote control car with AD9850 and arduino uno

ADVERTISEMENT
Controlling an RC car with AD9850 dds module and an arduino

If you have an rc car, an ad9850 board (cheap modules available on ebay) and an arduino uno, it is possible to convert them to a computer controlled or programmable rc car. After seeing a post on converting a cheap rc car by jon, to a programmable one by modifying the remote control with an arduino, i decided to make a universal controller which doesn't need to solder or modify the cars electronics. So this approach can add an independent control channel and some fun with decryping and cloning rc remotes of simple toy grade cars (without any encryption)

RC car control signals

A simple rc car uses an amplitude modulated carrier (rf signal) to send the control signal. So most of simple toys uses a 4 pulses of a specific length followed by a multiple of pulses to send a specific command (more of this in a later post). I used gnuradio and funcube dongle to capture the signals and to analyse the control signals. Brandon has a similar project where he used a raspberry pi's pll to directly generate the rf signals and brute force to identify the correct pulse lengths. You can use audacity to measure the pulse lengths after capturing the audio from a radio receiver or sdr tuned to 27mhz signals from the rc remote [read more on decoding the signals using sdr].

AD9850 control signals and speed issues

One of the challenge with this project was the time delays while updating the ad9850 frequency words via arduino. It was overcomed by using the hardware spi for controlling the frequency and usng the PORT commands instead of the digitalwrites. An other small issue was with analogue read delays, which was used to read the keypad inputs. This was solved with an adc update and later i found that all these issues were addressed in a project by Zisis Chiotis which implements an fm modulation on an arduino! (at around 16khz)

connecting ad9850 to arduino spi pins

Using a single analogue pin to assign multiple key functions to control the rc car


Data connections to AD9850. See here for the corresponding pins


  • MOSI = DATA (pin 11 on uno) 
  • CLK = W_CLK (pin 13 on uno) 
  • FU_UD (PIN 6 on uno)  (see port command here)
  • GND = RESET

Source code

//#AD9850 rc remote control  
//blog.riyas.org  
//courtesy to Zisis Chiotis
  
#define PLEN 550  //optimised pulse length adjust this to match your remote:-audacity and sdr
#define PRELEN PLEN*3
#define FREQUENCY 542879
#define FREQUENCY2 0
#define btnRIGHT 0
#define btnUP   1
#define btnDOWN  2
#define btnLEFT  3
#define btnNONE  4
int adc_key_in = 0;
int key = 0;
#include <SPI.h>
uint32_t frequency = FREQUENCY;    //The desired frequency must be divided by 50 ex. 34,7MHz/50 = 694000  
uint32_t tword = frequency * 3436 / 100;    //tuning word calculation  
byte W[5] = { 0, 0, 0, 0, 0 };
const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);    //these are the prescalers for the ADC sampling rate.  
const unsigned char PS_32 = (1 << ADPS2) | (1 << ADPS0);
const unsigned char PS_16 = (1 << ADPS2);
int mic = 512;
void
setup ()
{
  Serial.begin (9600);
  DDRD = DDRD | B01100000;    //  
  PORTD = 0x00;
  // Serial.begin(115200);          // everything can be controlled by serial comm  
  pinMode (A0, INPUT);        // here comes the audio frequency in  
  SPI.setDataMode (SPI_MODE0);    // mode 0 seems to be the right one  
  SPI.setClockDivider (SPI_CLOCK_DIV2);    // this is pretty fast  
  SPI.setBitOrder (LSBFIRST);    // AD9850 wants LSB first  
  SPI.begin ();
  // set up the ADC  
  ADCSRA &= ~PS_128;        // remove bits set by Arduino library  
  ADCSRA |= PS_32;        // setting the sampling rate at 16MHz/32 this makes the analogRead() complete in around 40μs  
}

void
loop ()
{          
  key = read_key ();
  switch (key)            
  {
  case btnRIGHT:
    {
      Serial.println ("FORWARD");
      sendcommand (10);
      break;
    }
  case btnLEFT:
    {
      Serial.println ("RIGHT");
      sendcommand (34);
      break;
    }
  case btnUP:
    {
      Serial.println ("BACKWARD");
      sendcommand (40);
      break;
    }
  case btnDOWN:
    {
      Serial.println ("LEFT");
      sendcommand (28);
      break;
    }
  case btnNONE:
    {
      //Serial.println("NONE");   
      break;
    }
  }
}

void
preample (int len)
{
  int i;
  for (i = 0; i < len; i++) {
    setfreq (FREQUENCY);
    delayMicroseconds (PRELEN);
    setfreq (FREQUENCY2);
    delayMicroseconds (PLEN);
  }
}

void
command (int len)
{
  int i;
  for (i = 0; i < len; i++) {
    setfreq (FREQUENCY);
    delayMicroseconds (PLEN);
    setfreq (FREQUENCY2);
    delayMicroseconds (PLEN);
  }
}

void
setfreq (uint32_t frequency)
{
  tword = frequency * 1718;    //calculating the tuning word for AD9850  
  W[0] = (byte) tword;
  W[1] = (byte) (tword >> 8);
  W[2] = (byte) (tword >> 16);    //converting it to bytes  
  W[3] = (byte) (tword >> 24);
  W[4] = 0;            //phase zero  
  //start sending with spi interface  
  PORTD = B01000000;
  PORTD = 0x00;            //pulse FU_UD  
  for (int j = 0; j < 5; j++) {
    SPI.transfer (W[j]);    //send the word  
  }
  PORTD = B01000000;
  PORTD = 0x00;            //pulse FU_UD  
}

int
read_key ()
{
  adc_key_in = analogRead (0);
  if (adc_key_in > 600)
    return btnNONE;
  if (adc_key_in > 500)
    return btnRIGHT;
  if (adc_key_in > 450)
    return btnUP;
  if (adc_key_in > 400)
    return btnDOWN;
  if (adc_key_in < 400)
    return btnLEFT;
  return btnNONE;
}

void
sendcommand (int n)
{
  for (int i = 0; i < 5; i++) {
    preample (4);
    command (n);
  }
}
end

10 comments:

  1. What a wonderful project! Well done and thanks for the reference! Keep up with good ideas like this!

    ReplyDelete
  2. hi,
    Thanks for sharing this project. I've try it with three 27MHZ cars but there is no effects. Is there some value to adjust please?

    Thanks
    Arnaud

    ReplyDelete
  3. How does the AD9850 send the signal? I don't see an antenna in the video, so where is the RF signal coming from?

    ReplyDelete
    Replies
    1. A good antenna will improve the range. I just used a piece of wire hooked to the rf/ clock output of the dds chip

      Delete
    2. Which pin is that? Also which pins need to be connected to the 5V power?

      Delete
  4. You wrote pin 7 for FU_UD, but you are mapping to Arduino digital pins 5 & 6 in setup than later you are mapping only pin 6. I think if you correct the type you can save people some time because its not clear using port registers that its wrong.

    ReplyDelete
    Replies
    1. Thanks Michael. Corrected the typo and you are right as it takes a while to sort it out due to port manipulation.

      Delete
  5. I have an RC controlled Protocol Helicopter which will no longer turn on (same thing that has happened to my RC 27 MHz train which is what brought me here). I believe the pot which is also the on/off switch has broken inside and I have not been able to locate a replacement. If you are looking for another project please consider the Helicopter my Grandson would love to see fly for 4 minutes.

    ReplyDelete
  6. I know that you done this project long time ago was interested you think it can be realized with LTC6904 ?

    ReplyDelete