Frequency hopping experiments on the NRF24

Any old or new electronic projects on the go
Post Reply
User avatar
Phil_G
Posts: 597
Joined: 15 Feb 2018, 23:32
Contact:

Frequency hopping experiments on the NRF24

Post by Phil_G »

Edit: 26th July, 2022 - the thread has become quite long so to help potential builders, I've started a more concise "how to". Its far from complete as yet but I thought I'd post it then update as I work on it:
homebrew-nrf.pdf
(6.43 MiB) Downloaded 263 times


This thread documents the development of a simple but functional homebrew NRF FHSS set.
Its a long story and this first post is just a summary, file repository and a bit of an index ;)

If you want to skip the story, attached to this post is a zip containing all the up-to-date code, diagrams & photos in both Proportional (with S/C emualtion) and pure SIngle-Channel button-transmitter formats. Many, many of these sets have been made and flown now, all working well. My own sets have been flown constantly in a variety of models since this thread was started - reliability has been 100%.


The NRF24 is quite a broad-band device, and Pete has posted a reminder that in the UK only NRF channels 1 to 82 are actually within our official 2.4ghz allocation. If you'd prefer to be 'legal' then limit your choice of channels to between 1 and 82 in the UK and for other countries check your allocations! Physically the project works fine using any of the NRF channels, this is purely a 'conformance' thing.

Edit Wednesday 13/7/22: Flight Link 'Duette' restore & FHSS conversion
Edit Thursday 3/6/22: Phil's personal view of 'model memories'
Link Friday 7/5/22: Tobes superb Receiver PCBs for the FHSS project (also see the two following posts, & PDF attached to 2nd)
Edit Thurs 14/1/21: New RF-Nano variant from Keywish, has u.fl connector BUT CE & CSN are swapped.
........................Use RF24 radio(9, 10); // CE & CSN for Keywish RF-Nano with u.fl

Edit Tuesday 22/9/2020 Screened version of the NRF24L01+PA+LNA (highly recommended!)
Edit Wednesday 5/8/2020 World Engines 'Talisman' conversion
Edit Wednesday 29/7/2020 yet more flight-testing - another day on the slope
Edit Wednesday 15/7/2020 RS Navigator Mk2 Single-Channel NRF24 FHSS conversion
Edit Saturday 11/7/2020 more flight-testing - a full day slope soaring
Edit Monday 15/6/2020: added a few more photos & diagrams to the zip file
Edit Tuesday 09/6/2020 same code suits 8/16mhz & tidy-up
Edit Monday 01/6/2020 added remote failsafe setting and switched to timer2
Edit Friday 29/5/2020: added a watchdog timer to the rx
Edit Friday 29/5/2020: Flight test
Edit Thursday 14/5/2020: Range test
Edit Thursday 14/5/2020: Added 5v traffic indicator LED
Edit Monday 27/4/2020: A full NRF24L01 FHSS Single Channel outfit

The story :D
As usual the thread charts the development of the set as a learning project, warts & all, from scratch to a simple but functional FHSS system. There are just a handful of NRF24 FHSS implementations on the net, some over-simplified (with manual re-sync!) and some so complex I just couldnt get my head around them. I wanted something simple but fully dependable and as its a 'lockdown learning project', starting from scratch seemed a good idea :lol:

I'm happy that the hybrid semi-hopping 'lockdown NRF24' project works really well in its current state, where the tx hops continuously but the rx only hops when necessary, but for interest it would be nice to implement full FHSS. Since its really another topic I've split the thread so the hybrid 'lockdown project' can be concluded.

Werner has demonstrated his own FHSS implementation on the NRF24L01, which is quite superb having been developed to or better than commercial standards - its complex, and its clear that an awful lot of work has gone into it! Thanks again for your support Werner, my next goal is my own 'simplified-but-proper' interrupt-driven hopping thing :D

Cheers
Phil


A few people are having a go at this project which is great, thanks lads, the most frequent query is about
connecting D9 and D10 to the NRF24L01. Heres a summary quoted from a later post:

