Skip to content

Instantly share code, notes, and snippets.

@srenfield
Last active March 11, 2023 12:10
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save srenfield/2d810b7b29d3aaa8b3301e50f9670f29 to your computer and use it in GitHub Desktop.
Save srenfield/2d810b7b29d3aaa8b3301e50f9670f29 to your computer and use it in GitHub Desktop.
Adding an encoder to a microdox

Here's the complete idiot's guide to adding a rotary encoder to a Microdox.

I am a complete idiot, and this is the guide I wish I had had. I can drive stickshift, make a teapot from raw clay, and knit very complicated sweaters, but I know nothing about coding and the finer points of C (although I can at least put a keymap together and compile my own firmware). The process is, in fact, trivially easy now that I know all the things I didn't know when I started, but it required a few helpful folks on the olkb discord to point me toward the answers (thank you, waffle and binaryape). I will park this ELI5 tome here for the next wide-eyed innocent humanities-major dipshit like me that wants to undertake this process.

The hardware part was no biggie, since the inexpensive encoders from keebio fit the pcb with only minor adjustment. From a firmware perspective, it was also not difficult but I did not know what I was doing and ran into some weirdness with trying to get everything working with the master on the right. I wanted the knob on the right because that was the keyswitch position that was the least critical in my world, and I could give it up without having to change anything important from the way I have the thumb keys mapped on my Corne and Kyria. I was told that to do this, I would need to have the knob on the master side, ergo the right side had to be the master. This meant I had to define EE_HANDS and flash differently than I usually do (see the part about flashing). The other thing I did not know is that doing this required me to treat the encoder as if it was the "second" encoder on the board rather than the first/only, which is why it didn't work at first. Other than that, I also had to reverse the rotation on the OLED displays (see * at the end of this post) so shit wasn't upside down. Also a minor thing once I understood how it worked.

Encoder installation:

Assemble the kit as normal, socketing the controllers and the OLEDs, and OMIT THE HOTSWAP SOCKET ON THE THUMB KEY CLOSEST TO THE CONTROLLER where you're going to put the encoder. Once the board was otherwise assembled, controllers flashed, and sockets tested to be sure that anything I screwed up was encoder related and not some other dumbshit thing I had done, I removed the right side Elite-C and ran two wires from the outermost positions of the open pins along the short edge (B7 and F0). I stripped the last cm of each wire, pushed it through from the underside, soldered it into place, and trimmed it after replacing the controller in its sockets whilst bending the wire to run horizontally away from the controller - there's enough room if you smoosh the wires into the gap between the sockets for the OLEDs and Elite C.

I very carefully filed down the clip legs on the encoder until they were a very very snug fit for the two holes normally occupied by the switch's mounting pins. The clip legs on the ALPS industrial encoders from littlekeyboards are much wider and I haven't tried that. I bent the pins of the encoder a little bit so that the two along the upper edge (that send the keystroke when the encoder is depressed) went through the holes where the mx hotswap socket would go. For the three on the lower edge (these are the ones that send the keystroke based on the encoder rotation), the middle one goes through one of the choc pad holes. The other two were bent outward to be parallel with the pcb.

And here's what should be poking through the pcb when viewed from below:

I stuck the encoder into the pcb, making sure the pins that need to go through were in the right spots and the clip legs were firmly in there (might need to bend the clip legs in or out to ensure a snug fit). I did not add solder or hot glue or anything to the underside of the clip legs for added security, but that could be done if you want to make sure that encoder is never coming out of there. I like to keep my options open and had a snug fit and I haven't yanked it out by accident yet.

The wire running from F0 (this is the right half, so the controller is face down) goes around the encoder, wrapping around to the left leg on the encoder, where it gets soldered in place. The other wire (closest to the edge of the board) will be much shorter, and gets soldered to the right leg. Before soldering, make sure the wires don't interfere with the OLED. Once soldered in place, I snipped the encoder legs back a touch so they weren't so close to the edge of the pcb.

Turning the pcb over, I ran a third wire from the middle leg of the encoder to the GND pin on the Elite-C (looking at the underside of the pcb with the usb port upwards, this is the left column, third pin down), taking care to avoid the LED in there if you installed them (I did not, since underglow is not my jam unless the case has lots of nice diffusion). If you want the encoder to register a keystroke, use tiny bits of wire to jump the two legs to the appropriate pads where the hotswap socket would have been soldered. Forming the stripped wire into a tight loop and putting that over the leg makes a nice easy joint to solder. Thanks, surface tension.

That's it for the hardware part. Here's the firmware part:

I added the following to my config.h file (placed in the folder with my keymap.c):

#define EE_HANDS

#define ENCODERS_PAD_A { B7 }
#define ENCODERS_PAD_B { F0 }
#define ENCODER_RESOLUTION 4

I added the following to my rules.mk (also in folder with keymap):

ENCODER_ENABLE = yes

In my keymap, the keystroke for pressing down on the encoder can be assigned the same as any normal key in that spot. For the twisty dial fun stuff, I added this right after my layout stuff to set the behavior of the encoder. You can define as many different pairs of functions as you have layers. In this case, I've told the robots that I want to undo/redo when on my base layer, and scroll using page up/down on any other layer:

#ifdef ENCODER_ENABLE
void encoder_update_user(uint8_t index, bool clockwise) {
    if (index == 1) {
    switch (biton32(layer_state)) {
        case QWERTY:
            // History scrubbing. 
            if (clockwise) {
                tap_code16(C(KC_Z));
            } else {
                tap_code16(C(KC_Y));
            }
            break;
   default:
           // Scrolling with PageUp and PgDn.
            if (clockwise) {
                tap_code(KC_PGUP);
            } else {
                tap_code(KC_PGDN);
            }
                break;
        }
    
    }

}

NOTE: Because the encoder is on the right hand, that third line needs to have the index == 1 rather than index == 0 (which should work if you put the encoder on the left side - see the example at https://beta.docs.qmk.fm/using-qmk/hardware-features/feature_encoders#callbacks, noting that for first and second encoders it would be left side and right side, respectively).

When flashing, I had to use the command line in MSYS2 to flash each side separately in order to have the right hand as master without stuff getting weird and mirrored. To do this, it's just like compiling a normal hex except instead of it being

make boardsource/microdox:your-keymap-name

you would run: make boardsource/microdox:your-keymap-name:dfu-split-left

with the left side plugged in, and when it finishes compiling you'll be prompted to hit the reset button (might say ERROR: Bootloader not found. Trying again in 5s. - just hit the reset button and it will autoflash). Repeat the process with the right side, except run

make boardsource/microdox:your-keymap-name:dfu-split-right

That's it.

*Because I was using the right hand as master, contrary to the default behavior of most split boards, I had to switch the rotation on the OLEDs so it wasn't all upside down. To do that, the code after #ifdef OLED_DRIVER_ENABLE in the keymap.c should look like this:

oled_rotation_t oled_init_user(oled_rotation_t rotation) {
  if (is_keyboard_master())
    return OLED_ROTATION_0;
  return OLED_ROTATION_180;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment