SonalPinto / purple-owl

Purple Owl 60% Keyboard
MIT License
31 stars 3 forks source link

Purple Owl

The Purple Owl is a uniform row staggered 60% keyboard designed around ortholinear 1u keycaps and hotswap sockets.

News

Layout

The Purple Owl was inspired off the works of Ziptyze with their Zlant (4x12) and ZlantXL (5x12). It is almost like a staggered 5-row Preonic but with more columns a narrow bottom row to offer the visual aesthetic of four dominant rows like a Plank. Each row has a 0.25u stagger unlike a traditional row-stagger. There are 14 columns to accommodate the outer symbols (-=[]\). The right shift can be traded in for an arrow cluster. The ctrl, shift and other mods are designed to be under the thumbs with a symmetric bottom row that has a 2u surrounded by two 1u on each side. The board runs on KMK, so the keys can be freely mapped.

Link to the base layout at Keyboard Layout Editor. KLE Raw is also available here

Electronics

Instead of a traditional diode matrix, the Purple Owl implements a scan chain to poll the state of its switches using only 3 logic pins. This opens the door to support a wide variety of off-the-shelf microcontrollers including the Pro Micro layout variants popular in the mechanical keyboard community. KMK supports a scan chain scanner by default. So, any CircuitPython compatible board with the pinouts shown below should snap into Purple Owl. There is a solder jumper to select between two popular layouts - the Pro Micro variant and the Seeed XIAO variant. The Purple Owl design needs two power pins (3V3 and GND) and three logic pins (CLK, DATA, LATCH).

