Pro Mini + NRF24 quadcopter receiver Blinking LED

Hi. I build a arduino pro mini based PPM receiver and transmitter to control my quadcopter. They are copy’s of iforce2d cheap-ass projects - Cheap-ass quadcopter build Part 12 - Arduino conversion of HK transmitter - YouTube
I added a LED to show me when RX and TX are connected and I got it working :slight_smile: but I would like to put it blinking when not connected and solid when they are but the blinking part is over my head, because I need to use millis and not delay. So any help would be appreciated :slight_smile: Thanks.

I fitted it to old Hitec 35mhz FM receiver case:

/*  
A basic receiver using the nRF24L01 module to receive 10 channels and convert them to PPM.
 */
 
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
 
////////////////////// PPM CONFIGURATION//////////////////////////
#define channel_number 10  //set the number of channels
#define sigPin 2  //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
//////////////////////////////////////////////////////////////////
 
const int ledPin =  3; // Led pin 3
 
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;
  byte dial1;
  byte dial2;
  byte switches; // bitflag
};
 
MyData data;
 
void resetData()
{
  // 'safe' values to use when no radio input is detected
  data.throttle = 0;
  data.yaw = 127;
  data.pitch = 127;
  data.roll = 127;
  data.dial1 = 0;
  data.dial2 = 0;
  data.switches = 0;
 
  setPPMValuesFromData();
}
 
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] = map(data.dial1,    0, 255, 1000, 2000);
  ppm[5] = map(data.dial2,    0, 255, 1000, 2000);  
  ppm[6] = data.switches & 0x1 ? 2000 : 1000;
  ppm[7] = data.switches & 0x2 ? 2000 : 1000;
  ppm[8] = data.switches & 0x4 ? 2000 : 1000;
  ppm[9] = data.switches & 0x8 ? 2000 : 1000;
}
 
/**************************************************/
 
void setupPPM() {
  pinMode(sigPin, OUTPUT);
  digitalWrite(sigPin, 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()
{
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  resetData();
  setupPPM();
 
  // 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));
    lastRecvTime = millis();
    digitalWrite(ledPin, HIGH); // Turn status LED on
  }
}
 
/**************************************************/
 
void loop()
{
  recvData();
 
  unsigned long now = millis();
  if ( now - lastRecvTime > 1000 ) {
    // signal lost?
    resetData();
    digitalWrite(ledPin, LOW); // Turn status LED off
  }
 
  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 2 // set this to 2 if you are using a 16MHz arduino, leave as 1 for an 8MHz arduino
 
ISR(TIMER1_COMPA_vect){
  static boolean state = true;
 
  TCNT1 = 0;
 
  if ( state ) {
    //end pulse
    PORTD = PORTD & ~B00000100; // turn pin 2 off. Could also use: digitalWrite(sigPin,0)
    OCR1A = PPM_PulseLen * clockMultiplier;
    state = false;
  }
  else {
    //start pulse
    static byte cur_chan_numb;
    static unsigned int calc_rest;
 
    PORTD = PORTD | B00000100; // turn pin 2 on. Could also use: digitalWrite(sigPin,1)
    state = true;
 
    if(cur_chan_numb >= channel_number) {
      cur_chan_numb = 0;
      calc_rest += PPM_PulseLen;
      OCR1A = (PPM_FrLen - calc_rest) * clockMultiplier;
      calc_rest = 0;
    }
    else {
      OCR1A = (ppm[cur_chan_numb] - PPM_PulseLen) * clockMultiplier;
      calc_rest += ppm[cur_chan_numb];
      cur_chan_numb++;
    }    
  }
}

Ok, I played a bit with the code and I almost get the millis() but still something wrong :slight_smile: Something wrong with the logic… I guess.

void loop()
{
  recvData();

  unsigned long now = millis();
  if ( now - lastRecvTime > 1000 ) {
    // signal lost?
    resetData();
 //------------------------------------------------------  
    digitalWrite(ledPin, !digitalRead(ledPin));
    now = lastBlinkTime;
    lastBlinkTime += blinkInterval;
 //-------------------------------------------------------
  }
  setPPMValuesFromData();
}

Full code -

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

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

////////////////////// PPM CONFIGURATION//////////////////////////
#define channel_number 10  //set the number of channels
#define sigPin 2  //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
//////////////////////////////////////////////////////////////////

const int ledPin =  3; // Led pin 3
const int blinkInterval = 750;
byte ledState = LOW;
unsigned long lastBlinkTime = 0;

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;
  byte dial1;
  byte dial2;
  byte switches; // bitflag
};

MyData data;

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

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] = map(data.dial1,    0, 255, 1000, 2000);
  ppm[5] = map(data.dial2,    0, 255, 1000, 2000);  
  ppm[6] = data.switches & 0x1 ? 2000 : 1000;
  ppm[7] = data.switches & 0x2 ? 2000 : 1000;
  ppm[8] = data.switches & 0x4 ? 2000 : 1000;
  ppm[9] = data.switches & 0x8 ? 2000 : 1000;
}

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

