Transmitting, read and writing PPM signals with a nrf24l01 [Solved]

Hi people,

i have made a drone, but using an Arduino as a flight controller. Whatever, I like to control my Drone using the PC and 2 nrf24l01, one connected to an Arduino (connected to PC) and the other one in Drone.

searching for it I found a tutorial to make you own transmitter and receiver in this video:

Then I take it as bases of my project.

I can send the information from the Arduino (transmitter) to the Arduino in drone, but I don’t know how I can read the information and transform it into an output PPM signal.
I do something, but it doesn’t work.

This is my transmitting code, that should read the values from each of the 4 channels and transmitting to the other nrf:

/*  
A basic 4 channel transmitter using the nRF24L01 module.
*/

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

const uint64_t pipeOut = 0xE8E8F0F0E1LL;

String inString = "";    // string to hold input
int cd,num,pcd,rpw,vall=0;
int val01,val2,val3,val4;

String ch ="";
RF24 radio(9, 10);

// The sizeof this struct should not exceed 32 bytes
struct MyData {
  byte throttle;
  byte yaw;
  byte pitch;
  byte roll;
};

MyData data;

void resetData() 
{
  data.throttle = 0;
  data.yaw = 127;
  data.pitch = 127;
  data.roll = 127;
}

void setup()
{
  radio.begin();
  radio.setAutoAck(false);
  radio.setDataRate(RF24_250KBPS);

  radio.openWritingPipe(pipeOut);

  resetData();

Serial.begin(115200);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  // send an intro:
  Serial.println("Ready");
}



void loop()
{
 readint();
calcRPY();
cal();
}




void readint() {
  // Read serial input:
  while (Serial.available() > 0) {
    int inChar = Serial.read();
   
    if (isDigit(inChar)) {    inString += (char)inChar;   } 
    
    if (inChar == '\n') {   cd=inString.toInt();      
      inString = "";
    }}}




void calcRPY()
{
  if (cd!=pcd)
 
  if(cd>0 && cd<5)              { rpw=cd; }
  else if (cd>5 && cd<1025)     { vall=cd; }  
pcd=cd;    
  }





  
void cal()
{

switch (cd)
{
 case 1:
  val01=vall;
 data.throttle = map( val01, 0, 1023, 1000, 2000 );Serial.print("throttle=" ); Serial.print(val01);Serial.print(" yaw=" ); Serial.print(val2);Serial.print(" pitch=" );Serial.print(val3); Serial.print(" roll=" );Serial.println(val4); 
 break;

case 2:
 val2=vall;
data.yaw      = map( val2,  0, 1023, 1000, 2000);Serial.print("throttle=" ); Serial.print(val01);Serial.print(" yaw=" ); Serial.print(val2);Serial.print(" pitch=" );Serial.print(val3); Serial.print(" roll=" );Serial.println(val4);    
 break;
 
case 3:
 val3=vall;
 data.pitch    = map( val3, 0, 1023, 1000, 2000);Serial.print("throttle=" ); Serial.print(val01);Serial.print(" yaw=" ); Serial.print(val2);Serial.print(" pitch=" );Serial.print(val3); Serial.print(" roll=" );Serial.println(val4);  
   break;

case 4:
val4=vall;
  data.roll     = map( val4, 0, 1023, 1000, 2000 );Serial.print("throttle=" ); Serial.print(val01);Serial.print(" yaw=" ); Serial.print(val2);Serial.print(" pitch=" );Serial.print(val3); Serial.print(" roll=" );Serial.println(val4);    
    break;

 
  }

  radio.write(&data, sizeof(MyData));
  
  }

And this is my Receiver code, but it doesn’t work, actually, i want to know what should I do to white the
PPM information on PINS 5,4, 7 and 8 that I need to contro my drone.

/*  
A basic receiver using the nRF24L01 module to receive 4 channels and convert them to PPM.
 */

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

////////////////////// PPM CONFIGURATION//////////////////////////
#define channel_number 4  //set the number of channels
#define PPM_FrLen 27000  //set the PPM frame length in microseconds (1ms = 1000µs)
#define PPM_PulseLen 400  //set the pulse length
//////////////////////////////////////////////////////////////////

int ppm[channel_number];

const uint64_t pipeIn =  0xE8E8F0F0E1LL;

RF24 radio(9, 10);

// The size of this struct should not exceed 32 bytes
struct MyData {
  byte throttle;
  byte yaw;
  byte pitch;
  byte roll;
};

MyData data;

void setPPMValuesFromData()
{
  ppm[0] = map(data.throttle, 0, 255, 1000, 2000);
  ppm[1] = map(data.yaw,      0, 255, 1000, 2000);
  ppm[2] = map(data.pitch,    0, 255, 1000, 2000);
  ppm[3] = map(data.roll,     0, 255, 1000, 2000);  
  ppm[4] = 1000;
  ppm[5] = 1000;
}

void resetData() 
{
  // 'safe' values to use when no radio input is detected
  data.throttle = 0;
  data.yaw = 127;
  data.pitch = 127;
  data.roll = 127;
  
  setPPMValuesFromData();
}



/**************************************************/


void setup()
{  
  resetData();


  // Set up radio module
  radio.begin();
  radio.setDataRate(RF24_250KBPS); // Both endpoints must have this set the same
  radio.setAutoAck(false);

  radio.openReadingPipe(1,pipeIn);
  radio.startListening();
}

/**************************************************/

unsigned long lastRecvTime = 0;

void recvData()
{  
  while ( radio.available() ) {        
    radio.read(&data, sizeof(MyData));
    
    CH1=data.throttle 
    CH2=data.yaw  
    CH3=data.pitch 
    CH4=data.roll
    
    lastRecvTime = millis();
  }
}

/**************************************************/

void loop()
{
  recvData();
  
  Serial.print("CH1:"); Serial.print(rc_values[RC_CH1]); Serial.print("\t");
  Serial.print("CH2:"); Serial.print(rc_values[RC_CH2]); Serial.print("\t");
  Serial.print("CH3:"); Serial.print(rc_values[RC_CH3]); Serial.print("\t");
  Serial.print("CH4:"); Serial.println(rc_values[RC_CH4]);
  unsigned long now = millis();
  if ( now - lastRecvTime > 1000 ) {
    // signal lost?
    resetData();
  }
  
  setPPMValuesFromData();
}

/**************************************************/

//#error This line is here to intentionally cause a compile error. Please make sure you set clockMultiplier below as appropriate, then delete this line.
#define clockMultiplier 1 // set this to 2 if you are using a 16MHz Arduino, leave as 1 for an 8MHz arduino

thanks, everyone.
I pretend poste here the project when it’s finished.

donraf:
I can send the information from the Arduino (transmitter) to the Arduino in drone, but I don't know how I can read the information and transform it into an output PPM signal.
I do something, but it doesn't work.

what should I do to white the PPM information on PINS 5,4, 7 and 8 that I need to contro my drone.

I am assuming that the data is received correctly.

It seems to me the data values are being put into the array ppm but there is no code that uses the data.

And I see no code that refers to pins 5, 4, 7 and 8 - are you missing part of the program?

I suspect that there should be code in setup() like

throttleServo.attach(5)

and then code in loop() like

throttleServo.writeMicroseconds(ppm[0])

By the way the code in your Tx program is unreadable because so many instructions are crammed into every line. Re-organize it so there is one instruction of each line like in your RX program. Use the AutoFormat tool.

...R

Hi Robin2,

thanks for the answer. But could you please show it in the code? I think I don’t really understand. :confused:
I try to define the ppm output pins from the Arduino like this:

#define sigPin_t 5  //set PPM signal output pin on the arduino
#define sigPin_y 6  //set PPM signal output pin on the arduino
#define sigPin_p 7  //set PPM signal output pin on the arduino
#define sigPin_r 8  //set PPM signal output pin on the arduino

pinMode(sigPin_t, OUTPUT);
  digitalWrite(sigPin_t, 0);  //set the PPM signal pin to the default state (off)
  
  pinMode(sigPin_y, OUTPUT);
  digitalWrite(sigPin_y, 0);  //set the PPM signal pin to the default state (off)
  
  pinMode(sigPin_p, OUTPUT);
  digitalWrite(sigPin_p, 0);  //set the PPM signal pin to the default state (off)
  
  pinMode(sigPin_r, OUTPUT);
  digitalWrite(sigPin_r, 0);  //set the PPM signal pin to the default state (off)

and write the outputs signals, to the pins 4, 5, 6 and 7 like this:

   recvData();
  
    CH1=data.throttle;
    CH2=data.yaw;
    CH3=data.pitch;
    CH4=data.roll;

  Serial.print("CH1:"); Serial.print(ppm[0]); Serial.print("\t");
  Serial.print("CH2:"); Serial.print(ppm[1]); Serial.print("\t");
  Serial.print("CH3:"); Serial.print(ppm[2]); Serial.print("\t");
  Serial.print("CH4:"); Serial.println(ppm[3]);

  
  sigPin_t.writeMicroseconds(ppm[0])
  sigPin_y.writeMicroseconds(ppm[1])
  sigPin_p.writeMicroseconds(ppm[2])
  sigPin_r.writeMicroseconds(ppm[3])

but then comes one error:
“exit status 1
request for member ‘writeMicroseconds’ in ‘5’, which is of non-class type ‘int’”

To test if my Arduino receiver the signals data from the other one Arduino I testes using the serial.print, according to the code, and it works very well, according I can show here:

Inputs signals from the transmitter Arduino:

throttle=500 yaw=6 pitch=500 roll=6
throttle=500 yaw=6 pitch=500 roll=6
throttle=500 yaw=6 pitch=500 roll=6

Output signals from the receiver Arduino:

CH1:1486 CH2:1003 CH3:1486 CH4:1003
CH1:1486 CH2:1003 CH3:1486 CH4:1003
CH1:1486 CH2:1003 CH3:1486 CH4:1003

