Asynchronous wireless data transmission using NRF24l01

Hi, here is what I am trying to do:

I pass a sine wave (generated using a signal generator) to the Analog pin A0 in Arduino. I read it using the analogRead function and store the values in an array of size 100.

Then I send the data to another Arduino using the NRF24l01 module and plot it on the other end. This is something like a wireless plotter.

I send the data when the buffer is full. When I plot it on the other side, on the serial plotter graph, after every 100 samples, there is a sudden jump in the plot. This is because the process of sending data is blocking and the Arduino stops sampling data when the code for transmission is being executed. When the data transmission is done, it goes back to reading value from pin A0.

Is there any way to make the process of transmission asynchronous, so that Arduino keeps reading values even when transmission is being done?

Then I guess you are sending the 100 byte block in 4 transmissions and not doing anything else while those are being sent. Are you sure you are reconstructing the 32 byte transmissions back into the 100 byte block in the proper order?

Paul

Arvind99:
Is there any way to make the process of transmission asynchronous, so that Arduino keeps reading values even when transmission is being done?

Without seeing the pair of programs you are using it is impossible to suggest an improvement.

How many times per second is analogRead() being called?

…R
Simple nRF24L01+ Tutorial

You have the source of analogRead.
It consists of three parts -

  1. input mux selection
  2. initiate conversion
  3. wait for conversion completion.

It is very simple to write a couple of functions to separate 1 & 2 from 3.

TheMemberFormerlyKnownAsAWOL:
It is very simple to write a couple of functions to separate 1 & 2 from 3.

I doubt if that is where the problem lies.

But we need to see the OP's code.

...R

Transmitter code (Arduino Due)

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

//buffer size for storing
#define BUFFER_SIZE 23500
//the array buffer
uint16_t buf[BUFFER_SIZE];

//RF pin setup
RF24 radio(7, 8); 
const byte rxAddr[6] = "00001";

//setting up the ADC in Arduino DUE
void setup()
{
 pmc_enable_periph_clk (ID_ADC);
 adc_init (ADC, SystemCoreClock, ADC_FREQ_MIN, ADC_STARTUP_FAST);
 adc_disable_interrupt (ADC, 0xFFFFFFFF);
 adc_set_resolution (ADC, ADC_10_BITS);
 adc_configure_power_save (ADC, ADC_MR_SLEEP_NORMAL, ADC_MR_FWUP_OFF);
 adc_configure_timing (ADC, 1, ADC_SETTLING_TIME_3, 1);
 adc_set_bias_current (ADC, 1);
 adc_disable_tag (ADC);
 adc_disable_ts (ADC);
 adc_stop_sequencer (ADC);
 adc_disable_channel_differential_input (ADC, ADC_CHANNEL_7);
 adc_disable_all_channel (ADC);
 adc_enable_channel (ADC, ADC_CHANNEL_7);
 adc_configure_trigger (ADC, ADC_TRIG_SW, 1);
 Serial.begin(115200);
 adc_start( ADC );
 
 //Setting up NRF24l01 for transmission
 radio.begin();
 radio.setRetries(15,15);
 radio.openWritingPipe(rxAddr);
 radio.stopListening();
}

void loop() 
{
 PDC_ADC->PERIPH_RPR = (uint32_t) buf; // address of buffer
 PDC_ADC->PERIPH_RCR = BUFFER_SIZE; 
 PDC_ADC->PERIPH_PTCR = PERIPH_PTCR_RXTEN; // enable receive
 while((adc_get_status(ADC) & ADC_ISR_ENDRX) == 0){};

 //sending the data to other arduino
 int val[8] = {0}; 
 int count = 0;
 for (int i=0; i<BUFFER_SIZE; i++)
 { 
     val[count] = buf[i];
     count++;
     //To send 8 values from the buffer at a time
     if(count == 8) {
       radio.write(val, sizeof(val));     
       count = 0;
   }
 }
}

Receiver code (Arduino Uno)

//Include Libraries
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

//create an RF24 object
RF24 radio(7, 8);  // CE, CSN

//address through which two modules communicate.
const byte address[6] = "00001";

void setup()
{
  while (!Serial);
    Serial.begin(9600);
  
  radio.begin();
  
  //set the address
  radio.openReadingPipe(0, address);
  
  //Set module as receiver
  radio.startListening();
}

void loop()
{
  //Read the data if available in buffer
  if (radio.available())
  {
    int val[16] = {0};
    radio.read(val, sizeof(val));
    for(int i = 0;i < 16;i=i+2) {
      Serial.println(val[i]);
    }
  }
}

I found these code for these on some blog. The issue is the sine wave plot I was talking about has kinks in between.

I don’t have the plots with me, but this is a rough figure (do not have signal generator with me right now, so here is a cheap plot as to how it looked like)

here is the link : Plot Image

I’ll update it with the proper plot once I acquire it.

Thanks

The first thing that strikes me is that you are sending a message containing 8 ints and you are trying to receive 16.

The receiver should be receiving what the sender is sending.

If I was sending a large piece of data in several chunks I would put a number into the message that identifies which part of the larger piece this chunk represents. That way the receiver can determine if one of the chunks gets lost in transmission.

I can’t tell from your program how long it takes to harvest the 100 samples and, consequently, how often wireless messages need to be sent. I asked about this in Reply #2

Is there any reason not to send wireless data after every 10 samples are harvested? That way all the data would fit into one message

…R

The NRF is no Serial device.
Send packets of data with id if more than one packet is neede for a data record.

You have a single available call controlling 16 reads, that is not clever at all.

I would use a buffer of 16 values and send it when it became full.
Data sampling would use two bufferes alternating.

Whandall:
I would use a buffer of 16 values and send it when it became full.

Thinking some more about this (since my Reply #6), it would probably be a good idea to have a serial number in every message. Even if the data are being sent as soon as they are harvested the receiver would need to be able to detect a missing chunk of data.

That suggests sending 15 values and the serial number.

...R

@OP

This is the plot at your receiver side.

Here you go, the link : Sine Plot Receiver

Please ignore the previous one I posted.

In the image, there are some kinks in between the sine wave. That is what i want to remove. It's happening after every 23500 points, which is my buffer size. Probably because the transmission is blocking.

Thank You

I response to Reply #6, I am using an array of size 16 even though I am sending 8 values is because the data I am receiving is padded with zeroes for some reason.

So If I send [1,2,3,4], I receive it as [1,0,2,0,3,0,4,0]. That is why at receiving side, size is 16.

Now as to how frequently I need to send the data, I am trying to build some sort of a real time wireless plotter. A few seconds (probably 3-4) worth of lag should be fine.

About the number of samples, the sampling rate should be around 3KHz, so that is 3000 samples per second. And the max sine wave I am plotting is around 100Hz. So for every , I will have 30 samples per cycle. And I feel that is a reasonable number. We can probably bring it down till 1.5KHz which is still well above the Nyquist Rate.

Also, I feel that the issue is not about a chunk of data getting lost rather, it is the unability to collect samples while sending the data.

So here is what I did today, I put time=millis() before and after radio.write() in the above code at the transmitter side. The value it gave me was on an average 60ms.

Assuming sine wave is 100Hz, the time period will be 10milliseconds. One sine wave's complete cycle is around 10ms. Now, once the buffer is full, it transmits the data array to receiver which takes 60ms to complete and while this is happening we are missing out on almost 6 cycles of the sine wave. Once transmission is done, it starts reading from pin A0 again but we have already lost 6 cycles worth of data.

So rather than losing data in transmission, the issue is about how to send data without blocking rest of the code. This is what I feel.

Thank You

Arvind99:
Here you go, the link : Sine Plot Receiver

Still, you have a kick on your plot which looks like QPSK Modulation?
sinePlotkick.png

sinePlotkick.png

Arvind99:
I response to Reply #6, I am using an array of size 16 even though I am sending 8 values is because the data I am receiving is padded with zeroes for some reason.

So If I send [1,2,3,4], I receive it as [1,0,2,0,3,0,4,0]. That is why at receiving side, size is 16.

Then there is a problem that needs to be fixed. Sending unnecessary data just wastes time that your system can ill afford.

So rather than losing data in transmission, the issue is about how to send data without blocking rest of the code. This is what I feel.

This line of code looks like it blocks the Arduino until it completes

while((adc_get_status(ADC) & ADC_ISR_ENDRX) == 0){};

but I am not familiar with the DUE.

My approach would be to take the samples one at a time and when (say) 15 have been obtained send a single wireless message. However ...

If you get one sample every 333 micro secs then 15 will be obtained in 5 millisecs. At 250k bits per second an nRF24 message and acknowledgement takes about 3 millisecs. It would probably be better to use a higher data rate for the nRF24 to ensure that the wireless can keep up with the data collection. Also, if you need a large number of retries (your code uses the max number) that will seriously slow the data transmission. If the transmission is unreliable it would be a good idea to try a different channel.

Another thought. I think the normal radio.write() command waits to see if the acknowledgement is received. You should separate the sending from the checking for the acknowledgement so the Arduino can immediately go back to gathering samples. I can't recall if that capability is a standard feature in the TMRh20 RF24 library

....R

Wow, thanks for the help. I'll try out these things and try to get it working. I'll let you know in case this works out.

Just in case it doesn't work out, is there any other way to achieve this, you know, perform sampling and keep transmitting at the same time. I don't need a high sampling rate as such, 3000 samples per sec would be enough. Any suggestions?

Arvind

Arvind99:
Just in case it doesn't work out, is there any other way to achieve this,

That's a wide open question. The answer include hiring an engineer.

If it does not work then tell exactly what happens and we will take it from there.

...R

Arvind99:
I don't need a high sampling rate as such, 3000 samples per sec would be enough. Any suggestions?

333 microseconds or 5328 instructions per sample is not so slow for gathering and transmission.

A system with dma and a higher clockrate would perform much better IMHO.

True. Maybe I'll also have to go a level below and use interrupts and direct access to reduce the time for everything. That might help me solve the time consumption related problems.