void setupPPM() {
  pinMode(sigPin, OUTPUT);
  digitalWrite(sigPin, 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()
{
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW); 
  resetData();
  setupPPM();
  
  // 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));
    lastRecvTime = millis();
    digitalWrite(ledPin, HIGH); // Turn LED on
  }
}

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

void loop()
{
  recvData();

  unsigned long now = millis();
  if ( now - lastRecvTime > 1000 ) {
    // signal lost?
    resetData();
    digitalWrite(ledPin, !digitalRead(ledPin));
    now = lastBlinkTime;
    lastBlinkTime += blinkInterval;
  }
  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 2 // set this to 2 if you are using a 16MHz arduino, leave as 1 for an 8MHz arduino

ISR(TIMER1_COMPA_vect){
  static boolean state = true;

  TCNT1 = 0;

  if ( state ) {
    //end pulse
    PORTD = PORTD & ~B00000100; // turn pin 2 off. Could also use: digitalWrite(sigPin,0)
    OCR1A = PPM_PulseLen * clockMultiplier;
    state = false;
  }
  else {
    //start pulse
    static byte cur_chan_numb;
    static unsigned int calc_rest;

    PORTD = PORTD | B00000100; // turn pin 2 on. Could also use: digitalWrite(sigPin,1)
    state = true;

    if(cur_chan_numb >= channel_number) {
      cur_chan_numb = 0;
      calc_rest += PPM_PulseLen;
      OCR1A = (PPM_FrLen - calc_rest) * clockMultiplier;
      calc_rest = 0;
    }
    else {
      OCR1A = (ppm[cur_chan_numb] - PPM_PulseLen) * clockMultiplier;
      calc_rest += ppm[cur_chan_numb];
      cur_chan_numb++;
    }     
  }
}

Ok, got it working :slight_smile:

last code receiver v2 -

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

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

////////////////////// PPM CONFIGURATION//////////////////////////
#define channel_number 10  //set the number of channels
#define sigPin 2  //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
//////////////////////////////////////////////////////////////////

const int ledPin =  3; // Led pin 3
unsigned long currentTime;
unsigned long loopTime;

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;
  byte dial1;
  byte dial2;
  byte switches; // bitflag
};

MyData data;

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

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] = map(data.dial1,    0, 255, 1000, 2000);
  ppm[5] = map(data.dial2,    0, 255, 1000, 2000);  
  ppm[6] = data.switches & 0x1 ? 2000 : 1000;
  ppm[7] = data.switches & 0x2 ? 2000 : 1000;
  ppm[8] = data.switches & 0x4 ? 2000 : 1000;
  ppm[9] = data.switches & 0x8 ? 2000 : 1000;
}

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

void setupPPM() {
  pinMode(sigPin, OUTPUT);
  digitalWrite(sigPin, 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()
{
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW); 
  resetData();
  setupPPM();
  
  // 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));
    lastRecvTime = millis();
    digitalWrite(ledPin, HIGH); // Turn LED on
  }
}

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

void loop()
{
  recvData();

  unsigned long now = millis();
  if ( now - lastRecvTime > 1000 ) {
    // signal lost?
    resetData();
  currentTime = millis();
  if(currentTime >= (loopTime + 500)){  
    digitalWrite(ledPin, !digitalRead(ledPin));   // toggles the LED on/off
    loopTime = currentTime;
  }
 }
  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 2 // set this to 2 if you are using a 16MHz arduino, leave as 1 for an 8MHz arduino

ISR(TIMER1_COMPA_vect){
  static boolean state = true;

  TCNT1 = 0;

  if ( state ) {
    //end pulse
    PORTD = PORTD & ~B00000100; // turn pin 2 off. Could also use: digitalWrite(sigPin,0)
    OCR1A = PPM_PulseLen * clockMultiplier;
    state = false;
  }
  else {
    //start pulse
    static byte cur_chan_numb;
    static unsigned int calc_rest;

    PORTD = PORTD | B00000100; // turn pin 2 on. Could also use: digitalWrite(sigPin,1)
    state = true;

    if(cur_chan_numb >= channel_number) {
      cur_chan_numb = 0;
      calc_rest += PPM_PulseLen;
      OCR1A = (PPM_FrLen - calc_rest) * clockMultiplier;
      calc_rest = 0;
    }
    else {
      OCR1A = (ppm[cur_chan_numb] - PPM_PulseLen) * clockMultiplier;
      calc_rest += ppm[cur_chan_numb];
      cur_chan_numb++;
    }     
  }
}

can you send me the code for transmitter as well??
my email:theoneyouknow018@gmail.com