Software

I used QMK for the firmware. It has a lot of features, support and is actively developed.

QMK uses the concept of layers. Each layer can have a defined action per key. The easiest way to think of this is by comparing it to the shift key. Without Shift when pressing the 1 you get a 1. With the Shift key down, when you hit the 1 key, instead of getting a 1 you get a !. Layers work the same way. You can designate keys that change their behavior depending on what layer you are on, or they can remain the same. Layers work by stacking, so if a key has a default action of 1 and nothing is defined for the active layer, then the default action takes over.

layout

To configure QMK I tell it about the keyboard (how many columns, rows, etc..) and then what pins are used for each. Once done I can then start creating the keyboard layout. Here I define four layers, and then the matrix layout.

QMK Layout

I use a KVM and it has a keyboard combination that allows me to switch machines. This keyboard combo is Scroll Lock, Scroll Lock and then a number (1-4). I defined the custom keycodes to a key here. This allows me to press a single key and have the computer register the appropriate keyboard combo.

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
  switch (keycode) {
    case KVM_1: // Send code to KVM to switch to computer 1
      if (record->event.pressed) {
        SEND_STRING(SS_TAP(X_SCROLLLOCK));
        SEND_STRING(SS_TAP(X_SCROLLLOCK));
        SEND_STRING("1");
      } else {
        // nothing goes here
      }
      break;
    case KVM_2: // Send code to KVM to switch to computer 2
      if (record->event.pressed) {
        SEND_STRING(SS_TAP(X_SCROLLLOCK));
        SEND_STRING(SS_TAP(X_SCROLLLOCK));
        SEND_STRING("2");
      } else {
        // nothing goes here
      }
      break;
    case KVM_3: // Send code to KVM to switch to computer 3
      if (record->event.pressed) {
        SEND_STRING(SS_TAP(X_SCROLLLOCK));
        SEND_STRING(SS_TAP(X_SCROLLLOCK));
        SEND_STRING("3");
      } else {
        // nothing goes here
      }
      break;
    case KVM_4: // Send code to KVM to switch to computer 4
      if (record->event.pressed) {
        SEND_STRING(SS_TAP(X_SCROLLLOCK));
        SEND_STRING(SS_TAP(X_SCROLLLOCK));
        SEND_STRING("4");
      } else {
        // nothing goes here
      }
      break;
  }
  return true;
}

Display

This is still a work in progress!

I don’t have a firm grasp of the display code yet… But I was able to define the logo using a custom font file and then defining the following:

logo

The Num Lock and Caps Lock indication is defined here.

static const char PROGMEM numlock_on[] = {
  0x4E, 0x55, 0x4D, 0x20, 0x20, 0x99, 0x9A, 0x00
};

static const char PROGMEM numlock_off[] = {
  0x20, 0x20, 0x20, 0x20, 0x20, 0x99, 0x9A, 0x00
};

static const char PROGMEM capslock_on[] = {
  0x43, 0x41, 0x50, 0x20, 0x20, 0xB9, 0xBA, 0x00
};

static const char PROGMEM capslock_off[] = {
  0x20, 0x20, 0x20, 0x20, 0x20, 0xB9, 0xBA, 0x00
};

Then all the display parts are brought together and displayed with this code. I wish I could define a variable for the version number, and then concatenate it, but I have so far been unable to do so.

static void render_status(void) {
  // Name and version info
  /* oled_write_P(PSTR("Pariah v0.1\n\n"), false); */

  // Layer status
  oled_write_P(PSTR("LAYER: "), false);
  switch (get_highest_layer(layer_state)) {
    case _QWERTY:
      oled_write_P(PSTR("DEFAULT   v0.1\n"), false);
      break;
    case _LOWER:
      oled_write_P(PSTR("LOWER     v0.1\n"), false);
      break;
    case _RAISE:
      oled_write_P(PSTR("RAISE     v0.1\n"), false);
      break;
    case _ADJUST:
      oled_write_P(PSTR("ADJUST    v0.1\n"), false);
      break;
    default:
      oled_write_P(PSTR("Undefined v0.1\n"), false);
  }

  // Host Keyboard LED Status
  led_t led_state = host_keyboard_led_state();
  if (led_state.num_lock) {
    oled_write_ln_P(numlock_on, false);
  } else {
    oled_write_ln_P(numlock_off, false);
  }
  if (led_state.caps_lock) {
    oled_write_ln_P(capslock_on, false);
  } else {
    oled_write_ln_P(capslock_off, false);
  }
  oled_write_ln_P(PSTR(""), false);

  // Logo
  render_logo();
}