From e11934ab2d8ceb5b4f63716c3cb8f495151a9104 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sat, 6 Oct 2018 01:32:17 -0700 Subject: [PATCH 01/21] Able to build a VERY basic KMK image for Feather M4 Express, flashable over UF2 only --- .gitignore | 1 + .gitmodules | 2 +- Makefile | 69 ++++++++++++++--- kmk/circuitpython/matrix.py | 25 +++++-- .../handwire/feather_m4_express.py | 31 ++++++++ kmk/firmware.py | 17 ++--- submodules.toml | 2 +- upy-freeze.txt | 8 +- .../klardotsh/feather_m4_express/fourfour.py | 74 +++++++++++++++++++ vendor/upy-lib | 2 +- 10 files changed, 198 insertions(+), 33 deletions(-) create mode 100644 kmk/entrypoints/handwire/feather_m4_express.py create mode 100644 user_keymaps/klardotsh/feather_m4_express/fourfour.py 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 From ae3eda26b9cc51a38fc554a1da6e7675fac1d7c2 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sat, 6 Oct 2018 03:58:19 -0700 Subject: [PATCH 02/21] Make kmk_keyboard_user importable on SAMD51 by reducing recursion (read: import) depth --- kmk/common/internal_keycodes.py | 183 ------------------------------ kmk/common/internal_state.py | 193 +++++++++++++++++++++++++++++++- kmk/firmware.py | 4 +- 3 files changed, 192 insertions(+), 188 deletions(-) delete mode 100644 kmk/common/internal_keycodes.py diff --git a/kmk/common/internal_keycodes.py b/kmk/common/internal_keycodes.py deleted file mode 100644 index b34fb97..0000000 --- a/kmk/common/internal_keycodes.py +++ /dev/null @@ -1,183 +0,0 @@ -import logging - -from kmk.common import kmktime -from kmk.common.event_defs import KEY_DOWN_EVENT, KEY_UP_EVENT -from kmk.common.keycodes import Keycodes, RawKeycodes - -GESC_TRIGGERS = { - Keycodes.Modifiers.KC_LSHIFT, Keycodes.Modifiers.KC_RSHIFT, - Keycodes.Modifiers.KC_LGUI, Keycodes.Modifiers.KC_RGUI, -} - - -def process_internal_key_event(state, action_type, changed_key, logger=None): - if logger is None: - logger = logging.getLogger(__name__) - - # Since the key objects can be chained into new objects - # with, for example, no_press set, always check against - # the underlying code rather than comparing Keycode - # objects - - if changed_key.code == RawKeycodes.KC_DF: - return df(state, action_type, changed_key, logger=logger) - elif changed_key.code == RawKeycodes.KC_MO: - return mo(state, action_type, changed_key, logger=logger) - elif changed_key.code == RawKeycodes.KC_LM: - return lm(state, action_type, changed_key, logger=logger) - elif changed_key.code == RawKeycodes.KC_LT: - return lt(state, action_type, changed_key, logger=logger) - elif changed_key.code == RawKeycodes.KC_TG: - return tg(state, action_type, changed_key, logger=logger) - elif changed_key.code == RawKeycodes.KC_TO: - return to(state, action_type, changed_key, logger=logger) - elif changed_key.code == RawKeycodes.KC_TT: - return tt(state, action_type, changed_key, logger=logger) - elif changed_key.code == Keycodes.KMK.KC_GESC.code: - return grave_escape(state, action_type, logger=logger) - elif changed_key.code == RawKeycodes.KC_UC_MODE: - return unicode_mode(state, action_type, changed_key, logger=logger) - elif changed_key.code == RawKeycodes.KC_MACRO: - return macro(state, action_type, changed_key, logger=logger) - else: - return state - - -def grave_escape(state, action_type, logger): - if action_type == KEY_DOWN_EVENT: - if any(key in GESC_TRIGGERS for key in state.keys_pressed): - # if Shift is held, KC_GRAVE will become KC_TILDE on OS level - state.keys_pressed.add(Keycodes.Common.KC_GRAVE) - return state - - # else return KC_ESC - state.keys_pressed.add(Keycodes.Common.KC_ESCAPE) - return state - - elif action_type == KEY_UP_EVENT: - state.keys_pressed.discard(Keycodes.Common.KC_ESCAPE) - state.keys_pressed.discard(Keycodes.Common.KC_GRAVE) - return state - - return state - - -def df(state, action_type, changed_key, logger): - """Switches the default layer""" - if action_type == KEY_DOWN_EVENT: - state.active_layers[0] = changed_key.layer - - return state - - -def mo(state, action_type, changed_key, logger): - """Momentarily activates layer, switches off when you let go""" - if action_type == KEY_UP_EVENT: - state.active_layers = [ - layer for layer in state.active_layers - if layer != changed_key.layer - ] - elif action_type == KEY_DOWN_EVENT: - state.active_layers.append(changed_key.layer) - - return state - - -def lm(state, action_type, changed_key, logger): - """As MO(layer) but with mod active""" - if action_type == KEY_DOWN_EVENT: - # Sets the timer start and acts like MO otherwise - state.start_time['lm'] = kmktime.ticks_ms() - state.keys_pressed.add(changed_key.kc) - state = mo(state, action_type, changed_key, logger) - elif action_type == KEY_UP_EVENT: - state.keys_pressed.discard(changed_key.kc) - state.start_time['lm'] = None - state = mo(state, action_type, changed_key) - - return state - - -def lt(state, action_type, changed_key, logger): - """Momentarily activates layer if held, sends kc if tapped""" - if action_type == KEY_DOWN_EVENT: - # Sets the timer start and acts like MO otherwise - state.start_time['lt'] = kmktime.ticks_ms() - state = mo(state, action_type, changed_key, logger) - elif action_type == KEY_UP_EVENT: - # On keyup, check timer, and press key if needed. - if state.start_time['lt'] and ( - kmktime.ticks_diff(kmktime.ticks_ms(), state.start_time['lt']) < state.tap_time - ): - state.pending_keys.add(changed_key.kc) - - state.start_time['lt'] = None - state = mo(state, action_type, changed_key, logger) - - return state - - -def tg(state, action_type, changed_key, logger): - """Toggles the layer (enables it if not active, and vise versa)""" - if action_type == KEY_DOWN_EVENT: - if changed_key.layer in state.active_layers: - state.active_layers = [ - layer for layer in state.active_layers - if layer != changed_key.layer - ] - else: - state.active_layers.append(changed_key.layer) - - return state - - -def to(state, action_type, changed_key, logger): - """Activates layer and deactivates all other layers""" - if action_type == KEY_DOWN_EVENT: - state.active_layers = [changed_key.layer] - - return state - - -def tt(state, action_type, changed_key, logger): - """Momentarily activates layer if held, toggles it if tapped repeatedly""" - # TODO Make this work with tap dance to function more correctly, but technically works. - if action_type == KEY_DOWN_EVENT: - if state.start_time['tt'] is None: - # Sets the timer start and acts like MO otherwise - state.start_time['tt'] = kmktime.ticks_ms() - state = mo(state, action_type, changed_key, logger) - elif kmktime.ticks_diff(kmktime.ticks_ms(), state.start_time['tt']) < state.tap_time: - state.start_time['tt'] = None - state = tg(state, action_type, changed_key, logger) - elif action_type == KEY_UP_EVENT and ( - state.start_time['tt'] is None or - kmktime.ticks_diff(kmktime.ticks_ms(), state.start_time['tt']) >= state.tap_time - ): - # On first press, works like MO. On second press, does nothing unless let up within - # time window, then acts like TG. - state.start_time['tt'] = None - state = mo(state, action_type, changed_key, logger) - - return state - - -def unicode_mode(state, action_type, changed_key, logger): - if action_type == KEY_DOWN_EVENT: - state.unicode_mode = changed_key.mode - - return state - - -def macro(state, action_type, changed_key, logger): - if action_type == KEY_UP_EVENT: - if changed_key.keyup: - state.macro_pending = changed_key.keyup - return state - - elif action_type == KEY_DOWN_EVENT: - if changed_key.keydown: - state.macro_pending = changed_key.keydown - return state - - return state diff --git a/kmk/common/internal_state.py b/kmk/common/internal_state.py index a80c471..b503ecf 100644 --- a/kmk/common/internal_state.py +++ b/kmk/common/internal_state.py @@ -1,17 +1,31 @@ import logging import sys +from kmk.common import kmktime from kmk.common.consts import DiodeOrientation, UnicodeModes from kmk.common.event_defs import (HID_REPORT_EVENT, INIT_FIRMWARE_EVENT, KEY_DOWN_EVENT, KEY_UP_EVENT, KEYCODE_DOWN_EVENT, KEYCODE_UP_EVENT, MACRO_COMPLETE_EVENT, NEW_MATRIX_EVENT, PENDING_KEYCODE_POP_EVENT) -from kmk.common.internal_keycodes import process_internal_key_event -from kmk.common.keycodes import FIRST_KMK_INTERNAL_KEYCODE, Keycodes +from kmk.common.keycodes import (FIRST_KMK_INTERNAL_KEYCODE, Keycodes, + RawKeycodes) + +GESC_TRIGGERS = { + Keycodes.Modifiers.KC_LSHIFT, Keycodes.Modifiers.KC_RSHIFT, + Keycodes.Modifiers.KC_LGUI, Keycodes.Modifiers.KC_RGUI, +} -class ReduxStore: +class Store: + ''' + A data store very loosely inspired by Redux, but with most of the fancy + functional and immutable abilities unavailable because microcontrollers. + This serves as the event dispatcher at the heart of KMK. All changes to the + state of the keyboard should be triggered by events (see event_defs.py) + dispatched through this store, and listened to (for side-effects or other + handling) by subscription functions. + ''' def __init__(self, reducer, log_level=logging.NOTSET): self.reducer = reducer self.logger = logging.getLogger(__name__) @@ -223,3 +237,176 @@ def kmk_reducer(state=None, action=None, logger=None): # On unhandled events, log and do not mutate state logger.warning('Unhandled event! Returning state unmodified.') return state + + +def process_internal_key_event(state, action_type, changed_key, logger=None): + if logger is None: + logger = logging.getLogger(__name__) + + # Since the key objects can be chained into new objects + # with, for example, no_press set, always check against + # the underlying code rather than comparing Keycode + # objects + + if changed_key.code == RawKeycodes.KC_DF: + return df(state, action_type, changed_key, logger=logger) + elif changed_key.code == RawKeycodes.KC_MO: + return mo(state, action_type, changed_key, logger=logger) + elif changed_key.code == RawKeycodes.KC_LM: + return lm(state, action_type, changed_key, logger=logger) + elif changed_key.code == RawKeycodes.KC_LT: + return lt(state, action_type, changed_key, logger=logger) + elif changed_key.code == RawKeycodes.KC_TG: + return tg(state, action_type, changed_key, logger=logger) + elif changed_key.code == RawKeycodes.KC_TO: + return to(state, action_type, changed_key, logger=logger) + elif changed_key.code == RawKeycodes.KC_TT: + return tt(state, action_type, changed_key, logger=logger) + elif changed_key.code == Keycodes.KMK.KC_GESC.code: + return grave_escape(state, action_type, logger=logger) + elif changed_key.code == RawKeycodes.KC_UC_MODE: + return unicode_mode(state, action_type, changed_key, logger=logger) + elif changed_key.code == RawKeycodes.KC_MACRO: + return macro(state, action_type, changed_key, logger=logger) + else: + return state + + +def grave_escape(state, action_type, logger): + if action_type == KEY_DOWN_EVENT: + if any(key in GESC_TRIGGERS for key in state.keys_pressed): + # if Shift is held, KC_GRAVE will become KC_TILDE on OS level + state.keys_pressed.add(Keycodes.Common.KC_GRAVE) + return state + + # else return KC_ESC + state.keys_pressed.add(Keycodes.Common.KC_ESCAPE) + return state + + elif action_type == KEY_UP_EVENT: + state.keys_pressed.discard(Keycodes.Common.KC_ESCAPE) + state.keys_pressed.discard(Keycodes.Common.KC_GRAVE) + return state + + return state + + +def df(state, action_type, changed_key, logger): + """Switches the default layer""" + if action_type == KEY_DOWN_EVENT: + state.active_layers[0] = changed_key.layer + + return state + + +def mo(state, action_type, changed_key, logger): + """Momentarily activates layer, switches off when you let go""" + if action_type == KEY_UP_EVENT: + state.active_layers = [ + layer for layer in state.active_layers + if layer != changed_key.layer + ] + elif action_type == KEY_DOWN_EVENT: + state.active_layers.append(changed_key.layer) + + return state + + +def lm(state, action_type, changed_key, logger): + """As MO(layer) but with mod active""" + if action_type == KEY_DOWN_EVENT: + # Sets the timer start and acts like MO otherwise + state.start_time['lm'] = kmktime.ticks_ms() + state.keys_pressed.add(changed_key.kc) + state = mo(state, action_type, changed_key, logger) + elif action_type == KEY_UP_EVENT: + state.keys_pressed.discard(changed_key.kc) + state.start_time['lm'] = None + state = mo(state, action_type, changed_key) + + return state + + +def lt(state, action_type, changed_key, logger): + """Momentarily activates layer if held, sends kc if tapped""" + if action_type == KEY_DOWN_EVENT: + # Sets the timer start and acts like MO otherwise + state.start_time['lt'] = kmktime.ticks_ms() + state = mo(state, action_type, changed_key, logger) + elif action_type == KEY_UP_EVENT: + # On keyup, check timer, and press key if needed. + if state.start_time['lt'] and ( + kmktime.ticks_diff(kmktime.ticks_ms(), state.start_time['lt']) < state.tap_time + ): + state.pending_keys.add(changed_key.kc) + + state.start_time['lt'] = None + state = mo(state, action_type, changed_key, logger) + + return state + + +def tg(state, action_type, changed_key, logger): + """Toggles the layer (enables it if not active, and vise versa)""" + if action_type == KEY_DOWN_EVENT: + if changed_key.layer in state.active_layers: + state.active_layers = [ + layer for layer in state.active_layers + if layer != changed_key.layer + ] + else: + state.active_layers.append(changed_key.layer) + + return state + + +def to(state, action_type, changed_key, logger): + """Activates layer and deactivates all other layers""" + if action_type == KEY_DOWN_EVENT: + state.active_layers = [changed_key.layer] + + return state + + +def tt(state, action_type, changed_key, logger): + """Momentarily activates layer if held, toggles it if tapped repeatedly""" + # TODO Make this work with tap dance to function more correctly, but technically works. + if action_type == KEY_DOWN_EVENT: + if state.start_time['tt'] is None: + # Sets the timer start and acts like MO otherwise + state.start_time['tt'] = kmktime.ticks_ms() + state = mo(state, action_type, changed_key, logger) + elif kmktime.ticks_diff(kmktime.ticks_ms(), state.start_time['tt']) < state.tap_time: + state.start_time['tt'] = None + state = tg(state, action_type, changed_key, logger) + elif action_type == KEY_UP_EVENT and ( + state.start_time['tt'] is None or + kmktime.ticks_diff(kmktime.ticks_ms(), state.start_time['tt']) >= state.tap_time + ): + # On first press, works like MO. On second press, does nothing unless let up within + # time window, then acts like TG. + state.start_time['tt'] = None + state = mo(state, action_type, changed_key, logger) + + return state + + +def unicode_mode(state, action_type, changed_key, logger): + if action_type == KEY_DOWN_EVENT: + state.unicode_mode = changed_key.mode + + return state + + +def macro(state, action_type, changed_key, logger): + if action_type == KEY_UP_EVENT: + if changed_key.keyup: + state.macro_pending = changed_key.keyup + return state + + elif action_type == KEY_DOWN_EVENT: + if changed_key.keydown: + state.macro_pending = changed_key.keydown + return state + + return state diff --git a/kmk/firmware.py b/kmk/firmware.py index 214fc1f..b58b3cf 100644 --- a/kmk/firmware.py +++ b/kmk/firmware.py @@ -1,7 +1,7 @@ import logging from kmk.common.event_defs import init_firmware -from kmk.common.internal_state import ReduxStore, kmk_reducer +from kmk.common.internal_state import Store, kmk_reducer class Firmware: @@ -19,7 +19,7 @@ class Firmware: self.hydrated = False - self.store = ReduxStore(kmk_reducer, log_level=log_level) + self.store = Store(kmk_reducer, log_level=log_level) self.store.subscribe( lambda state, action: self._subscription(state, action), ) From 0b11f42cc21fe50ebd4606e8ad3983e66a072bfc Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sat, 6 Oct 2018 05:58:07 -0700 Subject: [PATCH 03/21] Massive README update while waiting on builds to finish... --- README.md | 75 ++++++++++++++++++++++++++----------------------------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 0c07cda..9701aec 100644 --- a/README.md +++ b/README.md @@ -2,31 +2,54 @@ [![CircleCI](https://circleci.com/gh/KMKfw/kmk_firmware/tree/master.svg?style=svg)](https://circleci.com/gh/KMKfw/kmk_firmware/tree/master)[![CLA assistant](https://cla-assistant.io/readme/badge/KMKfw/kmk_firmware)](https://cla-assistant.io/KMKfw/kmk_firmware) -KMK is a work-in-progress and proof-of-concept firmware for (usually mechanical) -keyboards, written in [MicroPython](https://micropython.org/) and -[CircuitPython](https://github.com/adafruit/circuitpython). This allows for -high-level and expressive keyboard programming and creature comforts that C -simply doesn't make easy. KMK was heavily inspired by QMK - in fact, KMK was -only created because QMK didn't correctly support some hardware I bought, and -hacking support in was going to be a heavy uphill battle. +KMK is a firmware for (usually mechanical) keyboards, written in +[MicroPython](https://micropython.org/) and +[CircuitPython](https://github.com/adafruit/circuitpython), heavily inspired by +QMK (and with some additions of our own). Python may not be the fastest thing on +the planet, but it's a joy to write, and bringing that ease of maintainership to +keyboard firmware (often a world of C and all the crazy error states C can +provide) opens up custom keyboards to whole new demographics. KMK currently only +supports handwired keyboards (see "Supported Devices" below), but work has begun +on both ports to existing keyboards, as well as converter devices to allow +existing keyboards with Pro Micro pinouts to use KMK-supported microcontrollers. +As always in open-source, KMK is a work in progress, and help is welcome! This project is currently written and maintained by: -- [Josh Klar](https://github.com/klardotsh) -- [Kyle Brown](https://github.com/kdb424) +- [Josh Klar (@klardotsh)](https://github.com/klardotsh) +- [Kyle Brown (@kdb424)](https://github.com/kdb424) + +This project also owes a `$BEVERAGE_OF_CHOICE` to some wonderful people in the +ecosystem: + +- [Jack Humbert (@jackhumbert)](https://jackhumbert.com/), for writing QMK. + Without QMK, I'd have never been exposed to the wonderful world of + programmable keyboards. He's also just an awesometastic human in general, if + you ever catch him on Discord/Reddit/etc. + +- [Dan Halbert (@dhalbert)](https://danhalbert.org/), for his amazing and + unjudgemental support of two random dudes on Github asking all sorts of + bizzare (okay... and occasionally dumb) questions on the MicroPython and + CircuitPython Github projects and the Adafruit Discord. Dan, without your help + and pointers (even when those pointers are "Remember you're working with a + microcontroller with a few MHz of processing speed and a few KB of RAM"), this + project would have never gotten off the ground. Thank you, and an extended + thanks to Adafruit. ## Supported Devices | Board | Chipset | Python Platform | Notes | | ----- | ------- | --------------- | ----- | -| [pyboard v1.1](https://www.adafruit.com/product/2390) | STM32F405RG (Cortex M4F) | MicroPython | A very basic keyboard has been written for this, see `boards/klardotsh/threethree_matrix_pyboard.py` | +| [pyboard v1.1](https://www.adafruit.com/product/2390) | STM32F405RG (Cortex M4F) | MicroPython | Our reference board for basic USB keyboards | +| [Adafruit Feather M4 Express](https://www.adafruit.com/product/3857) | Atmel SAMD52 (Cortex M4F) | CircuitPython | A more economical solution for basic USB keyboards | ### Support Planned/WIP | Board | Chipset | Python Platform | Notes | | ----- | ------- | --------------- | ----- | -| [Seeed nRF52840 Micro Dev Kit](https://www.seeedstudio.com/nRF52840-Micro-Development-Kit-p-3079.html) | nRF52840 | [MicroPython](https://github.com/klardotsh/micropython/commit/4eac11a6d1ba2d269b4cdc663d4b5b788b288804) | This is basically as bleeding edge as it gets. Linked is my very unstable and somewhat broken uPy port to this device, WIP. Supports BLE HID and, allegedly, USB HID if I can figure it out. Another option is CircuitPython, since AdaFruit is working on a Feather Express with the same chipset, and commits have been made there to support USB HID. | -| [Planck rev6 Keyboard](https://olkb.com/planck) | STM32 of some sort | Probably MicroPython? | I have one on the way! We'll see what happens. | +| [Adafruit ItsyBitsy M4 Express](https://www.adafruit.com/product/3800) | Atmel SAMD52 (Cortex M4F) | CircuitPython | An EVEN MORE economical solution for basic USB keyboards | +| [Seeed nRF52840 Micro Dev Kit](https://www.seeedstudio.com/nRF52840-Micro-Development-Kit-p-3079.html) | nRF52840 | [CircuitPython](https://github.com/KMKfw/circuitpython/tree/topic-nrf52840-mdk) | This is basically as bleeding edge as it gets. Will support BLE HID to PC as well as BLE split boards | +| [Planck rev6 Keyboard](https://olkb.com/planck) | STM32 of some sort | MicroPython | Requires porting MicroPython to STM32F3, this work has begun but I'm pretty terrible at it. | | [Proton C Controller?](https://www.reddit.com/r/MechanicalKeyboards/comments/87cw36/render_of_the_qmk_proton_c_qmkpowered_pro_micro/) | ??? | ??? | Does not exist yet, the controller from a Planck rev6 in a Pro Micro pin-compat controller chip | @@ -49,34 +72,6 @@ are currently not, due to some deficiency uncovered in development/testing: | [Teensy 3.2 Controller](https://www.adafruit.com/product/2756) | | MicroPython | Lack of USB HID (SW - MP) | -## The Great Hackaround - -While it is required that at least the device talking over USB/BLE HID (the -"primary brain") be from the Supported Devices list and running the primary -component of KMK, it will soon be possible to build split keyboards with other, -otherwise unsupported devices (currently this means a Pro Micro), either to -reduce costs or to convert existing QMK boards to KMK. You'll need to flash -"dummy" firmware to each Pro Micro which simply scans a matrix and passes the -values over I2C to the "brain" device, which does the heavy lifting from there -(including actually sending HID events). - -The obvious downsides of this method are increased number of moving parts, -increased number of things to flash (though the Pro Micros only need flashed -when matricies change, which should almost never happen once a board is built), -and all downsides that go with those points (increased power usage, etc.) The -upside is that it can be a _ton_ cheaper to build a split keyboard this way - -cheapo Pro Micro clones can be had for as little as $4 CAD at time of writing, -whereas a HUZZAH32, for example, is closer to $26 CAD, and to build the -"traditional" way, you'd need N of them (where N is the number of split sections -of your keyboard). - -It is also possible to convert many QMK boards through this fashion - while -untested for now, just about anything with a TRRS jack should work (Ergodoxen, -just about anything from keeb.io, etc.) - -This hackaround is almost certainly pointless for non-split boards. - - ## License, Copyright, and Legal This project, and all source code within (even if the file is missing headers), From 472b08d77b4df424e1ca812589473c39ef1fc3c5 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sat, 6 Oct 2018 05:59:03 -0700 Subject: [PATCH 04/21] Support KEYBOARD and CONSUMER modes of HID on Feather M4 Express --- kmk/circuitpython/hid.py | 39 +++++ kmk/common/abstract/hid.py | 155 ++++++++++++++++++ kmk/common/consts.py | 39 ++++- kmk/common/internal_state.py | 2 +- .../handwire/feather_m4_express.py | 2 + kmk/micropython/pyb_hid.py | 152 ++--------------- .../klardotsh/feather_m4_express/fourfour.py | 2 +- 7 files changed, 240 insertions(+), 151 deletions(-) create mode 100644 kmk/circuitpython/hid.py create mode 100644 kmk/common/abstract/hid.py diff --git a/kmk/circuitpython/hid.py b/kmk/circuitpython/hid.py new file mode 100644 index 0000000..c86b0b5 --- /dev/null +++ b/kmk/circuitpython/hid.py @@ -0,0 +1,39 @@ +import usb_hid +from kmk.common.abstract.hid import AbstractHidHelper +from kmk.common.consts import (HID_REPORT_SIZES, HIDReportTypes, HIDUsage, + HIDUsagePage) + + +class HIDHelper(AbstractHidHelper): + REPORT_BYTES = 9 + + def post_init(self): + self.devices = {} + + for device in usb_hid.devices: + if device.usage_page == HIDUsagePage.CONSUMER and device.usage == HIDUsage.CONSUMER: + self.devices[HIDReportTypes.CONSUMER] = device + continue + + if device.usage_page == HIDUsagePage.KEYBOARD and device.usage == HIDUsage.KEYBOARD: + self.devices[HIDReportTypes.KEYBOARD] = device + continue + + if device.usage_page == HIDUsagePage.MOUSE and device.usage == HIDUsage.MOUSE: + self.devices[HIDReportTypes.MOUSE] = device + continue + + if ( + device.usage_page == HIDUsagePage.SYSCONTROL and + device.usage == HIDUsage.SYSCONTROL + ): + self.devices[HIDReportTypes.SYSCONTROL] = device + continue + + def hid_send(self, evt): + # int, can be looked up in HIDReportTypes + reporting_device_const = self.report_device[0] + + return self.devices[reporting_device_const].send_report( + evt[1:HID_REPORT_SIZES[reporting_device_const] + 1], + ) diff --git a/kmk/common/abstract/hid.py b/kmk/common/abstract/hid.py new file mode 100644 index 0000000..ef77831 --- /dev/null +++ b/kmk/common/abstract/hid.py @@ -0,0 +1,155 @@ +import logging + +from kmk.common.consts import HIDReportTypes +from kmk.common.event_defs import HID_REPORT_EVENT +from kmk.common.keycodes import (FIRST_KMK_INTERNAL_KEYCODE, ConsumerKeycode, + ModifierKeycode) + + +class AbstractHidHelper: + REPORT_BYTES = 8 + + def __init__(self, store, log_level=logging.NOTSET): + self.logger = logging.getLogger(__name__) + self.logger.setLevel(log_level) + + self.store = store + self.store.subscribe( + lambda state, action: self._subscription(state, action), + ) + + self._evt = bytearray(self.REPORT_BYTES) + self.report_device = memoryview(self._evt)[0:1] + self.report_device[0] = HIDReportTypes.KEYBOARD + + # Landmine alert for HIDReportTypes.KEYBOARD: byte index 1 of this view + # is "reserved" and evidently (mostly?) unused. However, other modes (or + # at least consumer, so far) will use this byte, which is the main reason + # this view exists. For KEYBOARD, use report_mods and report_non_mods + self.report_keys = memoryview(self._evt)[1:] + + self.report_mods = memoryview(self._evt)[1:2] + self.report_non_mods = memoryview(self._evt)[3:] + + self.post_init() + + def post_init(self): + pass + + def _subscription(self, state, action): + if action.type == HID_REPORT_EVENT: + self.clear_all() + + consumer_key = None + for key in state.keys_pressed: + if isinstance(key, ConsumerKeycode): + consumer_key = key + break + + reporting_device = self.report_device[0] + needed_reporting_device = HIDReportTypes.KEYBOARD + + if consumer_key: + needed_reporting_device = HIDReportTypes.CONSUMER + + if reporting_device != needed_reporting_device: + # If we are about to change reporting devices, release + # all keys and close our proverbial tab on the existing + # device, or keys will get stuck (mostly when releasing + # media/consumer keys) + self.send() + + self.report_device[0] = needed_reporting_device + + if consumer_key: + self.add_key(consumer_key) + else: + for key in state.keys_pressed: + if key.code >= FIRST_KMK_INTERNAL_KEYCODE: + continue + + if isinstance(key, ModifierKeycode): + self.add_modifier(key) + else: + self.add_key(key) + + if key.has_modifiers: + for mod in key.has_modifiers: + self.add_modifier(mod) + + self.send() + + def hid_send(self, evt): + raise NotImplementedError('hid_send(evt) must be implemented') + + def send(self): + self.logger.debug('Sending HID report: {}'.format(self._evt)) + self.hid_send(self._evt) + + return self + + def clear_all(self): + for idx, _ in enumerate(self.report_keys): + self.report_keys[idx] = 0x00 + + return self + + def clear_non_modifiers(self): + for idx, _ in enumerate(self.report_non_mods): + self.report_non_mods[idx] = 0x00 + + return self + + def add_modifier(self, modifier): + if isinstance(modifier, ModifierKeycode): + self.report_mods[0] |= modifier.code + else: + self.report_mods[0] |= modifier + + return self + + def remove_modifier(self, modifier): + if isinstance(modifier, ModifierKeycode): + self.report_mods[0] ^= modifier.code + else: + self.report_mods[0] ^= modifier + + return self + + def add_key(self, key): + # Try to find the first empty slot in the key report, and fill it + placed = False + + where_to_place = self.report_non_mods + + if self.report_device[0] == HIDReportTypes.CONSUMER: + where_to_place = self.report_keys + + for idx, _ in enumerate(where_to_place): + if where_to_place[idx] == 0x00: + where_to_place[idx] = key.code + placed = True + break + + if not placed: + self.logger.warning('Out of space in HID report, could not add key') + + return self + + def remove_key(self, key): + removed = False + + where_to_place = self.report_non_mods + + if self.report_device[0] == HIDReportTypes.CONSUMER: + where_to_place = self.report_keys + + for idx, _ in enumerate(where_to_place): + if where_to_place[idx] == key.code: + where_to_place[idx] = 0x00 + removed = True + + if not removed: + self.logger.warning('Tried to remove key that was not added') + + return self diff --git a/kmk/common/consts.py b/kmk/common/consts.py index ae681b5..064a9b0 100644 --- a/kmk/common/consts.py +++ b/kmk/common/consts.py @@ -5,10 +5,33 @@ class HIDReportTypes: SYSCONTROL = 4 +class HIDUsage: + KEYBOARD = 0x06 + MOUSE = 0x02 + CONSUMER = 0x01 + SYSCONTROL = 0x80 + + +class HIDUsagePage: + CONSUMER = 0x0C + KEYBOARD = MOUSE = SYSCONTROL = 0x01 + + +# Currently only used by the CircuitPython HIDHelper because CircuitPython +# actually enforces these limits with a ValueError. Unused on PyBoard because +# we can happily send full reports there and it magically works. +HID_REPORT_SIZES = { + HIDReportTypes.KEYBOARD: 8, + HIDReportTypes.MOUSE: 4, + HIDReportTypes.CONSUMER: 2, + HIDReportTypes.SYSCONTROL: 8, # TODO find the correct value for this +} + + HID_REPORT_STRUCTURE = bytes([ # Regular keyboard - 0x05, 0x01, # Usage Page (Generic Desktop) - 0x09, 0x06, # Usage (Keyboard) + 0x05, HIDUsagePage.KEYBOARD, # Usage Page (Generic Desktop) + 0x09, HIDUsage.KEYBOARD, # Usage (Keyboard) 0xA1, 0x01, # Collection (Application) 0x85, HIDReportTypes.KEYBOARD, # Report ID (1) 0x05, 0x07, # Usage Page (Keyboard) @@ -39,8 +62,8 @@ HID_REPORT_STRUCTURE = bytes([ 0x91, 0x01, # Output (Constant) 0xC0, # End Collection # Regular mouse - 0x05, 0x01, # Usage Page (Generic Desktop) - 0x09, 0x02, # Usage (Mouse) + 0x05, HIDUsagePage.MOUSE, # Usage Page (Generic Desktop) + 0x09, HIDUsage.MOUSE, # Usage (Mouse) 0xA1, 0x01, # Collection (Application) 0x09, 0x01, # Usage (Pointer) 0xA1, 0x00, # Collection (Physical) @@ -73,8 +96,8 @@ HID_REPORT_STRUCTURE = bytes([ 0xC0, # End Collection 0xC0, # End Collection # Consumer ("multimedia") keys - 0x05, 0x0C, # Usage Page (Consumer) - 0x09, 0x01, # Usage (Consumer Control) + 0x05, HIDUsagePage.CONSUMER, # Usage Page (Consumer) + 0x09, HIDUsage.CONSUMER, # Usage (Consumer Control) 0xA1, 0x01, # Collection (Application) 0x85, HIDReportTypes.CONSUMER, # Report ID (n) 0x75, 0x10, # Report Size (16) @@ -86,8 +109,8 @@ HID_REPORT_STRUCTURE = bytes([ 0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0xC0, # End Collection # Power controls - 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) - 0x09, 0x80, # Usage (Sys Control) + 0x05, HIDUsagePage.SYSCONTROL, # Usage Page (Generic Desktop Ctrls) + 0x09, HIDUsage.SYSCONTROL, # Usage (Sys Control) 0xA1, 0x01, # Collection (Application) 0x85, HIDReportTypes.SYSCONTROL, # Report ID (n) 0x75, 0x02, # Report Size (2) diff --git a/kmk/common/internal_state.py b/kmk/common/internal_state.py index b503ecf..861cb13 100644 --- a/kmk/common/internal_state.py +++ b/kmk/common/internal_state.py @@ -52,7 +52,7 @@ class Store: cb(self.state, action) except Exception as e: self.logger.error('Callback failed, moving on') - print(sys.print_exception(e), file=sys.stderr) + sys.print_exception(e) def get_state(self): return self.state diff --git a/kmk/entrypoints/handwire/feather_m4_express.py b/kmk/entrypoints/handwire/feather_m4_express.py index acbd4dd..8ce7d06 100644 --- a/kmk/entrypoints/handwire/feather_m4_express.py +++ b/kmk/entrypoints/handwire/feather_m4_express.py @@ -1,6 +1,7 @@ import sys from logging import DEBUG +from kmk.circuitpython.hid import HIDHelper from kmk.circuitpython.matrix import MatrixScanner from kmk.common.consts import UnicodeModes from kmk.firmware import Firmware @@ -23,6 +24,7 @@ def main(): unicode_mode=unicode_mode, log_level=DEBUG, matrix_scanner=MatrixScanner, + hid=HIDHelper, ) firmware.go() diff --git a/kmk/micropython/pyb_hid.py b/kmk/micropython/pyb_hid.py index 1dfa807..97edc60 100644 --- a/kmk/micropython/pyb_hid.py +++ b/kmk/micropython/pyb_hid.py @@ -1,11 +1,7 @@ -import logging - from pyb import USB_HID, delay, hid_keyboard -from kmk.common.consts import HID_REPORT_STRUCTURE, HIDReportTypes -from kmk.common.event_defs import HID_REPORT_EVENT -from kmk.common.keycodes import (FIRST_KMK_INTERNAL_KEYCODE, ConsumerKeycode, - ModifierKeycode) +from kmk.common.abstract.hid import AbstractHidHelper +from kmk.common.consts import HID_REPORT_STRUCTURE def generate_pyb_hid_descriptor(): @@ -14,80 +10,20 @@ def generate_pyb_hid_descriptor(): return tuple(existing_keyboard) -class HIDHelper: - def __init__(self, store, log_level=logging.NOTSET): - self.logger = logging.getLogger(__name__) - self.logger.setLevel(log_level) - - self.store = store - self.store.subscribe( - lambda state, action: self._subscription(state, action), - ) +class HIDHelper(AbstractHidHelper): + # For some bizarre reason this can no longer be 8, it'll just fail to send + # anything. This is almost certainly a bug in the report descriptor sent + # over in the boot process. For now the sacrifice is that we only support + # 5KRO until I figure this out, rather than the 6KRO HID defines. + REPORT_BYTES = 7 + def post_init(self): self._hid = USB_HID() - - # For some bizarre reason this can no longer be 8, it'll just fail to - # send anything. This is almost certainly a bug in the report descriptor - # sent over in the boot process. For now the sacrifice is that we only - # support 5KRO until I figure this out, rather than the 6KRO HID defines. - self._evt = bytearray(7) - self.report_device = memoryview(self._evt)[0:1] - - # Landmine alert for HIDReportTypes.KEYBOARD: byte index 1 of this view - # is "reserved" and evidently (mostly?) unused. However, other modes (or - # at least consumer, so far) will use this byte, which is the main reason - # this view exists. For KEYBOARD, use report_mods and report_non_mods - self.report_keys = memoryview(self._evt)[1:] - - self.report_mods = memoryview(self._evt)[1:2] - self.report_non_mods = memoryview(self._evt)[3:] - - def _subscription(self, state, action): - if action.type == HID_REPORT_EVENT: - self.clear_all() - - consumer_key = None - for key in state.keys_pressed: - if isinstance(key, ConsumerKeycode): - consumer_key = key - break - - reporting_device = self.report_device[0] - needed_reporting_device = HIDReportTypes.KEYBOARD - - if consumer_key: - needed_reporting_device = HIDReportTypes.CONSUMER - - if reporting_device != needed_reporting_device: - # If we are about to change reporting devices, release - # all keys and close our proverbial tab on the existing - # device, or keys will get stuck (mostly when releasing - # media/consumer keys) - self.send() - - self.report_device[0] = needed_reporting_device - - if consumer_key: - self.add_key(consumer_key) - else: - for key in state.keys_pressed: - if key.code >= FIRST_KMK_INTERNAL_KEYCODE: - continue - - if isinstance(key, ModifierKeycode): - self.add_modifier(key) - else: - self.add_key(key) - - if key.has_modifiers: - for mod in key.has_modifiers: - self.add_modifier(mod) - - self.send() + self.hid_send = self._hid.send def send(self): self.logger.debug('Sending HID report: {}'.format(self._evt)) - self._hid.send(self._evt) + self.hid_send(self._evt) # Without this delay, events get clobbered and you'll likely end up with # a string like `heloooooooooooooooo` rather than `hello`. This number @@ -101,69 +37,3 @@ class HIDHelper: delay(5) return self - - def clear_all(self): - for idx, _ in enumerate(self.report_keys): - self.report_keys[idx] = 0x00 - - return self - - def clear_non_modifiers(self): - for idx, _ in enumerate(self.report_non_mods): - self.report_non_mods[idx] = 0x00 - - return self - - def add_modifier(self, modifier): - if isinstance(modifier, ModifierKeycode): - self.report_mods[0] |= modifier.code - else: - self.report_mods[0] |= modifier - - return self - - def remove_modifier(self, modifier): - if isinstance(modifier, ModifierKeycode): - self.report_mods[0] ^= modifier.code - else: - self.report_mods[0] ^= modifier - - return self - - def add_key(self, key): - # Try to find the first empty slot in the key report, and fill it - placed = False - - where_to_place = self.report_non_mods - - if self.report_device[0] == HIDReportTypes.CONSUMER: - where_to_place = self.report_keys - - for idx, _ in enumerate(where_to_place): - if where_to_place[idx] == 0x00: - where_to_place[idx] = key.code - placed = True - break - - if not placed: - self.logger.warning('Out of space in HID report, could not add key') - - return self - - def remove_key(self, key): - removed = False - - where_to_place = self.report_non_mods - - if self.report_device[0] == HIDReportTypes.CONSUMER: - where_to_place = self.report_keys - - for idx, _ in enumerate(where_to_place): - if where_to_place[idx] == key.code: - where_to_place[idx] = 0x00 - removed = True - - if not removed: - self.logger.warning('Tried to remove key that was not added') - - return self diff --git a/user_keymaps/klardotsh/feather_m4_express/fourfour.py b/user_keymaps/klardotsh/feather_m4_express/fourfour.py index 661a69d..5a31835 100644 --- a/user_keymaps/klardotsh/feather_m4_express/fourfour.py +++ b/user_keymaps/klardotsh/feather_m4_express/fourfour.py @@ -49,7 +49,7 @@ ANGRY_TABLE_FLIP = unicode_sequence([ keymap = [ [ [KC.GESC, KC.A, KC.RESET], - [KC.MO(1), KC.B, KC.C], + [KC.MO(1), KC.B, KC.MUTE], [KC.LT(2, KC.EXCLAIM), KC.HASH, KC.ENTER], [KC.TT(3), KC.SPACE, KC.LSHIFT], ], From c4c28ad039c911e703f6dae564809c48faa5abb6 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sat, 6 Oct 2018 22:15:55 -0700 Subject: [PATCH 05/21] Decrease typing lag by about 80 billion percent by upping the logging threshold to WARNING on SAMD51 --- kmk/entrypoints/handwire/feather_m4_express.py | 4 ++-- user_keymaps/klardotsh/feather_m4_express/fourfour.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kmk/entrypoints/handwire/feather_m4_express.py b/kmk/entrypoints/handwire/feather_m4_express.py index 8ce7d06..b695541 100644 --- a/kmk/entrypoints/handwire/feather_m4_express.py +++ b/kmk/entrypoints/handwire/feather_m4_express.py @@ -1,5 +1,5 @@ import sys -from logging import DEBUG +from logging import WARNING from kmk.circuitpython.hid import HIDHelper from kmk.circuitpython.matrix import MatrixScanner @@ -22,7 +22,7 @@ def main(): col_pins=cols, diode_orientation=diode_orientation, unicode_mode=unicode_mode, - log_level=DEBUG, + log_level=WARNING, matrix_scanner=MatrixScanner, hid=HIDHelper, ) diff --git a/user_keymaps/klardotsh/feather_m4_express/fourfour.py b/user_keymaps/klardotsh/feather_m4_express/fourfour.py index 5a31835..54a2fb7 100644 --- a/user_keymaps/klardotsh/feather_m4_express/fourfour.py +++ b/user_keymaps/klardotsh/feather_m4_express/fourfour.py @@ -51,7 +51,7 @@ keymap = [ [KC.GESC, KC.A, KC.RESET], [KC.MO(1), KC.B, KC.MUTE], [KC.LT(2, KC.EXCLAIM), KC.HASH, KC.ENTER], - [KC.TT(3), KC.SPACE, KC.LSHIFT], + [KC.TT(3), KC.SPACE, ANGRY_TABLE_FLIP], ], [ [KC.TRNS, KC.B, KC.C], From d379acfc97de6211c0b281ccff80e2d6f6a89fb2 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 00:22:54 -0700 Subject: [PATCH 06/21] Support ItsyBitsy M4 Express as an effective clone of the Feather M4 Express with less pins. Because that's what it is. --- Makefile | 28 ++++++++ README.md | 2 +- .../handwire/itsybitsy_m4_express.py | 33 ++++++++++ .../itsybitsy_m4_express/threethree.py | 65 +++++++++++++++++++ 4 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 kmk/entrypoints/handwire/itsybitsy_m4_express.py create mode 100644 user_keymaps/klardotsh/itsybitsy_m4_express/threethree.py diff --git a/Makefile b/Makefile index 1408727..5b2134c 100644 --- a/Makefile +++ b/Makefile @@ -137,6 +137,10 @@ 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-itsybitsy-m4-express: + @echo "===> Building CircuitPython" + @make -C build/circuitpython/ports/atmel-samd BOARD=itsybitsy_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 @@ -148,6 +152,13 @@ circuitpy-flash-feather-m4-express: @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-itsybitsy-m4-express: + @echo "Flashing not available for ItsyBitsy M4 Express over bossa right now" + @echo "First, double tap the reset button on the ItsyBitsy. 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-itsybitsy_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 @@ -206,6 +217,23 @@ flash-feather-nrf52832: lint devdeps circuitpy-deps circuitpy-freeze-kmk-nrf @$(MAKE) circuitpy-flash-nrf circuitpy-flash-nrf-entrypoint endif +ifndef USER_KEYMAP +build-itsybitsy-m4-express: + @echo "===> Must provide a USER_KEYMAP (usually from user_keymaps/...) to build!" && exit 1 + +flash-itsybitsy-m4-express: + @echo "===> Must provide a USER_KEYMAP (usually from user_keymaps/...) to build!" && exit 1 +else +build-itsybitsy-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-itsybitsy-m4-express + +flash-itsybitsy-m4-express: lint devdeps circuitpy-deps circuitpy-freeze-kmk-atmel-samd + @echo "===> Preparing keyboard script for bundling into CircuitPython" + @$(MAKE) build-itsybitsy-m4-express circuitpy-flash-itsybitsy-m4-express +endif + ifndef USER_KEYMAP build-teensy-3.1: @echo "===> Must provide a USER_KEYMAP (usually from user_keymaps/...) to build!" && exit 1 diff --git a/README.md b/README.md index 9701aec..383fd42 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,11 @@ ecosystem: | ----- | ------- | --------------- | ----- | | [pyboard v1.1](https://www.adafruit.com/product/2390) | STM32F405RG (Cortex M4F) | MicroPython | Our reference board for basic USB keyboards | | [Adafruit Feather M4 Express](https://www.adafruit.com/product/3857) | Atmel SAMD52 (Cortex M4F) | CircuitPython | A more economical solution for basic USB keyboards | +| [Adafruit ItsyBitsy M4 Express](https://www.adafruit.com/product/3800) | Atmel SAMD52 (Cortex M4F) | CircuitPython | An EVEN MORE economical solution for basic USB keyboards | ### Support Planned/WIP | Board | Chipset | Python Platform | Notes | | ----- | ------- | --------------- | ----- | -| [Adafruit ItsyBitsy M4 Express](https://www.adafruit.com/product/3800) | Atmel SAMD52 (Cortex M4F) | CircuitPython | An EVEN MORE economical solution for basic USB keyboards | | [Seeed nRF52840 Micro Dev Kit](https://www.seeedstudio.com/nRF52840-Micro-Development-Kit-p-3079.html) | nRF52840 | [CircuitPython](https://github.com/KMKfw/circuitpython/tree/topic-nrf52840-mdk) | This is basically as bleeding edge as it gets. Will support BLE HID to PC as well as BLE split boards | | [Planck rev6 Keyboard](https://olkb.com/planck) | STM32 of some sort | MicroPython | Requires porting MicroPython to STM32F3, this work has begun but I'm pretty terrible at it. | | [Proton C Controller?](https://www.reddit.com/r/MechanicalKeyboards/comments/87cw36/render_of_the_qmk_proton_c_qmkpowered_pro_micro/) | ??? | ??? | Does not exist yet, the controller from a Planck rev6 in a Pro Micro pin-compat controller chip | diff --git a/kmk/entrypoints/handwire/itsybitsy_m4_express.py b/kmk/entrypoints/handwire/itsybitsy_m4_express.py new file mode 100644 index 0000000..b695541 --- /dev/null +++ b/kmk/entrypoints/handwire/itsybitsy_m4_express.py @@ -0,0 +1,33 @@ +import sys +from logging import WARNING + +from kmk.circuitpython.hid import HIDHelper +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=WARNING, + matrix_scanner=MatrixScanner, + hid=HIDHelper, + ) + + firmware.go() + except Exception as e: + sys.print_exception(e) + sys.exit(1) diff --git a/user_keymaps/klardotsh/itsybitsy_m4_express/threethree.py b/user_keymaps/klardotsh/itsybitsy_m4_express/threethree.py new file mode 100644 index 0000000..711c5b1 --- /dev/null +++ b/user_keymaps/klardotsh/itsybitsy_m4_express/threethree.py @@ -0,0 +1,65 @@ +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.itsybitsy_m4_express import main +from kmk.firmware import Firmware + +cols = (board.A4, board.A5, board.D13) +rows = (board.D12, board.D11, board.D10) + +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.MUTE], + [KC.LT(2, KC.EXCLAIM), KC.HASH, KC.ENTER], + ], + [ + [KC.MUTE, KC.B, KC.C], + [KC.TRNS, KC.D, KC.E], + [KC.F, KC.G, KC.H], + ], + [ + [KC.VOLU, KC.MUTE, ANGRY_TABLE_FLIP], + [KC.NO, KC.PIPE, MACRO_TEST_SIMPLE], + [KC.TRNS, KC.P, MACRO_TEST_STRING], + ], +] From a23acc61318548c6a88cacc780558c141d0e4f69 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 00:44:04 -0700 Subject: [PATCH 07/21] Update various internal key handling stuff to be CircuitPython-compatible --- kmk/common/event_defs.py | 7 ++----- kmk/common/kmktime.py | 28 ++++++++++++++++++++++++---- kmk/common/macros/simple.py | 2 +- kmk/common/util.py | 13 ------------- 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/kmk/common/event_defs.py b/kmk/common/event_defs.py index 0a232e6..51ba8eb 100644 --- a/kmk/common/event_defs.py +++ b/kmk/common/event_defs.py @@ -4,6 +4,7 @@ from collections import namedtuple from micropython import const from kmk.common.keycodes import Keycodes +from kmk.common.util import reset_bootloader KEY_UP_EVENT = const(1) KEY_DOWN_EVENT = const(2) @@ -117,11 +118,7 @@ def matrix_changed(new_pressed): dispatch(hid_report_event()) if Keycodes.KMK.KC_RESET in state.keys_pressed: - try: - import machine - machine.bootloader() - except ImportError: - logger.warning('Tried to reset to bootloader, but not supported on this chip?') + reset_bootloader() if state.pending_keys: for key in state.pending_keys: diff --git a/kmk/common/kmktime.py b/kmk/common/kmktime.py index 163b330..ea97a9e 100644 --- a/kmk/common/kmktime.py +++ b/kmk/common/kmktime.py @@ -1,13 +1,33 @@ +import math try: - import utime + import utime as time + USE_UTIME = True except ImportError: - pass + import time + USE_UTIME = False + + +def sleep_ms(ms): + ''' + Tries to sleep for a number of milliseconds in a cross-implementation + way. Will raise an ImportError if time is not available on the platform. + ''' + if USE_UTIME: + return time.sleep_ms(ms) + else: + return time.sleep(ms / 1000) def ticks_ms(): - return utime.ticks_ms() + if USE_UTIME: + return time.ticks_ms() + else: + return math.floor(time.monotonic() * 1000) def ticks_diff(new, old): - return utime.ticks_diff(new, old) + if USE_UTIME: + return time.ticks_diff(new, old) + else: + return new - old diff --git a/kmk/common/macros/simple.py b/kmk/common/macros/simple.py index 29f6c14..0bb77ba 100644 --- a/kmk/common/macros/simple.py +++ b/kmk/common/macros/simple.py @@ -3,7 +3,7 @@ import string from kmk.common.event_defs import (hid_report_event, keycode_down_event, keycode_up_event) from kmk.common.keycodes import Keycodes, Macro, RawKeycodes, char_lookup -from kmk.common.util import sleep_ms +from kmk.common.kmktime import sleep_ms def simple_key_sequence(seq): diff --git a/kmk/common/util.py b/kmk/common/util.py index e90d85b..2ff06d1 100644 --- a/kmk/common/util.py +++ b/kmk/common/util.py @@ -29,16 +29,3 @@ def reset_bootloader(): import microcontroller microcontroller.on_next_reset(microcontroller.RunMode.BOOTLOADER) microcontroller.reset() - - -def sleep_ms(ms): - ''' - Tries to sleep for a number of milliseconds in a cross-implementation - way. Will raise an ImportError if time is not available on the platform. - ''' - try: - import time - time.sleep_ms(ms) - except AttributeError: - import time - time.sleep(ms / 1000) From 8e743642db457645e0a6149ccb3c6b66ac0878b7 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 00:49:33 -0700 Subject: [PATCH 08/21] Add the SAMD51 chips to Circle config. Finally banish the Teensy to save build jobs --- .circleci/config.yml | 81 +++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9a655cc..26f491e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -40,6 +40,44 @@ jobs: - run: apt-get update && apt-get install -y gcc-arm-none-eabi gettext wget unzip rsync - run: make test + build_feather_m4_express: + docker: + - image: 'python:3.7' + + environment: + KMK_TEST: 1 + PIPENV_VENV_IN_PROJECT: 1 + + steps: + - checkout + - restore_cache: + keys: + - v1-kmk-venv-{{ checksum "Pipfile.lock" }} + + - run: pip install pipenv==2018.7.1 + - run: apt-get update && apt-get install -y gcc-arm-none-eabi gettext wget unzip rsync + + - run: make SKIP_KEYMAP_VALIDATION=1 USER_KEYMAP=user_keymaps/noop.py build-feather-m4-express + + build_itsybitsy_m4_express: + docker: + - image: 'python:3.7' + + environment: + KMK_TEST: 1 + PIPENV_VENV_IN_PROJECT: 1 + + steps: + - checkout + - restore_cache: + keys: + - v1-kmk-venv-{{ checksum "Pipfile.lock" }} + + - run: pip install pipenv==2018.7.1 + - run: apt-get update && apt-get install -y gcc-arm-none-eabi gettext wget unzip rsync + + - run: make SKIP_KEYMAP_VALIDATION=1 USER_KEYMAP=user_keymaps/noop.py build-itsybitsy-m4-express + build_pyboard: docker: - image: 'python:3.7' @@ -59,25 +97,6 @@ jobs: - run: make SKIP_KEYMAP_VALIDATION=1 USER_KEYMAP=user_keymaps/noop.py build-pyboard - build_teensy_31: - docker: - - image: 'python:3.7' - - environment: - KMK_TEST: 1 - PIPENV_VENV_IN_PROJECT: 1 - - steps: - - checkout - - restore_cache: - keys: - - v1-kmk-venv-{{ checksum "Pipfile.lock" }} - - - run: pip install pipenv==2018.7.1 - - run: apt-get update && apt-get install -y gcc-arm-none-eabi gettext wget unzip rsync - - - run: make USER_KEYMAP=user_keymaps/noop.py build-teensy-3.1 - workflows: version: 2 build-deploy: @@ -96,6 +115,22 @@ workflows: only: /.*/ requires: - lint + - build_feather_m4_express: + filters: + branches: + only: /.*/ + tags: + only: /.*/ + requires: + - test + - build_itsybitsy_m4_express: + filters: + branches: + only: /.*/ + tags: + only: /.*/ + requires: + - test - build_pyboard: filters: branches: @@ -104,11 +139,3 @@ workflows: only: /.*/ requires: - test - - build_teensy_31: - filters: - branches: - only: /.*/ - tags: - only: /.*/ - requires: - - test From d61d3b7b3806346cc63d1d51584fd6dac7b0cd03 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 00:51:37 -0700 Subject: [PATCH 09/21] Makefile fix on installing devdeps --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5b2134c..276565e 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ ARDUINO ?= /usr/share/arduino PIPENV ?= $(shell which pipenv) .devdeps: Pipfile.lock - @$(PIPENV) install --dev --ignore-pipfile/ + @$(PIPENV) install --dev --ignore-pipfile @touch .devdeps devdeps: .devdeps From 004b80e4b6bac8a3cbd85b53c1d2f5c256a8f080 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 01:01:37 -0700 Subject: [PATCH 10/21] Some Makefile fixes --- Makefile | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 276565e..18d0306 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ test: micropython-build-unix @echo "===> Pulling dependencies, this may take several minutes" @git submodule sync @git submodule update --init --recursive - @rsync -avh vendor/ build/ + @rsync -ah vendor/ build/ @touch .submodules .circuitpy-deps: .submodules @@ -80,7 +80,7 @@ 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 $< | egrep -v '(^#|^\s*$|^\s*\t*#)' | grep MICROPY | cut -d'|' -f2- | \ - xargs -I '{}' cp -a {} build/circuitpython/ports/teensy/freeze/ + xargs -I '{}' cp -a {} build/micropython/ports/unix/freeze/ @touch $@ build/circuitpython/ports/atmel-samd/modules/.kmk_frozen: upy-freeze.txt submodules.toml @@ -189,8 +189,15 @@ build-feather-m4-express: flash-feather-m4-express: @echo "===> Must provide a USER_KEYMAP (usually from user_keymaps/...) to build!" && exit 1 else +ifndef SKIP_KEYMAP_VALIDATION build-feather-m4-express: lint devdeps circuitpy-deps circuitpy-freeze-kmk-atmel-samd +else +build-feather-m4-express: lint devdeps circuitpy-deps circuitpy-freeze-kmk-atmel-samd micropython-build-unix +endif @echo "===> Preparing keyboard script for bundling into CircuitPython" +ifndef SKIP_KEYMAP_VALIDATION + @MICROPYPATH=./ ./bin/micropython.sh bin/keymap_sanity_check.py ${USER_KEYMAP} +endif @cp -av ${USER_KEYMAP} build/circuitpython/ports/atmel-samd/modules/kmk_keyboard_user.py @$(MAKE) circuitpy-build-feather-m4-express @@ -224,8 +231,15 @@ build-itsybitsy-m4-express: flash-itsybitsy-m4-express: @echo "===> Must provide a USER_KEYMAP (usually from user_keymaps/...) to build!" && exit 1 else +ifndef SKIP_KEYMAP_VALIDATION build-itsybitsy-m4-express: lint devdeps circuitpy-deps circuitpy-freeze-kmk-atmel-samd +else +build-itsybitsy-m4-express: lint devdeps circuitpy-deps circuitpy-freeze-kmk-atmel-samd micropython-build-unix +endif @echo "===> Preparing keyboard script for bundling into CircuitPython" +ifndef SKIP_KEYMAP_VALIDATION + @MICROPYPATH=./ ./bin/micropython.sh bin/keymap_sanity_check.py ${USER_KEYMAP} +endif @cp -av ${USER_KEYMAP} build/circuitpython/ports/atmel-samd/modules/kmk_keyboard_user.py @$(MAKE) circuitpy-build-itsybitsy-m4-express From ec9591ad8afacc3fb8a52606390631d4ef909c43 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 01:39:04 -0700 Subject: [PATCH 11/21] Makefile: Enable keymap checker on SAMD51. Fix Unix MPY build --- Makefile | 14 +++++++------- upy-unix-stubs/digitalio.py | 3 +++ upy-unix-stubs/usb_hid.py | 1 + 3 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 upy-unix-stubs/digitalio.py create mode 100644 upy-unix-stubs/usb_hid.py diff --git a/Makefile b/Makefile index 18d0306..d747289 100644 --- a/Makefile +++ b/Makefile @@ -78,22 +78,22 @@ freeze-stm32-build-deps: build/micropython/ports/stm32/freeze/.kmk_frozen 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 $< | egrep -v '(^#|^\s*$|^\s*\t*#)' | grep MICROPY | cut -d'|' -f2- | \ - xargs -I '{}' cp -a {} build/micropython/ports/unix/freeze/ + @rm -rf build/micropython/ports/unix/modules/* + @cat upy-freeze.txt | egrep -v '(^#|^\s*$|^\s*\t*#)' | grep MICROPY | cut -d'|' -f2- | \ + xargs -I '{}' cp -a {} build/micropython/ports/unix/modules/ @touch $@ 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- | \ + @cat upy-freeze.txt | 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 $< | egrep -v '(^#|^\s*$|^\s*\t*#)' | grep CIRCUITPY | cut -d'|' -f2- | \ + @cat upy-freeze.txt | egrep -v '(^#|^\s*$|^\s*\t*#)' | grep CIRCUITPY | cut -d'|' -f2- | \ xargs -I '{}' cp -a {} build/circuitpython/ports/nrf/freeze/ @touch $@ @@ -101,7 +101,7 @@ build/micropython/ports/teensy/freeze/.kmk_frozen: upy-freeze.txt submodules.tom @echo "===> Preparing builded dependencies for bundling" @mkdir -p build/micropython/ports/teensy/freeze/ @rm -rf build/micropython/ports/teensy/freeze/* - @cat $< | egrep -v '(^#|^\s*$|^\s*\t*#)' | grep MICROPY | cut -d'|' -f2- | \ + @cat upy-freeze.txt | egrep -v '(^#|^\s*$|^\s*\t*#)' | grep MICROPY | cut -d'|' -f2- | \ xargs -I '{}' cp -a {} build/circuitpython/ports/teensy/freeze/ @touch $@ @@ -109,7 +109,7 @@ 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 $< | egrep -v '(^#|^\s*$|^\s*\t*#)' | grep MICROPY | cut -d'|' -f2- | \ + @cat upy-freeze.txt | egrep -v '(^#|^\s*$|^\s*\t*#)' | grep MICROPY | cut -d'|' -f2- | \ xargs -I '{}' cp -a {} build/micropython/ports/stm32/freeze/ @touch $@ diff --git a/upy-unix-stubs/digitalio.py b/upy-unix-stubs/digitalio.py new file mode 100644 index 0000000..b293fa2 --- /dev/null +++ b/upy-unix-stubs/digitalio.py @@ -0,0 +1,3 @@ +class DigitalInOut: + def __init__(self, *args, **kwargs): + pass diff --git a/upy-unix-stubs/usb_hid.py b/upy-unix-stubs/usb_hid.py new file mode 100644 index 0000000..1b2034a --- /dev/null +++ b/upy-unix-stubs/usb_hid.py @@ -0,0 +1 @@ +devices = [] From 392f8d7e51cc6c6cae0fc96b8fbb10b8814bd652 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 01:40:09 -0700 Subject: [PATCH 12/21] Abstract away platform-dependent pin references Quote taken straight from the docstring of get_pin: >Cross-platform method to find a pin by string. > >The pin definitions are platform-dependent, but this provides >a way to say "I'm using pin D20" without rolling a D20 and >having to actually learn MicroPython/CircuitPython and the >differences in how they handle pinouts. > >This also makes the keymap sanity checker actually work for >CircuitPython boards, since it's not possible in CPY to >define a module stub for `board` that uses Passthrough >natively (which is how the MicroPython stub worked originally) --- kmk/common/pins.py | 40 +++++++++++++++++++ kmk/common/types.py | 16 ++++++++ .../kdb424/handwire_planck_pyboard.py | 9 ++--- .../klardotsh/feather_m4_express/fourfour.py | 7 ++-- .../itsybitsy_m4_express/threethree.py | 7 ++-- .../klardotsh/threethree_matrix_pyboard.py | 8 ++-- 6 files changed, 68 insertions(+), 19 deletions(-) create mode 100644 kmk/common/pins.py diff --git a/kmk/common/pins.py b/kmk/common/pins.py new file mode 100644 index 0000000..b469a5a --- /dev/null +++ b/kmk/common/pins.py @@ -0,0 +1,40 @@ +try: + import board + + PLATFORM = 'CircuitPython' + PIN_SOURCE = board +except ImportError: + import machine + + PLATFORM = 'MicroPython' + PIN_SOURCE = machine.Pin.board +except ImportError: + from kmk.common.types import Passthrough + + PLATFORM = 'Unit Testing' + PIN_SOURCE = Passthrough() + + +def get_pin(pin): + ''' + Cross-platform method to find a pin by string. + + The pin definitions are platform-dependent, but this provides + a way to say "I'm using pin D20" without rolling a D20 and + having to actually learn MicroPython/CircuitPython and the + differences in how they handle pinouts. + + This also makes the keymap sanity checker actually work for + CircuitPython boards, since it's not possible in CPY to + define a module stub for `board` that uses Passthrough + natively (which is how the MicroPython stub worked originally) + ''' + return getattr(PIN_SOURCE, pin) + + +class PinLookup: + def __getattr__(self, attr): + return get_pin(attr) + + +Pin = PinLookup() diff --git a/kmk/common/types.py b/kmk/common/types.py index 5befe08..aeacb85 100644 --- a/kmk/common/types.py +++ b/kmk/common/types.py @@ -8,3 +8,19 @@ class AttrDict(dict): ''' def __getattr__(self, key): return self[key] + + +class Anything: + ''' + A stub class which will repr as a provided name + ''' + def __init__(self, name): + self.name = name + + def __repr__(self): + return 'Anything<{}>'.format(self.name) + + +class Passthrough: + def __getattr__(self, attr): + return Anything(attr) diff --git a/user_keymaps/kdb424/handwire_planck_pyboard.py b/user_keymaps/kdb424/handwire_planck_pyboard.py index c01b9a6..3671270 100644 --- a/user_keymaps/kdb424/handwire_planck_pyboard.py +++ b/user_keymaps/kdb424/handwire_planck_pyboard.py @@ -1,14 +1,11 @@ -import machine - from kmk.common.consts import DiodeOrientation, UnicodeModes from kmk.common.keycodes import KC from kmk.common.macros.unicode import unicode_sequence +from kmk.common.pins import Pin as P from kmk.entrypoints.handwire.pyboard import main -p = machine.Pin.board - -cols = (p.Y12, p.Y11, p.Y10, p.Y9, p.X8, p.X7, p.X6, p.X5, p.X4, p.X3, p.X2, p.X1) -rows = (p.Y1, p.Y2, p.Y3, p.Y4) +cols = (P.Y12, P.Y11, P.Y10, P.Y9, P.X8, P.X7, P.X6, P.X5, P.X4, P.X3, P.X2, P.X1) +rows = (P.Y1, P.Y2, P.Y3, P.Y4) diode_orientation = DiodeOrientation.COLUMNS diff --git a/user_keymaps/klardotsh/feather_m4_express/fourfour.py b/user_keymaps/klardotsh/feather_m4_express/fourfour.py index 54a2fb7..11d084e 100644 --- a/user_keymaps/klardotsh/feather_m4_express/fourfour.py +++ b/user_keymaps/klardotsh/feather_m4_express/fourfour.py @@ -1,14 +1,13 @@ -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.common.pins import Pin as P 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) +cols = (P.D11, P.D10, P.D9) +rows = (P.A2, P.A3, P.A4, P.A5) diode_orientation = DiodeOrientation.COLUMNS unicode_mode = UnicodeModes.LINUX diff --git a/user_keymaps/klardotsh/itsybitsy_m4_express/threethree.py b/user_keymaps/klardotsh/itsybitsy_m4_express/threethree.py index 711c5b1..5c0267b 100644 --- a/user_keymaps/klardotsh/itsybitsy_m4_express/threethree.py +++ b/user_keymaps/klardotsh/itsybitsy_m4_express/threethree.py @@ -1,14 +1,13 @@ -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.common.pins import Pin as P from kmk.entrypoints.handwire.itsybitsy_m4_express import main from kmk.firmware import Firmware -cols = (board.A4, board.A5, board.D13) -rows = (board.D12, board.D11, board.D10) +cols = (P.A4, P.A5, P.D13) +rows = (P.D12, P.D11, P.D10) diode_orientation = DiodeOrientation.COLUMNS unicode_mode = UnicodeModes.LINUX diff --git a/user_keymaps/klardotsh/threethree_matrix_pyboard.py b/user_keymaps/klardotsh/threethree_matrix_pyboard.py index 12260e5..b334317 100644 --- a/user_keymaps/klardotsh/threethree_matrix_pyboard.py +++ b/user_keymaps/klardotsh/threethree_matrix_pyboard.py @@ -1,14 +1,12 @@ -import machine - 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.common.pins import Pin as P from kmk.entrypoints.handwire.pyboard import main -p = machine.Pin.board -cols = (p.X10, p.X11, p.X12) -rows = (p.X1, p.X2, p.X3) +cols = (P.X10, P.X11, P.X12) +rows = (P.X1, P.X2, P.X3) diode_orientation = DiodeOrientation.COLUMNS unicode_mode = UnicodeModes.LINUX From 71af18819f2d925557a4ba51a89b464b3459d11a Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 01:55:41 -0700 Subject: [PATCH 13/21] Makefile: Simplify flash rules to have cleaner dependency trees. Fully remove targets for unsupported Teensy. --- Makefile | 94 ++++++++------------------------------------------------ 1 file changed, 13 insertions(+), 81 deletions(-) diff --git a/Makefile b/Makefile index d747289..7a8206b 100644 --- a/Makefile +++ b/Makefile @@ -73,7 +73,6 @@ 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 submodules.toml @@ -97,14 +96,6 @@ build/circuitpython/ports/nrf/freeze/.kmk_frozen: upy-freeze.txt submodules.toml xargs -I '{}' cp -a {} build/circuitpython/ports/nrf/freeze/ @touch $@ -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 upy-freeze.txt | 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 submodules.toml @echo "===> Preparing builded dependencies for bundling" @mkdir -p build/micropython/ports/stm32/freeze/ @@ -116,22 +107,17 @@ build/micropython/ports/stm32/freeze/.kmk_frozen: upy-freeze.txt submodules.toml 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/ + @rsync -ah 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* - @cp -av kmk build/circuitpython/ports/nrf/freeze/ - -micropython-freeze-kmk-teensy3.1: freeze-teensy3.1-build-deps - @echo "===> Preparing KMK source for bundling into MicroPython" - @rm -rf build/micropython/ports/teensy/kmk* - @cp -av kmk build/micropython/ports/teensy/memzip_files/ + @rsync -ah kmk build/circuitpython/ports/nrf/freeze/ micropython-freeze-kmk-stm32: freeze-stm32-build-deps @echo "===> Preparing KMK source for bundling into MicroPython" @rm -rf build/micropython/ports/stm32/freeze/kmk* - @cp -av kmk build/micropython/ports/stm32/freeze/ + @rsync -ah kmk build/micropython/ports/stm32/freeze/ circuitpy-build-feather-m4-express: @echo "===> Building CircuitPython" @@ -163,12 +149,6 @@ 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 -micropython-build-teensy3.1: - @make -C build/micropython/ports/teensy/ BOARD=TEENSY_3.1 all - -micropython-flash-teensy3.1: micropython-build-teensy3.1 - @make -C build/micropython/ports/teensy/ BOARD=TEENSY_3.1 deploy - micropython-build-pyboard: @make -j4 -C build/micropython/ports/stm32/ BOARD=PYBV11 FROZEN_MPY_DIR=freeze all @@ -198,12 +178,10 @@ endif ifndef SKIP_KEYMAP_VALIDATION @MICROPYPATH=./ ./bin/micropython.sh bin/keymap_sanity_check.py ${USER_KEYMAP} endif - @cp -av ${USER_KEYMAP} build/circuitpython/ports/atmel-samd/modules/kmk_keyboard_user.py + @rsync -ah ${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 +flash-feather-m4-express: build-feather-m4-express circuitpy-flash-feather-m4-express endif ifndef USER_KEYMAP @@ -215,13 +193,10 @@ flash-feather-nrf52832: else build-feather-nrf52832: lint devdeps circuitpy-deps circuitpy-freeze-kmk-nrf @echo "===> Preparing keyboard script for bundling into CircuitPython" - @cp -av ${USER_KEYMAP} build/circuitpython/ports/nrf/freeze/kmk_keyboard_user.py + @rsync -ah ${USER_KEYMAP} build/circuitpython/ports/nrf/freeze/kmk_keyboard_user.py @$(MAKE) circuitpy-build-nrf -flash-feather-nrf52832: lint devdeps circuitpy-deps circuitpy-freeze-kmk-nrf - @echo "===> Preparing keyboard script for bundling into CircuitPython" - @cp -av ${USER_KEYMAP} build/circuitpython/ports/nrf/freeze/kmk_keyboard_user.py - @$(MAKE) circuitpy-flash-nrf circuitpy-flash-nrf-entrypoint +flash-feather-nrf52832: build-feather-nrf52832 circuitpy-flash-nrf circuitpy-flash-nrf-endpoint endif ifndef USER_KEYMAP @@ -240,30 +215,10 @@ endif ifndef SKIP_KEYMAP_VALIDATION @MICROPYPATH=./ ./bin/micropython.sh bin/keymap_sanity_check.py ${USER_KEYMAP} endif - @cp -av ${USER_KEYMAP} build/circuitpython/ports/atmel-samd/modules/kmk_keyboard_user.py + @rsync -ah ${USER_KEYMAP} build/circuitpython/ports/atmel-samd/modules/kmk_keyboard_user.py @$(MAKE) circuitpy-build-itsybitsy-m4-express -flash-itsybitsy-m4-express: lint devdeps circuitpy-deps circuitpy-freeze-kmk-atmel-samd - @echo "===> Preparing keyboard script for bundling into CircuitPython" - @$(MAKE) build-itsybitsy-m4-express circuitpy-flash-itsybitsy-m4-express -endif - -ifndef USER_KEYMAP -build-teensy-3.1: - @echo "===> Must provide a USER_KEYMAP (usually from user_keymaps/...) to build!" && exit 1 - -flash-teensy-3.1: - @echo "===> Must provide a USER_KEYMAP (usually from user_keymaps/...) to build!" && exit 1 -else -build-teensy-3.1: lint devdeps micropython-deps micropython-freeze-kmk-teensy3.1 - @echo "===> Preparing keyboard script for bundling into MicroPython" - @cp -av ${USER_KEYMAP} build/micropython/ports/teensy/freeze/kmk_keyboard_user.py - @$(MAKE) ARDUINO=${ARDUINO} micropython-build-teensy3.1 - -flash-teensy-3.1: lint devdeps micropython-deps micropython-freeze-kmk-teensy3.1 - @echo "===> Preparing keyboard script for bundling into MicroPython" - @cp -av ${USER_KEYMAP} build/micropython/ports/teensy/freeze/kmk_keyboard_user.py - @$(MAKE) ARDUINO=${ARDUINO} micropython-flash-teensy3.1 +flash-itsybitsy-m4-express: build-itsybitsy-m4-express circuitpy-flash-itsybitsy-m4-express endif ifndef USER_KEYMAP @@ -282,15 +237,12 @@ endif ifndef SKIP_KEYMAP_VALIDATION @MICROPYPATH=./ ./bin/micropython.sh bin/keymap_sanity_check.py ${USER_KEYMAP} endif - @cp -av ${USER_KEYMAP} build/micropython/ports/stm32/freeze/kmk_keyboard_user.py + @rsync -ah ${USER_KEYMAP} build/micropython/ports/stm32/freeze/kmk_keyboard_user.py + @rsync -ah kmk/entrypoints/global.py build/micropython/ports/stm32/freeze/_main.py + @rsync -ah kmk/entrypoints/handwire/pyboard_boot.py build/micropython/ports/stm32/freeze/_boot.py @$(MAKE) AMPY_PORT=/dev/ttyACM0 AMPY_BAUD=115200 micropython-build-pyboard -flash-pyboard: lint devdeps micropython-deps micropython-freeze-kmk-stm32 - @echo "===> Preparing keyboard script for bundling into MicroPython" - @cp -av ${USER_KEYMAP} build/micropython/ports/stm32/freeze/kmk_keyboard_user.py - @cp -av kmk/entrypoints/global.py build/micropython/ports/stm32/freeze/_main.py - @cp -av kmk/entrypoints/handwire/pyboard_boot.py build/micropython/ports/stm32/freeze/_boot.py - @$(MAKE) micropython-flash-pyboard +flash-pyboard: build-pyboard micropython-flash-pyboard endif reset-bootloader: @@ -298,23 +250,3 @@ reset-bootloader: reset-board: @-timeout -k 5s 10s $(PIPENV) run ampy -p /dev/ttyACM0 -d ${AMPY_DELAY} -b ${AMPY_BAUD} run util/reset.py - -# Fully wipe the board with only stock CircuitPython -burn-it-all-with-fire: lint devdeps - @echo "===> Flashing STOCK CircuitPython with no KMK or user keyboard scripts, and wiping entrypoint" - @echo "===> This is the nuclear option. Ctrl-C to cancel, or any key to continue" - @read - @echo "===> Pulling dependencies, this may take several minutes" - @git submodule update --init --recursive - @echo "===> Building circuitpython/mpy-cross" - @make -C build/circuitpython/mpy-cross - @echo "===> Pulling Nordic BLE stack" - @cd build/circuitpython/ports/nrf && ./drivers/bluetooth/download_ble_stack.sh 2>/dev/null >/dev/null - @echo "===> Preparing KMK source for bundling into CircuitPython" - @rm -rf build/circuitpython/ports/nrf/* - @echo "===> Building CircuitPython WITHOUT KMK or user keyboard script to induce ImportError" - @$(MAKE) circuitpy-flash-nrf - @echo "===> Wiping keyboard config" - @sleep 2 - @-timeout -k 5s 10s $(PIPENV) run ampy -p ${AMPY_PORT} -d ${AMPY_DELAY} -b ${AMPY_BAUD} rm main.py 2>/dev/null - @echo "===> Wiped! Probably safe to flash keyboard, try Python serial REPL to verify?" From f6bebdcc2429c298416f4904e0db8085dbf83b9f Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 03:23:36 -0700 Subject: [PATCH 14/21] Move off D13 to avoid clobbering the onboard LED just in case --- user_keymaps/klardotsh/itsybitsy_m4_express/threethree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_keymaps/klardotsh/itsybitsy_m4_express/threethree.py b/user_keymaps/klardotsh/itsybitsy_m4_express/threethree.py index 5c0267b..af8746f 100644 --- a/user_keymaps/klardotsh/itsybitsy_m4_express/threethree.py +++ b/user_keymaps/klardotsh/itsybitsy_m4_express/threethree.py @@ -6,7 +6,7 @@ from kmk.common.pins import Pin as P from kmk.entrypoints.handwire.itsybitsy_m4_express import main from kmk.firmware import Firmware -cols = (P.A4, P.A5, P.D13) +cols = (P.A4, P.A5, P.D7) rows = (P.D12, P.D11, P.D10) diode_orientation = DiodeOrientation.COLUMNS From ed593ec9d3e25300be7fb1e650ceb51bd039e35d Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 03:24:47 -0700 Subject: [PATCH 15/21] Bump to build of CircuitPython that trims MSC and adds frozen _main.py support --- submodules.toml | 2 +- vendor/circuitpython | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/submodules.toml b/submodules.toml index 62336f0..c778cde 100644 --- a/submodules.toml +++ b/submodules.toml @@ -1,4 +1,4 @@ [submodules] -"vendor/circuitpython" = "d751740" +"vendor/circuitpython" = "5c606de" "vendor/micropython" = "65a49fa" "vendor/upy-lib" = "451b1c0" diff --git a/vendor/circuitpython b/vendor/circuitpython index d751740..5c606de 160000 --- a/vendor/circuitpython +++ b/vendor/circuitpython @@ -1 +1 @@ -Subproject commit d7517409e93be2b098b463cf5430c8dbc76cef07 +Subproject commit 5c606deecc93c573516c46787486cee846a6d599 From 50a834a9f06c4dd2c3327dab1f727dca0070c11c Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 03:51:14 -0700 Subject: [PATCH 16/21] Use frozen _main.py on atmel --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 7a8206b..dfc3a65 100644 --- a/Makefile +++ b/Makefile @@ -179,6 +179,7 @@ ifndef SKIP_KEYMAP_VALIDATION @MICROPYPATH=./ ./bin/micropython.sh bin/keymap_sanity_check.py ${USER_KEYMAP} endif @rsync -ah ${USER_KEYMAP} build/circuitpython/ports/atmel-samd/modules/kmk_keyboard_user.py + @rsync -ah kmk/entrypoints/global.py build/circuitpython/ports/atmel-samd/modules/_main.py @$(MAKE) circuitpy-build-feather-m4-express flash-feather-m4-express: build-feather-m4-express circuitpy-flash-feather-m4-express @@ -216,6 +217,7 @@ ifndef SKIP_KEYMAP_VALIDATION @MICROPYPATH=./ ./bin/micropython.sh bin/keymap_sanity_check.py ${USER_KEYMAP} endif @rsync -ah ${USER_KEYMAP} build/circuitpython/ports/atmel-samd/modules/kmk_keyboard_user.py + @rsync -ah kmk/entrypoints/global.py build/circuitpython/ports/atmel-samd/modules/_main.py @$(MAKE) circuitpy-build-itsybitsy-m4-express flash-itsybitsy-m4-express: build-itsybitsy-m4-express circuitpy-flash-itsybitsy-m4-express From 7d0278695976c5631d27a4dc6658f52d49580a74 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 04:51:31 -0700 Subject: [PATCH 17/21] Remove a stupid sanity check that is not always correct anyway --- bin/keymap_sanity_check.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bin/keymap_sanity_check.py b/bin/keymap_sanity_check.py index 677ee03..121152d 100755 --- a/bin/keymap_sanity_check.py +++ b/bin/keymap_sanity_check.py @@ -59,8 +59,6 @@ assert len(user_keymap.keymap), 'Keymap must contain at least one layer' for lidx, layer in enumerate(user_keymap.keymap): assert len(layer), 'Layer {} must contain at least one row'.format(lidx) assert all(len(row) for row in layer), 'Layer {} must not contain empty rows'.format(lidx) - assert all(len(row) == len(layer[0]) for row in user_keymap.keymap), \ - 'All rows in layer {} must be of the same length'.format(lidx) for ridx, row in enumerate(layer): for cidx, key in enumerate(row): From 6d204176e019510f9f836b9f90ee7f7e838356bf Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 04:54:25 -0700 Subject: [PATCH 18/21] Unbreak pyboards --- kmk/entrypoints/handwire/pyboard.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kmk/entrypoints/handwire/pyboard.py b/kmk/entrypoints/handwire/pyboard.py index fb11c53..72b467f 100644 --- a/kmk/entrypoints/handwire/pyboard.py +++ b/kmk/entrypoints/handwire/pyboard.py @@ -3,6 +3,7 @@ from logging import DEBUG from kmk.common.consts import UnicodeModes from kmk.firmware import Firmware +from kmk.micropython.matrix import MatrixScanner from kmk.micropython.pyb_hid import HIDHelper @@ -23,6 +24,7 @@ def main(): unicode_mode=unicode_mode, hid=HIDHelper, log_level=DEBUG, + matrix_scanner=MatrixScanner, ) firmware.go() From 614f49759f61678d4b14b4e886a040f248693f43 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 18:08:32 -0700 Subject: [PATCH 19/21] Rewrite Circle config to hopefully support gcc-arm-embedded needed by atmel-samd --- .circleci/config.yml | 63 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 26f491e..f14f580 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,19 +2,26 @@ version: 2 jobs: lint: docker: - - image: 'python:3.7' + - image: 'ubuntu:bionic' environment: KMK_TEST: 1 PIPENV_VENV_IN_PROJECT: 1 steps: + - run: apt-get update && apt-get install -y software-properties-common git ssh wget + - run: add-apt-repository ppa:deadsnakes/ppa + - run: apt-get update && apt-get install -y python3.7 python3.7-dev build-essential + - run: wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py + - run: python3.7 /tmp/get-pip.py + - run: python3.7 -m pip install pip==18.0 # downgrade pip to work around https://github.com/pypa/pipenv/issues/2924 + - checkout - restore_cache: keys: - v1-kmk-venv-{{ checksum "Pipfile.lock" }} - - run: pip install pipenv==2018.7.1 + - run: python3.7 -m pip install pipenv==2018.7.1 - run: make lint - save_cache: @@ -24,76 +31,104 @@ jobs: test: docker: - - image: 'python:3.7' + - image: 'ubuntu:bionic' environment: KMK_TEST: 1 PIPENV_VENV_IN_PROJECT: 1 steps: + - run: apt-get update && apt-get install -y software-properties-common git ssh + - run: add-apt-repository ppa:team-gcc-arm-embedded/ppa + - run: add-apt-repository ppa:deadsnakes/ppa + - run: apt-get update && apt-get install -y gcc-arm-embedded gettext wget unzip rsync python3.7 python3.7-dev build-essential + - run: wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py + - run: python3.7 /tmp/get-pip.py + - run: python3.7 -m pip install pip==18.0 # downgrade pip to work around https://github.com/pypa/pipenv/issues/2924 + - checkout - restore_cache: keys: - v1-kmk-venv-{{ checksum "Pipfile.lock" }} - - run: pip install pipenv==2018.7.1 - - run: apt-get update && apt-get install -y gcc-arm-none-eabi gettext wget unzip rsync + - run: python3.7 -m pip install pipenv==2018.7.1 - run: make test build_feather_m4_express: docker: - - image: 'python:3.7' + - image: 'ubuntu:bionic' environment: KMK_TEST: 1 PIPENV_VENV_IN_PROJECT: 1 steps: + - run: apt-get update && apt-get install -y software-properties-common git ssh + - run: add-apt-repository ppa:team-gcc-arm-embedded/ppa + - run: add-apt-repository ppa:deadsnakes/ppa + - run: apt-get update && apt-get install -y gcc-arm-embedded gettext wget unzip rsync python3.7 python3.7-dev build-essential + - run: wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py + - run: python3.7 /tmp/get-pip.py + - run: python3.7 -m pip install pip==18.0 # downgrade pip to work around https://github.com/pypa/pipenv/issues/2924 + - checkout - restore_cache: keys: - v1-kmk-venv-{{ checksum "Pipfile.lock" }} - - run: pip install pipenv==2018.7.1 - - run: apt-get update && apt-get install -y gcc-arm-none-eabi gettext wget unzip rsync + - run: python3.7 -m pip install pipenv==2018.7.1 - run: make SKIP_KEYMAP_VALIDATION=1 USER_KEYMAP=user_keymaps/noop.py build-feather-m4-express build_itsybitsy_m4_express: docker: - - image: 'python:3.7' + - image: 'ubuntu:bionic' environment: KMK_TEST: 1 PIPENV_VENV_IN_PROJECT: 1 steps: + - run: apt-get update && apt-get install -y software-properties-common git ssh + - run: add-apt-repository ppa:team-gcc-arm-embedded/ppa + - run: add-apt-repository ppa:deadsnakes/ppa + - run: apt-get update && apt-get install -y gcc-arm-embedded gettext wget unzip rsync python3.7 python3.7-dev build-essential + - run: wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py + - run: python3.7 /tmp/get-pip.py + - run: python3.7 -m pip install pip==18.0 # downgrade pip to work around https://github.com/pypa/pipenv/issues/2924 + - checkout - restore_cache: keys: - v1-kmk-venv-{{ checksum "Pipfile.lock" }} - - run: pip install pipenv==2018.7.1 - - run: apt-get update && apt-get install -y gcc-arm-none-eabi gettext wget unzip rsync + - run: python3.7 -m pip install pipenv==2018.7.1 - run: make SKIP_KEYMAP_VALIDATION=1 USER_KEYMAP=user_keymaps/noop.py build-itsybitsy-m4-express build_pyboard: docker: - - image: 'python:3.7' + - image: 'ubuntu:bionic' environment: KMK_TEST: 1 PIPENV_VENV_IN_PROJECT: 1 steps: + - run: apt-get update && apt-get install -y software-properties-common git ssh + - run: add-apt-repository ppa:team-gcc-arm-embedded/ppa + - run: add-apt-repository ppa:deadsnakes/ppa + - run: apt-get update && apt-get install -y gcc-arm-embedded gettext wget unzip rsync python3.7 python3.7-dev build-essential + - run: wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py + - run: python3.7 /tmp/get-pip.py + - run: python3.7 -m pip install pip==18.0 # downgrade pip to work around https://github.com/pypa/pipenv/issues/2924 + - checkout - restore_cache: keys: - v1-kmk-venv-{{ checksum "Pipfile.lock" }} - - run: pip install pipenv==2018.7.1 - - run: apt-get update && apt-get install -y gcc-arm-none-eabi gettext wget unzip rsync + - run: python3.7 -m pip install pipenv==2018.7.1 - run: make SKIP_KEYMAP_VALIDATION=1 USER_KEYMAP=user_keymaps/noop.py build-pyboard From f9c19a19b401c7b3f3db42fb03f86f68d1a34d94 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 18:33:27 -0700 Subject: [PATCH 20/21] Make sure we use pipenv-provided Python within nested Make targets --- Makefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index dfc3a65..98d2c3c 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,7 @@ circuitpy-deps: .circuitpy-deps micropython-deps: .micropython-deps build/micropython/ports/unix/micropython: micropython-deps build/micropython/ports/unix/modules/.kmk_frozen - @make -j4 -C build/micropython/ports/unix + @pipenv run $(MAKE) -j4 -C build/micropython/ports/unix micropython-build-unix: build/micropython/ports/unix/micropython @@ -121,15 +121,15 @@ micropython-freeze-kmk-stm32: freeze-stm32-build-deps 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 + @pipenv run $(MAKE) -C build/circuitpython/ports/atmel-samd BOARD=feather_m4_express FROZEN_MPY_DIRS="modules" clean all circuitpy-build-itsybitsy-m4-express: @echo "===> Building CircuitPython" - @make -C build/circuitpython/ports/atmel-samd BOARD=itsybitsy_m4_express FROZEN_MPY_DIRS="modules" clean all + @pipenv run $(MAKE) -C build/circuitpython/ports/atmel-samd BOARD=itsybitsy_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 + @pipenv run $(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" @@ -150,10 +150,10 @@ circuitpy-flash-nrf: circuitpy-build-nrf @make -C build/circuitpython/ports/nrf BOARD=feather_nrf52832 SERIAL=${AMPY_PORT} SD=s132 FROZEN_MPY_DIR=freeze dfu-gen dfu-flash micropython-build-pyboard: - @make -j4 -C build/micropython/ports/stm32/ BOARD=PYBV11 FROZEN_MPY_DIR=freeze all + @pipenv run $(MAKE) -j4 -C build/micropython/ports/stm32/ BOARD=PYBV11 FROZEN_MPY_DIR=freeze all micropython-flash-pyboard: micropython-build-pyboard - @make -j4 -C build/micropython/ports/stm32/ BOARD=PYBV11 FROZEN_MPY_DIR=freeze deploy + @pipenv run $(MAKE) -j4 -C build/micropython/ports/stm32/ BOARD=PYBV11 FROZEN_MPY_DIR=freeze deploy circuitpy-flash-nrf-entrypoint: @echo "===> Flashing entrypoint if it doesn't already exist" From 247cb28d637fb4cc2436410c605f5cc9cd328d44 Mon Sep 17 00:00:00 2001 From: Josh Klar Date: Sun, 7 Oct 2018 18:47:26 -0700 Subject: [PATCH 21/21] More CI/Makefile fixes --- .circleci/config.yml | 10 +++++----- Makefile | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f14f580..7da06f6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -11,7 +11,7 @@ jobs: steps: - run: apt-get update && apt-get install -y software-properties-common git ssh wget - run: add-apt-repository ppa:deadsnakes/ppa - - run: apt-get update && apt-get install -y python3.7 python3.7-dev build-essential + - run: apt-get update && apt-get install -y python3.7 python3.7-dev build-essential pkg-config libffi-dev - run: wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py - run: python3.7 /tmp/get-pip.py - run: python3.7 -m pip install pip==18.0 # downgrade pip to work around https://github.com/pypa/pipenv/issues/2924 @@ -41,7 +41,7 @@ jobs: - run: apt-get update && apt-get install -y software-properties-common git ssh - run: add-apt-repository ppa:team-gcc-arm-embedded/ppa - run: add-apt-repository ppa:deadsnakes/ppa - - run: apt-get update && apt-get install -y gcc-arm-embedded gettext wget unzip rsync python3.7 python3.7-dev build-essential + - run: apt-get update && apt-get install -y gcc-arm-embedded gettext wget unzip rsync python3.7 python3.7-dev build-essential pkg-config libffi-dev - run: wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py - run: python3.7 /tmp/get-pip.py - run: python3.7 -m pip install pip==18.0 # downgrade pip to work around https://github.com/pypa/pipenv/issues/2924 @@ -66,7 +66,7 @@ jobs: - run: apt-get update && apt-get install -y software-properties-common git ssh - run: add-apt-repository ppa:team-gcc-arm-embedded/ppa - run: add-apt-repository ppa:deadsnakes/ppa - - run: apt-get update && apt-get install -y gcc-arm-embedded gettext wget unzip rsync python3.7 python3.7-dev build-essential + - run: apt-get update && apt-get install -y gcc-arm-embedded gettext wget unzip rsync python3.7 python3.7-dev build-essential pkg-config libffi-dev - run: wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py - run: python3.7 /tmp/get-pip.py - run: python3.7 -m pip install pip==18.0 # downgrade pip to work around https://github.com/pypa/pipenv/issues/2924 @@ -92,7 +92,7 @@ jobs: - run: apt-get update && apt-get install -y software-properties-common git ssh - run: add-apt-repository ppa:team-gcc-arm-embedded/ppa - run: add-apt-repository ppa:deadsnakes/ppa - - run: apt-get update && apt-get install -y gcc-arm-embedded gettext wget unzip rsync python3.7 python3.7-dev build-essential + - run: apt-get update && apt-get install -y gcc-arm-embedded gettext wget unzip rsync python3.7 python3.7-dev build-essential pkg-config libffi-dev - run: wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py - run: python3.7 /tmp/get-pip.py - run: python3.7 -m pip install pip==18.0 # downgrade pip to work around https://github.com/pypa/pipenv/issues/2924 @@ -118,7 +118,7 @@ jobs: - run: apt-get update && apt-get install -y software-properties-common git ssh - run: add-apt-repository ppa:team-gcc-arm-embedded/ppa - run: add-apt-repository ppa:deadsnakes/ppa - - run: apt-get update && apt-get install -y gcc-arm-embedded gettext wget unzip rsync python3.7 python3.7-dev build-essential + - run: apt-get update && apt-get install -y gcc-arm-embedded gettext wget unzip rsync python3.7 python3.7-dev build-essential pkg-config libffi-dev - run: wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py - run: python3.7 /tmp/get-pip.py - run: python3.7 -m pip install pip==18.0 # downgrade pip to work around https://github.com/pypa/pipenv/issues/2924 diff --git a/Makefile b/Makefile index 98d2c3c..f47f8e8 100644 --- a/Makefile +++ b/Makefile @@ -52,14 +52,14 @@ test: micropython-build-unix .circuitpy-deps: .submodules @echo "===> Building circuitpython/mpy-cross" - @make -C build/circuitpython/mpy-cross + @pipenv run $(MAKE) -C build/circuitpython/mpy-cross @echo "===> Pulling Nordic BLE stack" @cd build/circuitpython/ports/nrf && ./drivers/bluetooth/download_ble_stack.sh 2>/dev/null >/dev/null @touch .circuitpy-deps .micropython-deps: .submodules @echo "===> Building micropython/mpy-cross" - @make -C build/micropython/mpy-cross + @pipenv run $(MAKE) -C build/micropython/mpy-cross @touch .micropython-deps submodules: .submodules @@ -147,7 +147,7 @@ circuitpy-flash-itsybitsy-m4-express: 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 + @pipenv run $(MAKE) -C build/circuitpython/ports/nrf BOARD=feather_nrf52832 SERIAL=${AMPY_PORT} SD=s132 FROZEN_MPY_DIR=freeze dfu-gen dfu-flash micropython-build-pyboard: @pipenv run $(MAKE) -j4 -C build/micropython/ports/stm32/ BOARD=PYBV11 FROZEN_MPY_DIR=freeze all