The three main sections of this code are for controlling the NeoPixels, using the MCP23017's, and sending serial commands to the Bluefruit EZ-Key.

You can find all of the code documented below.

To reference the tables I used for the serial commands, should you want to adapt the code for some other use, go to the wonderfully table-y page below.

//Written by Timothy Reese, October 11th, 2015. 
//This code is a mix of Adafruit examples and my own customization.

// Connect pin #12 of the expander to Analog 5 (i2c clock)
// Connect pin #13 of the expander to Analog 4 (i2c data)
// Connect pins #15, 16 and 17 of the expander to ground (address selection)
// Connect pin #9 of the expander to 5V (power)
// Connect pin #10 of the expander to ground (common ground)
// Connect pin #18 through a ~10kohm resistor to 5V (reset pin, active low)

// Input #0 is on pin 21 so connect a button or switch from there to ground

#include <Wire.h>
#include "Adafruit_MCP23017.h"
#include <SoftwareSerial.h>
#include <Adafruit_NeoPixel.h>

#ifdef __AVR__
  #include <avr/power.h>
#endif

Adafruit_MCP23017 mcp0;
Adafruit_MCP23017 mcp1;
Adafruit_MCP23017 mcp2;

#define PIN 6 // for the NeoPixel data line.

Adafruit_NeoPixel strip = Adafruit_NeoPixel(14, PIN, NEO_GRB + NEO_KHZ800);

SoftwareSerial BT(4,3);  //RX, and then TX. These are not your hardware serials, don't get them confused or gibberish will ensue on boot.

void setup() {  
  Serial.begin(9600);
  BT.begin(9600);
  mcp0.begin(0);      // use default address 0
  mcp1.begin(1);      // use address 1
  mcp2.begin(2);      // use address 2

  strip.begin();
  strip.show(); // Initialize all pixels to 'off'

//  This could probably be an array
  mcp0.pinMode(0, INPUT);
  mcp0.pullUp(0, HIGH);  // turn on a 100K pullup internally
  mcp0.pinMode(1, INPUT);
  mcp0.pullUp(1, HIGH);  // turn on a 100K pullup internally
  mcp0.pinMode(2, INPUT);
  mcp0.pullUp(2, HIGH);  // turn on a 100K pullup internally
  mcp0.pinMode(3, INPUT);
  mcp0.pullUp(3, HIGH);  // turn on a 100K pullup internally
  mcp0.pinMode(4, INPUT);
  mcp0.pullUp(4, HIGH);  // turn on a 100K pullup internally
  mcp0.pinMode(5, INPUT);
  mcp0.pullUp(5, HIGH);  // turn on a 100K pullup internally
  mcp0.pinMode(6, INPUT);
  mcp0.pullUp(6, HIGH);  // turn on a 100K pullup internally
  mcp0.pinMode(7, INPUT);
  mcp0.pullUp(7, HIGH);  // turn on a 100K pullup internally
  mcp0.pinMode(8, INPUT);
  mcp0.pullUp(8, HIGH);  // turn on a 100K pullup internally
  mcp0.pinMode(9, INPUT);
  mcp0.pullUp(9, HIGH);  // turn on a 100K pullup internally
  mcp0.pinMode(10, INPUT);
  mcp0.pullUp(10, HIGH);  // turn on a 100K pullup internally
  mcp0.pinMode(11, INPUT);
  mcp0.pullUp(11, HIGH);  // turn on a 100K pullup internally
  mcp0.pinMode(12, INPUT);
  mcp0.pullUp(12, HIGH);  // turn on a 100K pullup internally
  mcp0.pinMode(13, INPUT);
  mcp0.pullUp(13, HIGH);  // turn on a 100K pullup internally
  mcp0.pinMode(14, INPUT);
  mcp0.pullUp(14, HIGH);  // turn on a 100K pullup internally
  mcp0.pinMode(15, INPUT);
  mcp0.pullUp(15, HIGH);  // turn on a 100K pullup internally
  
  mcp1.pinMode(0, INPUT);
  mcp1.pullUp(0, HIGH);  // turn on a 100K pullup internally
  mcp1.pinMode(1, INPUT);
  mcp1.pullUp(1, HIGH);  // turn on a 100K pullup internally
  mcp1.pinMode(2, INPUT);
  mcp1.pullUp(2, HIGH);  // turn on a 100K pullup internally
  mcp1.pinMode(3, INPUT);
  mcp1.pullUp(3, HIGH);  // turn on a 100K pullup internally
  mcp1.pinMode(4, INPUT);
  mcp1.pullUp(4, HIGH);  // turn on a 100K pullup internally
  mcp1.pinMode(5, INPUT);
  mcp1.pullUp(5, HIGH);  // turn on a 100K pullup internally
  mcp1.pinMode(6, INPUT);
  mcp1.pullUp(6, HIGH);  // turn on a 100K pullup internally
  mcp1.pinMode(7, INPUT);
  mcp1.pullUp(7, HIGH);  // turn on a 100K pullup internally
  mcp1.pinMode(8, INPUT);
  mcp1.pullUp(8, HIGH);  // turn on a 100K pullup internally
  mcp1.pinMode(9, INPUT);
  mcp1.pullUp(9, HIGH);  // turn on a 100K pullup internally
  mcp1.pinMode(10, INPUT);
  mcp1.pullUp(10, HIGH);  // turn on a 100K pullup internally
  mcp1.pinMode(11, INPUT);
  mcp1.pullUp(11, HIGH);  // turn on a 100K pullup internally
  mcp1.pinMode(12, INPUT);
  mcp1.pullUp(12, HIGH);  // turn on a 100K pullup internally
  mcp1.pinMode(13, INPUT);
  mcp1.pullUp(13, HIGH);  // turn on a 100K pullup internally
  mcp1.pinMode(14, INPUT);
  mcp1.pullUp(14, HIGH);  // turn on a 100K pullup internally
  mcp1.pinMode(15, INPUT);
  mcp1.pullUp(15, HIGH);  // turn on a 100K pullup internally

  mcp2.pinMode(0, INPUT);
  mcp2.pullUp(0, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode(1, INPUT);
  mcp2.pullUp(1, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode(2, INPUT);
  mcp2.pullUp(2, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode(3, INPUT);
  mcp2.pullUp(3, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode(4, INPUT);
  mcp2.pullUp(4, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode(5, INPUT);
  mcp2.pullUp(5, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode(6, INPUT);
  mcp2.pullUp(6, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode(7, INPUT);
  mcp2.pullUp(7, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode(8, INPUT);
  mcp2.pullUp(8, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode(9, INPUT);
  mcp2.pullUp(9, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode(10, INPUT);
  mcp2.pullUp(10, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode(11, INPUT);
  mcp2.pullUp(11, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode(12, INPUT);
  mcp2.pullUp(12, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode(13, INPUT);
  mcp2.pullUp(13, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode(14, INPUT);
  mcp2.pullUp(14, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode(15, INPUT);
  mcp2.pullUp(15, HIGH);  // turn on a 100K pullup internally

 // pinMode(13, OUTPUT);  // use the p13 LED as debugging
}

// For the Raw HID commands, here's the function.

void keyCommand(uint8_t modifiers, uint8_t keycode1, uint8_t keycode2 = 0, uint8_t keycode3 = 0, 
                uint8_t keycode4 = 0, uint8_t keycode5 = 0, uint8_t keycode6 = 0) {
  BT.write(0xFD);       // our command
  BT.write(modifiers);  // modifier!
  BT.write((byte)0x00); // 0x00  
  BT.write(keycode1);   // key code #1
  BT.write(keycode2); // key code #2
  BT.write(keycode3); // key code #3
  BT.write(keycode4); // key code #4
  BT.write(keycode5); // key code #5
  BT.write(keycode6); // key code #6
}

void loop() {
  // The LED will 'echo' the button - used for debugging.
//  digitalWrite(13, mcp0.digitalRead(0));

//NeoPixel Arcade Button Colors

strip.setPixelColor(0, 255, 165, 0);
strip.setPixelColor(1, 255, 165, 0);
strip.setPixelColor(2, 255, 165, 0);
strip.setPixelColor(3, 255, 165, 0);
strip.setPixelColor(4, 255, 165, 0);
strip.setPixelColor(5, 255, 165, 0);
strip.setPixelColor(6, 255, 0, 0);
strip.setPixelColor(7, 255, 0, 0);
strip.setPixelColor(8, 255, 0, 0);
strip.setPixelColor(9, 255, 0, 0);
strip.setPixelColor(10, 255, 0, 0);
strip.setPixelColor(11, 255, 0, 0);
strip.setPixelColor(12, 255, 0, 0);
strip.setPixelColor(13, 255, 165, 0);
strip.show();

  
// All keyCommands are from the Raw HID Keyboard Reports section, and you just count up from 4, 
//starting at 4 on the list. So "B" would be a 5.
// Likewise "I" would be a 12.
// The BT.write commands are using the Hex values for ASCII.
// I've included both because I used both while writing the original code, and also so you can 
//pick and choose if you wish to use it for another project.
  
  //Preview 1
  
  if (mcp0.digitalRead(0) == LOW) {
    Serial.println("Success Preview 1!");
  keyCommand(0,30);
  strip.setPixelColor(6, 255, 255, 255);
  strip.show();
  delay(250);
  keyCommand(0,0);
  }
  
  //Preview 2

if (mcp0.digitalRead(1) == LOW) {
    Serial.println("Success Preview 2!");
   keyCommand(0,31);
   strip.setPixelColor(7, 255, 255, 255);
  strip.show();
     delay(250);
   keyCommand(0,0);

  }

  //Preview 3

if (mcp0.digitalRead(2) == LOW) {
    Serial.println("Success Preview 3!");
   keyCommand(0,32);
   strip.setPixelColor(8, 255, 255, 255);
  strip.show();
     delay(250);
   keyCommand(0,0);

  }

  //Preview 4

if (mcp0.digitalRead(3) == LOW) {
    Serial.println("Success Preview 4!");
   keyCommand(0,33);
   strip.setPixelColor(9, 255, 255, 255);
  strip.show();
     delay(250);
   keyCommand(0,0);

  }

  //Preview 5

if (mcp0.digitalRead(4) == LOW) {
    Serial.println("Success Preview 5!");
   keyCommand(0,34);
   strip.setPixelColor(10, 255, 255, 255);
   strip.show();
     delay(250);
   keyCommand(0,0);

  }

  //Preview 6

if (mcp0.digitalRead(5) == LOW) {
    Serial.println("Success Preview 6!");
   keyCommand(0,35);
   strip.setPixelColor(11, 255, 255, 255);
   strip.show();
     delay(250);
   keyCommand(0,0);

  }

  //Auto Transition (Space)

if (mcp0.digitalRead(6) == LOW) {
    Serial.println("Success Auto Transition!");
   keyCommand(0,44);
   strip.setPixelColor(12, 255, 255, 255);
   strip.show();
   delay(250);
   keyCommand(0,0);

  }


  //Prog 1

if (mcp0.digitalRead(7) == LOW) {
    Serial.println("Success F1!");
  BT.write(0x0F);
  strip.setPixelColor(5, 255, 255, 255);
  strip.show();
     delay(250);
  }

  //Prog 2

if (mcp0.digitalRead(8) == LOW) {
    Serial.println("Success F2!");
  BT.write(0x10);
  strip.setPixelColor(4, 255, 255, 255);
  strip.show();
     delay(250);
  }

  //Prog 3

if (mcp0.digitalRead(9) == LOW) {
    Serial.println("Success F3!");
  BT.write(0x11);
  strip.setPixelColor(3, 255, 255, 255);
  strip.show();
     delay(250);
  }

  //Prog 4

if (mcp0.digitalRead(10) == LOW) {
    Serial.println("Success F4!");
  BT.write(0x12);
  strip.setPixelColor(2, 255, 255, 255);
  strip.show();
     delay(250);

  }

  //Prog 5

if (mcp0.digitalRead(11) == LOW) {
    Serial.println("Success F5!");
  BT.write(0x13);
  strip.setPixelColor(1, 255, 255, 255);
  strip.show();
     delay(250);
  }

  //Prog 6

if (mcp0.digitalRead(12) == LOW) {
    Serial.println("Success F6!");
  BT.write(0x14);
  strip.setPixelColor(0, 255, 255, 255);
  strip.show();
     delay(250);
  }

  //Cut (enter)

if (mcp0.digitalRead(13) == LOW) {
    Serial.println("Success Cut!");
   BT.write(0x0A);
   strip.setPixelColor(13, 255, 255, 255);
   strip.show();
     delay(250);

  }

  // Record (home)

if (mcp0.digitalRead(14) == LOW) {
    Serial.println("Success Record!");
  BT.write(0x02);
     delay(250);
  }

  // Go Live (end)

if (mcp0.digitalRead(15) == LOW) {
    Serial.println("Success Go Live!");
  BT.write(0x05);
     delay(250);
  }
  

  // Edit Media 1 (z)

if (mcp1.digitalRead(0) == LOW) {
    Serial.println("Success Edit Media 1!");
  BT.write(0x7A);
     delay(250);
  }
  

  // Edit Graphic 1 (q)

if (mcp1.digitalRead(1) == LOW) {
    Serial.println("Success Edit Graphic 1!");
  BT.write(0x71);
     delay(250);
  }
  

  // Edit Graphic 2 (w)

if (mcp1.digitalRead(2) == LOW) {
    Serial.println("Success Edit Graphic 2!");
  BT.write(0x77);
     delay(250);
  }


  // Edit Graphic 3 (e)

if (mcp1.digitalRead(3) == LOW) {
    Serial.println("Success Edit Graphic 3!");
  BT.write(0x65);
     delay(250);
  }
  
  
  // Go to Start (g)

if (mcp1.digitalRead(4) == LOW) {
    Serial.println("Success Go to Start!");
  BT.write(0x67);
     delay(250);
  }
  
  
  // Play/Pause (k)

if (mcp1.digitalRead(5) == LOW) {
    Serial.println("Success Play/Pause!");
  BT.write(0x6b);
     delay(250);
  }
  

  // Go to End (')

if (mcp1.digitalRead(6) == LOW) {
    Serial.println("Success Go to End!");
  BT.write(0x27);
     delay(250);
  }

  // Edit Media 2 (x)

if (mcp1.digitalRead(7) == LOW) {
    Serial.println("Success Edit Media 2!");
  BT.write(0x78);
     delay(250);
  }
  
    // Edit Audio (a)

if (mcp1.digitalRead(8) == LOW) {
    Serial.println("Success Edit Audio!");
  BT.write(0x61);
     delay(250);
  }

  // Edit Stream (s)

if (mcp1.digitalRead(9) == LOW) {
    Serial.println("Success Edit Stream!");
  BT.write(0x73);
     delay(250);
  }

  // Edit Transition (d)

if (mcp1.digitalRead(10) == LOW) {
    Serial.println("Success Edit Transition!");
  BT.write(0x64);
     delay(250);
  }

  // Go to In (u)

if (mcp1.digitalRead(11) == LOW) {
    Serial.println("Success Go to In!");
  BT.write(0x75);
     delay(250);
  }

  // Set In (i)

if (mcp1.digitalRead(12) == LOW) {
    Serial.println("Success Set In!");
  BT.write(0x69);
     delay(250);
  }

  // Set Out (o)

if (mcp1.digitalRead(13) == LOW) {
    Serial.println("Success Set Out!");
  BT.write(0x6F);
     delay(250);
  }

  // Graphics 1 Preview (Numpad 1)

if (mcp1.digitalRead(14) == LOW) {
    Serial.println("Success Graphics 1 Preview!");
   keyCommand(0,89);
     delay(250);
   keyCommand(0,0);

  }
  
  // Graphics 2 Preview (Numpad 2)

if (mcp1.digitalRead(15) == LOW) {
    Serial.println("Success Graphics 2 Preview!");
   keyCommand(0,90);
     delay(250);
   keyCommand(0,0);

  }

  // Graphics 3 Preview (Numpad 3)

if (mcp2.digitalRead(0) == LOW) {
    Serial.println("Success Graphics 3 Preview!");
   keyCommand(0,91);
     delay(250);
   keyCommand(0,0);

  }

  // Graphics 1 Push (Numpad 4)

if (mcp2.digitalRead(1) == LOW) {
    Serial.println("Success Graphics 1 Push!");
   keyCommand(0,92);
     delay(250);
   keyCommand(0,0);

  }

  // Graphics 2 Push (Numpad 5)

if (mcp2.digitalRead(2) == LOW) {
    Serial.println("Success Graphics 2 Push!");
   keyCommand(0,93);
     delay(250);
   keyCommand(0,0);

  }

  // Graphics 3 Push (Numpad 6)

if (mcp2.digitalRead(3) == LOW) {
    Serial.println("Success Graphics 3 Push!");
   keyCommand(0,94);
     delay(250);
   keyCommand(0,0);

  }

  // Graphics 1 Pull (Numpad 7)

if (mcp2.digitalRead(4) == LOW) {
    Serial.println("Success Graphics 1 Pull!");
   keyCommand(0,95);
     delay(250);
   keyCommand(0,0);

  }

  // Graphics 2 Pull (Numpad 8)

if (mcp2.digitalRead(5) == LOW) {
    Serial.println("Success Graphics 2 Pull!");
   keyCommand(0,96);
     delay(250);
   keyCommand(0,0);
   
  }

  // Graphics 3 Pull (Numpad 9)

if (mcp2.digitalRead(6) == LOW) {
    Serial.println("Success Graphics 3 Pull!");
   keyCommand(0,97);
   delay(250);
   keyCommand(0,0);
     
  }

}

//This is a breakdown of the Function Commands via Hex, 
//since they're not spelled out on the Bluefruit guide.
//0x0F  = F1
//0x10 = F2
//0x11 = F3
//0x12 = F4
//0x13 = F5
//0x14  = F6
//0x15  = F7
//0x16  = F8
//0x17  = F9
//0x18  = F10
//0x19  = F11
//0x1A  = F12

// https://learn.adafruit.com/introducing-bluefruit-ez-key-diy-bluetooth-hid-keyboard/sending-keys-via-serial
// http://www.instructables.com/id/different-ways-to-count/
This guide was first published on Nov 29, 2015. It was last updated on Nov 17, 2018. This page (Code) was last updated on Nov 29, 2015.