By popular request, here's an update to the first code for the Button Pad Controller SPI. This sketch now includes interactive feedback, in the form of changing colours based on key-presses.

[caption id="" align="alignnone" width="267" caption="Click to view full size image."]Click view full size image.[/caption] [caption id="" align="alignnone" width="267" caption="Click to play video."]Click to play video.[/caption]

Download the sketch here.

// The wires
#define CS 10
#define MISO 11
#define MOSI 12
#define SCK  13

// Frame buffer for lights
int lights = 16;
int depth = 3;
byte buf[16][3];

boolean wait = false;
boolean ready = true;

void setup() {
  pinMode(CS, OUTPUT);
  pinMode(MISO, OUTPUT);
  pinMode(MOSI, INPUT);
  pinMode(SCK, OUTPUT);

  digitalWrite(CS, LOW);
  delay(100);

  blank();

  Serial.begin(9600);
  Serial.println("Starting");
}

void blank() {
  // init frame buffer
  for(int l = 0; l < lights; l++) {
    for(int d = 0; d < depth; d++) {
      buf[l][d] = 0;
    }
  }
}

void loop() {
  digitalWrite(SCK, HIGH);
  digitalWrite(CS, HIGH);
  delayMicroseconds(15);

  // Set lights: 3 x 16 bytes
  for(int d = 0; d < depth; d++) {
      for(int l = 0; l < lights; l++) {
        writeByte(buf[l][d]);
      }
  }

  if(!ready) {
    wait = true;
    ready = true;
  }

  // Read the buttons: 16 bytes.
  for(int l = 0; l < lights; l++) {
    // readByte returns a non-zero value when a button is pressed.
    byte b = readByte();

    // If a button is pressed, change the colour.
    if (b > 0 && ready && !wait) {
      if      (buf[l][2] > 0) { buf[l][2] = 0; }
      else if (buf[l][1] > 0) { buf[l][1] = 0; buf[l][2] = 255; }
      else if (buf[l][0] > 0) { buf[l][0] = 0; buf[l][1] = 255; }
      else {                                   buf[l][0] = 255; }

      Serial.print("Button ");
      Serial.print(l);
      Serial.print(": ");
      Serial.print((int)b);
      Serial.println();

      // Change colours slowly.
      ready = false;
    }
  }

  digitalWrite(CS, LOW);

  // After the second cycle, get ready to read new button clicks.
  if(wait) {
    delay(500);
    wait = false;
    ready = true;
  }

  delayMicroseconds(400);
}

// Write out a byte.
// This is for a single colour channel, for a single button.
void writeByte(byte data) {
  for(int i = 0; i < 8; i++) {
    digitalWrite(SCK, LOW);
    delayMicroseconds(5);

    digitalWrite(MISO, (data & (1 << i)) >> i);
    delayMicroseconds(5);

    digitalWrite(SCK, HIGH);
    delayMicroseconds(10);
  }
}

// Read a byte.
// Returns the value for a single button.
// Non-zero if it is pressed, or 0 otherwise.
byte readByte() {
  byte result = 0;
  for(int i = 0; i < 8; i++) {
    digitalWrite(SCK, LOW);
    delayMicroseconds(5);

    result += (!digitalRead(MOSI)) * i;
    delayMicroseconds(5);

    digitalWrite(SCK, HIGH);
    delayMicroseconds(10);
  }

  return result;
}