Compare commits
No commits in common. "dc61988af7dfc1752c1c54d819e34649a4ec23f0" and "c35962e50a72130f32cade45b02f4f8f62d155d4" have entirely different histories.
dc61988af7
...
c35962e50a
19
README.md
19
README.md
|
@ -1,20 +1 @@
|
|||
This code is experimental.
|
||||
|
||||
## NeoPixel
|
||||
|
||||
Put [this](https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_NeoPixel/main/neopixel.py) in `lib` folder of CircuitPython disk (compile to `mpy` for nice!nano.
|
||||
|
||||
## Compile mpy files for nice!nano kmk
|
||||
|
||||
``` bash
|
||||
docker run -v /scratch/kmk_firmware:/opt/kmk_firmware --rm -it --entrypoint=/bin/bash python:latest
|
||||
cd /opt
|
||||
wget https://adafruit-circuit-python.s3.amazonaws.com/bin/mpy-cross/mpy-cross.static-amd64-linux-8.0.5
|
||||
chmod a+x mpy-cross.static-amd64-linux-8.0.5
|
||||
./mpy-cross.static-amd64-linux-8.0.5 --help
|
||||
mv mpy-cross.static-amd64-linux-8.0.5 /usr/bin/mpy-cross
|
||||
mpy-cross --help
|
||||
cd /opt/kmk_firmware
|
||||
python util/compile.py
|
||||
cp ~.compiled/kmk~ folder -> mcu
|
||||
```
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
from kmk.modules.combos import Chord
|
||||
|
||||
# TODO: fix up pass by reference trick (wrapped in array) thats used below
|
||||
# likely deserves a global but KmN couldnt figure it out
|
||||
class ArduxChord(Chord):
|
||||
# Override default constructor to allow passing of required fields
|
||||
def __init__(
|
||||
self,
|
||||
match: Tuple[Union[Key, int], ...],
|
||||
result: Key,
|
||||
fast_reset=None,
|
||||
per_key_timeout=None,
|
||||
timeout=None,
|
||||
match_coord=None,
|
||||
ardux_keyboard=[],
|
||||
layers=[],
|
||||
):
|
||||
super().__init__(match, result, fast_reset, per_key_timeout, match_coord)
|
||||
self.ardux_keyboard = ardux_keyboard
|
||||
self.layers = layers
|
||||
|
||||
# Override standard kmk match logic to first check active vs allowed layers
|
||||
def matches(self, key: Key, int_coord: int):
|
||||
if self.ardux_keyboard is None or len(self.ardux_keyboard) == 0 or len(self.layers) == 0 or any(i in self.ardux_keyboard[0].active_layers for i in self.layers):
|
||||
return super().matches(key, int_coord)
|
||||
|
||||
return False
|
55
ardux/kb.py
55
ardux/kb.py
|
@ -1,55 +0,0 @@
|
|||
import os
|
||||
|
||||
import board
|
||||
from kmk.quickpin.pro_micro.kb2040 import pinout as pins
|
||||
|
||||
from kmk.kmk_keyboard import KMKKeyboard
|
||||
from kmk.scanners.keypad import KeysScanner
|
||||
|
||||
from kmk.modules.layers import Layers
|
||||
from kmk.modules.combos import Combos, Chord
|
||||
from ardux.chord import ArduxChord
|
||||
|
||||
from kmk.keys import KC
|
||||
|
||||
class ArduxKeyboard(KMKKeyboard):
|
||||
coord_mapping = [
|
||||
0, 1, 2, 3,
|
||||
4, 5, 6, 7,
|
||||
]
|
||||
|
||||
keymap = [
|
||||
[KC.S, KC.T, KC.R, KC.A,
|
||||
KC.O, KC.I, KC.Y, KC.E]
|
||||
]
|
||||
|
||||
# Init / constructor / setup
|
||||
def __init__(self):
|
||||
# Enable debugging if appropriate
|
||||
if os.getenv('ARDUX_KMK_DEBUGGING'):
|
||||
self.debug_enabled = True
|
||||
|
||||
# setup modules/extensions arrays
|
||||
self.modules = []
|
||||
self.extensions = []
|
||||
|
||||
# Direct wire & matrix setup
|
||||
self.matrix = KeysScanner([pins[16], pins[17], pins[18], pins[19], pins[12], pins[13], pins[14], pins[15]])
|
||||
|
||||
# Layers
|
||||
self.modules.append(Layers())
|
||||
|
||||
# Combos
|
||||
self.combo_module = Combos()
|
||||
self.modules.append(self.combo_module)
|
||||
self.setup_combos()
|
||||
|
||||
# Define combos for ardux
|
||||
def setup_combos(self):
|
||||
self.combo_module.combos = []
|
||||
|
||||
combo_enter = ArduxChord((KC.A, KC.E), KC.ENTER, ardux_keyboard=[self], layers=[0])
|
||||
self.combo_module.combos.append(combo_enter)
|
||||
|
||||
combo_space = ArduxChord((KC.O, KC.I, KC.Y, KC.E), KC.SPACE, ardux_keyboard=[self], layers=[1])
|
||||
self.combo_module.combos.append(combo_space)
|
34
boot.py
34
boot.py
|
@ -1,8 +1,17 @@
|
|||
print('START boot.py')
|
||||
|
||||
# Used http://kmkfw.ioy/docs/boot/ as starting point
|
||||
# Used http://kmkfw.io/docs/boot/ as starting point
|
||||
|
||||
import supervisor
|
||||
import board
|
||||
import digitalio
|
||||
import os
|
||||
import storage
|
||||
import usb_cdc
|
||||
import usb_hid
|
||||
|
||||
from kmk.quickpin.pro_micro.kb2040 import pinout as pins
|
||||
from usb_hid import Device
|
||||
|
||||
# Print env vars if debugging enabled
|
||||
if os.getenv('ARDUX_KMK_DEBUGGING'):
|
||||
|
@ -18,10 +27,8 @@ if os.getenv('ARDUX_KMK_DEBUGGING'):
|
|||
else:
|
||||
print('debugging disabled')
|
||||
|
||||
# If this/these key(s) is/are held during boot, don't run the code which hides the storage and disables serial
|
||||
# If this key is held during boot, don't run the code which hides the storage and disables serial
|
||||
# bottom row, index finger key / bottom row pinky key
|
||||
import digitalio
|
||||
from kmk.quickpin.pro_micro.kb2040 import pinout as pins
|
||||
key_1 = digitalio.DigitalInOut(pins[12])
|
||||
key_2 = digitalio.DigitalInOut(pins[15])
|
||||
|
||||
|
@ -29,29 +36,22 @@ key_2 = digitalio.DigitalInOut(pins[15])
|
|||
key_1.switch_to_input(pull=digitalio.Pull.UP)
|
||||
key_2.switch_to_input(pull=digitalio.Pull.UP)
|
||||
|
||||
# Pull up means 'active low' so invert pin values for less convoluted logic below
|
||||
# Pull up means 'active low' so invert pin values for positive logic below
|
||||
key_1_val = not (key_1.value)
|
||||
key_2_val = not (key_2.value)
|
||||
|
||||
# Check for key hold and disable any dangerous features if not held
|
||||
# Check for key hold and disable any dangerous features
|
||||
if not (key_1_val or key_2_val) and not os.getenv('ARDUX_KMK_DEBUGGING'):
|
||||
# dont expose storage by default
|
||||
if not os.getenv('ARDUX_KMK_USB_DISK_ALWAYS'):
|
||||
import storage
|
||||
storage.disable_usb_drive()
|
||||
# disable usb cdc stuff thats only useful when debugging
|
||||
import usb_cdc
|
||||
usb_cdc.disable() # Equivalent to usb_cdc.enable(console=False, data=False)
|
||||
|
||||
# Enable use w/ bios when not debugging (serial device from debug messes things up)
|
||||
# this only works if *both* cdc and storage are disabled above ; add added logic to avoid crash on boot
|
||||
import usb_hid
|
||||
from usb_hid import Device
|
||||
if not os.getenv('ARDUX_KMK_USB_DISK_ALWAYS'):
|
||||
usb_hid.enable((Device.KEYBOARD,), boot_device=1)
|
||||
|
||||
# Deinit pins so they can be setup per the kmk keymap post-boot
|
||||
key_1.deinit()
|
||||
key_2.deinit()
|
||||
|
||||
# Enable use w/ BIOS
|
||||
|
||||
usb_hid.enable(boot_device=1)
|
||||
|
||||
print('END boot.py')
|
||||
|
|
27
kb.py
Normal file
27
kb.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
import board
|
||||
|
||||
from kmk.kmk_keyboard import KMKKeyboard as _KMKKeyboard
|
||||
from kmk.quickpin.pro_micro.kb2040 import pinout as pins
|
||||
from kmk.scanners.keypad import KeysScanner
|
||||
|
||||
# Direct wire config
|
||||
_KEY_CFG = [
|
||||
pins[16],
|
||||
pins[17],
|
||||
pins[18],
|
||||
pins[19],
|
||||
pins[12],
|
||||
pins[13],
|
||||
pins[14],
|
||||
pins[15]
|
||||
]
|
||||
|
||||
class KMKKeyboard(_KMKKeyboard):
|
||||
def __init__(self):
|
||||
# create and register the scanner for direct wire
|
||||
self.matrix = KeysScanner(_KEY_CFG)
|
||||
|
||||
coord_mapping = [
|
||||
0, 1, 2, 3,
|
||||
4, 5, 6, 7,
|
||||
]
|
180
lib/neopixel.py
180
lib/neopixel.py
|
@ -1,180 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2016 Damien P. George
|
||||
# SPDX-FileCopyrightText: 2017 Scott Shawcroft for Adafruit Industries
|
||||
# SPDX-FileCopyrightText: 2019 Carter Nelson
|
||||
# SPDX-FileCopyrightText: 2019 Roy Hooper
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
`neopixel` - NeoPixel strip driver
|
||||
====================================================
|
||||
|
||||
* Author(s): Damien P. George, Scott Shawcroft, Carter Nelson, Rose Hooper
|
||||
"""
|
||||
|
||||
import sys
|
||||
import board
|
||||
import digitalio
|
||||
from neopixel_write import neopixel_write
|
||||
|
||||
import adafruit_pixelbuf
|
||||
|
||||
try:
|
||||
# Used only for typing
|
||||
from typing import Optional, Type
|
||||
from types import TracebackType
|
||||
import microcontroller
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
__version__ = "0.0.0+auto.0"
|
||||
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel.git"
|
||||
|
||||
|
||||
# Pixel color order constants
|
||||
RGB = "RGB"
|
||||
"""Red Green Blue"""
|
||||
GRB = "GRB"
|
||||
"""Green Red Blue"""
|
||||
RGBW = "RGBW"
|
||||
"""Red Green Blue White"""
|
||||
GRBW = "GRBW"
|
||||
"""Green Red Blue White"""
|
||||
|
||||
|
||||
class NeoPixel(adafruit_pixelbuf.PixelBuf):
|
||||
"""
|
||||
A sequence of neopixels.
|
||||
|
||||
:param ~microcontroller.Pin pin: The pin to output neopixel data on.
|
||||
:param int n: The number of neopixels in the chain
|
||||
:param int bpp: Bytes per pixel. 3 for RGB and 4 for RGBW pixels.
|
||||
:param float brightness: Brightness of the pixels between 0.0 and 1.0 where 1.0 is full
|
||||
brightness
|
||||
:param bool auto_write: True if the neopixels should immediately change when set. If False,
|
||||
`show` must be called explicitly.
|
||||
:param str pixel_order: Set the pixel color channel order. GRBW is set by default.
|
||||
|
||||
Example for Circuit Playground Express:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import neopixel
|
||||
from board import *
|
||||
|
||||
RED = 0x100000 # (0x10, 0, 0) also works
|
||||
|
||||
pixels = neopixel.NeoPixel(NEOPIXEL, 10)
|
||||
for i in range(len(pixels)):
|
||||
pixels[i] = RED
|
||||
|
||||
Example for Circuit Playground Express setting every other pixel red using a slice:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import neopixel
|
||||
from board import *
|
||||
import time
|
||||
|
||||
RED = 0x100000 # (0x10, 0, 0) also works
|
||||
|
||||
# Using ``with`` ensures pixels are cleared after we're done.
|
||||
with neopixel.NeoPixel(NEOPIXEL, 10) as pixels:
|
||||
pixels[::2] = [RED] * (len(pixels) // 2)
|
||||
time.sleep(2)
|
||||
|
||||
.. py:method:: NeoPixel.show()
|
||||
|
||||
Shows the new colors on the pixels themselves if they haven't already
|
||||
been autowritten.
|
||||
|
||||
The colors may or may not be showing after this function returns because
|
||||
it may be done asynchronously.
|
||||
|
||||
.. py:method:: NeoPixel.fill(color)
|
||||
|
||||
Colors all pixels the given ***color***.
|
||||
|
||||
.. py:attribute:: brightness
|
||||
|
||||
Overall brightness of the pixel (0 to 1.0)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
pin: microcontroller.Pin,
|
||||
n: int,
|
||||
*,
|
||||
bpp: int = 3,
|
||||
brightness: float = 1.0,
|
||||
auto_write: bool = True,
|
||||
pixel_order: str = None
|
||||
):
|
||||
if not pixel_order:
|
||||
pixel_order = GRB if bpp == 3 else GRBW
|
||||
elif isinstance(pixel_order, tuple):
|
||||
order_list = [RGBW[order] for order in pixel_order]
|
||||
pixel_order = "".join(order_list)
|
||||
|
||||
self._power = None
|
||||
if (
|
||||
sys.implementation.version[0] >= 7
|
||||
and getattr(board, "NEOPIXEL", None) == pin
|
||||
):
|
||||
power = getattr(board, "NEOPIXEL_POWER_INVERTED", None)
|
||||
polarity = power is None
|
||||
if not power:
|
||||
power = getattr(board, "NEOPIXEL_POWER", None)
|
||||
if power:
|
||||
try:
|
||||
self._power = digitalio.DigitalInOut(power)
|
||||
self._power.switch_to_output(value=polarity)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
super().__init__(
|
||||
n, brightness=brightness, byteorder=pixel_order, auto_write=auto_write
|
||||
)
|
||||
|
||||
self.pin = digitalio.DigitalInOut(pin)
|
||||
self.pin.direction = digitalio.Direction.OUTPUT
|
||||
|
||||
def deinit(self) -> None:
|
||||
"""Blank out the NeoPixels and release the pin."""
|
||||
self.fill(0)
|
||||
self.show()
|
||||
self.pin.deinit()
|
||||
if self._power:
|
||||
self._power.deinit()
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
exception_type: Optional[Type[BaseException]],
|
||||
exception_value: Optional[BaseException],
|
||||
traceback: Optional[TracebackType],
|
||||
):
|
||||
self.deinit()
|
||||
|
||||
def __repr__(self):
|
||||
return "[" + ", ".join([str(x) for x in self]) + "]"
|
||||
|
||||
@property
|
||||
def n(self) -> int:
|
||||
"""
|
||||
The number of neopixels in the chain (read-only)
|
||||
"""
|
||||
return len(self)
|
||||
|
||||
def write(self) -> None:
|
||||
""".. deprecated: 1.0.0
|
||||
|
||||
Use ``show`` instead. It matches Micro:Bit and Arduino APIs."""
|
||||
self.show()
|
||||
|
||||
def _transmit(self, buffer: bytearray) -> None:
|
||||
neopixel_write(self.pin, buffer)
|
65
main.py
65
main.py
|
@ -1,10 +1,15 @@
|
|||
import board
|
||||
import os
|
||||
|
||||
from kb import KMKKeyboard
|
||||
from kmk.keys import KC
|
||||
|
||||
keyboard = KMKKeyboard()
|
||||
|
||||
#####
|
||||
# Main keyboard object
|
||||
from ardux.kb import ArduxKeyboard
|
||||
ardux_keyboard = ArduxKeyboard()
|
||||
# Enable debugging
|
||||
if os.getenv('ARDUX_KMK_DEBUGGING'):
|
||||
keyboard.debug_enabled = True
|
||||
|
||||
#####
|
||||
# NeoPixel on kb2040 (tune accordingly / remove if different mcu)
|
||||
|
@ -14,12 +19,60 @@ rgb_ext = RGB(
|
|||
num_pixels=1,
|
||||
val_limit=100,
|
||||
val_default=25,
|
||||
animation_mode=AnimationModes.BREATHING_RAINBOW
|
||||
animation_mode=AnimationModes.RAINBOW
|
||||
)
|
||||
ardux_keyboard.extensions.append(rgb_ext)
|
||||
keyboard.extensions.append(rgb_ext)
|
||||
|
||||
#####
|
||||
# Layers
|
||||
from kmk.modules.layers import Layers
|
||||
keyboard.modules.append(Layers())
|
||||
|
||||
#####
|
||||
# Combos
|
||||
from kmk.modules.combos import Combos, Chord
|
||||
combos = Combos()
|
||||
keyboard.modules.append(combos)
|
||||
|
||||
class KmNChord(Chord):
|
||||
def __init__(
|
||||
self,
|
||||
match: Tuple[Union[Key, int], ...],
|
||||
result: Key,
|
||||
fast_reset=None,
|
||||
per_key_timeout=None,
|
||||
timeout=None,
|
||||
match_coord=None,
|
||||
keyboard=None,
|
||||
layers=[],
|
||||
):
|
||||
super().__init__(match, result, fast_reset, per_key_timeout, match_coord)
|
||||
self.keyboard = keyboard
|
||||
self.layers = layers
|
||||
|
||||
def matches(self, key: Key, int_coord: int):
|
||||
if keyboard is None or len(self.layers) == 0 or any(i in self.keyboard.active_layers for i in self.layers):
|
||||
return super().matches(key, int_coord)
|
||||
|
||||
return False
|
||||
|
||||
combos.combos = []
|
||||
|
||||
combo_enter = KmNChord((KC.A, KC.E), KC.ENTER, keyboard=keyboard, layers=[0])
|
||||
combos.combos.append(combo_enter)
|
||||
|
||||
combo_space = KmNChord((KC.O, KC.I, KC.Y, KC.E), KC.SPACE, keyboard=keyboard, layers=[1])
|
||||
combos.combos.append(combo_space)
|
||||
|
||||
#####
|
||||
# Keymap
|
||||
keyboard.keymap = [
|
||||
[KC.S, KC.T, KC.R, KC.A,
|
||||
KC.O, KC.I, KC.Y, KC.E]
|
||||
]
|
||||
|
||||
#####
|
||||
# Main
|
||||
if __name__ == '__main__':
|
||||
ardux_keyboard.go()
|
||||
keyboard.go()
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
CIRCUITPY_BLE_NAME = "ARDUX [L|R] [board]"
|
||||
#ARDUX_KMK_DEBUGGING = 1 # Code only looks for value ; Uncomment/Comment to enable/disable
|
||||
ARDUX_KMK_DEBUGGING = 1 # Code only looks for value ; Uncomment/Comment to enable/disable
|
||||
ARDUX_KMK_USB_DISK_ALWAYS = 1 # Code only looks for value ; Uncomment/Comment to enable/disable
|
||||
ARDUX_SIZE = "[STANDARD|BIG|40%]"
|
||||
ARDUX_HAND = "[LEFT|RIGHT]"
|
||||
|
|
Loading…
Reference in a new issue