Adding Pimoroni Keybow and Keybow 2040
This commit is contained in:
		
							
								
								
									
										28
									
								
								boards/pimoroni/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								boards/pimoroni/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					# Pimoroni Keybow family
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A family of macro pads based on raspberry pi hardware:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(Original) Keybow - Raspberry Pi hat. 4x3 hotswap keys, with an APA102 LED per key.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Keybow 2040 - custom RP2040 board. 4x4 hotswap keys, with an RGB LED per key driven by a shared IS31FL3731 controller.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					These boards share the 'feature' of using a single GPIO per key rather than a row and column matrix, so these both
 | 
				
			||||||
 | 
					use CircuitPython's `keypad.Keys` module instead of the regular KMK matrix scanner.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Retailers
 | 
				
			||||||
 | 
					### UK
 | 
				
			||||||
 | 
					- Pimoroni
 | 
				
			||||||
 | 
					  - [Keybow](https://shop.pimoroni.com/products/keybow)
 | 
				
			||||||
 | 
					  - [Keybow 2040](https://shop.pimoroni.com/products/keybow-2040)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### AU
 | 
				
			||||||
 | 
					- Core Electronics
 | 
				
			||||||
 | 
					  - [Keybow](https://core-electronics.com.au/pimoroni-keybow-mini-mechanical-keyboard-kit-clicky-keys.html)
 | 
				
			||||||
 | 
					  - [Keybow 2040](https://core-electronics.com.au/pimoroni-keybow-2040-tactile-keys.html)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Extensions enabled by default  
 | 
				
			||||||
 | 
					- [Layers](https://github.com/KMKfw/kmk_firmware/tree/master/docs/layers.md) Need more keys than switches? Use layers.
 | 
				
			||||||
 | 
					- [RGB](https://github.com/KMKfw/kmk_firmware/tree/master/docs/rgb.md) Light it up (Keybow only so far)
 | 
				
			||||||
 | 
					- [MediaKeys](https://github.com/KMKfw/kmk_firmware/tree/master/docs/media_keys.md) Control volume and other media functions
 | 
				
			||||||
							
								
								
									
										0
									
								
								boards/pimoroni/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								boards/pimoroni/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								boards/pimoroni/keybow/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								boards/pimoroni/keybow/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										24
									
								
								boards/pimoroni/keybow/code.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								boards/pimoroni/keybow/code.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					from keybow import Keybow
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from kmk.extensions.media_keys import MediaKeys
 | 
				
			||||||
 | 
					from kmk.keys import KC
 | 
				
			||||||
 | 
					from kmk.modules.layers import Layers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					keybow = Keybow()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# fmt: off
 | 
				
			||||||
 | 
					keybow.keymap = [
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					        KC.A, KC.B, KC.C,
 | 
				
			||||||
 | 
					        KC.E, KC.F, KC.G,
 | 
				
			||||||
 | 
					        KC.I, KC.J, KC.K,
 | 
				
			||||||
 | 
					        KC.M, KC.N, KC.O,
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					keybow.extensions.extend([MediaKeys()])
 | 
				
			||||||
 | 
					keybow.modules.extend([Layers()])
 | 
				
			||||||
 | 
					# fmt: on
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    keybow.go()
 | 
				
			||||||
							
								
								
									
										94
									
								
								boards/pimoroni/keybow/keybow.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								boards/pimoroni/keybow/keybow.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
				
			|||||||
 | 
					'''
 | 
				
			||||||
 | 
					KMK keyboard for Pimoroni Keybow.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WARNING: This doesn't currently function correctly on the Raspberry Pi Zero,
 | 
				
			||||||
 | 
					some of the keys are stuck in the 'pressed' position. There's either a bug in
 | 
				
			||||||
 | 
					the keypad implementation on the rpi0, or the pin numbers don't match the pins
 | 
				
			||||||
 | 
					in linux.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This is a 4x3 macro pad designed to fit the rpi's GPIO connector. Each key is
 | 
				
			||||||
 | 
					attached to a single GPIO and has an APA102 LED mounted underneath it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The layout of the board is as follows (GPIO connector on the left):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					R0 | D20    D6   D22
 | 
				
			||||||
 | 
					R1 | D17   D16   D12
 | 
				
			||||||
 | 
					R2 | D24   D27   D26
 | 
				
			||||||
 | 
					R0 | D13    D5   D23
 | 
				
			||||||
 | 
					    ------------------
 | 
				
			||||||
 | 
					      C0    C1    C2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This board also functions with an adaptor (see
 | 
				
			||||||
 | 
					https://learn.adafruit.com/itsybitsy-keybow-mechanical-keypad/) to work with an
 | 
				
			||||||
 | 
					itsybitsy in place of the rpi, which uses an alternate pin mapping:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					R0 |  A2    A1    A0
 | 
				
			||||||
 | 
					R1 |  A5    A4    A3
 | 
				
			||||||
 | 
					R2 | D10    D9    D7
 | 
				
			||||||
 | 
					R3 | D11   D12    D2
 | 
				
			||||||
 | 
					    ------------------
 | 
				
			||||||
 | 
					      C0    C1    C2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This keyboard file should automatically select the correct mapping at runtime.
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import board
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import adafruit_dotstar
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from kmk.extensions.rgb import RGB, AnimationModes
 | 
				
			||||||
 | 
					from kmk.kmk_keyboard import KMKKeyboard
 | 
				
			||||||
 | 
					from kmk.scanners.native_keypad_scanner import keys_scanner
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# fmt: off
 | 
				
			||||||
 | 
					def raspi_pins():
 | 
				
			||||||
 | 
					    return [
 | 
				
			||||||
 | 
					        [board.D20, board.D16, board.D26],
 | 
				
			||||||
 | 
					        [board.D6,  board.D12, board.D13],
 | 
				
			||||||
 | 
					        [board.D22, board.D24, board.D5],
 | 
				
			||||||
 | 
					        [board.D17, board.D27, board.D23],
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def itsybitsy_pins():
 | 
				
			||||||
 | 
					    return [
 | 
				
			||||||
 | 
					        [board.D11, board.D12, board.D2],
 | 
				
			||||||
 | 
					        [board.D10, board.D9,  board.D7],
 | 
				
			||||||
 | 
					        [board.A5,  board.A4,  board.A3],
 | 
				
			||||||
 | 
					        [board.A2,  board.A1,  board.A0],
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					# fmt: on
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def isPi():
 | 
				
			||||||
 | 
					    return sys.platform == 'BROADCOM'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if isPi():
 | 
				
			||||||
 | 
					    _KEY_CFG = raspi_pins()
 | 
				
			||||||
 | 
					    _LED_PINS = (board.SCK, board.MOSI)
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
					    _KEY_CFG = itsybitsy_pins()
 | 
				
			||||||
 | 
					    _LED_PINS = (board.SCK, board.MOSI)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					led_strip = adafruit_dotstar.DotStar(_LED_PINS[0], _LED_PINS[1], 12)
 | 
				
			||||||
 | 
					rgb_ext = RGB(
 | 
				
			||||||
 | 
					    pixel_pin=0,
 | 
				
			||||||
 | 
					    pixels=led_strip,
 | 
				
			||||||
 | 
					    num_pixels=12,
 | 
				
			||||||
 | 
					    animation_mode=AnimationModes.BREATHING_RAINBOW,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Keybow(KMKKeyboard):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    Default keyboard config for the Keybow.
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    extensions = [rgb_ext]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        self.matrix = keys_scanner(_KEY_CFG)
 | 
				
			||||||
							
								
								
									
										0
									
								
								boards/pimoroni/keybow_2040/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								boards/pimoroni/keybow_2040/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										20
									
								
								boards/pimoroni/keybow_2040/code.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								boards/pimoroni/keybow_2040/code.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					from keybow_2040 import Keybow2040
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from kmk.keys import KC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					keybow = Keybow2040()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# fmt: off
 | 
				
			||||||
 | 
					keybow.keymap = [
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					        KC.A, KC.B, KC.C, KC.D,
 | 
				
			||||||
 | 
					        KC.E, KC.F, KC.G, KC.H,
 | 
				
			||||||
 | 
					        KC.I, KC.J, KC.K, KC.L,
 | 
				
			||||||
 | 
					        KC.M, KC.N, KC.O, KC.P,
 | 
				
			||||||
 | 
					        KC.Q
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					# fmt: on
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    keybow.go()
 | 
				
			||||||
							
								
								
									
										76
									
								
								boards/pimoroni/keybow_2040/keybow_2040.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								boards/pimoroni/keybow_2040/keybow_2040.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					'''
 | 
				
			||||||
 | 
					KMK keyboard for Pimoroni Keybow 2040.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This is a 4x4 macro pad based on the RP2040. Each key is attached to a single
 | 
				
			||||||
 | 
					GPIO, so the KMK matrix scanner needs to be overridden. Additionally, each
 | 
				
			||||||
 | 
					key has an RGB LED controlled by an IS31FL3731 controller which is incompatible
 | 
				
			||||||
 | 
					with the default RGB module.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The layout of the board is as follows:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [RESET] [USB-C] [BOOT]
 | 
				
			||||||
 | 
					R0 | SW3     SW7     SW11    SW15
 | 
				
			||||||
 | 
					R1 | SW2     SW6     SW10    SW14
 | 
				
			||||||
 | 
					R2 | SW1     SW5     SW9     SW13
 | 
				
			||||||
 | 
					R3 | SW0     SW4     SW8     SW12
 | 
				
			||||||
 | 
					    -----------------------------
 | 
				
			||||||
 | 
					     C0      C1      C2      C3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The binding defined in the _KEY_CFG array binds the switches to keys such that
 | 
				
			||||||
 | 
					the keymap can be written in a way that lines up with the natural order of the
 | 
				
			||||||
 | 
					key switches, then adds [BOOT] in (4,0). [RESET] can't be mapped as a key.
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import board
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from adafruit_is31fl3731.keybow2040 import Keybow2040 as KeybowLeds
 | 
				
			||||||
 | 
					from adafruit_pixelbuf import PixelBuf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# from kmk.extensions.rgb import RGB
 | 
				
			||||||
 | 
					from kmk.kmk_keyboard import KMKKeyboard
 | 
				
			||||||
 | 
					from kmk.scanners.native_keypad_scanner import keys_scanner
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# fmt: off
 | 
				
			||||||
 | 
					_KEY_CFG = [
 | 
				
			||||||
 | 
					    [board.SW3,  board.SW7,  board.SW11, board.SW15],
 | 
				
			||||||
 | 
					    [board.SW2,  board.SW6,  board.SW10, board.SW14],
 | 
				
			||||||
 | 
					    [board.SW1,  board.SW5,  board.SW9,  board.SW13],
 | 
				
			||||||
 | 
					    [board.SW0,  board.SW4,  board.SW8,  board.SW12],
 | 
				
			||||||
 | 
					    [board.USER_SW],
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					# fmt: on
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Keybow2040Leds(PixelBuf):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    Minimal PixelBuf wrapper for the Keybow 2040's LED array.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    NOTE: Currently broken.
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, size: int):
 | 
				
			||||||
 | 
					        self.leds = KeybowLeds(board.I2C)
 | 
				
			||||||
 | 
					        super().__init__(size, byteorder='RGB')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _transmit(self, buffer):
 | 
				
			||||||
 | 
					        for pixel in range(self._pixels):
 | 
				
			||||||
 | 
					            r = buffer[pixel * 3 + 0]
 | 
				
			||||||
 | 
					            g = buffer[pixel * 3 + 1]
 | 
				
			||||||
 | 
					            b = buffer[pixel * 3 + 2]
 | 
				
			||||||
 | 
					            self.leds.pixel(pixel // 4, pixel % 4, (r, g, b))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# rgb_ext = RGB(0, pixels=Keybow2040Leds(16), num_pixels=16)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Keybow2040(KMKKeyboard):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    Default keyboard config for the Keybow2040.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TODO: Map the LEDs as well.
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # extensions = [rgb_ext]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        self.matrix = keys_scanner(_KEY_CFG)
 | 
				
			||||||
@@ -244,12 +244,15 @@ class KMKKeyboard:
 | 
				
			|||||||
        Ensure the provided configuration is *probably* bootable
 | 
					        Ensure the provided configuration is *probably* bootable
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        assert self.keymap, 'must define a keymap with at least one row'
 | 
					        assert self.keymap, 'must define a keymap with at least one row'
 | 
				
			||||||
        assert self.row_pins, 'no GPIO pins defined for matrix rows'
 | 
					 | 
				
			||||||
        assert self.col_pins, 'no GPIO pins defined for matrix columns'
 | 
					 | 
				
			||||||
        assert self.diode_orientation is not None, 'diode orientation must be defined'
 | 
					 | 
				
			||||||
        assert (
 | 
					        assert (
 | 
				
			||||||
            self.hid_type in HIDModes.ALL_MODES
 | 
					            self.hid_type in HIDModes.ALL_MODES
 | 
				
			||||||
        ), 'hid_type must be a value from kmk.consts.HIDModes'
 | 
					        ), 'hid_type must be a value from kmk.consts.HIDModes'
 | 
				
			||||||
 | 
					        if not self.matrix:
 | 
				
			||||||
 | 
					            assert self.row_pins, 'no GPIO pins defined for matrix rows'
 | 
				
			||||||
 | 
					            assert self.col_pins, 'no GPIO pins defined for matrix columns'
 | 
				
			||||||
 | 
					            assert (
 | 
				
			||||||
 | 
					                self.diode_orientation is not None
 | 
				
			||||||
 | 
					            ), 'diode orientation must be defined'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return self
 | 
					        return self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -262,6 +265,9 @@ class KMKKeyboard:
 | 
				
			|||||||
        To save RAM on boards that don't use Split, we don't import Split
 | 
					        To save RAM on boards that don't use Split, we don't import Split
 | 
				
			||||||
        and do an isinstance check, but instead do string detection
 | 
					        and do an isinstance check, but instead do string detection
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
 | 
					        if self.matrix and self.matrix.coord_mapping:
 | 
				
			||||||
 | 
					            self.coord_mapping = self.matrix.coord_mapping
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if any(x.__class__.__module__ == 'kmk.modules.split' for x in self.modules):
 | 
					        if any(x.__class__.__module__ == 'kmk.modules.split' for x in self.modules):
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,9 @@ class Scanner:
 | 
				
			|||||||
    Base class for scanners.
 | 
					    Base class for scanners.
 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        self.coord_mapping = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def scan_for_changes(self):
 | 
					    def scan_for_changes(self):
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        Scan for key events and return a key report if an event exists.
 | 
					        Scan for key events and return a key report if an event exists.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,6 @@ class NativeKeypadScanner(Scanner):
 | 
				
			|||||||
    def __init__(self, pin_map, kp):
 | 
					    def __init__(self, pin_map, kp):
 | 
				
			||||||
        self.pin_map = pin_map
 | 
					        self.pin_map = pin_map
 | 
				
			||||||
        self.keypad = kp
 | 
					        self.keypad = kp
 | 
				
			||||||
        # self.coord_mapping = [ic(row, col) for (row, col) in self.pin_map]
 | 
					 | 
				
			||||||
        self.coord_mapping = list(range(len(pin_map)))
 | 
					        self.coord_mapping = list(range(len(pin_map)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.curr_event = keypad.Event()
 | 
					        self.curr_event = keypad.Event()
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user