To explain, the ce and csn pins can be on any digital pins, so most NRF24 examples you see on the net are different, whatever the author chose. In my case I chose 9 for CE and 10 for CSN, to keep all the pins adjacent. This subsequently proved to be an unfortunate choice, as when we discovered the RF-Nano, it was the opposite, 10 for CE and 9 for CSN. Obviously since the RF-Nano cant be changed its best to stick with the RF-Nano convention, but I'd already made several radios by then, hence the two options in the code. Ignore the 'old' reference :D

The diagram here is in the RF-Nano order, CE on 10 and CSN on 9. For an RF-Nano it has to be this way, its hard wired, so to create an instance use:
RF24 radio(10,9); // ce, csn
I would use this for all new builds.

But I still have four radios from earlier experiments with
RF24 radio(9,10); // ce, csn
hence this remains in some of the sketches but is commented out.

Crucially of course the wiring has to match the code, in the case of the RF-Nano its already wired,
but to update my working car radios to 'RF-Nano order' means dismantling and rewiring.
Getting ce and csn mixed up does no harm, it wont break anything, it just doesnt transmit.
But for new builds, lets stick with the RF-Nano convention, CE on D10 and CSN on D9 :D Like this:
RF24 radio(10,9); // ce, csn

Edit 21/1/2021: A new RF-Nano format has appeared, this one is distinguished by having a u.fl external aerial connector, selected by a link.
The link by default connects the PCB antenna and its a bit fiddly to change, but perfectly do-able. Keywish have moved CW and CSN so fFor this one the setup is:
RF24 radio(9,10); // ce, csn


Here is the tx & rx code, datasheets, libraries, diagrams, photos etc as at 6/6/2020.
Most recent changes were using timer2 for packet timing, remote failsafe setting from the tx, a few more diagrams and in the receiver "set F/S" moved to D8, channels set to Futaba AETR order, auto 16/8mhz in rx code.
Attachments
nrf24l01_fhss_lockdown_project_philg_160620.zip
File & pics collection 16/6/2020
(18.8 MiB) Downloaded 659 times
WernerL
Posts: 27
Joined: 22 Jan 2020, 02:30

Re: Frequency hopping experiments on the NRF24

Post by WernerL »

