Code just slightly too slow to wirelessly stream audio via Nrf24L01+?

Status
Not open for further replies.

Chris314

Member
Hello,

I tried to make a wireless live mono audio link with a Teensy 3.2 as a transmitter and Teensy 4.0 as a receiver.
Mainly copied a code example I found somewhere in the forum and changed it slightly.. Want to stream audio from a mic + several times a secound data from a Joystick on a different pipe but the same channel.
Everything seems to work fine but there is still a little "popping" sound several times a secound.
Without the joystick data there is no problem. Any Ideas, how to get it working/ make the code faster?
Another strange observation I made is that it only works at specific CPU Speeds: Teensy 3.2@120MHz and Teensy 4.0@24MHz. Anything else, even higher, produces extreme jitter. Logically, a higher speed should bring higher performance, I thought?

Here is the code for the Teensy 3.2 (transmitter):

Code:
//Teensy 3.2 @ 120Mhz - transmitter

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

#define audiobuffersize 128 // one block in audio library has 128 values of 2 byte

#define JoyX_Pin 16
#define JoyY_Pin 17
#define JoySwitch_Pin 18

uint16_t JoyX;
uint16_t JoyY;
bool JoySwitch;
byte ControlBuffer[8];

RF24 radio(20,21); // Teensy 3.6 pin 21=CE, Teensy 3.6 pin 15=CSN

const byte pipeaddress[2][5] = {
    {'1','N','O','D','E'},
    {'2','N','O','D','E'}
};

elapsedMillis millisec;

// GUItool: begin automatically generated code
// defs for audio library
AudioInputI2S i2s1;
AudioOutputI2S i2s2;
AudioRecordQueue queue1;
AudioSynthWaveformSine sine1;
AudioConnection patchCord1(i2s1, queue1);

AudioControlSGTL5000 sgtl5000_1;
// GUItool: end automatically generated code

uint8_t bufr[audiobuffersize * 2]; //buffer to hold 256 byte of audio data

void setup()
{
//Serial.begin(9600);
//Serial.println("Start Transmitter");


AudioMemory(100); //give the Audio Library some memory to work with

Serial.begin(36000);

// use alternate SPI pins for nrf24
SPI.setMOSI(7);
SPI.setMISO(8);
SPI.setSCK(14);
SPI.begin();

// Setup the SGTL5000 AIC
//sgtl5000_1.enable(); //activate AIC

sgtl5000_1.enable(); //Enable the SGTL5000
//sgtl5000_1.volume(0.4);


delay(100);

radio.begin();
//radio.setPayloadSize(32); //this is the maximum payload size for a packet transfer of nrf24
radio.setAutoAck(false); // no Ack
//radio.enableDynamicPayloads();
//radio.setRetries(0,0);
radio.disableCRC(); // no CRC
radio.setPALevel(RF24_PA_MAX);
radio.setDataRate(RF24_2MBPS);
radio.setChannel(121);
delay(50);

radio.openWritingPipe(pipeaddress[0]);
radio.stopListening(); // switch to transmit mode
delay(100);

queue1.begin();
}

void loop()
{
  
  if(millisec>=100){
  JoyX=analogRead(JoyX_Pin);
Serial.print("Position X: ");
Serial.print(JoyX);

JoyY=analogRead(JoyY_Pin);
Serial.print("          Position Y: ");
Serial.print(JoyY);


Serial.print("    SW: ");
Serial.println(JoySwitch);


if(analogRead(JoySwitch_Pin)<=50){
 JoySwitch=true; 
}
else{
  JoySwitch=false;
}

ControlBuffer[0]=JoyX;
ControlBuffer[4]=JoyY;
ControlBuffer[7]=JoySwitch;

delayMicroseconds(5);
radio.openWritingPipe(pipeaddress[0]);
radio.write(&ControlBuffer,8);
millisec=0;

  }

  
if (queue1.available())
{

memcpy(bufr, queue1.readBuffer(), audiobuffersize*2); // copy 256 byte audio data from audio library buffer to bufr
queue1.freeBuffer();

delayMicroseconds(5);
radio.openWritingPipe(pipeaddress[1]);
// send audio data in 8 packages a 32 byte to the FIFO buffers
for (int i=0; i<8; i++)
{
radio.writeFast(&bufr[i * 32],32);
}
}
}

And here for the Teensy 4.0 (receiver):

Code:
//Teensy 4.0@24 MHz (receiver)

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

#define audiobuffersize 128 // one block in audio library has 128 values of 2 byte

RF24 radio(14,15); // Teensy 3.6 pin 21=CE, Teensy 3.6 pin 15=CSN

const byte pipeaddress[2][5] = {
    {'1','N','O','D','E'},
    {'2','N','O','D','E'}
};

// GUItool: begin automatically generated code
// defs for audio library
AudioPlayQueue queue1;
AudioOutputI2S i2s1;
AudioConnection patchCord1(queue1, 0, i2s1, 0);
AudioConnection patchCord2(queue1, 0, i2s1, 1);
//AudioConnection patchCord(sine1,0,i2s1,0);
AudioControlSGTL5000 sgtl5000_1;
// GUItool: end automatically generated code

uint8_t bufr[audiobuffersize * 2]; //buffer to hold 256 byte of audio data
uint8_t bufcount; //received nrf24l01+ packet counter
byte ControlBuffer[8];
uint8_t ZielPipe;

int i; //Counter
int k; //temp

void setup()
{
//Serial.begin(9600);
bufcount = 0;

AudioMemory(200); //give the Audio Library some memory to work with
/*
// use alternate SPI pins for nrf24
SPI.setMOSI(7);
SPI.setMISO(8);
SPI.setSCK(14);
SPI.begin();
*/
// Setup the SGTL5000 AIC
sgtl5000_1.enable(); //Enable the SGTL5000
sgtl5000_1.volume(0.5); //set the headphone volume
//sgtl5000_1.lineOutLevel(5, 5);
/*
sine1.frequency(500);
sine1.amplitude(0.3);
*/
radio.begin();
//radio.setPayloadSize(32); //this is the maximum payload size for a packet transfer of nrf24
radio.setAutoAck(false); //no Ack
//radio.enableAckPayload();
//radio.setRetries(1,2);
radio.disableCRC(); //no CRC
radio.setPALevel(RF24_PA_MAX);
radio.setDataRate(RF24_2MBPS);
radio.setChannel(121);
delay(100);

radio.openReadingPipe(1, pipeaddress[0]);
radio.openReadingPipe(2, pipeaddress[1]);
delay(50);

radio.startListening();
}

void loop()
{

if (radio.available(&ZielPipe))
{
  
  if(ZielPipe==1){
    
    radio.read(&ControlBuffer,8);
    
    for(i=0;i<8;i++){
      Serial.print(ControlBuffer[i]);
      
    }
    Serial.println("");
    
  }
// get 8 packets a 32 byte audio data
else if(ZielPipe==2){
if ((bufcount >= 0) && (bufcount < 8))
{
// get 32 byte audio packet data
radio.read(&bufr[bufcount * 32], 32);

// test if all 8 packets received
if (bufcount == 7)
{
bufcount = 0;
int16_t *p = queue1.getBuffer();
memcpy(p, &bufr[0], (audiobuffersize * 2));
queue1.playBuffer();
} else bufcount++;
}
}
else{}
}
}

Any ideas what to change/improve?

Greetings, Chris
 
have you tried the optimised teensy3 spi library spi4teensy3.h ? also if its just occuring with the joystick , is it the analogreads causing a problem, they are quite slow.
try using this sort of thing.

#include <ADC_Module.h>
#include <RingBuffer.h>
#include <RingBufferDMA.h>
#include <spi4teensy3.h>

ADC *adc = new ADC(); // adc object
ADC::Sync_result syncvals;

etc etc

void setup()
{

spi4teensy3::init();

// setup youre rf24 here ;

// setup adc
adc->setReference(ADC_REFERENCE::REF_3V3,0);
adc->setReference(ADC_REFERENCE::REF_3V3,1);

adc->setResolution(12,0); // 0 to 1023
adc->setResolution(12,1); // 0 to 1023

adc->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED,0);
adc->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED,1);

adc->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED,0);
adc->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED,1);
}
void loop()
{
// useing syncvals you can red BOTH ADC's at the same time save a lot of time.
syncvals = adc->analogSynchronizedRead(xpi,ypi); // xpi ypi the analog in pins you use
SentRec is a struct with the fields xp int , yp int.

SentRec.xp = syncvals.result_adc0;
SentRec.yp = syncvals.result_adc1;// x+ y+

// send data
if (!radio.writeFast((char *)&SentRec,sizeof(SentRec),1)) {
}
}
}

might help.
 