While, I currently have a bunch of RP2040 boards in various layouts, I eventually might test out KMK BLE wireless with some CircuitPython compatible nrf52840 board like the nice!nano, Seeed nrf52840 or Adafruit QTPy Bluefruit (doesn't exist yet?). Anything with a JST connector for the battery.

Seeed XIAO Mechanical Keyboard Contest

I came across an article for this contest on KBD.news. The Purple Owl was designed to be powered by a Seeed XIAO RP2040 (or any CircuitPython compatible Seeed XIAO for that matter) and will be my entry in the contest! Seeed is sponsoring two fully assembled PCBs - and that is an opportunity I simply couldn't pass on.

The tiny size of the Seeed XIAO RP2040 will be perfect to highlight the GPIO-efficient scan-chain design. Aside from the 61 switches, the Seeed XIAO RP2040 will drive 16 underglow LEDs which will look stunning in a laser cut case.

Gallery

PCB

done

Externals

Prototype v0.1

To get a feel for the layout I prototyped a board that runs off a RP2040 microcontroller, the Waveshare RP2040 Zero. When I started the prototype, I intended for the the final Purple Owl PCB to have all electronics integrated on the board, including the RP2040. The prototype PCB is simply a diode matrix of hotswap sockets and diodes hooked up to the Zero. No LEDs or anything fancy yet.

I referred to the "recommended minimums" on Acheron Setup for board rules which are more constrained for some fields that the default JLCPCB setup. Laying out the switches is a cakewalk once you set the grid to 19.05mm (1u or 0.75in). For the stagger, set the grid to 4.7625mm (0.25u or 19.05/4mm). The controller is tilted by 26' which is along the zslant of the columns.

I checked for clearance pretty early on as soon as I had the swtiches and controller laid out to make sure the slightly tilted controller and USB cable seemed ok on the right side. I printed half(~ish) the layout flipped on paper to scale.

For assembly, I wanted to trial JLCPCB's assembly service - especially for hotswap sockets. While, I could simply solder the sockets myself, I was curious about JLCPCB's work. And, if it did work, then building the proper Purple Owl would be fairly turnkey.

The footprint for the JLCPCB part for the Kailh hotswap socket is centered around the socket itself. But, the switch footprint on the PCB is centered around the stem hole. So, I wrote a small python snippet to fix the socket position and rotation in the POS assembly file. Below, you can see how it looks before (right) and after (left) the fix on the JLCPCB assembly preview.

The cost for the PCB fabrication and assembly came up to $85 for 5 boards assembled. The 305 hotswap sockets (61 per board) going at 78 cents a piece for a total of about $24. In hindsight, I should have just assembled 2 boards considering this was a prototype, but my excitement got the better of me.

Firmware Note: The Zero supports CircuitPython and thus, KMK. So, not much work to be done aside from the KMK config for the matrix. I'd need to add CircuitPython support for the RP2040 layout integrated on the proper Purple Owl (if I went that route), but that seems easy looking at how it was ported for the Zero.

Revelation: The Scan Chain

A while back, I had considered shift registers for scanning the switch matrix in passing when I had read this Hackaday article. Along the lines of using 2 shift registers for an 8x8 matrix - one serial-to-parallel to drive the columns and a parallel-to-serial to poll the rows. While this would use only two 8-bit shift registers, I'd still need diodes per switch for NKRO. However, I'd need no more than 5-6 pins to interface with the switch matrix over the two shift registers. At first glance, the approach didn't really seem any better than using a traditional diode matrix and routing the columns and rows to the IOs of the microcontroller (especially since I was considering integrating the RP2040 on the board).

Then, I stumbled upon an article for the Hello World Smart Keyboard project when Dovenyi posted their #89 issue of the KBD News Digest on r/mk. It was a curiously interesting design with 74HC165 shift registers polling all switches latched in parallel along a 1xN matrix. Surely, this would need a lot of shift registers (one IC per 8 keys) and pull up resistors per switch. So, in terms of parts, the design used relatively more parts (and proper active components). Nonetheless, the most attractive part of the design was that it needed merely 3 pins to interface with the matrix.

While I was running a embarrassingly one-sided rubber ducking session with Dovenyi on that reddit thread, I realized something. Using fewer fins suddenly opens up a design where I can attach off-the-shelf (OTS) microcontroller boards right in between the switches (alongside the hotswap sockets) - underneath the switches and not on the side. Because, I'd only need to solder enough headers for functional and mechanical purpose. I could use something tiny like a Adafruit QTPy RP2040 or Pimoroni Tiny RP2040 and merely use just one side of the pins between the sockets, and some on the other side for mechanical stability.

These uC (microcontroller) boards are typically 700mil wide with their columns of pins 600mil apart. They don't fit around hotswap sockets if you are using all the pins. It does work for soldered switch footprints like in the case of ErgoDash or ErgoTravel, but not for hotswap sockets because of the wider footprint. Hence, in the prototype I had the uC on the side. Secondly, the uC boards that have enough pins for a diode matrix are typically longer than 1U and won't fit across the rows of a row-staggered layout (while they do work for ortho or column staggered layouts). Now, these problems are washed out when you realize that you don't need to drill and solder headers for all the pins. Just enough to get the job done. And, you don't really need to use a small board like the QTPy. Even a KB2040 would work - since you don't have to drill holes across the rows to access all the pins.

I had almost gone down the path of designing an integrated RP2040 board. Researched a bunch of popular designs and minimal RP2040 circuits, and printed all their schematics. I had even picked out the parts on LCSC and had calculated the trace widths and separation required for the differential pair routing for the USB Full Speed mode (90ohm Z_diff). Because I didn't think there was a good design I where I could have the uC board underneath and not on the side...

And now, there was a way.

Firmware Note: Looks like KMK has a scanner for shift registers (see) that wraps CircuitPython's keypad.ShiftRegisterKeys(). Great!

Production v1.0

The design uses 8 74HC165 (TSSOP-16 package) to form the scan chain. 7 of these poll the top 4 rows across 14 columns and the last one polls the bottom row. I've also added 16 underglow LEDs (WS2812B-V5). The scan chain for the first 6 columns is shown below.

Compared to the prototype, the design now has a clean board edge tightly fitting the switches, since the uC now goes underneath. The uC is socketed to the board with through-hole headers. Out of the 14 pins of the Seeed XIAO RP2040, 11 are connected.

There are jumpers to select the 3V3 source depending on the attached uC. And jumpers to select the power source (3.3V or 5V) for the LEDs. Though off-spec, The LEDs work fine on 3.3V, albeit slightly less bright than if powered by 5V. There wasn't enough space (while staying close to the board edge) to allow a through hole header for the 5V, so the design offers a jumper/through-hole that needs to be wired between the board and the jumper site if 5V is desired.

For assembly, Seeed is sponsoring two fully assembled boards πŸŽ‰

Firmware

This project was my first experience with CircuitPython, let alone KMK. I am stunned how wasy it was to work with. Never did I think it would take just few lines of python to establish a fully functional keyboard with layers and underglow LEDs.

There are bringup scripts for the both PCB variants which test the PCB and demonstrate interfacing with the keyboard switches using native CircuitPython.

The setup for the prototype v0.1 diode matrix was fairly straightforward with KMK. I included three basic layers to start with (I really need the tilde/grave in my line of work). The physical layout of the switches were mapped to the layout with the coord_mapping setup.

The build for the v1.0 PCB inherited the same layers. The firmware switched the scanner to the ShiftRegisterKeys() and updated the coord_mapping to the physical layout of the scan chain. KMK offeres a neat RGB extension to support the underglow LEDs. The underglow RGB controls are added on a layer. Looking at the sheer simplicity of the RGB extension I might consider writing a fancy underglow mode or two. Since, this was a proper build, the boot.py was updated with a "dev mode" which hides CircuitPython enumerations (serial, midi and storage) except for the USB HID keyboard.

Laser cut case

For the case, I had envisioned a laser cut case with 1/8" acrylic layers. With the top layers as dark and the bottom layer as translucent to diffuse the underglow. This was the first time I used Fusion360 as well. Lots of firsts for me in this project. Thankfully, there are a ton of YouTube videos to learn from, and I only need the basics of sketching in 2D to set up the layers for the laser cut case.

Using plate.keeb.io I obtained the cut outs for the switches and stabilizer to form the top switch plate. Then, I designed the middle layers to be spaced around the PCB by importing the PCB edge-cuts layer into the design and offset it. All the layers except for the top switch plate have cut outs for the Seeed XIAO RP2040 that is on the underside of the PCB. All the holes are cut out for M2 hardware. I also added holes for some feet that I found on FlashQuark. The links to the files for the laser cut case are below.

I used p95 matte black acrylic sourced from a local TAP plastics for the top three layers (1xtop, 2xmiddle), and a satinice acrylic for the bottom layer.

Credits