Slow Rotary Encoder Keystroke on Bluefruit Feather 32u4

I’m still relatively new to arduino. I’ve been making a bluetooth controller to send HID keystrokes to a bluetooth-connected computer using a Bluefruit Feather 32u4. I’m using a rotary encoder to send keystrokes on every increment, but the keystrokes do not keep up with how fast I am rotating the wheel (the wheel is read on an interrupt, but the keystrokes are sent as part of the key report built in the main loop). Can anyone give me some guidance as to how to improve its responsiveness?

I logged keystrokes in the serial monitor, and more keystrokes are reported there than actually make it to the connected device as well.
Thanks for your time and help; full code is attached as a zip.

I broke it up into code blocks below for readability; setup, main loop, helpers, and a setup file that dictates some pinout for the board itself.

Setup:

        // Set up keyboard report variables:
        typedef struct
        {
          uint8_t modifier;   /**< Keyboard modifier keys  */
          uint8_t reserved;   /**< Reserved for OEM use, always set to 0. */
          uint8_t keycode[6]; /**< Key codes of the currently pressed keys. */
        } hid_keyboard_report_t;
        
        // Report that sends to Central every scanning period
        hid_keyboard_report_t keyReport = { 0, 0, { 0 } };
        
        // Report sent previously. This is used to prevent sending the same report over time.
        // Notes: HID Central intepretes no new report as no changes, which is the same as
        // sending very same report multiple times. This will help to reduce traffic especially
        // when most of the time there is no keys pressed.
        // - Init to different with keyReport
        hid_keyboard_report_t previousReport = { 0, 0, { 1 } };
        
        // Rotary encoder pins and helpers
        // usually the rotary encoders three pins have the ground pin in the middle
        enum PinAssignments {
          encoderPinA = 2,   // center pin, green wire on proto
          encoderPinB = 3,   // left pin, blue wire on proto
          encoderButton = 5    // switch (separate button)
        // connect the +5v and gnd appropriately - no +V; gnd is right pin, yellow wire
        };
        
        volatile unsigned int encoderPos = 0;  // a counter for the dial
        unsigned int lastReportedPos = 1;   // change management
        static boolean rotating=false;      // debounce management
        
        // interrupt service routine vars
        boolean A_set = false;              
        boolean B_set = false;
        //end rotary encoder pins and helpers
    /**************************************************************************/
    /*!
        @brief  Sets up the HW an the BLE module (this function is called
                automatically on startup)
    */
    /**************************************************************************/
    void setup(void)
    {
      //while (!Serial);  // required for Flora & Micro
      delay(500);
    
      Serial.begin(115200); 
    
      /* Initialise the module */
      Serial.print(F("Initialising the Bluefruit LE module: "));
    
      if ( !ble.begin(VERBOSE_MODE) )
      {
        error(F("Couldn't find Bluefruit, make sure it's in Command mode & check wiring?"));
      }
      Serial.println( F("OK!") );
    
      if ( FACTORYRESET_ENABLE )
      {
        /* Perform a factory reset to make sure everything is in a known state */
        Serial.println(F("Performing a factory reset: "));
        ble.factoryReset();
      }
    
      /* Disable command echo from Bluefruit */
      ble.echo(false);
    
      Serial.println("Requesting Bluefruit info:");
      /* Print Bluefruit information */
      ble.info();
    
      /* Enable HID Service if not enabled */
      int32_t hid_en = 0;
    
      if (! ble.sendCommandCheckOK(F( "AT+GAPDEVNAME=Spider" )) ) {
        error(F("Could not set device name?"));
      }
      
      ble.sendCommandWithIntReply( F("AT+BleHIDEn"), &hid_en);
    
      if ( !hid_en )
      {
        Serial.println(F("Enable HID Service (including Keyboard): "));
        ble.sendCommandCheckOK(F( "AT+BleHIDEn=On" ));
    
        /* Add or remove service requires a reset */
        Serial.println(F("Performing a SW reset (service changes require a reset): "));
        !ble.reset();
      }
      
      Serial.println();
      Serial.println(F("Go to your phone's Bluetooth settings to pair your device"));
      Serial.println(F("then open an application that accepts keyboard input"));
      Serial.println();
    
      // Encoder interrupt setup
      pinMode(encoderPinA, INPUT_PULLUP); // new method of enabling pullups
      pinMode(encoderPinB, INPUT_PULLUP); 
      pinMode(encoderButton, INPUT_PULLUP);
    
      // encoder pin on interrupt 0 (pin 2)
      attachInterrupt(0, doEncoderA, CHANGE);
      // encoder pin on interrupt 1 (pin 3)
      attachInterrupt(1, doEncoderB, CHANGE);
    
      // End encoder setup
    }

Main Loop:

  void loop(void)
    {
    
      /* scan GPIO, since each report can have up to 6 keys
       * we can just assign a slot in the report for each GPIO 
       */
      if ( ble.isConnected() )
      {
        
        //Rotary Encoder logic
        rotating = true;  // reset the debouncer
    
        if (lastReportedPos != encoderPos) {
        //Serial.print("Testing Key Command:");
        //Serial.println(encoderPos, DEC);
    
        // Send key commands
          if (lastReportedPos < encoderPos) {
            Serial.print("[");
            // need to report "[" keycode: 0x2f HID_KEY_BRACKET_LEFT
            keyReport.keycode[5] = HID_KEY_BRACKET_LEFT;
          }
          else if (lastReportedPos > encoderPos) {
            Serial.print("]");
            // need to report "]" keycode: 0x30 HID_KEY_BRACKET_RIGHT
            keyReport.keycode[5] = HID_KEY_BRACKET_RIGHT;
          }
        
        lastReportedPos = encoderPos;
        } else {
          keyReport.keycode[5] = 0;
        }
      //End Rotary Encoder logic
    
        sendKeyReport();
    
        } //end of ifBLEconnected condition
     
    //   // scanning period is 5 ms
      delay(5);
    
    } //end of main loop

Helper functions and Interrupts

    // Interrupt on A changing state
    void doEncoderA(){
      // debounce
      if ( rotating ) delay (1);  // wait a little until the bouncing is done
    
      // Test transition, did things really change? 
      if( digitalRead(encoderPinA) != A_set ) {  // debounce once more
        A_set = !A_set;
    
        // adjust counter + if A leads B
        if ( A_set && !B_set ) 
          encoderPos += 1;
          
        rotating = false;  // no more debouncing until loop() hits again
      }
    }
    
    // Interrupt on B changing state, same as A above
    void doEncoderB(){
      if ( rotating ) delay (1);
      if( digitalRead(encoderPinB) != B_set ) {
        B_set = !B_set;
        //  adjust counter - 1 if B leads A
        if( B_set && !A_set ) 
          encoderPos -= 1;
    
        rotating = false;
      }
    }
    
    void sendKeyReport() {
      // Only send if it is not the same as previous report
          if ( memcmp(&previousReport, &keyReport, 8) )
          {
            // Send keyboard report
            ble.atcommand("AT+BLEKEYBOARDCODE", (uint8_t*) &keyReport, 8);
    
            // copy to previousReport
            memcpy(&previousReport, &keyReport, 8);
          }
    
          if (keyReport.keycode[5] != 0) {
            //testing to see if this is what is slowing down my rotary encoder
            // Send keyboard report
            ble.atcommand("AT+BLEKEYBOARDCODE", (uint8_t*) &keyReport, 8);
          }
    }

Slowtary_Encoder.zip (5.35 KB)

Can anyone give me some guidance as to how to improve its responsiveness?

Remove line 239:

  delay(5);