anyjoy? what setting do use for spi on teensy with the rf24, mine dont want to play. radio. begin just hangs.
 
Hey Phil, finally had success transferring audio and data at the same time. I simply changed the SPI clock divider in the RF 24 Library (rf24.cpp) to a somewhat lower value.
Furthermore i found out that one can't send too soon after an address change, otherwise the receiver might register a payload under the old address.

Here you can see the MOSI of the TX on the bottom (yellow) and the MISO of the RX on the top (purple). It's so "noisy" because of the "if(radio.available())".
DSC_0897_1.JPG
The spikes are an address change, 8 blocks of audio data, another address change and finally the joystick 'databufr'.
Can't really be spaced much tighter without problems, but still a little room left - maybe for some kind of redundancy check/resend of lost packets. But i couldn't get this working yet. But still it works without errors if the two NRF24L01s are not too far apart (about 2m).

The Code (TX):
Code:
//TX - Teensy 4.0 @ 600MHz

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

#define JoyX_PIN 16
#define JoyY_PIN 17
#define JoySW_PIN 18

uint16_t JoyX;
uint16_t JoyY;
bool JoySW;

#define audiobuffersize 128 // one block in audio library has 128 values of 2 byte

RF24 radio(14,15); // CE, CSN

uint8_t databufr[32];

const byte pipeaddress[][6] = {"1Node","2Node"};

// GUItool: begin automatically generated code
AudioInputI2S i2s1;
AudioOutputI2S i2s2;
AudioRecordQueue queue1;
AudioConnection patchCord1(i2s1, queue1);
// GUItool: end automatically generated code

uint8_t bufr[audiobuffersize * 2]; //buffer to hold 256 byte of audio data


void setup()
{
Serial.begin(9600);
sine1.frequency(300);
sine1.amplitude(0.02);

AudioMemory(100); //ca.100 oder mehr

pinMode(JoyX_PIN, INPUT);
pinMode(JoyY_PIN, INPUT);
pinMode(JoySW_PIN, INPUT_PULLUP);

delay(100);

radio.begin();
radio.setPayloadSize(32); //this is the maximum payload size for a packet transfer of nrf24
radio.setAutoAck(false); // no Ack
radio.disableCRC(); // no CRC
radio.setPALevel(RF24_PA_MAX);
radio.setDataRate(RF24_2MBPS);
radio.setChannel(117);
delay(50);

radio.openWritingPipe(pipeaddress[0]);
radio.stopListening();
delay(100);

queue1.begin();
}

void loop()
{
if (queue1.available())
{
radio.openWritingPipe(pipeaddress[0]);
delayMicroseconds(150);

memcpy(bufr, queue1.readBuffer(), audiobuffersize*2); // copy 256 byte audio data from audio library buffer to bufr
queue1.freeBuffer();

// send audio data in 8 packages a 32 byte to the FIFO buffers
for (int i=0; i<8; i++)
{
radio.writeFast(&bufr[i * 32],32);
delayMicroseconds(90);
}
JoyTX();  //after 8 Blocks of audio are sent
}
}

void JoyTX(){      //collect Data from the Joystick and send it in a 32 Byte "databufr" to a different address than the audio.
  delayMicroseconds(150);
  radio.openWritingPipe(pipeaddress[1]);
  JoyX=analogRead(16);
  JoyY=analogRead(17);
  
  if(analogRead(JoySW_PIN)<=50){
    JoySW=true; 
    }
      else{
      JoySW=false;
      }
  databufr[0]=JoyX;
  databufr[4]=JoyY;
  databufr[7]=JoySW;
  
  delayMicroseconds(200);
  radio.write(&databufr,32);
  delayMicroseconds(150);
  
}

Code (RX):
Code:
//RX - Teensy 4.0 @ 600MHz
// 

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


#define audiobuffersize 128 // one block in audio library has 128 values of 2 byte

RF24 radio(16,17); // CE, CSN

//const byte IRQ_Pin = 0;

uint8_t pipeaddress[][6] = {"1Node","2Node"};
uint8_t pipeNr;

