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)