Directly jump to the content and enable reader mode
Article image

have read aloud

Reading time: about 5 min Print version

Building a keyboard filter part 3

If you are new to the article series, please start with part 1.
If you haven't read part 2, please do this before reading on.

Where we left

In part 2 we defined the different states we will implement this time as well as some helping function not yet discussed, EEPROM reading/writing and scroll lock LED blinking.

Main loop and definitions

Lets start with the definition of our states. To recap, here is the main loop:

We could use numeric states, but if we define an enum we get descriptive names and the compile will help us by throwing a warning if we do not implement all stages.

uint8_t ignoredKey = 0;
enum States {

  NONE = 0,
} currentState = NONE;

I also added the default values as well as our ignored key variable.

I cleaned up the main loop a bit, so the full code with handing of stage 0 (NONE) looks like this:

void loop() {
    ps2::UsbKeyboardLeds newLedState = (ps2::UsbKeyboardLeds)BootKeyboard.getLeds();
    if (newLedState != ledValueLastSentToPs2)
        ledValueLastSentToPs2 = newLedState;

    ps2::KeyboardOutput scanCode = ps2Keyboard.readScanCode();
    if (scanCode != ps2::KeyboardOutput::none && scanCode != ps2::KeyboardOutput::garbled)
        ps2::UsbKeyAction action = keyMapping.translatePs2Keycode(scanCode);
        KeyboardKeycode hidCode = (KeyboardKeycode)action.hidCode;
        if (hidCode == ignoredKey) {
        switch(currentState) {
          case NONE:
            if (action.gesture == ps2::UsbKeyAction::KeyDown) {
              if (hidCode == KeyboardKeycode::KEY_LEFT_SHIFT) {
                // currentState = PROGRAMMING_SHIFT;
                Serial.println("left shift => state 2");
              } else if (hidCode == KeyboardKeycode::KEY_RIGHT_SHIFT) {
                // currentState = APPLY_SHIFT;
                Serial.println("right shift => state 6");
        if (activeFilter != 255) {
          if (filters[activeFilter]->isFiltered(hidCode)) {
            action.gesture = ps2::UsbKeyAction::None;
        switch (action.gesture) {
            case ps2::UsbKeyAction::KeyDown:
            case ps2::UsbKeyAction::KeyUp:

Pressing left or right shift show the message in the serial console, so everything works as expected. This is mainly all the logic elements we need, now it's time for the repetitive task of doing all stages.

Reading from and writing to EEPROM

The final step is the reading and writing of our configured keys to the EEPROM so they become reset or power cycling safe.

The format is simple: for every filter config (of those 12) we need 10 bytes.

  • 1 byte: number of keys (0 to 8)
  • 8x 1 byte: the keys
  • 1 byte: a primitive checksum

The checksum is calculated by adding the decimal values of the key, nothing fancy, but prevents garbage reads.
Each filter calculates the position simply by it's own number (starting from 0) * 10.

The final code

The final code can be found on our new git repo
Feel free to use, but please give me shout-out by linking to this article series.
Also please tell me what projects you come up with based on this.

A few suggestions for improvements or modifications:

You could use a coded rotary switch to select and program the shortcut, way easier but less learning.
They only use 4 pins for selection plus one for pushing.
In combination with hard coded filters this only be a few dozen lines of code instead of hundred.
Or go the other route and make it more complicated with an LCD, or rotary encoder or whatever you can dream of.

I learned enough to start my next project, which was my main goal.


Report article

Our algorithm thinks, these articles are relevant: