Wireless (nrf24l01) altimeter for RC plane (completed project with code)

Hello,

I have two nRF24l01 units that I would eventually like to use for wireless transmission of data from a BMP085 pressure sensor. However, after spending many hours looking through examples, I feel I am no closer to finding a solution than I was at the beginning. While I have found a few examples that attempt to solve a similar problem, I would like to understand what the code is actually doing. Therefore, I have started with simplest code I can find, and I hope to slowly build it up to what I actually need. So far, the simplest code I was able to find is the following:

Transmit code:

#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>
#include <SPI.h>
int msg[1];
RF24 radio(9,10);
const uint64_t pipe = 0xE8E8F0F0E1LL;
 
void setup(void)
{
  Serial.begin(9600);
  radio.begin();
  radio.openWritingPipe(pipe);
}
 
void loop(void)
{
  
  for (int x=0;x<30;x++)
  {
  msg[0] = x;
  radio.write(msg, 1);
  }
}

Receive code:

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


int msg[1];
RF24 radio(9,10);
const uint64_t pipe = 0xE8E8F0F0E1LL;
int lastmsg = 1;
 
void setup(void)
{
  Serial.begin(9600);
  radio.begin();
  radio.openReadingPipe(1,pipe);
  radio.startListening();

}
 
void loop(void)
{
  if (radio.available())
  {
    bool done = false;  
    while (!done)
    {
      done = radio.read(msg, 2); 
      Serial.println(msg[0]);
    }
  }
}

I have a few questions from this basic example that I feel I need to understand before I can start doing anything meaningful:

Transmit code questions:

  1. I have not previously defined something like “int msg[1]”. Does the [1] mean that msg is an array of 1? If so, why is this necessary. Why can’t I just leave it at “int msg”?
  2. Why is msg first defined as “msg[1]”, but later defined as “msg[0]” in the for loop? Why the switch from 1 to 0?
  3. In the “for (int x=0;x<30;x++)” loop, the largest number I can use is 255. If I try, for example, int x=0;x<300;x++, the loop only actually runs to 255, then rolls over and starts again at 0. Why is this?
  4. Any suggestions on how I could transmit text, such as “Testing”, rather than just sending 1, 2, 3…255?

Receive code questions:

  1. “done” looks like it is defined as a Boolean value. How can it then be set = radio.read(msg, 2)? I thought it could only be “true” or "false.
  2. In “radio.read(msg, 2);” what does the “2” refer to? I have looked at the documentation for this library, and I found the following "read (void buf, uint8_t len)". I can’t understand what this is telling me. Also, what is written in the code “radio.read(msg, 2)” doesn’t seem to match the same format (I would have expected something like radio.read(xy,z)).)

I realize these questions are probably fairly basic, but I have found that the RF24 library is a big jump for beginners like myself. Many of the other libraries I have used have had clear, well documented examples that I could follow, but I find the examples for this library are very difficult for me to understand. I would really appreciate any answers, as well as any clear suggestions as to where I could find more information.

Thanks a lot,

Dustin

Hi Dustin, the "int msg[1]" is really a declaration of array of ints. I don't understand why you would need that and I suggest for the minimal working example you change that to simple "int msg;" and later in the loop just use "msg = x;".

Then the biggest problem with your code is, that the second argument of write as well as read functions is lenght of the message being transmitted in bytes. It has to be the same on both ends and the same as the size of msg. The simple int is 2 bytes long, so use 2 or better yet use "sizeof(msg)".

Another problem I see is that the first parameter of write and read has to be a reference to the variable used. Use the "&" operator for that.

So the transmit line should be:

radio.write(&msg, sizeof(msg) );

and the receiving should be:

done = radio.read(&msg, sizeof(msg)); 
Serial.println(msg);

I think, with these changes you should be able to get the minimal example of one int transmission working. Good luck.

Vasek

Hi Vasek,

Your suggestions were a HUGE help to me. Thank you.

I changed the code as you suggested, and not only does it still work, but I actually find this a lot more understandable. For my actual project, I will need to transfer "float" variables. I tried simply changing "int" to "float" and it worked perfectly, so I think my project will now be fairly smooth going since this is all I really need to do.

However, I do still have two questions:

1) Although not necessary for my current project, I tried changing "int msg;" to "String msg;" in both the send and receive code, in an effort to send text. The code compiled just fine, but when I tried to send "test", what I received was "hÿ»[". If I try sending "Testing" I receive "^Ô.í}ý¬". Why do I get this nonsense when I try to transmit a string?

2) I would still like to know why I was only able to transmit numbers from 0-255 in the original code. Your suggestion fixed this problem perfectly, but I'm not sure why. I guess the original code was somehow limiting the values to 2^8, but I don't see how/why. Any suggestions?

Thanks again Vasek, I really appreciate the help.

Dustin

I tried changing "int msg;" to "String msg;" in both the send and receive code, in an effort to send text.

What, exactly, does a pointer to a String instance do for you?

Use NULL terminated char arrays, also known as strings, instead of pissing away resources on Strings.

Hi,
I don’t think sending strings or chars is convenient with the nRF24l01s:

  • The maximal payload size is 32 bytes, meaning you can not transmit longer strings/chars or you have to concatenate several transmissions, which is a pain.
  • The shorter the payloayd, the greater the chance it will transmit ok. Transmitting anything in human readable form is always less efficient than raw binary.

In the beggining of my playing with nRF24l01, I though transmitting string or even xml encoded messages would be a good idea. Turns out for the reasons above, that it is not. I ended up with only sending binary numbers. I needed to send more numbers at once and somehow distinguis what is what. Instead of the xml/string encoding, I now use structs - similarly as is described in this article: http://www.bajdi.com/sending-structs-with-nrf24l01-modules-and-the-rf24-library/.

Good luck

Hi Vasek,

Thanks for the advice. I appreciate the clarity of your explanations.

I now have my project mostly finished. I am just waiting for a few Arduino pro mini's to arrive in the mail before I start assembling everything into a more permanent arrangement. Based on my breadboard version, I have a really neat wireless altimeter (also transmits temperature) so that I can have real-time data transmitted from my RC plane to my receiver on the ground. Once I have put everything into a more professional configuration, I will post the code/project details.

Thanks again,

Dustin

Based on my breadboard version, I have a really neat wireless altimeter (also transmits temperature) so that I can have real-time data transmitted from my RC plane to my receiver on the ground

Have you tested that you can actually transmit over the distances you need, when the plane is actually flying?

Is the plane electric or gas? Does the RC controller interfere with the nRF's?

Vasek42: I don't think sending strings or chars[] is convenient with the nRF24l01s:

It works fine, and if you need to send a string longer than the maximum message size then it is easy enough to send it in multiple messages and reassemble the original string at the receiver. This is no easier or harder than sending messages one byte at a time over a serial link. You're right that textual encoding schemes tend to be less efficient than binary ones, and XML tends to be very verbose as textual encoding schemes go, and that may not work well in a memory-starved environment, but I don't see RF24 itself as a reason not to use textual encoding.

Hello All,

Just wanted to give a quick update. I have successfully prepared a wireless altimeter for my RC plane. It works perfectly, and the RC plane does not interfere with the signal. I have also tried testing the range of this altimeter, and found at least 1 km range (I actually gave up trying to find the max range after ~ 1 km). I will be posting the full code and images of the completed project within in the next few weeks. Hopefully it will be helpful to others who are interested in a similar project.

Thanks all,

Dustin

That is an impressive range.

Hi Peter,

I know, I was actually quite surprised. Keep in mind, this was perfect line of sight, on a beach in a rural area. I tried a similar experiment in the city, without direct line of sight, and got ~ 220 m. Still better than I had imagined, and overall I am extremely impressed with these wireless units. I also turned the data transfer rate down to 250 kbs which I had read would improve my range.

Dustin

Can you post your final code for both units please ?

Hi Dustin

Are you using additional antennas with the units, or just the PCB track antenna?

Thanks

Ray

Hi Ray,

I am using the nrf24l01 pa lna units, which come with an antenna (I'm not sure if this is the "PCB track antenna" you are referring to. Here is a link to the unit I am using: http://www.ebay.com/itm/NRF24L01-PA-LNA-SMA-Antenna-Wireless-Transceiver-communication-module-2-4G-1100m-/310651702557?pt=LH_DefaultDomain_0&hash=item485448cd1d

I have heard other users state that they weren't achieving the maximum range. I am certainly no expert in this area, and thus I can't imagine that I did anything particularly different to enhance the range, but I have verified a minimum of ~ 1000 m range with this unit.

Please let me know if that answers your question.

Good luck!

Dustin

michaeli: Can you post your final code for both units please ?

Hello,

I would be happy to post the full code. I have gone through and tried to annotate the code to make it as readable as possible. However, can somebody please confirm that this is the best place for me to post the code. Would it be better for me to post the full completed project in another forum, or should I just continue to use this thread. I will also soon be posting a link to a youtube video of the altimeter working with an RC plane.

In addition, I will soon be adding a gps unit to the transmitter to allow real time speed to be reported on my receiver unit. I will update the code when this is complete.

Thanks!

Dustin

Thank You. Looking forward to it. - Scotty

Hi All,

I was hoping to find some time to put together some more detailed information regarding the full project, but it has been over a month now and I just never seem to do it. Therefore, I will just post my code here for you to look at. If you have any questions, please let me know. Be warned, I am certainly not an expert, so what you will be seeing is likely not an example of “good coding”. However, it works, and I have had some great results.

Here is a summary of what it does:

  1. The transmitter, with battery, weighs 32 g.
  2. The range is at least 1 km (direct line of sight).
  3. Reads altitude(in ft since this was the request made by the person I prepared it for).
  4. Temperature in Celsius. I realize it is odd to report altitude in feet and temp. in Celsius. We have a saying in Canada that we are moving towards the metric system inch by inch…
  5. The maximum altitude is recorded and displayed where the temperature is normally displayed (the two values are cycled every second).
  6. The transmitter is attached to the plane, and the plane is placed on the ground. A button on the receiver is then pressed which causes the Pro Mini to record the measured altitude. This altitude is then subtracted from all subsequent readings, thus “zeroing” the altimeter.
  7. My dad (the person this altimeter was made for) has his RC plane equipped with a servo to drop parachutes (and other other things…). Since it is hard to look at the altimeter while flying, I have allowed the user to pre-set an altitude at which an alarm will sound. By flicking a switch, the LCD displays the “triggerHeight” instead of the usual altitude/temperature/max height. When this switch is on, a potentiometer can be used to adjust the “triggerHeight” in increments of 50 ft. See my code for how I did this. When the plane flies above this set point, the alarm sounds, and the RC plane operator can drop his payload. If the operator does not have payload dropping functionality, it is still pretty useful since you can tell when you have reached a certain altitude without looking at the display.
  8. I will soon be adding a gps unit to allow real time display of velocity. I think I will have velocity displayed instead of temperature, since I haven’t found temperature to change much (and it is not very exciting…). I will update this thread when that is complete (still waiting for the gps in the mail…).

My transmitter consists of:
nrf24l01+pa+lna
Arduino Pro Mini (3.3 V)
bmp85
3.7 V LiPo battery.

My receiver contains:

  1. LCD screen (like this): http://www.ebay.ca/itm/1602-16x2-Character-LCD-Display-Module-HD44780-Controller-Blue-Blacklight-/170817946781?pt=LH_DefaultDomain_0&hash=item27c58b049d&_uhb=1
  2. 10 k potentiometers. One to control the LCD contrast, and one to set the “drop height” (see the code for more detail or feel free to ask).
  3. Alarm (sounds when the “drop height” has been reached).
  4. Switch (to allow the drop height to be displayed on the LCD screen vs. the height/temp).
  5. Button (used to zero the altimeter).

Transmitter Code:

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

RF24 radio(9,10);
const uint64_t pipe = 0xE8E8F0F0E1LL;


#include <Wire.h>
#include <Adafruit_BMP085.h>
Adafruit_BMP085 bmp;
float Actual;
float Temp;
float data[2]; // 2 element array to hold temp and altitude
 
void setup(void)
{
  Serial.begin(57600);
  radio.begin();
  radio.openWritingPipe(pipe);
 
  if (!bmp.begin()) 
      {
	Serial.println("BMP085 could not be found");
	while (1) {}
      }  
}
 
void loop(void)
{
  data[0] = bmp.readAltitude();
  data[1] = bmp.readTemperature();
      for (int i = 1; i < 3; i++)
      {
        radio.write(data, sizeof(data));
      }
  delay(500);
}

Receiver code:

#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>
#include <SPI.h>
int Temp;
float Zero = 100;
float Actual;
int height;
unsigned long time;
float data[2]; // 2 element array to hold the temp and pressure data
int MaxHeight;
boolean TempOrMaxHeight = true;
int tarePin = 14;
int setTriggerHeight = 15;
int triggerHeight = 16;
int alarmPin = 17;
int val = 0;
float var;
int beepingHeight = 1000; //I set this at 1000 so that the unit does not start beeping as soon as it is turned on.
int i = 0;

RF24 radio(9,10);
const uint64_t pipe = 0xE8E8F0F0E1LL;

#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 7, 6, 4, 3, 5);

void setup(void)
{
  //Code for radio begins
  Serial.begin(9600);
  radio.begin();
  radio.openReadingPipe(1,pipe);
  radio.startListening();
  //Ends
  
  //Code for LCD begin
  pinMode(tarePin,INPUT);
  pinMode(triggerHeight, INPUT);
  pinMode(setTriggerHeight,INPUT);
  pinMode(alarmPin, OUTPUT);
  
  Zero = 100;  // I just set this to give myself a baseline when testing
  lcd.begin(16, 2);
      
      //This will show up, then scroll off the screen when the unit is first turned on. 
      lcd.print("TwoChain inc.");
      delay(2000);
        for (int positionCounter = 0; positionCounter < 16; positionCounter++) 
        {
        // scroll one position left:
        lcd.scrollDisplayLeft(); 
        // wait a bit:
        delay(300);
        }  
      lcd.clear(); 
}
  //Code for LCD ends
 
void loop(void)
{
  //Code for radio begins
  if (radio.available())
  {
    time = millis(); // Check time when last signal was detected. I do this to help identify if my plane has flown out of range. 
    bool done = false;  
    while (!done)
    {
      done = radio.read(data, sizeof(data));
    }
  // Ends
  
  
  digitalWrite(triggerHeight, HIGH); // I am using the internal pull up resistor to keep this pin high until a button is pressed.
  // Code for LCD
    if (digitalRead(tarePin) == LOW) // Check when tare button has been pressed
    {
      Zero = Actual; // Set "Zero" to the last measured altiitude. It will then be continually subtracted from the measured altitude.
      MaxHeight = 0;
      i = 0; // i is also reset to zero if the "zero" button is pressed.
      lcd.clear();
      lcd.print("Calibrated!");
      delay(2000);
      lcd.clear();
      for (int i = 0; i < 3;i++) // This loop is not necessary, but I find it exciting!
      {
      lcd.print("Ready for ");
      lcd.setCursor(0,1);
      lcd.print("takeoff!!!");
      delay(500);
      lcd.clear();
      delay(500);
      }
    }
    
    while (digitalRead(triggerHeight) == LOW) // If my "set switch" is pressed, the user can set the altitude at which the alarm will trigger.
    {
      lcd.clear();
      val = analogRead(setTriggerHeight);
      var =(1000-val*24)/100; // I am switching between a float and int, and playing with the math, to make the "alarm altitude" increase in 50 f increments.
      beepingHeight = var*50+500;
      lcd.print("Alarm will sound");
      lcd.setCursor(0,1);
      lcd.print("at ");
      lcd.print(beepingHeight);
      lcd.print(" ft");
      delay(500);
     }
    
    
    Actual = data[0]; // Convert the float to an int cause there is no point in reporting the extra decimal places due to variations in pressure
    lcd.clear();
    lcd.print("Altitude ");
    height = (Actual-Zero)*3.28; // Multiplying by 3.28 to convert to ft.
    lcd.print(height);
    lcd.print(" ft");
    
    lcd.setCursor(0,1); // Begin the temp and max height display
    if (height > MaxHeight) // Continually store the maximum altitude to be displayed later on.
    {
     MaxHeight = height; 
    }
        
    if (TempOrMaxHeight == true)
    {
    lcd.print("Temp ");
    lcd.print(data[1]);
    lcd.print(" ");
    lcd.print((char)223);
    lcd.print("C");
    }
    else
    {
    lcd.print("Max Alt. ");
      lcd.print(MaxHeight);
    lcd.print(" ft");
    }
    TempOrMaxHeight = !TempOrMaxHeight; // This is to keep the LCD display continually switching from displaying temp. and max altitude.
    delay(500);
    
  }
  
  else
  {
    if (millis()-time > 5000) // Check how long it has been since last successful signal was detected.
     {
    lcd.clear();
    lcd.print("No Signal!");    
    lcd.setCursor(0,1);
    lcd.print("Out of Range!");
    delay(1000);
    }
  }
  
  if (height > beepingHeight && i < 1) // Sound an alarm if the max altitude is reached.
  {
   tone(alarmPin,750,2000);
   i = 1;
  }
  
  
}

A video of this unit will be posted on youtube. I will update this thread when the video has been uploaded. In the meantime, there are some nice videos of the RC planes dropping their payloads (and some other odd things…) on the same channel: http://www.youtube.com/user/cosmohunter1

Finally, I would like to thank the many people on this forum who helped me with this project. In particular,
terryking228, Crofter, and Vasek42 for providing crucial input early on to help me get my wireless communication working, which was by far the most challenging part of this project for me.

I would be happy to answer any questions (if I can) that you may have.

Dustin

Dustin, This is WAY Cool! You have the main project success tool: Persistence!

Which antennas did you use? How were they mounted..?

Thanks for the great description..

Hi Terry,

Thanks a lot, and thanks for your assistance with my project!

I used the antenna that comes with the NRF24L01+PA+LNA module. For the receiver, I have the antenna sticking out of a project box (you can see it at 6:42 of the video poster here: http://www.youtube.com/watch?v=RNMemNLEfaQ). For the transmitter, I actually just have the Pro Mini, NRF, and BMP085 bundled together in bubble wrap. While it doesn't look aesthetically pleasing, weight is critical on these RC planes, so I figured this was the best option.

I actually just received a gy-neo6mv2 GPS unit in the mail last night, so I will playing around with it this weekend. My goal is to be able to transmit altitude (from the BMP unit) and speed (from the GPS unit). Once this is done, I think my project will be complete. I will post an update once this is done.

Thanks,

Dustin

Very cool. I saw the whole video. You need a IP camera so you can see the video live & see where you're going.