MIDI transmission via NRF24 slow

I'm trying to make a MIDI keyboard wireless by adding an arduino (nano CHG340 clone from amazon) and an NRF24 module.
The code I have now does work, however it's nowhere near as fast as I expected it to be. I know (from testing) that with this packet size I can get a maximum of 1150 packets/second if the arduino is doing nothing else. However, I only seem to be getting 10-15 packets per second maximum when reading from the serial pin. I'd like to see this number closer to 40-50.
I have Rx connected to the midi port via an optocoupler, and that works fine even at high speeds. My problem is that if, say, I play notes fast or turn a MIDI CC knob, it will continue transmitting for between 10-15 seconds after I stop touching the keyboard. (it's bad enough that I can back it up by rapidly tapping just 2 keys)
Obviously the serial input buffer is backing up, and I can't figure out why, as I know the NRF24 runs fast enough to not be the problem, and I assume the Serial library would be fast enough to handle this.

Any help with this would be greatly appreciated.

/*
NRF24    Arduino
CE    -> 9
CSN   -> 10 (Hardware SPI SS)
MOSI  -> 11 (Hardware SPI MOSI)
MISO  -> 12 (Hardware SPI MISO)
SCK   -> 13 (Hardware SPI SCK)
IRQ   -> No connection
VCC   -> No more than 3.6 volts
GND   -> GND
*/

#include <SPI.h>
#include <NRFLite.h>

uint8_t RADIO_ID = 2;
// Transmitter radio ID
uint8_t DESTINATION_RADIO_ID = 1;
// Reciever radio ID
const static uint8_t PIN_RADIO_CE = 9;
const static uint8_t PIN_RADIO_CSN = 10;
//radio pins

struct RadioPacket
{
 uint8_t FromRadioId;
 uint8_t Data1;
 uint8_t Data2;
 uint8_t Data3;
};
NRFLite _radio;
RadioPacket _radioData;

void setup(){
 Serial.begin(31250); //MIDI input baud rate
 _radio.init(RADIO_ID, PIN_RADIO_CE, PIN_RADIO_CSN);// default radio settings, channel 100 @2MBps
 _radioData.FromRadioId = RADIO_ID;
}

void loop(){
 while (Serial.available()>2){//if at least 3 bytes available (1 packet)
  _radioData.Data1=Serial.read();
  _radioData.Data2=Serial.read();
  _radioData.Data3=Serial.read();
  //read one midi packet
  _radio.send(DESTINATION_RADIO_ID, &_radioData, sizeof(_radioData), NRFLite::NO_ACK);
  //send packet via NRF24
  }
 }

 //void serialEvent(){}
 //also tried putting the while loop inside serialEvent(), no change in speed

(yes, I've verified the reciever can handle faster inputs)

I am not familiar with the NRFLIte library. The examples in my Simple nRF24L01+ Tutorial use the TMRh20 version of the RF24 library

I don't see anything in your short program that would slow things down - other than the serial input. Wireless requires a receiving program as well as a transmitting program - maybe that is where the problem lies?

Separately ... I think your program will work just as well if you change your WHILE to an IF and allow loop() to do the repetition.

You do run the risk of your 3 bytes getting out of sync as you have no means to check whether the first byte really is the first of the three. I am not familiar with MIDI so I have no idea how likely that is.

...R

I did at one point have code that would verify it was a valid packet (midi always has three bytes starting 1,0,0 respectively), write it to an array, and then another function would read from that array and transmit, acting as an input buffer.
However, it was running slowly, so I tried to minimize my code, but this current sketch behaves exactly the same way (except it only has a 21-packet hardware buffer instead of a 256-packet software buffer).
I know the NRFlite library isn't to blame, because I tested this same code with static data instead of serial input, and that was able to reach 1148 packets per second (verified by a reciever sketch).
I know the keyboard is sending packets correctly, because I can plug it into my other midi equipment and it works fine (midi is one-way, so it won't be waiting for an acknowledgement or anything).
I know my baud rate is correct, and I know the radios are all connected correctly.
The only thing that could be slowing it down, to my (admittedly somewhat limited) knowledge, is the serial library. This is where I wish I was using the official IDE, but I'm using ArduinoDroid instead. I don't have a computer currently, and AD has worked flawlessly for me, so I don't think it's the issue. (as it's an app on an un-rooted phone, I can't just open the included hardware libraries to check them because they're in protected app storage).
Are there any known issues with the Serial library, or any different ways of reading the serial buffer I could try?

I'll post my current reciever code below, it's really simple and it can handle the packets I'm sending it at just about any speed.

//Wireless Midi Reciever

/*
NRF24    Arduino
CE    -> 9
CSN   -> 10 (Hardware SPI SS)
MOSI  -> 11 (Hardware SPI MOSI)
MISO  -> 12 (Hardware SPI MISO)
SCK   -> 13 (Hardware SPI SCK)
IRQ   -> No connection
VCC   -> 3.3v regulator
GND   -> GND
*/

#include <SPI.h>
#include <NRFLite.h>

byte RADIO_ID = 1;
// Unit ID
const static uint8_t PIN_RADIO_CE = 9;
const static uint8_t PIN_RADIO_CSN = 10;

struct RadioPacket // Any packet up to 32 bytes can be sent.
{
 uint8_t FromRadioId;
 uint8_t Data1;
 uint8_t Data2;
 uint8_t Data3;
};
NRFLite _radio;
RadioPacket _radioData;

void setup()
{
 Serial.begin(31250);//transmittery
 //Serial.begin(57600); Serial.println("On Line");//debuggery

    if (!_radio.init(RADIO_ID, PIN_RADIO_CE, PIN_RADIO_CSN))
    {
  //Serial.println("Cannot communicate with radio");
  digitalWrite(13,HIGH);
  while (1); // Wait here forever.
  }
}

void loop()
{
 while (_radio.hasData())
  {
  _radio.readData(&_radioData);
  Serial.write(_radioData.Data1);
  Serial.write(_radioData.Data2);
  Serial.write(_radioData.Data3);
  }
 }

Have you tried my suggestion of replacing WHILE with IF ?

...R

Yes, after replacing the while() loop with an if() statement, it acts exactly the same (which makes sense as it's still just looping the one statement).
Do you think Serial.readBytes() could somehow work better than Serial.read()?

theaechbomb:
Do you think Serial.readBytes() could somehow work better than Serial.read()?

You could try it but I would expect it to be worse as it waits for input rather than responding immediately.

What happens if you modify the Tx program so that, rather than use wireless , it simply prints the values to the Serial Monitor? If that is fast enough then you know that the serial reception of the midi data is working fine.

I think you said you have a version of the Tx program that does work at the speed you want - can you post that program?

...R

I do have a transmission sketch that runs very fast, and all I changed is removing the serial read code.
it transmits midi pitchbend code, and the reciever can verify that it recieves 1140+ packets per second.
I just don't understand why the serial library specifically would be slowing it down.

#include <SPI.h>
#include <NRFLite.h>
byte PB=0;

byte RADIO_ID = 2;
// Transmitter ID
byte DESTINATION_RADIO_ID = 1;
// Reciever ID
const static uint8_t PIN_RADIO_CE = 9;
const static uint8_t PIN_RADIO_CSN = 10;

struct RadioPacket 
{
 uint8_t FromRadioId;
 uint8_t Data1;
 uint8_t Data2;
 uint8_t Data3;
};
NRFLite _radio;
RadioPacket _radioData;

void setup(){
 Serial.println("Setup");
 if(_radio.init(RADIO_ID, PIN_RADIO_CE, PIN_RADIO_CSN)){
  Serial.println("Initialized");
  }
 //_radio.init(RADIO_ID, PIN_RADIO_CE, PIN_RADIO_CSN, NRFLite::BITRATE2MBPS, 100);
 // default radio settings
 _radioData.FromRadioId = RADIO_ID;
}

void loop(){
 PB++;
 if(PB==128){
  PB=0;
  }
  _radioData.Data1 = 0b11100000; //midi channel 1 pitchbend
  _radioData.Data2 = PB;
  _radioData.Data3 = PB;
  _radio.send(DESTINATION_RADIO_ID, &_radioData, sizeof(_radioData), NRFLite::NO_ACK);
 }

theaechbomb:
I do have a transmission sketch that runs very fast, and all I changed is removing the serial read code.

You also need to do a test with the opposite - remove all the wireless code and test how serial is performing, as I suggested in Reply #5

...R

(midi always has three bytes starting 1,0,0 respectively),

That is not quite true, there are single byte commands as well, but the start of a command always has bit 7 HIGH and any data bytes always have bit 7 LOW. For the rest it is a 7-bit transfer within an 8-bit format. The designers thought the whole thing through in the mid-seventies when data transmission was much more challenging than these days.

I wrote and tested a serial test sketch, all it does is switch the state of an LED (pin 13) when it recieves a packet. It still puts the serial input into a 4-byte strucrure and everything too.
This sketch works as fast as I can, even twisting multiple CC knobs.
At this point I'm assuming my problem is some conflict between NRFlite and the Serial library, I'll try switching it over to another NRF library as suggested earlier on.

uint8_t TEST_ID = 2;
long int time = 0;
struct TestPacket
{
 uint8_t testId;
 uint8_t Data1;
 uint8_t Data2;
 uint8_t Data3;
};
TestPacket _testData;

void setup(){
 Serial.begin(31250); //MIDI input baud rate
 _testData.testId = TEST_ID;
}

void loop(){
 while (Serial.available()>2){//if at least 3 bytes available (1 packet)
  _testData.Data1=Serial.read();
  _testData.Data2=Serial.read();
  _testData.Data3=Serial.read();
  //read one midi packet
  digitalWrite(13,(1^digitalRead(13)));
  time=millis();
  }
 if ((millis()-time)>1000){
  digitalWrite(13,LOW);
  time=millis();
  }
 }

(sorry if this is a double post, I thought I replied earlier, but it's not showing up)

I switched over to using the RF24 library, and after being very confused about the example sketches (mostly because I still don't actually understand the NRF24 in full detail, data pipes and such), I managed to get a sketch working.
It's much faster than before, and while there is still a tiny bit of slowdown with fast packet streams, it'll work perfectly for my use case.
Thank you for the link to the better library, I never would've realized that was the problem.

theaechbomb:
after being very confused about the example sketches

If you need help with any of the example programs in my Tutorial please let me know what part you don't understand.

...R

the problem isn't your tutorial (actually, I forgot you linked it, I'm giving it a read now), the problem was that NRFlite was so simple (set channel, give it an ID, tell it what radio to send to, give it data, send) that I didn't understand anything about how the radio actually functions at a base level, so it took me awhile to figure it out from the library's example sketches.

your tutorial looks really good, and I'll probably be improving my sketch soon because of it (I'm planning to add batteries to the keyboard anyway, so I might as well update the sketch too)

thank you for all the help troubleshooting!