Chorded Feather Code

Using three switches with their short and long keypresses you can have 6 different functions defined for your switch control. But you may wish to add even more functionality without adding more switches. You can do so by using a "chord" system. This means that you press more than one switch simultaneous like you are playing a chord on a piano keyboard.

Suppose you have three switches we will label "1", "2", & "3". You can get an additional function by pressing "1" & "2" simultaneously or similarly "2" & "3" simultaneously. In my own personal application my switches are 3 small lever micro switches that I push with my thumb. I can bridge my thumb across two adjacent switches easily. I can also bridge all three switches and press them all simultaneously. However in that configuration I cannot press "1" & "3" simultaneously because I'm only pushing switches with with my thumb. Depending on your switch configuration and your user's capability you may be able to play the "13" chord and/or you may not be able to press all three simultaneously. Given that I can press each of the three switches individually plus the "12", "23", and "123" combinations that gives me 6 combos. If we include both long and short presses that gives me up to 12 possible switches. I use the "123" combination to switch out of switch control mode and into my IR remote control mode. You can configure this anyway you want. 

Altogether using 3 switches in all combinations and long and short presses you can get 14 different combinations. Currently there are only 13 possible functions that you can control in iOS switch control and you may not want to assign switch combinations to all of them. In our previous examples we got by really well with just six functions. However if you have a multimode application, you may want to use some of those extra capabilities in your other application.

There is however a problem using short and long keypresses when using a chorded system. In our previous code, the instant that you pressed any switch, it would send the keypress signal immediately and not send the release signal until you released the switch. However the possibility of pressing 2 switches at precisely the exact same instant is infinitesimal. That means when you tried to do a two switch chord, one of the two switches would get transmitted before you could get to the other one.

When using a chorded system, you cannot transmit the BLE signal upon pressing the switch. When you detect that a switch has been pressed, you have to wait to see if other switches are going to be pressed as well. You have to do a logical "or" of all of the switches that are pressed until such time that all of the switches have been released. Then and only then can you transmit the proper BLE signal.

For example if I press "1" and while holding it press "2" then I release "1" and then finally release "2" I want this to be transmitted as a "12" signal. But I can't transmit that signal until everything has been released.

This means that I cannot use the iOS switch control long press feature. Instead I need to keep track of how long the switch presses were in my Arduino/feather sketch and instead send a different code. So we will not be configuring iOS Switch Control to use long presses. Everything will be considered a momentary short press as far as our iOS device is concerned. However within our Arduino code we will keep track of short and long presses and send different commands based on how long the switches were pressed.

For example in the simple version of the sketch, when you press the "Select" switch it would send the letter "s". Then when you release that switch however long you held it, it would send a release command. The switch control software in iOS would determine whether it was in short or long press and act accordingly. In this chorded system, if you press the switch briefly it will send "s" but if you press it a long time it will send "l". The iOS software treats it as if you pressed an entirely different switch for a short time.

Here is the sample "chord" code.

//iOS switch control example
//Multiple mode version
#define MY_DEBUG 1
#include "BluefruitRoutines.h"

//Pin numbers for switches
#define PREVIOUS_SWITCH A0
#define SELECT_SWITCH A1
#define NEXT_SWITCH A2

//Actions
#define DO_PREVIOUS 1
#define DO_SELECT 2
#define DO_NEXT   4
#define DO_SPLIT  (DO_PREVIOUS+DO_NEXT)
#define DO_DOWN   (DO_SELECT+DO_NEXT)
#define DO_UP     (DO_PREVIOUS+DO_SELECT)
#define DO_ALL    (DO_PREVIOUS+DO_SELECT+DO_NEXT)
#define DO_LONG   8
#define DO_END    (DO_PREVIOUS+DO_LONG)
#define DO_LONG_SEL (DO_SELECT+DO_LONG)
#define DO_HOME   (DO_NEXT+DO_LONG)
#define DO_RIP    (DO_SPLIT+DO_LONG)
#define DO_PGUP   (DO_UP+DO_LONG)
#define DO_PGDN   (DO_DOWN+DO_LONG)
#define DO_EXIT   (DO_ALL+DO_LONG)

//Flag to tell if you are using switch control or other functions
uint8_t Mode;
uint8_t Prev, Buttons;
#define SWITCH_MODE 1
#define OTHER_MODE 2

uint8_t readSwitches(void) {
  return (~(digitalRead(PREVIOUS_SWITCH)*DO_PREVIOUS
      + digitalRead(SELECT_SWITCH)*DO_SELECT
      + digitalRead (NEXT_SWITCH)*DO_NEXT)
     ) & (DO_PREVIOUS+ DO_SELECT+ DO_NEXT);
}