/\ I try different types of signals and it works well for everyone.

the complete Receiver code is expolsed here (perhaps you can find some lines that don’t really are needed. conform I said before, I use one existing code and try to adapt it to my problem). :

 /*  
A basic receiver using the nRF24L01 module to receive 4 channels and convert them to PPM.
 */

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

////////////////////// PPM CONFIGURATION//////////////////////////
#define channel_number 6  //set the number of channels

#define sigPin_t 5  //set PPM signal output pin on the arduino
#define sigPin_y 6  //set PPM signal output pin on the arduino
#define sigPin_p 7  //set PPM signal output pin on the arduino
#define sigPin_r 8  //set PPM signal output pin on the arduino

#define PPM_FrLen 27000  //set the PPM frame length in microseconds (1ms = 1000µs)
#define PPM_PulseLen 400  //set the pulse length
//////////////////////////////////////////////////////////////////

int CH1, CH2, CH3, CH4;

int throttle, yaw, pitch, roll;

int ppm[channel_number];

const uint64_t pipeIn =  0xE8E8F0F0E1LL;

RF24 radio(9, 10);

// The sizeof this struct should not exceed 32 bytes
struct MyData {
  byte throttle;
  byte yaw;
  byte pitch;
  byte roll;
};

MyData data;

void setPPMValuesFromData()
{
  ppm[0] = map(data.throttle, 0, 255, 1000, 2000);
  ppm[1] = map(data.yaw,      0, 255, 1000, 2000);
  ppm[2] = map(data.pitch,    0, 255, 1000, 2000);
  ppm[3] = map(data.roll,     0, 255, 1000, 2000);  
  ppm[4] = 1000;
  ppm[5] = 1000;
}

void resetData() 
{
  // 'safe' values to use when no radio input is detected
  data.throttle = 0;
  data.yaw = 127;
  data.pitch = 127;
  data.roll = 127;
  
  setPPMValuesFromData();
}



/**************************************************/

void setupPPM() {
  pinMode(sigPin_t, OUTPUT);
  digitalWrite(sigPin_t, 0);  //set the PPM signal pin to the default state (off)
  
  pinMode(sigPin_y, OUTPUT);
  digitalWrite(sigPin_y, 0);  //set the PPM signal pin to the default state (off)
  
  pinMode(sigPin_p, OUTPUT);
  digitalWrite(sigPin_p, 0);  //set the PPM signal pin to the default state (off)
  
  pinMode(sigPin_r, OUTPUT);
  digitalWrite(sigPin_r, 0);  //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 (not very important, sets the timeout for the first interrupt)
  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 setup()
{  
  resetData();
  setupPPM();
  
  // Set up radio module
  radio.begin();
  radio.setDataRate(RF24_250KBPS); // Both endpoints must have this set the same
  radio.setAutoAck(false);
  
Serial.begin(115200);

  radio.openReadingPipe(1,pipeIn);
  radio.startListening();

}

/**************************************************/

unsigned long lastRecvTime = 0;

void recvData()
{  
  while ( radio.available() ) {        
    radio.read(&data, sizeof(MyData));
    lastRecvTime = millis();
  }
}

/**************************************************/

void loop()
{
  recvData();
  
    CH1=data.throttle;
    CH2=data.yaw;
    CH3=data.pitch;
    CH4=data.roll;

  Serial.print("CH1:"); Serial.print(ppm[0]); Serial.print("\t");
  Serial.print("CH2:"); Serial.print(ppm[1]); Serial.print("\t");
  Serial.print("CH3:"); Serial.print(ppm[2]); Serial.print("\t");
  Serial.print("CH4:"); Serial.println(ppm[3]);

  
  sigPin_t.writeMicroseconds(ppm[0])
  sigPin_y.writeMicroseconds(ppm[1])
  sigPin_p.writeMicroseconds(ppm[2])
  sigPin_r.writeMicroseconds(ppm[3])

  unsigned long now = millis();
  if ( now - lastRecvTime > 1000 ) {
    // signal lost?
    resetData();
  }
  
  setPPMValuesFromData();
}

Is this a program you got from somewhere or is it a program you wrote entirely by yourself? Or maybe it is a program you downloaded and modified?

It seems to be part one thing and part another with incompatible parts.

For example you call recvData() but you never call setPPMValuesFromData() to transfer the received data.

And this makes no sense at all

sigPin_t.writeMicroseconds(ppm[0])

writeMicroseconds() is a function from the Servo library and you are trying to apply it to an I/O pin

At the top of your program you need

#include <Servo.h>

Servo throttleServo;
Servo yawServo;
// etc

and in setup() you need

throttleServo.attach(sigPin_t);
yawServo.attach(sigPin_y);
// etc

and then, in place of

sigPin_t.writeMicroseconds(ppm[0])

you can do

throttleServo.writeMicroseconds(ppm[0])

Read up about the Servo library in the Reference section

…R

PS. sigPin_t is an awful name. Why not just call it throttleServoPin.

Robin2,

it works great, thank you very much.