// GUItool: begin automatically generated code
// defs for audio library
AudioPlayQueue queue1;
AudioOutputI2S i2s1;
AudioConnection patchCord1(queue1,0, i2s1, 0);
AudioConnection patchCord2(queue1, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000; 
// GUItool: end automatically generated code

uint8_t bufr[audiobuffersize * 2]; //buffer to hold 256 byte of audio data
uint8_t bufcount; //received nrf24l01+ packet counter
uint8_t databufr[32];

int k=0; //zeitmessung, debug
elapsedMicros microsec;

void setup()
{
//Serial.begin(9600);
bufcount = 0;

// Setup the SGTL5000 AIC
sgtl5000.enable(); //Enable the SGTL5000
sgtl5000.volume(0.5); //set the headphone volume
//sgtl5000_1.lineOutLevel(5, 5);
/*
sine1.frequency(300);
sine1.amplitude(0.2);
*/
AudioMemory(150); //give the Audio Library some memory to work with



radio.begin();
radio.setPayloadSize(32); //this is the maximum payload size for a packet transfer of nrf24
radio.setAutoAck(false); //no Ack
radio.disableCRC(); //no CRC
radio.setPALevel(RF24_PA_LOW);
radio.setDataRate(RF24_2MBPS);
radio.setChannel(117);
delay(100);

radio.openReadingPipe(1, pipeaddress[0]);
radio.openReadingPipe(2, pipeaddress[1]);
delay(50);

radio.startListening();
//pinMode(IRQ_Pin, INPUT_PULLUP);
//attachInterrupt(IRQ_Pin,rfRead,FALLING);
}

void loop()
{  
  if(radio.available(&pipeNr)){
    
    
    if(pipeNr==1){
      Serial.println("audiorecieved");
      if ((bufcount >= 0) && (bufcount < 8)){
      radio.read(&bufr[bufcount * 32], 32);
        // test if all 8 packets received
        if (bufcount == 7)
          {
          bufcount = 0;

          int16_t *p = queue1.getBuffer();
          memcpy(p, &bufr[0], (audiobuffersize * 2));    
          queue1.playBuffer();
          } 
        else {bufcount++;}
      }
     }
     else if(pipeNr==2){
      Serial.println("data");
     radio.read(&databufr,32);
     Serial.println(databufr[0]);
    }
   }
}

Chris
 
ill have a look in abit when i get some time , maybe monday got a lot on at present. how many radio.writes do you get a second? it seems really slow for me i need to get up to the 50k writes a second ideally but it just manages about 7k !!! the analog read side is fine but when i put in the write it kills it.
 
What do you need those many transactions per second for? Maybe you can buffer some data and send it in packages? For my particular setup and code the minimum distance between two radio.write() was about 80usec. But maybe there is some faster way, I'm not sure..
 
i am trying to replace an anaolog cable system with a wireless link, it send a minimum of 25k samples a sec but can be upto 50k. it is positional information so has to be accurate in the time domain. the receiver has to convert back to analog to send to the equipment under control. its a real old standard should have been digitised years ago but never was. unfortunately changiing the receiver to pure digitlal is not an option.
 
Maybe you could set the payload length to the minimum required for your data? don't know if it helps much, but maybe a little
 
im sending a struct of 8bit inetgers alredy setting the maxsize
radio.setPayloadSize(IldaSize * InDacRes);
dependent on number of signals i need and input dac resolution.
//Struct for data tansmission
struct IldaData{
unsigned int id : 8;
unsigned int xp : 8;
unsigned int yp : 8;
unsigned int rp : 8;
unsigned int gp : 8;
unsigned int bp : 8;
#if IldaSize > 5
unsigned int xn : 8;
unsigned int yn : 8;
#if IldaSize > 7
unsigned int rn : 8;
unsigned int gn : 8;
unsigned int bn : 8;
#endif
#endif
}SentRec,OutRec;
 
Hey Phil, finally had success transferring audio and data at the same time. I simply changed the SPI clock divider in the RF 24 Library (rf24.cpp) to a somewhat lower value.
Furthermore i found out that one can't send too soon after an address change, otherwise the receiver might register a payload under the old address.

Here you can see the MOSI of the TX on the bottom (yellow) and the MISO of the RX on the top (purple). It's so "noisy" because of the "if(radio.available())".
View attachment 18130
The spikes are an address change, 8 blocks of audio data, another address change and finally the joystick 'databufr'.
Can't really be spaced much tighter without problems, but still a little room left - maybe for some kind of redundancy check/resend of lost packets. But i couldn't get this working yet. But still it works without errors if the two NRF24L01s are not too far apart (about 2m).

Chris

Hey Chris,

I was just wondering what NRF24L01+ library are you using for this project?

Thanks.
 
Status
Not open for further replies.
Back
Top