uint8_t getChord() {
  uint32_t Delta, Start;
  uint8_t Sample;
  Prev=Buttons; //Save previous button state
  Buttons=readSwitches();//Get current button state
  if (Buttons) {
    if(Buttons!=Prev) {//If it's different, restart timer
      Delta=0; Start=millis();
    }
    do {
      delay(50);
      //Take another sample and logical bitwise "or" together
      Buttons |= (Sample = readSwitches());
      Delta=millis()-Start;//Compute difference
    } while (Sample);
    if (Delta > 1000)  Buttons |= 8;//Greater than one second is a long
  } else {
    Prev=0;
  }
  return  Buttons;
};

// This reads the switches and decides what keypresses
// to send to the BLE device.
void doSwitchMode (void) {
  uint8_t i=getChord();
  if (i) {
    ble.print(F("AT+BleKeyboard="));
    switch (i) {
      case DO_PREVIOUS: ble.println("p"); MESSAGE(F("PREV")); break; 
      case DO_SELECT:   ble.println("s"); MESSAGE(F("SELECT")); break; 
      case DO_NEXT:     ble.println("n"); MESSAGE(F("NEXT")); break;
      case DO_SPLIT:    ble.println("v"); MESSAGE(F("SPLIT")); break;  
      case DO_DOWN:     ble.println("d"); MESSAGE(F("DOWN")); break;
      case DO_UP:       ble.println("u"); MESSAGE(F("UP")); break;
      case DO_PGUP:     ble.println("t"); MESSAGE(F("PAGE UP")); break;  
      case DO_LONG_SEL: ble.println("l"); MESSAGE(F("LONG SEL")); break; 
      case DO_PGDN: ble.println("b"); MESSAGE(F("PAGE DOWN")); break;
      case DO_RIP:  ble.println("i"); MESSAGE(F("RIP")); break;
      case DO_END:  ble.println("e"); MESSAGE(F("END")); break;
      case DO_HOME: ble.println("h"); MESSAGE(F("HOME")); break;
      case DO_ALL:    
      case DO_EXIT: ble.println("a"); MESSAGE(F("ALL3")); 
          Mode=OTHER_MODE; delay(500); break;
    }
  }
}

// This routine can perform some alternate function other than
// iOS switch control. It will be engaged if you hold a button
// for a very very long time.
void doOtherMode (void) {
  /*Insert your code here*/
  MESSAGE(F("Doing other mode."));
  //For demonstration purposes we will just blink the LED pin 13
  //until we press the exit code
  digitalWrite(13, HIGH); delay(1000);
  digitalWrite(13, LOW);  delay(1000);
  if(getChord()== DO_ALL) {
    Mode=SWITCH_MODE;
    MESSAGE(F("Returning to switch mode"));  
  }
}

void setup() {
#if(MY_DEBUG)
  while (! Serial) {}; delay (500);
  Serial.begin(9600); Serial.println("Debug output");
#endif
  pinMode(SELECT_SWITCH, INPUT_PULLUP);
  pinMode(NEXT_SWITCH, INPUT_PULLUP);
  pinMode(PREVIOUS_SWITCH, INPUT_PULLUP);
  pinMode(13, OUTPUT);//Blink LED for other mode
  initializeBluefruit();
  Mode=SWITCH_MODE;
  Prev,  Buttons=0;
}

void loop() {
  switch(Mode) {
    case SWITCH_MODE: doSwitchMode(); break;
    case OTHER_MODE:  doOtherMode(); break;
  }
}

Note: This code is based on the "multimode" version of the sketch.

When configuring your iOS device, you will not configure long press versions of each of the three switches. Instead you will have as many as 13 switches to configure. 

Of course you need not actually use every combination. You only need to configure as many as you would reasonably use.

This card uses a different command to send keypresses over the BLE connection. It does not use a separate press and release command. It does it all with one command. We recommend that you use unique lowercase letters when using this command in switch control applications. We have not tested other types of keystrokes and do not recommend using anything other than lowercase letters.

Upload the sketch and open the serial monitor. Try pressing different combinations of switches for different amounts of time. We have set it up so that if you press all three switches simultaneously for either a short or long period of time, it will switch you into the other mode which will blink the pin 13 LED. Unlike the previous multimode example, we will stay in this other mode and tell you again press all three switches simultaneously. Then it will revert back into normal iOS switch control mode.

Again our "other mode" is not very useful but it's just to demonstrate the concept.

Once you have everything working properly you should disable debugging by editing the line near the top to read #define MY_DEBUG 0

Last updated on 2017-02-13 at 02.05.22 PM Published on 2017-02-13 at 01.45.01 PM