Phil_G wrote: 10 Apr 2020, 10:49 Thanks again for your support Werner, I will try the full interrupt-driven hopping thing soon, are your 20 channels hard-coded, scanned quiet-channels, or chosen at random? I have a rough 'find-free-channels' routine worked out based on the < -64db flag.
The frequency hopping stuff is quite simple really.
Both transmitter and receiver use a pre-arranged list of 20 hop frequencies (exchanged through a one-time binding procedure, which I don't talk about for now to keep things simple).

On the transmitter side, a packet is sent every 5 ms. For each packet, the transmitter changes to the next hop frequency in the list, round-robin style. The 5ms timing is kept as precise as possible.

On the receiver side, the receiver stays on the first hop frequency until it catches a packet from the transmitter. (20 hops, one hop every 5 ms means after worst case within 100 ms the receiver should get a packet). Once the packet has been received, it starts a timer that is a bit larger than the 5 ms, and changes the receive channel to listen on next hop frequency. It listens now for the next packet on this new hop frequency. If the packet arrives, the 5+ ms timer is restarted and the next hop frequency is chosen, and so on.

If the packet got lost, the slightly-greater-than-5 ms timer expires. We then switch to the next hop channel and restart the time out, but this time with exactly 5 ms (because we don't want to drift away from the transmitter, we just want the timeout to be slightly behind the transmitter).

This continues until we either receive a packet, or if we miss multiple packets in a row (I think 6 in my case). If we miss 6 packets in a row, then the receiver assumes it is completely out of sync with the transmitter, and switches to the first hop frequency, where it listens until it receives a valid packet from the transmitter (should be less than 100 ms). Then the hopping restarts as described above.

Failsafe kicks in if there are no received packets within 600 ms.

With my system, the 20 frequencies and the "pipeOut" number (basically an address that have to match on the Tx and Rx for the Rx to receive a packet) are randomly generated every time I create a new model.
This information is stored alongside with all the mixer setup etc, so when I load a model into the transmitter then the RF values are being set correctly for that model as well.
This method has the positive side effect that you can't fly a model with the wrong mixer settings, and you can quickly switch transmitters without having to re-bind the receivers. And since the RF settings belong to the model, you can use different transmitters without having to re-bind receivers.


The RF protocol has a special binding mode, where infrequently special bind packets are transmitted on a fixed channel, with a fixed pipeOut number (address) and a lower transmit power. The transmitter sends those bind packets for the first 15 seconds after power-up, interleaved with the regular stick packets.
On the receiver there is a bind button. If I press it, the receiver switches to the bind frequency and listens on the fixed pipeOut number for bind packets. The bind packet holds the model pipeOut number and the list of 20 hop frequencies. Since this is too much payload to send in one packet, the data is actually split across multiple bind packets, with one byte indicating which part of the overall data is contained.

Attached is a full description of the reverse engineered protocol of a commercial nRF24 based RC system (hkr3000-info.md, opens in any text editor).

cheers, Werner
Attachments
hkr3000-info.zip
(2.35 KiB) Downloaded 398 times
pipeout-and-hop-channels.png
User avatar
Phil_G
Posts: 597
Joined: 15 Feb 2018, 23:32
Contact:

Frequency hopping experiments on the NRF24

Post by Phil_G »

Thanks Werner :D
Well, my humble first attempt kinda works but as yet its not syncing properly. I'm posting this in the hopes that writing it down might kick my fuddled brain into gear.

The transmitter is fine, spot on 5ms packets sequenced over 16 FHSS channels.
The receiver code is giving me the runaround. I'm sure I have a flaw in the sequence, but I've worked through it a gazillion times and I cant see it. But tomorrows another day. Hopefully the answer might wake me up at 3:30am as these problems tend to do!

Basically I followed Werners text description in the previous post.
I've a 6ms timer interrupt which sets a timeout flag. With perfect reception the 6ms should never timeout. If it does, we've missed a packet so we switch to a 5ms timeout and carry on hopping.
If we miss 6 packets we restart from the first channel, and if we miss 200 it goes to failsafe.
Visually the set works fine, no servo jitters and instant response, failsafe works, but somethings not right...

For testing, I've put a bit-flip on D7 in the packet-receive section, for my scope, where I'd expect to see alternating 5ms high, 5ms low, with an occasional glitch expected when a packet was missed and the hopping was resyncing.
What I actually see is a 5ms high followed by 80ms of low, which indicates two packets received then it has a bit of a lie down, then two more...

I have it set up for 16 FHSS channels, which is where the 80ms is coming from, 5ms per channel. Which makes me think its either restarting constantly, or sitting on one channel and catching every 16th packet.
The servo drive is commented out in case its upsetting the timing - but makes no difference in or out.

Code: Select all

 #####
#     #  #####    ####    ####   #       ######   #####  ######
#     #  #    #  #       #    #  #       #          #    #
#     #  #####    ####   #    #  #       #####      #    #####
#     #  #    #       #  #    #  #       #          #    #
#     #  #    #  #    #  #    #  #       #          #    #
 #####   #####    ####    ####   ######  ######     #    ######
// Phil_G's lockdown project, March 2020.
// Simple 4 Channel R/C Receiver with frequency hopping and failsafe on all functions
// Use a 3v3 Promini, Vcc to NRF24 pos, 4-cell pack or BEC to Vraw and servo positives. All negs common.
// Servo signals on pins D2, D3, D4, D5.
// Set failsafe by linking pin 7, servo wiggles to confirm its stored.
// If failsafe is not set, loss of signal reverts to defaults - low throttle and neutral controls.
// Choose a unique 'pipeIn' value and use same value in tx, I suggest the last 5 digits of your phone number

#include <EEPROM.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <ServoTimer2.h>
#define failSafePin 7
#define failSafeGnd 8
#define failSafeTime 200  // failsafe if no signal for one full second 
#define lostpkTime 50      // try next channel if no packet received here within a couple of frametimes
#define thr_failsafe 1000

 #####
#     #  #####    ####    ####   #       ######   #####  ######
#     #  #    #  #       #    #  #       #          #    #
#     #  #####    ####   #    #  #       #####      #    #####
#     #  #    #       #  #    #  #       #          #    #
#     #  #    #  #    #  #    #  #       #          #    #
 #####   #####    ####    ####   ######  ######     #    ######

ServoTimer2 ch1, ch2, ch3, ch4;

struct Packet {
  unsigned int throttle = thr_failsafe;
  unsigned int elevator = 1500;
  unsigned int aileron  = 1500;
  unsigned int rudder   = 1500;
};

Packet data;
unsigned long newChannelTime = 0, lastRecvTime = 0, now = 0;
volatile bool timeout = 0, hop = 1;

 #####
#     #  #####    ####    ####   #       ######   #####  ######
#     #  #    #  #       #    #  #       #          #    #
#     #  #####    ####   #    #  #       #####      #    #####
#     #  #    #       #  #    #  #       #          #    #
#     #  #    #  #    #  #    #  #       #          #    #
 #####   #####    ####    ####   ######  ######     #    ######

/************* Radio config - make it unique! **********************************/
const byte pipeIn[] = "39964";   // last 5 digits of your phone number
const byte mychannels[] = {117, 99, 57, 71, 35, 13, 7, 17, 23, 27, 31, 41, 45, 123, 111, 3};  // 16 FHSS hopping channels
/*******************************************************************************/

RF24 radio(9, 10);
int rfchan = 0, missedcounter = 0; // NRF channel index, number of packets missed

void setup()
{
  //  pinMode(failSafePin, INPUT_PULLUP); // momentarily link to gnd to set failsafe
  pinMode(failSafeGnd, OUTPUT); // momentarily link to gnd to set failsafe
  digitalWrite(failSafeGnd, 0); // convenient return for F/S link
  pinMode(A0, OUTPUT); // channel indicator led
  pinMode(A1, OUTPUT); // channel indicator led
  pinMode(A2, OUTPUT); // channel indicator led
  pinMode(A3, OUTPUT); // channel indicator led
  PORTC = 0;
  pinMode(7, OUTPUT); // scope

  // Servo pins
  ch1.attach(2);
  ch2.attach(3);
  ch3.attach(4);
  ch4.attach(5);

  //Configure the NRF24 module
  radio.begin(); delayMicroseconds(500);
  radio.openReadingPipe(1, pipeIn);  delayMicroseconds(500);
  radio.setAutoAck(false); delayMicroseconds(500);
  radio.setDataRate(RF24_250KBPS); delayMicroseconds(500);
  radio.setPALevel(RF24_PA_MIN); delayMicroseconds(500);
  radio.setChannel(mychannels[0]); delayMicroseconds(500);
  radio.startListening();

  // set up 5ms interrupt
  TCCR1A = 0;        // reset Timer1 control reg A
  TCCR1B = 0;
  // set prescaler to /8 0b010
  TCCR1B &= ~(1 << CS10);   // clear CS10
  TCCR1B |=  (1 << CS11);   // set CS11
  TCCR1B &= ~(1 << CS12);   // clear CS12

  TCNT1 = 0;      // reset timer1 to zero and set compare value
  OCR1A = 12000; // 5ms x16mhz/8 = 10000; 6ms x16mhz/8 = 12000
  TIMSK1 = (1 << OCIE1A);   // enble timer1 compare interrupt
  sei();        // enable global interrupts
}

 #####
#     #  #####    ####    ####   #       ######   #####  ######
#     #  #    #  #       #    #  #       #          #    #
#     #  #####    ####   #    #  #       #####      #    #####
#     #  #    #       #  #    #  #       #          #    #
#     #  #    #  #    #  #    #  #       #          #    #
 #####   #####    ####    ####   ######  ######     #    ######


void loop()  {
  TCNT1 = 0; timeout = 0;      // reset timer1 to zero
  while (timeout == 0 || hop == 0) { // dont timeout if we're resyncing
    if (hop == 0) TCNT1 = 0;
    if (radio.available() ) {   // is there a packet on the current RF channel?
      TCNT1 = 0; OCR1A = 12000; timeout = 0;    // reset timer1 and set compare value to 6ms x16mhz/8 = 12000
      radio.read(&data, sizeof(Packet));
      ch1.write(data.throttle); ch2.write(data.elevator); ch3.write(data.aileron); ch4.write(data.rudder);   // Servo outputs
      PORTD ^= (1 << PD7);    // flip D7 for scope, all being well every 5ms
      hop = 1;
      missedcounter = 0;
      rfchan = (rfchan + hop) % 16; radio.setChannel(mychannels[rfchan]); // cycle through mychannels[]
   }
  }
  // 6ms timeout
  if (++missedcounter < 7) {  // if we've missed 1 to 5 packets, hop on 5ms at a time until we see data
    TCNT1 = 0; OCR1A = 10000; timeout = 0;    // reset timer1 and set compare value to 5ms x16mhz/8 = 10000
    hop = 1;
    rfchan = (rfchan + hop) % 16; radio.setChannel(mychannels[rfchan]); // try next channel
  }

  else {  // we missed six packets on the trot, restart from zero
    rfchan = 0; hop = 0;
    radio.setChannel(mychannels[rfchan]);
    TCNT1 = 0; OCR1A = 12000; timeout = 0;    // reset timer1 and set compare value to 6ms x16mhz/8 = 12000
  }

  if (missedcounter > 200) {  // failsafe if nothing heard for 1 second
    failSafe(); // Signal lost.. revert to failsafe values, either saved or default
  }
}

// This function will write a 2 byte integer to the eeprom at the specified address and address + 1
void EEPROMWriteInt(int p_address, int p_value)
{
  byte lowByte = p_value % 256;
  byte highByte = p_value / 256;
  EEPROM.write(p_address, lowByte);
  EEPROM.write(p_address + 1, highByte);
}

//This function will read a 2 byte integer from the eeprom at the specified address and address + 1
unsigned int EEPROMReadInt(int p_address)
{
  byte lowByte = EEPROM.read(p_address);
  byte highByte = EEPROM.read(p_address + 1);
  return lowByte + highByte * 256;
}


void failSafe()
{
  rfchan = 0; hop = 0;

  if (EEPROMReadInt(0) == 0x5AA5) {   // if failsafe set
    data.throttle = EEPROMReadInt(2); // throttle
    data.elevator = EEPROMReadInt(4); // elevator
    data.aileron  = EEPROMReadInt(6); // ailerons
    data.rudder   = EEPROMReadInt(8); // rudder
  }
  else {
    data.throttle = thr_failsafe; // Low throttle
    data.elevator = 1500; // Neutral
    data.aileron  = 1500; // Neutral
    data.rudder   = 1500; // Neutral
  }
  ch1.write(data.throttle); ch2.write(data.elevator); ch3.write(data.aileron); ch4.write(data.rudder);   // Servo outputs
}

ISR(TIMER1_COMPA_vect) {   // timer, set to 6ms whilst in sync, 5ms when resyncing
  timeout = 1;
}

 #####
#     #  #####    ####    ####   #       ######   #####  ######
#     #  #    #  #       #    #  #       #          #    #
#     #  #####    ####   #    #  #       #####      #    #####
#     #  #    #       #  #    #  #       #          #    #
#     #  #    #  #    #  #    #  #       #          #    #
 #####   #####    ####    ####   ######  ######     #    ######
Fun & games eh.
Tomorrows plan is to slow the tx & rx right down to a visible hopping speed (just change the prescaler), maybe I'll see the flaw.
WernerL
Posts: 27
Joined: 22 Jan 2020, 02:30

Re: Frequency hopping experiments on the NRF24

Post by WernerL »

Phil_G wrote: 29 Apr 2020, 22:17
What I actually see is a 5ms high followed by 80ms of low, which indicates two packets received then it has a bit of a lie down, then two more...
Ah! Fun memories of debugging the hopping ...
I too think that it currently may only listen to the first frequency.

I remember you previously had issues switching frequency with the nRF library you are using, having to reset a lot of settings? Maybe you are dealing with the same issue here?
Since you do receive a packet on the first channel every 80ms, maybe the receiver is never actually switching channel and always keep listening on the first hop frequency.

To verify this theory you could change all 16 hop frequencies on the TX to the same frequency, but keep the random hop frequency on the RX side. If you then see packets every 5ms then the receiver is clearly not changing channel properly.

It may also be that you are hopping slightly too late. The RX must already listen to the new frequency before the TX starts transmitting the packet. It is unlikely that this is a problem though, as it would also affect your 17th hop (=transmitting again on the 1st hop channel) after 80ms.

I am sure you will get it working soon, Werner
User avatar
Phil_G
Posts: 597
Joined: 15 Feb 2018, 23:32
Contact:

Re: Frequency hopping experiments on the NRF24

Post by Phil_G »

Still struggling here, I've proven that the tx hops correctly and tx timing is spot on,
at the receiver side it picks up channel 0 ok then picks up channel 1 ok, then nothing until the 6-missed-packets counter forces a restart from zero, at which point it receives channels zero and one again...
I've spent two full days thinking and trying various things including (amongst many other experiments and much pondering):
Tried Werners suggestion of setting all 16 tx channels to RF channel 0, rx is hopping ok but not receiving after the first channel (0 ok, 1 times out, 2 times out... etc to 6 then it restarts on RF ch0 ok...) this and other tests has convinced me the rx is cycling through the RF channels ok.
changed the tx & rx timing by increasing the prescaler from /8 to /1024 which gives a packet every .64 seconds so its slow enough to see whats happening.
Set the aileron channel data so instead of stick position it sends the actual current RF channel number, this confirmed 0 & 1 are received ok then 2 and onwards are not.
Changed 'hop' from a bool to an int in case some weird casting was limiting channels to 0 & 1. Nope!
I'm sure its a logic flow problem but for the life of me I cant see it yet. The restart-on-6-lost-packets bit is working fine, as is failsafe (except where I've temporarily forced it to stay in the rx loop)
The "two good channels then 6 fails causing a restart from 0" is absolutely consistent, the same every time, so its not genuine lost packets.
With perfect reception it should never exit the while loop since there will never be a 6ms timeout (or 768ms when the prescaler is 1024)

The TIL311 has proven a very useful tool, as a direct register write to PORTC takes insignificant time.
I used it to show 'hop', rfchan, missedpackets etc at various stages :D
Martin
Posts: 744
Joined: 16 Feb 2018, 14:11
Location: Warwickshire

Re: Frequency hopping experiments on the NRF24

Post by Martin »

I'm working on a version that hops around all the legal channels, in a pseudo-random order that's calculated based on the supplied ID (which must be common for the transmitter and receiver sketch - I've not implemented any kind of 'binding' yet).

It doesn't use any of the nRF libraries - it hits the nRF24 hardware direct. I'm using the Shockburst feature to do the ID and CRC checking, but not the Enhanced Shockburst which relies on two-way communication.

The transmitter hops around all the channels in the pseudo-random order with fairly constant timing (done by a timer interrupt) but I've deliberately introduced a tiny bit of random jitter - partly so that two transmitters running similar code can't remain in transmit synch for long (this wouldn't really matter since they would rarely happen to clash on the same channel anyway) and partly to help test out the "receiver lock" performance.

When the receiver gets a valid packet, it knows which channel to listen on next - because it has the same pseudo-random order list that the transmitter is using.

The important thing is that the receiver starts listening before the transmitter transmits. When the receiver gets a valid packet it knows exactly when the next packet should arrive (plus or minus any 'random' jitter). If the receiver misses a packet, it still switches to the next frequency in its list based on a timer interrupt. The length of the listening time is longer than the transmitted packet time, and a valid received packet normally happens somewhere near the middle of the allotted 'listen time'. Even with a chain of ten missed packets, the small amount of deliberate jitter on the transmit spacing means that the packet still falls within the listening window.

If the receiver misses ten packets running, then it assumes it's lost sync and starts switching channels more slowly and listening for longer on each one. It still eventually switches around all the channels in its pseudo-random order list, but does so in the reverse order. Of course, as soon as it receives a valid packet it switches back into 'locked' mode.

The number of transmitted packets per second is pretty constant - because it's controlled by a timer with just the small amount of deliberate jitter. The receiver counts the number of packets it receives per second and sends that count (serially) to the IDE for debug purposes. When there is no interference and no bugs you expect the receiver to get the same packets per second as are being transmitted. The ratio of received packets per second to the (fairly constant) transmitted packets per second, gives a pretty good indication of how well the link is working.

The receiver debug code also keeps track of how many times it's lost sync. On a finished flying version, I intend to do a flashing LED (similar to Spektrum receivers) where counting the flashes after a flight allows the pilot to see how many times (if any) the sync was lost after first being acquired.

Still needs some debugging, and I've not written any code yet to encode/decode actual model-controlling data into and out of the transmitted data stream.
User avatar
Phil_G
Posts: 597
Joined: 15 Feb 2018, 23:32
Contact:

Re: Frequency hopping experiments on the NRF24

Post by Phil_G »

That sounds spot on Martin, I think you've 'left no stone unturned' there, and avoiding the existing libraries is definitely a good call. I appreciate you've a lot more experience with the NRF24 so yours will be brilliant. I really think with the right code these modules are a complete answer to the R/C module availability problem, and at a fraction of the cost. The only downside I can see is having to make receivers, though its simple enough and the RFnano has answered that one anyway :D

I found the NRF datasheet 'not the easiest' to follow, with much scrolling to and fro :D
Martin
Posts: 744
Joined: 16 Feb 2018, 14:11
Location: Warwickshire

Re: Frequency hopping experiments on the NRF24

Post by Martin »

Phil_G wrote: 02 May 2020, 18:46I found the NRF datasheet 'not the easiest' to follow, with much scrolling to and fro :D
Me too. :? I found the nRF2401 datasheet for the older, simpler chip (without Enhanced Shockburst) helpful - the nRF24L01 datasheet concentrates on the Enhanced mode which, as far as I can see, is no use for our kind of frequency hopping. The L01 version is supposed to be backward compatible with the older, non-L version.
User avatar
_AL_
Posts: 160
Joined: 17 Feb 2018, 01:09
Location: Sydney Australia

Re: Frequency hopping experiments on the NRF24

Post by _AL_ »

This is for the most part way over my head but a massively impressive project none the less..

Al
User avatar
Phil_G
Posts: 597
Joined: 15 Feb 2018, 23:32
Contact:

Re: Frequency hopping experiments on the NRF24

Post by Phil_G »

Eureka!!! (not the Swedish TV star Tobe) :D

Oh, I'm chuffed to bits, I'd almost given up on this but after hours and hours working through hoops & loops I've definitely found the problem - and its not the code! Some NRF24 library users have reported that the radio.available() function doesnt always clear as it should after data is read. Its purpose is to flag that a packet has been received and is ready to read, then you read it using radio.read() which should clear the 'available' flag. As others have said, it didnt. Clearing it manually after every packet using radio.flush_rx() has fixed it. Obviously this cant be a problem with the NRF chip itself so I'm thinking it has to be the library again. The problem is masked in most sketches where radio.read() is within a while (radio.available()) loop so it re-reads the same data into the same buffer for as long as it takes the radio.available() flag to eventually clear, which works but gives the function unpredictable timing.

It works! It does almost everything right, staying in sync even if a few packets are missed, then restarting from zero and resyncing if more are missed. 'Almost' in that there remains one funny - whilst everything is working, if you then power cycle the transmitter, the receiver doesnt relink, but that should be easily fixed. [Fixed!] I'm relieved to finally get a simple but real fhss scheme working but in view of Martin & Werners way, way superior code it is just a learning exercise for my own amusement :D

Edit:
Phil_G wrote: 03 May 2020, 18:10 ...there remains one funny - whilst everything is working, if you then power cycle the transmitter, the receiver doesnt relink, but that should be easily fixed.
Sorted, all behaving perfectly now, but lots of soak testing next... happy chappie ! :D
Much later edit: You can (and we do!) turn the tx off as you wish during a flight, it failsafes but links instantly on switching back on. Scares the bejesus out of the Futaba and Spekky flyers!
I've been twiddling sticks for over an hour with the NRF receiver surrounded by as many other transmitters as I could find, including two FASST, a few Frskys, two Oranges, and a Flysky, all on full power whilst the NRF transmitter is set to minimum RF power, and no glitches, stuttering or delays - perfect :D
The scope now shows packets every 5ms with an occasional one or two missing as you'd expect.
Range on the very lowest power setting seems to be about 30 - 40 yards, beyond that failsafe kicks in. Walking back it hooks up again at about the same distance. This is using the low-power integrated PCB antenna modules for both tx and rx, with a high power module I expect the range to be as good as any.

Thanks very much to Werner, whose description above is the basis for this FHSS project, I guessed 6ms as the "just over 5ms" value and that works fine, not sure if tweaking would improve anything. The only part I changed from your description is when several packets are lost and we restart from zero (ie the first of mychannels[]), rather than wait there for a good packet, it waits a while then moves on, just in case the first is being blocked by interference.

Honestly I'm chuffed to bits, a proper happy chappie :D

All the code, diagrams & photos are attached to the opening post.
Post Reply