Frequency hopping experiments on the NRF24
- Phil_G
- Posts: 629
- Joined: 15 Feb 2018, 23:32
- Contact:
Frequency hopping experiments on the NRF24
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:
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
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
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
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
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 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.
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
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
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
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
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 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 744 times
-
- Posts: 27
- Joined: 22 Jan 2020, 02:30
Re: Frequency hopping experiments on the NRF24
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 481 times
- Phil_G
- Posts: 629
- Joined: 15 Feb 2018, 23:32
- Contact:
Frequency hopping experiments on the NRF24
Thanks Werner
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.
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.
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;
}
#####
# # ##### #### #### # ###### ##### ######
# # # # # # # # # # #
# # ##### #### # # # ##### # #####
# # # # # # # # # # #
# # # # # # # # # # # #
##### ##### #### #### ###### ###### # ######
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.
-
- Posts: 27
- Joined: 22 Jan 2020, 02:30
Re: Frequency hopping experiments on the NRF24
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
- Phil_G
- Posts: 629
- Joined: 15 Feb 2018, 23:32
- Contact:
Re: Frequency hopping experiments on the NRF24
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
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
-
- Posts: 748
- Joined: 16 Feb 2018, 14:11
- Location: Warwickshire
Re: Frequency hopping experiments on the NRF24
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.
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.
- Phil_G
- Posts: 629
- Joined: 15 Feb 2018, 23:32
- Contact:
Re: Frequency hopping experiments on the NRF24
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
I found the NRF datasheet 'not the easiest' to follow, with much scrolling to and fro
I found the NRF datasheet 'not the easiest' to follow, with much scrolling to and fro
-
- Posts: 748
- Joined: 16 Feb 2018, 14:11
- Location: Warwickshire
Re: Frequency hopping experiments on the NRF24
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.
- _AL_
- Posts: 163
- Joined: 17 Feb 2018, 01:09
- Location: Sydney Australia
Re: Frequency hopping experiments on the NRF24
This is for the most part way over my head but a massively impressive project none the less..
Al
Al
- Phil_G
- Posts: 629
- Joined: 15 Feb 2018, 23:32
- Contact:
Re: Frequency hopping experiments on the NRF24
Eureka!!! (not the Swedish TV star Tobe)
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
Edit:
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
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
All the code, diagrams & photos are attached to the opening post.
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
Edit:
Sorted, all behaving perfectly now, but lots of soak testing next... happy chappie !
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
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
All the code, diagrams & photos are attached to the opening post.