RC Receiver for Quadcopter

I’m trying to build an Arduino transmitter and receiver for a quadcopter, using nrf24l01 modules. Now, I have found quite a bit of information and examples, but I can’t any of them to work.

So, in order to find the problem, I have been trying the simplest setup first - which is connecting an arduino (pro micro) with a potentiometer to the flight controller (SP Racing F3) and try to send a PPM signal that the flight controller understands. But even this would not work.

I have been using the following code. What is wrong? How do I know if the frame and pulse length are correct?

#define INPUT_FREQUENCY 50

#define CHANNEL_MAX  2000
#define CHANNEL_MIN 1000
#define CHANNEL_MID 1500
#define CHANNEL_NUMBER 7

#define ANALOG_INPUTS 4
#define DIGITAL_INPUTS 2

#define FRAME_LENGTH 22500  //set the PPM frame length in microseconds (1ms = 1000µs)
#define PULSE_LENGTH 300  //set the pulse length
#define onState 1  //set polarity of the pulses: 1 is positive, 0 is negative
#define sigPin 3  //set PPM signal output pin on the arduino

int analogInputPins[ANALOG_INPUTS] = {A2, A3, A1, A0};
int digitalInputPins[DIGITAL_INPUTS] = {12, 11};
int switchPins[1] = {10};

int channel_input[ANALOG_INPUTS] = {};
int channel_center[ANALOG_INPUTS] = {};
int channel_min[ANALOG_INPUTS] = {};
int channel_max[ANALOG_INPUTS] = {};

int ppm[CHANNEL_NUMBER];

int prevSwitch = LOW;
byte currentState = LOW;

void setup() {
  delay(300);
  Serial.begin(9600);
  
  //initiallize default ppm values
  for (int i=0; i<CHANNEL_NUMBER; i++){
      ppm[i]= CHANNEL_MID;
  }

  ppm[CHANNEL_NUMBER - 1] = 1000;

  for (int i = 0; i < sizeof(digitalInputPins); i++) {
    pinMode(digitalInputPins[i], INPUT_PULLUP);
  }

  for (int i = 0; i < ANALOG_INPUTS; i++) {
    channel_center[i] = analogRead(analogInputPins[i]);
  }

  pinMode(switchPins[0], INPUT_PULLUP);

  pinMode(sigPin, OUTPUT);
  digitalWrite(sigPin, !onState);  //set the PPM signal pin to the default state (off)

  cli();
  TCCR1A = 0; // set entire TCCR1 register to 0
  TCCR1B = 0;
  
  OCR1A = 100;  // compare match register, change this
  TCCR1B |= (1 << WGM12);  // turn on CTC mode
  TCCR1B |= (1 << CS11);  // 8 prescaler: 0,5 microseconds at 16mhz
  TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
  sei();
}

void process_analog_channel(int channel) {
  channel_input[channel] = analogRead(analogInputPins[channel]);

  int diff = channel_center[channel] - channel_input[channel];

  if (diff > channel_max[channel]) {
    channel_max[channel] = diff;
  }

  if (diff < channel_min[channel]) {
    channel_min[channel] = diff;
  }

  if (diff > 0) {
    ppm[channel] = map(diff, 0, channel_max[channel], CHANNEL_MID, CHANNEL_MAX);
  } else {
    ppm[channel] = map(diff, channel_min[channel], 0, CHANNEL_MIN, CHANNEL_MID);
  }
  
}

void loop() {
  
  for (int i = 0; i < ANALOG_INPUTS; i++) {
    process_analog_channel(i);
  }

  for (int i = 0; i < DIGITAL_INPUTS; i++) {
    if (digitalRead(digitalInputPins[i]) == LOW) {
      ppm[i + ANALOG_INPUTS] = 2000;
    } else {
      ppm[i + ANALOG_INPUTS] = 1000;
    }
  }

  int swPin = digitalRead(switchPins[0]);

  if (swPin == LOW && prevSwitch == HIGH) {
    currentState = !currentState;
  }

  if (currentState == HIGH) {
    ppm[CHANNEL_NUMBER - 1] = 2000;
  } else {
    ppm[CHANNEL_NUMBER - 1] = 1000;
  }

  prevSwitch = swPin;
  
  delay(1000 / INPUT_FREQUENCY);

}

ISR(TIMER1_COMPA_vect){  //leave this alone
  static boolean state = true;
  
  TCNT1 = 0;
  
  if (state) {  //start pulse
    digitalWrite(sigPin, onState);
    OCR1A = PULSE_LENGTH * 2;
    state = false;
  } else{  //end pulse and calculate when to start the next pulse
    static byte cur_chan_numb;
    static unsigned int calc_rest;
  
    digitalWrite(sigPin, !onState);
    state = true;

    if(cur_chan_numb >= CHANNEL_NUMBER){
      cur_chan_numb = 0;
      calc_rest = calc_rest + PULSE_LENGTH;// 
      OCR1A = (FRAME_LENGTH - calc_rest) * 2;
      calc_rest = 0;
    }
    else{
      OCR1A = (ppm[cur_chan_numb] - PULSE_LENGTH) * 2;
      calc_rest = calc_rest + ppm[cur_chan_numb];
      cur_chan_numb++;
    }     
  }
}

I am confused. Doesn't the nrf24l01 allow you to send data directly? Why try to create PPM data? Just send the data values to the receiver arduino like this example with joysticks:

https://arduino-info.wikispaces.com/Nrf24L01-2.4GHz-ExampleSketches#bm1

I haven't made myself clear then. First, I want to create a simple test setup without any NRF24L01 modules. Just an arduino, generating a PPM signal that can be read by the flight controller. But none of the sketches seem to work, as the flight controller does not register any PPM input.

So my question really is, whether someone has a working sketch that generates a PPM signal that is recognized by a cleanflight flight controller?

Ah HAH! This is the point at which you provide technical specs on this controller you have. I took a guess and read this manual for a cleanflight controller.

I see that it takes many forms of input. If all else fails, using standard servo communication should be supported. But I see the desire to dedicate a single pin to the PPM communication.

I did just purchase some RC receivers with PPM out so that I could use a single pin for multiple channel RC input. But I have not created the same signal to feed another device.

It is that controller indeed. PWM input does work, using the servo library. But I'd rather use the single pin solution via PPM as you mentioned.

So, about 5 items down on my to-do list is to tackle decoding the PPM so that RC receiver input can be handled on one pin.
Here is a guy who claims to have generated a PPM signal.
https://code.google.com/archive/p/generate-ppm-signal/downloads
I have no clue if it works. I have no devices that consume such a signal, only generate it.

More interesting reads. http://singlechannellersreunited.co.uk/phpbb3/viewtopic.php?f=9&t=716

Be careful, if you are using chinese bad made nrf you wouldn t be able to have the same “sensibility” that you have right now with whatever rc you are using

These are definitely chinese made. It will be interesting to see what resolution I get.

If you don t want to fly with that it is ok, i m interested to see what do you manage to get, so after some test share the results! :D

If you want to go a step further you should try 5.8ghz but maybe your fpv gear is at the same frequency

@reynard80
I did order parts to make the PPM monitor discussed in the singlechannelersreunited link I posted.
I will use this to look at the PPM output of my receiver, but you could use it to test the output of your PPM generator.
I’ll post back here with what I find.
Parts are on a slow boat from china (literally) so it will be several weeks before I can start.
PPMdisplay.jpg