diff --git a/.gitignore b/.gitignore index 9609410..40778ad 100644 --- a/.gitignore +++ b/.gitignore @@ -107,6 +107,7 @@ venv.bak/ .submodules .circuitpy-deps .micropython-deps +.devdeps # Pycharms cruft .idea diff --git a/.gitmodules b/.gitmodules index 89141d4..e390585 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,7 +4,7 @@ ignore = dirty [submodule "upy-lib"] path = vendor/upy-lib - url = https://github.com/micropython/micropython-lib.git + url = https://github.com/kmkfw/micropython-lib.git ignore = dirty [submodule "micropython"] path = vendor/micropython diff --git a/Makefile b/Makefile index 4e6df27..1408727 100644 --- a/Makefile +++ b/Makefile @@ -12,8 +12,11 @@ AMPY_DELAY ?= 1.5 ARDUINO ?= /usr/share/arduino PIPENV ?= $(shell which pipenv) -devdeps: Pipfile.lock - @$(PIPENV) install --dev --ignore-pipfile +.devdeps: Pipfile.lock + @$(PIPENV) install --dev --ignore-pipfile/ + @touch .devdeps + +devdeps: .devdeps lint: devdeps @$(PIPENV) run flake8 @@ -42,6 +45,7 @@ test: micropython-build-unix .submodules: .gitmodules submodules.toml @echo "===> Pulling dependencies, this may take several minutes" + @git submodule sync @git submodule update --init --recursive @rsync -avh vendor/ build/ @touch .submodules @@ -67,36 +71,53 @@ build/micropython/ports/unix/micropython: micropython-deps build/micropython/por micropython-build-unix: build/micropython/ports/unix/micropython +freeze-atmel-samd-build-deps: build/circuitpython/ports/atmel-samd/modules/.kmk_frozen freeze-nrf-build-deps: build/circuitpython/ports/nrf/freeze/.kmk_frozen freeze-teensy3.1-build-deps: build/micropython/ports/teensy/freeze/.kmk_frozen freeze-stm32-build-deps: build/micropython/ports/stm32/freeze/.kmk_frozen -build/micropython/ports/unix/modules/.kmk_frozen: upy-freeze.txt +build/micropython/ports/unix/modules/.kmk_frozen: upy-freeze.txt submodules.toml @echo "===> Preparing builded dependencies for local development" @rm -rf build/micropython/ports/unix/freeze/* - @cat $< | xargs -I '{}' cp -a {} build/micropython/ports/unix/modules/ + @cat $< | egrep -v '(^#|^\s*$|^\s*\t*#)' | grep MICROPY | cut -d'|' -f2- | \ + xargs -I '{}' cp -a {} build/circuitpython/ports/teensy/freeze/ @touch $@ -build/circuitpython/ports/nrf/freeze/.kmk_frozen: upy-freeze.txt +build/circuitpython/ports/atmel-samd/modules/.kmk_frozen: upy-freeze.txt submodules.toml + @echo "===> Preparing builded dependencies for bundling" + @rm -rf build/circuitpython/ports/atmel-samd/modules/* + @cat $< | egrep -v '(^#|^\s*$|^\s*\t*#)' | grep CIRCUITPY | cut -d'|' -f2- | \ + xargs -I '{}' cp -a {} build/circuitpython/ports/atmel-samd/modules/ + @touch $@ + +build/circuitpython/ports/nrf/freeze/.kmk_frozen: upy-freeze.txt submodules.toml @echo "===> Preparing builded dependencies for bundling" @rm -rf build/circuitpython/ports/nrf/freeze/* - @cat $< | xargs -I '{}' cp -a {} build/circuitpython/ports/nrf/freeze/ + @cat $< | egrep -v '(^#|^\s*$|^\s*\t*#)' | grep CIRCUITPY | cut -d'|' -f2- | \ + xargs -I '{}' cp -a {} build/circuitpython/ports/nrf/freeze/ @touch $@ -build/micropython/ports/teensy/freeze/.kmk_frozen: upy-freeze.txt +build/micropython/ports/teensy/freeze/.kmk_frozen: upy-freeze.txt submodules.toml @echo "===> Preparing builded dependencies for bundling" @mkdir -p build/micropython/ports/teensy/freeze/ @rm -rf build/micropython/ports/teensy/freeze/* - @cat $< | xargs -I '{}' cp -a {} build/micropython/ports/teensy/freeze/ + @cat $< | egrep -v '(^#|^\s*$|^\s*\t*#)' | grep MICROPY | cut -d'|' -f2- | \ + xargs -I '{}' cp -a {} build/circuitpython/ports/teensy/freeze/ @touch $@ -build/micropython/ports/stm32/freeze/.kmk_frozen: upy-freeze.txt +build/micropython/ports/stm32/freeze/.kmk_frozen: upy-freeze.txt submodules.toml @echo "===> Preparing builded dependencies for bundling" @mkdir -p build/micropython/ports/stm32/freeze/ @rm -rf build/micropython/ports/stm32/freeze/* - @cat $< | xargs -I '{}' cp -a {} build/micropython/ports/stm32/freeze/ + @cat $< | egrep -v '(^#|^\s*$|^\s*\t*#)' | grep MICROPY | cut -d'|' -f2- | \ + xargs -I '{}' cp -a {} build/micropython/ports/stm32/freeze/ @touch $@ +circuitpy-freeze-kmk-atmel-samd: freeze-atmel-samd-build-deps + @echo "===> Preparing KMK source for bundling into CircuitPython" + @rm -rf build/circuitpython/ports/atmel-samd/modules/kmk* + @cp -av kmk build/circuitpython/ports/atmel-samd/modules/ + circuitpy-freeze-kmk-nrf: freeze-nrf-build-deps @echo "===> Preparing KMK source for bundling into CircuitPython" @rm -rf build/circuitpython/ports/nrf/kmk* @@ -112,10 +133,21 @@ micropython-freeze-kmk-stm32: freeze-stm32-build-deps @rm -rf build/micropython/ports/stm32/freeze/kmk* @cp -av kmk build/micropython/ports/stm32/freeze/ +circuitpy-build-feather-m4-express: + @echo "===> Building CircuitPython" + @make -C build/circuitpython/ports/atmel-samd BOARD=feather_m4_express FROZEN_MPY_DIRS="modules" clean all + circuitpy-build-nrf: @echo "===> Building CircuitPython" @make -C build/circuitpython/ports/nrf BOARD=feather_nrf52832 SERIAL=${AMPY_PORT} SD=s132 FROZEN_MPY_DIR=freeze clean all +circuitpy-flash-feather-m4-express: + @echo "Flashing not available for Feather M4 Express over bossa right now" + @echo "First, double tap the reset button on the Feather. You should see a red light near the USB port" + @echo "Then, find and (if necessary) mount the USB drive that will show up (should be about 4MB)" + @echo "Copy build/circuitpython/ports/atmel-samd/build-feather_m4_express/firmware.uf2 to this device" + @echo "The device will auto-reboot. You may need to forcibly unmount the drive on Linuxes, with umount -f path/to/mountpoint" + circuitpy-flash-nrf: circuitpy-build-nrf @echo "===> Flashing CircuitPython with KMK and your keymap" @make -C build/circuitpython/ports/nrf BOARD=feather_nrf52832 SERIAL=${AMPY_PORT} SD=s132 FROZEN_MPY_DIR=freeze dfu-gen dfu-flash @@ -139,6 +171,23 @@ circuitpy-flash-nrf-entrypoint: @-timeout -k 5s 10s $(PIPENV) run ampy -p ${AMPY_PORT} -d ${AMPY_DELAY} -b ${AMPY_BAUD} put entrypoints/feather_nrf52832.py main.py @echo "===> Flashed keyboard successfully!" +ifndef USER_KEYMAP +build-feather-m4-express: + @echo "===> Must provide a USER_KEYMAP (usually from user_keymaps/...) to build!" && exit 1 + +flash-feather-m4-express: + @echo "===> Must provide a USER_KEYMAP (usually from user_keymaps/...) to build!" && exit 1 +else +build-feather-m4-express: lint devdeps circuitpy-deps circuitpy-freeze-kmk-atmel-samd + @echo "===> Preparing keyboard script for bundling into CircuitPython" + @cp -av ${USER_KEYMAP} build/circuitpython/ports/atmel-samd/modules/kmk_keyboard_user.py + @$(MAKE) circuitpy-build-feather-m4-express + +flash-feather-m4-express: lint devdeps circuitpy-deps circuitpy-freeze-kmk-atmel-samd + @echo "===> Preparing keyboard script for bundling into CircuitPython" + @$(MAKE) build-feather-m4-express circuitpy-flash-feather-m4-express +endif + ifndef USER_KEYMAP build-feather-nrf52832: @echo "===> Must provide a USER_KEYMAP (usually from user_keymaps/...) to build!" && exit 1 diff --git a/kmk/circuitpython/matrix.py b/kmk/circuitpython/matrix.py index 666a2ca..3c1b458 100644 --- a/kmk/circuitpython/matrix.py +++ b/kmk/circuitpython/matrix.py @@ -2,6 +2,7 @@ import digitalio from kmk.common.abstract.matrix_scanner import AbstractMatrixScanner from kmk.common.consts import DiodeOrientation +from kmk.common.event_defs import matrix_changed class MatrixScanner(AbstractMatrixScanner): @@ -17,6 +18,7 @@ class MatrixScanner(AbstractMatrixScanner): self.cols = [digitalio.DigitalInOut(pin) for pin in cols] self.rows = [digitalio.DigitalInOut(pin) for pin in rows] self.diode_orientation = diode_orientation + self.last_pressed_len = 0 if self.diode_orientation == DiodeOrientation.COLUMNS: self.outputs = self.cols @@ -35,15 +37,22 @@ class MatrixScanner(AbstractMatrixScanner): for pin in self.inputs: pin.switch_to_input(pull=digitalio.Pull.DOWN) - def _normalize_matrix(self, matrix): - return super()._normalize_matrix(matrix) + def scan_for_pressed(self): + pressed = [] - def raw_scan(self): - matrix = [] - - for opin in self.outputs: + for oidx, opin in enumerate(self.outputs): opin.value = True - matrix.append([ipin.value for ipin in self.inputs]) + + for iidx, ipin in enumerate(self.inputs): + if ipin.value: + pressed.append( + (oidx, iidx) if self.diode_orientation == DiodeOrientation.ROWS else (iidx, oidx) # noqa + ) + opin.value = False - return self._normalize_matrix(matrix) + if len(pressed) != self.last_pressed_len: + self.last_pressed_len = len(pressed) + return matrix_changed(pressed) + + return None # The default, but for explicitness diff --git a/kmk/entrypoints/handwire/feather_m4_express.py b/kmk/entrypoints/handwire/feather_m4_express.py new file mode 100644 index 0000000..acbd4dd --- /dev/null +++ b/kmk/entrypoints/handwire/feather_m4_express.py @@ -0,0 +1,31 @@ +import sys +from logging import DEBUG + +from kmk.circuitpython.matrix import MatrixScanner +from kmk.common.consts import UnicodeModes +from kmk.firmware import Firmware + + +def main(): + from kmk_keyboard_user import cols, diode_orientation, keymap, rows + + try: + from kmk_keyboard_user import unicode_mode + except Exception: + unicode_mode = UnicodeModes.NOOP + + try: + firmware = Firmware( + keymap=keymap, + row_pins=rows, + col_pins=cols, + diode_orientation=diode_orientation, + unicode_mode=unicode_mode, + log_level=DEBUG, + matrix_scanner=MatrixScanner, + ) + + firmware.go() + except Exception as e: + sys.print_exception(e) + sys.exit(1) diff --git a/kmk/firmware.py b/kmk/firmware.py index b97c1b0..214fc1f 100644 --- a/kmk/firmware.py +++ b/kmk/firmware.py @@ -3,18 +3,17 @@ import logging from kmk.common.event_defs import init_firmware from kmk.common.internal_state import ReduxStore, kmk_reducer -try: - from kmk.circuitpython.matrix import MatrixScanner -except ImportError: - from kmk.micropython.matrix import MatrixScanner - class Firmware: def __init__( self, keymap, row_pins, col_pins, diode_orientation, unicode_mode=None, hid=None, log_level=logging.NOTSET, + matrix_scanner=None, ): + assert matrix_scanner is not None + self.matrix_scanner = matrix_scanner + logger = logging.getLogger(__name__) logger.setLevel(log_level) @@ -25,14 +24,14 @@ class Firmware: lambda state, action: self._subscription(state, action), ) - if not hid: + if hid: + self.hid = hid(store=self.store, log_level=log_level) + else: logger.warning( "Must provide a HIDHelper (arg: hid), disabling HID\n" "Board will run in debug mode", ) - self.hid = hid(store=self.store, log_level=log_level) - self.store.dispatch(init_firmware( keymap=keymap, row_pins=row_pins, @@ -43,7 +42,7 @@ class Firmware: def _subscription(self, state, action): if not self.hydrated: - self.matrix = MatrixScanner( + self.matrix = self.matrix_scanner( state.col_pins, state.row_pins, state.diode_orientation, diff --git a/submodules.toml b/submodules.toml index d55a883..62336f0 100644 --- a/submodules.toml +++ b/submodules.toml @@ -1,4 +1,4 @@ [submodules] "vendor/circuitpython" = "d751740" "vendor/micropython" = "65a49fa" -"vendor/upy-lib" = "f20d89c" +"vendor/upy-lib" = "451b1c0" diff --git a/upy-freeze.txt b/upy-freeze.txt index 6d0b8a7..2e2157a 100644 --- a/upy-freeze.txt +++ b/upy-freeze.txt @@ -1,3 +1,5 @@ -vendor/upy-lib/collections/collections -vendor/upy-lib/logging/logging.py -vendor/upy-lib/string/string.py +# CircuitPython provides collections, don't overwrite it +MICROPY|vendor/upy-lib/collections/collections + +MICROPYCIRCUITPY|vendor/upy-lib/logging/logging.py +MICROPYCIRCUITPY|vendor/upy-lib/string/string.py diff --git a/user_keymaps/klardotsh/feather_m4_express/fourfour.py b/user_keymaps/klardotsh/feather_m4_express/fourfour.py new file mode 100644 index 0000000..661a69d --- /dev/null +++ b/user_keymaps/klardotsh/feather_m4_express/fourfour.py @@ -0,0 +1,74 @@ +import board + +from kmk.common.consts import DiodeOrientation, UnicodeModes +from kmk.common.keycodes import KC +from kmk.common.macros.simple import send_string, simple_key_sequence +from kmk.common.macros.unicode import unicode_sequence +from kmk.entrypoints.handwire.feather_m4_express import main +from kmk.firmware import Firmware + +cols = (board.D11, board.D10, board.D9) +rows = (board.A2, board.A3, board.A4, board.A5) + +diode_orientation = DiodeOrientation.COLUMNS +unicode_mode = UnicodeModes.LINUX + +MACRO_TEST_SIMPLE = simple_key_sequence([ + KC.LSHIFT(KC.H), + KC.E, + KC.L, + KC.L, + KC.O, + + KC.SPACE, + + KC.MACRO_SLEEP_MS(500), + + KC.LSHIFT(KC.K), + KC.LSHIFT(KC.M), + KC.LSHIFT(KC.K), + KC.EXCLAIM, +]) + +MACRO_TEST_STRING = send_string("Hello! from, uhhhh, send_string | and some other WEIRD STUFF` \\ like this' \"\t[]") + +ANGRY_TABLE_FLIP = unicode_sequence([ + "28", + "30ce", + "ca0", + "75ca", + "ca0", + "29", + "30ce", + "5f61", + "253b", + "2501", + "253b", +]) + +keymap = [ + [ + [KC.GESC, KC.A, KC.RESET], + [KC.MO(1), KC.B, KC.C], + [KC.LT(2, KC.EXCLAIM), KC.HASH, KC.ENTER], + [KC.TT(3), KC.SPACE, KC.LSHIFT], + ], + [ + [KC.TRNS, KC.B, KC.C], + [KC.NO, KC.D, KC.E], + [KC.F, KC.G, KC.H], + [KC.I, KC.J, KC.K], + ], + [ + [KC.VOLU, KC.MUTE, ANGRY_TABLE_FLIP], + [KC.TRNS, KC.PIPE, MACRO_TEST_SIMPLE], + [KC.VOLD, KC.P, MACRO_TEST_STRING], + [KC.L, KC.M, KC.N], + ], + [ + [KC.NO, KC.UC_MODE_NOOP, KC.C], + [KC.NO, KC.UC_MODE_LINUX, KC.E], + [KC.TRNS, KC.UC_MODE_MACOS, KC.H], + [KC.O, KC.P, KC.Q], + ], +] diff --git a/vendor/upy-lib b/vendor/upy-lib index f20d89c..451b1c0 160000 --- a/vendor/upy-lib +++ b/vendor/upy-lib @@ -1 +1 @@ -Subproject commit f20d89c6aad9443a696561ca2a01f7ef0c8fb302 +Subproject commit 451b1c07567de85062ef35b672b2101647285e9a