Merge pull request #6 from klardotsh/topic-event-loop
The basic "Redux" style event loop to unify all state
This commit is contained in:
commit
a2742984ee
5
.gitmodules
vendored
5
.gitmodules
vendored
@ -2,10 +2,7 @@
|
||||
path = vendor/circuitpython
|
||||
url = https://github.com/adafruit/circuitpython.git
|
||||
branch = "9b98ad779468676c3d5f1efdc06b454aaed7c407"
|
||||
[submodule "pydux"]
|
||||
path = vendor/pydux
|
||||
url = https://github.com/usrlocalben/pydux.git
|
||||
branch = "943ca1c75357b9289f55f17ff2d997a66a3313a4"
|
||||
ignore = dirty
|
||||
[submodule "upy-lib"]
|
||||
path = vendor/upy-lib
|
||||
url = https://github.com/micropython/micropython-lib.git
|
||||
|
28
Makefile
28
Makefile
@ -6,6 +6,10 @@
|
||||
freeze-nrf-vendor-deps \
|
||||
lint
|
||||
|
||||
NRF_DFU_PORT ?= /dev/ttyUSB0
|
||||
NRF_DFU_BAUD ?= 115200
|
||||
NRF_DFU_DELAY ?= 1.5
|
||||
|
||||
devdeps: Pipfile.lock
|
||||
@pipenv install --dev --ignore-pipfile
|
||||
|
||||
@ -39,26 +43,26 @@ circuitpy-freeze-kmk-nrf: freeze-nrf-vendor-deps
|
||||
@rm -rf vendor/circuitpython/ports/nrf/kmk*
|
||||
@cp -av kmk vendor/circuitpython/ports/nrf/freeze/
|
||||
|
||||
circuitpy-flash-nrf: circuitpy-freeze-kmk-nrf
|
||||
circuitpy-flash-nrf:
|
||||
@echo "===> Building and flashing CircuitPython with KMK and your keymap"
|
||||
@make -C vendor/circuitpython/ports/nrf BOARD=feather_nrf52832 SERIAL=/dev/ttyUSB0 SD=s132 FROZEN_MPY_DIR=freeze clean dfu-gen dfu-flash
|
||||
@make -C vendor/circuitpython/ports/nrf BOARD=feather_nrf52832 SERIAL=${NRF_DFU_PORT} SD=s132 FROZEN_MPY_DIR=freeze clean dfu-gen dfu-flash
|
||||
|
||||
circuitpy-flash-nrf-entrypoint:
|
||||
@echo "===> Flashing entrypoint if it doesn't already exist"
|
||||
@sleep 2
|
||||
@-timeout -k 5s 10s pipenv run ampy rm main.py 2>/dev/null
|
||||
@-timeout -k 5s 10s pipenv run ampy put entrypoints/feather_nrf52832.py main.py
|
||||
@-timeout -k 5s 10s pipenv run ampy -p ${NRF_DFU_PORT} -d ${NRF_DFU_DELAY} -b ${NRF_DFU_BAUD} rm main.py 2>/dev/null
|
||||
@-timeout -k 5s 10s pipenv run ampy -p ${NRF_DFU_PORT} -d ${NRF_DFU_DELAY} -b ${NRF_DFU_BAUD} put entrypoints/feather_nrf52832.py main.py
|
||||
@echo "===> Flashed keyboard successfully!"
|
||||
|
||||
build-feather-test: lint devdeps circuitpy-deps circuitpy-freeze-kmk-nrf
|
||||
ifndef BOARD
|
||||
build-feather-nrf52832:
|
||||
@echo "===> Must provide a board (usually from boards/...) to build!"
|
||||
else
|
||||
build-feather-nrf52832: lint devdeps circuitpy-deps circuitpy-freeze-kmk-nrf
|
||||
@echo "===> Preparing keyboard script for bundling into CircuitPython"
|
||||
@cp -av boards/klardotsh/twotwo_matrix_feather.py vendor/circuitpython/ports/nrf/freeze/kmk_keyboard_user.py
|
||||
@$(MAKE) circuitpy-flash-nrf circuitpy-flash-nrf-entrypoint
|
||||
|
||||
build-feather-noop: lint devdeps circuitpy-deps circuitpy-freeze-kmk-nrf
|
||||
@echo "===> Preparing keyboard script for bundling into CircuitPython"
|
||||
@cp -av boards/noop.py vendor/circuitpython/ports/nrf/freeze/kmk_keyboard_user.py
|
||||
@cp -av ${BOARD} vendor/circuitpython/ports/nrf/freeze/kmk_keyboard_user.py
|
||||
@$(MAKE) circuitpy-flash-nrf circuitpy-flash-nrf-entrypoint
|
||||
endif
|
||||
|
||||
# Fully wipe the board with only stock CircuitPython
|
||||
burn-it-all-with-fire: lint devdeps
|
||||
@ -77,5 +81,5 @@ burn-it-all-with-fire: lint devdeps
|
||||
@$(MAKE) circuitpy-flash-nrf
|
||||
@echo "===> Wiping keyboard config"
|
||||
@sleep 2
|
||||
@-pipenv run ampy rm main.py 2>/dev/null
|
||||
@-timeout -k 5s 10s pipenv run ampy -p ${NRF_DFU_PORT} -d ${NRF_DFU_DELAY} -b ${NRF_DFU_BAUD} rm main.py 2>/dev/null
|
||||
@echo "===> Wiped! Probably safe to flash keyboard, try Python serial REPL to verify?"
|
||||
|
7
Pipfile
7
Pipfile
@ -4,11 +4,18 @@ verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
pydux = "*"
|
||||
|
||||
[dev-packages]
|
||||
adafruit-ampy = "*"
|
||||
"flake8" = "*"
|
||||
"flake8-comprehensions" = "*"
|
||||
ipython = "*"
|
||||
ipdb = "*"
|
||||
"flake8-commas" = "*"
|
||||
isort = "*"
|
||||
"flake8-isort" = "*"
|
||||
neovim = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.7"
|
||||
|
210
Pipfile.lock
generated
210
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "e246783475c5feb51445494a3f30c397ad6ddaed35c45decc9846970d001ad50"
|
||||
"sha256": "44e8c37b94a71b7f47fc43f2c98bf17d546f4a5ef7ad1cad5076d4a47fc4515a"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@ -15,7 +15,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {},
|
||||
"default": {
|
||||
"pydux": {
|
||||
"hashes": [
|
||||
"sha256:5cb9217be9d8c7ff79b028f6f02597bbb055b107ce8eecbe5f631f3fc76d793f",
|
||||
"sha256:bed123b5255d566f792b9ceebad87e3f9c1d2d85abed4b9a9475ffc831035879"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.2.2"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"adafruit-ampy": {
|
||||
"hashes": [
|
||||
@ -24,6 +33,13 @@
|
||||
"index": "pypi",
|
||||
"version": "==1.0.5"
|
||||
},
|
||||
"backcall": {
|
||||
"hashes": [
|
||||
"sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4",
|
||||
"sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"
|
||||
],
|
||||
"version": "==0.1.0"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d",
|
||||
@ -31,6 +47,13 @@
|
||||
],
|
||||
"version": "==6.7"
|
||||
},
|
||||
"decorator": {
|
||||
"hashes": [
|
||||
"sha256:2c51dff8ef3c447388fe5e4453d24a2bf128d3a4c32af3fabef1f01c6851ab82",
|
||||
"sha256:c39efa13fbdeb4506c476c9b3babf6a718da943dab7811c206005a4a956c080c"
|
||||
],
|
||||
"version": "==4.3.0"
|
||||
},
|
||||
"flake8": {
|
||||
"hashes": [
|
||||
"sha256:7253265f7abd8b313e3892944044a365e3f4ac3fcdcfb4298f55ee9ddf188ba0",
|
||||
@ -39,6 +62,14 @@
|
||||
"index": "pypi",
|
||||
"version": "==3.5.0"
|
||||
},
|
||||
"flake8-commas": {
|
||||
"hashes": [
|
||||
"sha256:d3005899466f51380387df7151fb59afec666a0f4f4a2c6a8995b975de0f44b7",
|
||||
"sha256:ee2141a3495ef9789a3894ed8802d03eff1eaaf98ce6d8653a7c573ef101935e"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.0.0"
|
||||
},
|
||||
"flake8-comprehensions": {
|
||||
"hashes": [
|
||||
"sha256:b83891fec0e680b07aa1fd92e53eb6993be29a0f3673a09badbe8da307c445e0",
|
||||
@ -47,6 +78,76 @@
|
||||
"index": "pypi",
|
||||
"version": "==1.4.1"
|
||||
},
|
||||
"flake8-isort": {
|
||||
"hashes": [
|
||||
"sha256:298d7904ac3a46274edf4ce66fd7e272c2a60c34c3cc999dea000608d64e5e6e",
|
||||
"sha256:5992850626ce96547b1f1c7e8a7f0ef49ab2be44eca2177934566437b636fa3c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.5"
|
||||
},
|
||||
"greenlet": {
|
||||
"hashes": [
|
||||
"sha256:0411b5bf0de5ec11060925fd811ad49073fa19f995bcf408839eb619b59bb9f7",
|
||||
"sha256:131f4ed14f0fd28d2a9fa50f79a57d5ed1c8f742d3ccac3d773fee09ef6fe217",
|
||||
"sha256:13510d32f8db72a0b3e1720dbf8cba5c4eecdf07abc4cb631982f51256c453d1",
|
||||
"sha256:31dc4d77ef04ab0460d024786f51466dbbc274fda7c8aad0885a6df5ff8d642e",
|
||||
"sha256:35021d9fecea53b21e4defec0ff3ad69a8e2b75aca1ceddd444a5ba71216547e",
|
||||
"sha256:426a8ef9e3b97c27e841648241c2862442c13c91ec4a48c4a72b262ccf30add9",
|
||||
"sha256:58217698193fb94f3e6ff57eed0ae20381a8d06c2bc10151f76c06bb449a3a19",
|
||||
"sha256:5f45adbbb69281845981bb4e0a4efb8a405f10f3cd6c349cb4a5db3357c6bf93",
|
||||
"sha256:5fdb524767288f7ad161d2182f7ed6cafc0a283363728dcd04b9485f6411547c",
|
||||
"sha256:71fbee1f7ef3fb42efa3761a8faefc796e7e425f528de536cfb4c9de03bde885",
|
||||
"sha256:80bd314157851d06f7db7ca527082dbb0ee97afefb529cdcd59f7a5950927ba0",
|
||||
"sha256:b843c9ef6aed54a2649887f55959da0031595ccfaf7e7a0ba7aa681ffeaa0aa1",
|
||||
"sha256:c6a05ef8125503d2d282ccf1448e3599b8a6bd805c3cdee79760fa3da0ea090e",
|
||||
"sha256:deeda2769a52db840efe5bf7bdf7cefa0ae17b43a844a3259d39fb9465c8b008",
|
||||
"sha256:e66f8b09eec1afdcab947d3a1d65b87b25fde39e9172ae1bec562488335633b4",
|
||||
"sha256:e8db93045414980dbada8908d49dbbc0aa134277da3ff613b3e548cb275bdd37",
|
||||
"sha256:f1cc268a15ade58d9a0c04569fe6613e19b8b0345b64453064e2c3c6d79051af",
|
||||
"sha256:fe3001b6a4f3f3582a865b9e5081cc548b973ec20320f297f5e2d46860e9c703",
|
||||
"sha256:fe85bf7adb26eb47ad53a1bae5d35a28df16b2b93b89042a3a28746617a4738d"
|
||||
],
|
||||
"version": "==0.4.14"
|
||||
},
|
||||
"ipdb": {
|
||||
"hashes": [
|
||||
"sha256:7081c65ed7bfe7737f83fa4213ca8afd9617b42ff6b3f1daf9a3419839a2a00a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.11"
|
||||
},
|
||||
"ipython": {
|
||||
"hashes": [
|
||||
"sha256:007dcd929c14631f83daff35df0147ea51d1af420da303fd078343878bd5fb62",
|
||||
"sha256:b0f2ef9eada4a68ef63ee10b6dde4f35c840035c50fd24265f8052c98947d5a4"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==6.5.0"
|
||||
},
|
||||
"ipython-genutils": {
|
||||
"hashes": [
|
||||
"sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8",
|
||||
"sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"
|
||||
],
|
||||
"version": "==0.2.0"
|
||||
},
|
||||
"isort": {
|
||||
"hashes": [
|
||||
"sha256:1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af",
|
||||
"sha256:b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8",
|
||||
"sha256:ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.3.4"
|
||||
},
|
||||
"jedi": {
|
||||
"hashes": [
|
||||
"sha256:b409ed0f6913a701ed474a614a3bb46e6953639033e31f769ca7581da5bd1ec1",
|
||||
"sha256:c254b135fb39ad76e78d4d8f92765ebc9bf92cbc76f49e97ade1d5f5121e1f6f"
|
||||
],
|
||||
"version": "==0.12.1"
|
||||
},
|
||||
"mccabe": {
|
||||
"hashes": [
|
||||
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
||||
@ -54,6 +155,70 @@
|
||||
],
|
||||
"version": "==0.6.1"
|
||||
},
|
||||
"msgpack": {
|
||||
"hashes": [
|
||||
"sha256:0b3b1773d2693c70598585a34ca2715873ba899565f0a7c9a1545baef7e7fbdc",
|
||||
"sha256:0bae5d1538c5c6a75642f75a1781f3ac2275d744a92af1a453c150da3446138b",
|
||||
"sha256:0ee8c8c85aa651be3aa0cd005b5931769eaa658c948ce79428766f1bd46ae2c3",
|
||||
"sha256:1369f9edba9500c7a6489b70fdfac773e925342f4531f1e3d4c20ac3173b1ae0",
|
||||
"sha256:22d9c929d1d539f37da3d1b0e16270fa9d46107beab8c0d4d2bddffffe895cee",
|
||||
"sha256:2ff43e3247a1e11d544017bb26f580a68306cec7a6257d8818893c1fda665f42",
|
||||
"sha256:31a98047355d34d047fcdb55b09cb19f633cf214c705a765bd745456c142130c",
|
||||
"sha256:8767eb0032732c3a0da92cbec5ac186ef89a3258c6edca09161472ca0206c45f",
|
||||
"sha256:8acc8910218555044e23826980b950e96685dc48124a290c86f6f41a296ea172",
|
||||
"sha256:ab189a6365be1860a5ecf8159c248f12d33f79ea799ae9695fa6a29896dcf1d4",
|
||||
"sha256:cfd6535feb0f1cf1c7cdb25773e965cc9f92928244a8c3ef6f8f8a8e1f7ae5c4",
|
||||
"sha256:e274cd4480d8c76ec467a85a9c6635bbf2258f0649040560382ab58cabb44bcf",
|
||||
"sha256:f86642d60dca13e93260187d56c2bef2487aa4d574a669e8ceefcf9f4c26fd00",
|
||||
"sha256:f8a57cbda46a94ed0db55b73e6ab0c15e78b4ede8690fa491a0e55128d552bb0",
|
||||
"sha256:fcea97a352416afcbccd7af9625159d80704a25c519c251c734527329bb20d0e"
|
||||
],
|
||||
"version": "==0.5.6"
|
||||
},
|
||||
"neovim": {
|
||||
"hashes": [
|
||||
"sha256:6ce58a742e0427491c0e1c8108556ee72ba33844209bd9e226b8da9538299276"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.2.6"
|
||||
},
|
||||
"parso": {
|
||||
"hashes": [
|
||||
"sha256:35704a43a3c113cce4de228ddb39aab374b8004f4f2407d070b6a2ca784ce8a2",
|
||||
"sha256:895c63e93b94ac1e1690f5fdd40b65f07c8171e3e53cbd7793b5b96c0e0a7f24"
|
||||
],
|
||||
"version": "==0.3.1"
|
||||
},
|
||||
"pexpect": {
|
||||
"hashes": [
|
||||
"sha256:2a8e88259839571d1251d278476f3eec5db26deb73a70be5ed5dc5435e418aba",
|
||||
"sha256:3fbd41d4caf27fa4a377bfd16fef87271099463e6fa73e92a52f92dfee5d425b"
|
||||
],
|
||||
"markers": "sys_platform != 'win32'",
|
||||
"version": "==4.6.0"
|
||||
},
|
||||
"pickleshare": {
|
||||
"hashes": [
|
||||
"sha256:84a9257227dfdd6fe1b4be1319096c20eb85ff1e82c7932f36efccfe1b09737b",
|
||||
"sha256:c9a2541f25aeabc070f12f452e1f2a8eae2abd51e1cd19e8430402bdf4c1d8b5"
|
||||
],
|
||||
"version": "==0.7.4"
|
||||
},
|
||||
"prompt-toolkit": {
|
||||
"hashes": [
|
||||
"sha256:1df952620eccb399c53ebb359cc7d9a8d3a9538cb34c5a1344bdbeb29fbcc381",
|
||||
"sha256:3f473ae040ddaa52b52f97f6b4a493cfa9f5920c255a12dc56a7d34397a398a4",
|
||||
"sha256:858588f1983ca497f1cf4ffde01d978a3ea02b01c8a26a8bbc5cd2e66d816917"
|
||||
],
|
||||
"version": "==1.0.15"
|
||||
},
|
||||
"ptyprocess": {
|
||||
"hashes": [
|
||||
"sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0",
|
||||
"sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"
|
||||
],
|
||||
"version": "==0.6.0"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
"sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766",
|
||||
@ -68,6 +233,13 @@
|
||||
],
|
||||
"version": "==1.6.0"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d",
|
||||
"sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc"
|
||||
],
|
||||
"version": "==2.2.0"
|
||||
},
|
||||
"pyserial": {
|
||||
"hashes": [
|
||||
"sha256:6e2d401fdee0eab996cf734e67773a0143b932772ca8b42451440cfed942c627",
|
||||
@ -81,6 +253,40 @@
|
||||
"sha256:4a205787bc829233de2a823aa328e44fd9996fedb954989a21f1fc67c13d7a77"
|
||||
],
|
||||
"version": "==0.9.1"
|
||||
},
|
||||
"simplegeneric": {
|
||||
"hashes": [
|
||||
"sha256:dc972e06094b9af5b855b3df4a646395e43d1c9d0d39ed345b7393560d0b9173"
|
||||
],
|
||||
"version": "==0.8.1"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
|
||||
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
|
||||
],
|
||||
"version": "==1.11.0"
|
||||
},
|
||||
"testfixtures": {
|
||||
"hashes": [
|
||||
"sha256:7e4df89a8bf8b8905464160f08aff131a36f0b33654fe4f9e4387afe546eae25",
|
||||
"sha256:bcadbad77526cc5fc38bfb2ab80da810d7bde56ffe4c7fdb8e2bba122ded9620"
|
||||
],
|
||||
"version": "==6.2.0"
|
||||
},
|
||||
"traitlets": {
|
||||
"hashes": [
|
||||
"sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835",
|
||||
"sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9"
|
||||
],
|
||||
"version": "==4.3.2"
|
||||
},
|
||||
"wcwidth": {
|
||||
"hashes": [
|
||||
"sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
|
||||
"sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
|
||||
],
|
||||
"version": "==0.1.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,28 @@
|
||||
from logging import DEBUG
|
||||
|
||||
import board
|
||||
|
||||
from kmk.circuitpython.matrix import MatrixScanner
|
||||
from kmk.common.consts import DiodeOrientation
|
||||
from kmk.common.keymap import Keymap
|
||||
from kmk.firmware import Firmware
|
||||
|
||||
|
||||
def main():
|
||||
cols = (board.A4, board.A5)
|
||||
rows = (board.D27, board.A6)
|
||||
|
||||
matrix = MatrixScanner(
|
||||
cols=cols, rows=rows,
|
||||
diode_orientation=DiodeOrientation.COLUMNS,
|
||||
)
|
||||
diode_orientation = DiodeOrientation.COLUMNS
|
||||
|
||||
keymap = Keymap([
|
||||
keymap = [
|
||||
['A', 'B'],
|
||||
['C', 'D'],
|
||||
])
|
||||
]
|
||||
|
||||
while True:
|
||||
keymap.parse(matrix.raw_scan())
|
||||
firmware = Firmware(
|
||||
keymap=keymap,
|
||||
row_pins=rows,
|
||||
col_pins=cols,
|
||||
diode_orientation=diode_orientation,
|
||||
log_level=DEBUG,
|
||||
)
|
||||
|
||||
firmware.go()
|
||||
|
@ -1,5 +1,12 @@
|
||||
import sys
|
||||
|
||||
from kmk.circuitpython.util import feather_red_led_flash
|
||||
from kmk_keyboard_user import main
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
try:
|
||||
main()
|
||||
except Exception as e:
|
||||
sys.print_exception(e)
|
||||
feather_red_led_flash(duration=10, rate=0.5)
|
||||
sys.exit(1)
|
||||
|
@ -1,9 +1,10 @@
|
||||
import digitalio
|
||||
|
||||
from kmk.common.abstract.matrix_scanner import AbstractMatrixScanner
|
||||
from kmk.common.consts import DiodeOrientation
|
||||
|
||||
|
||||
class MatrixScanner:
|
||||
class MatrixScanner(AbstractMatrixScanner):
|
||||
def __init__(self, cols, rows, diode_orientation=DiodeOrientation.COLUMNS):
|
||||
# A pin cannot be both a row and column, detect this by combining the
|
||||
# two tuples into a set and validating that the length did not drop
|
||||
@ -35,20 +36,7 @@ class MatrixScanner:
|
||||
pin.switch_to_input(pull=digitalio.Pull.DOWN)
|
||||
|
||||
def _normalize_matrix(self, matrix):
|
||||
'''
|
||||
We always want to internally look at a keyboard as a list of rows,
|
||||
where a "row" is a list of keycodes (columns).
|
||||
|
||||
This will convert DiodeOrientation.COLUMNS matrix scans into a
|
||||
ROWS scan, so we never have to think about these things again.
|
||||
'''
|
||||
if self.diode_orientation == DiodeOrientation.ROWS:
|
||||
return matrix
|
||||
|
||||
return [
|
||||
[col[col_entry] for col in matrix]
|
||||
for col_entry in range(max(len(col) for col in matrix))
|
||||
]
|
||||
return super()._normalize_matrix(matrix)
|
||||
|
||||
def raw_scan(self):
|
||||
matrix = []
|
||||
|
@ -1,22 +1,17 @@
|
||||
import time
|
||||
|
||||
import board
|
||||
import digitalio
|
||||
import time
|
||||
import sys
|
||||
|
||||
|
||||
def feather_signal_error_with_led_flash(rate=0.5):
|
||||
def feather_red_led_flash(duration=10, rate=0.5):
|
||||
'''
|
||||
Flash the red LED for 10 seconds, alternating every $rate
|
||||
Could be useful as an uncaught exception handler later on,
|
||||
but is for now unused
|
||||
Flash the red LED for $duration seconds, alternating every $rate
|
||||
'''
|
||||
|
||||
rled = digitalio.DigitalInOut(board.LED1)
|
||||
rled.direction = digitalio.Direction.OUTPUT
|
||||
|
||||
# blink for 5 seconds and exit
|
||||
for cycle in range(10):
|
||||
for cycle in range(duration / rate):
|
||||
rled.value = cycle % 2
|
||||
time.sleep(rate)
|
||||
|
||||
sys.exit(1)
|
||||
|
0
kmk/common/abstract/__init__.py
Normal file
0
kmk/common/abstract/__init__.py
Normal file
25
kmk/common/abstract/matrix_scanner.py
Normal file
25
kmk/common/abstract/matrix_scanner.py
Normal file
@ -0,0 +1,25 @@
|
||||
from kmk.common.consts import DiodeOrientation
|
||||
|
||||
|
||||
class AbstractMatrixScanner():
|
||||
def __init__(self, cols, rows, diode_orientation=DiodeOrientation.COLUMNS):
|
||||
raise NotImplementedError('Abstract implementation')
|
||||
|
||||
def _normalize_matrix(self, matrix):
|
||||
'''
|
||||
We always want to internally look at a keyboard as a list of rows,
|
||||
where a "row" is a list of keycodes (columns).
|
||||
|
||||
This will convert DiodeOrientation.COLUMNS matrix scans into a
|
||||
ROWS scan, so we never have to think about these things again.
|
||||
'''
|
||||
if self.diode_orientation == DiodeOrientation.ROWS:
|
||||
return matrix
|
||||
|
||||
return [
|
||||
[col[col_entry] for col in matrix]
|
||||
for col_entry in range(max(len(col) for col in matrix))
|
||||
]
|
||||
|
||||
def raw_scan(self):
|
||||
raise NotImplementedError('Abstract implementation')
|
33
kmk/common/event_defs.py
Normal file
33
kmk/common/event_defs.py
Normal file
@ -0,0 +1,33 @@
|
||||
from micropython import const
|
||||
|
||||
KEY_UP_EVENT = const(1)
|
||||
KEY_DOWN_EVENT = const(2)
|
||||
INIT_FIRMWARE_EVENT = const(3)
|
||||
|
||||
|
||||
def init_firmware(keymap, row_pins, col_pins, diode_orientation):
|
||||
return {
|
||||
'type': INIT_FIRMWARE_EVENT,
|
||||
'keymap': keymap,
|
||||
'row_pins': row_pins,
|
||||
'col_pins': col_pins,
|
||||
'diode_orientation': diode_orientation,
|
||||
}
|
||||
|
||||
|
||||
def key_up_event(keycode, row, col):
|
||||
return {
|
||||
'type': KEY_UP_EVENT,
|
||||
'keycode': keycode,
|
||||
'row': row,
|
||||
'col': col,
|
||||
}
|
||||
|
||||
|
||||
def key_down_event(keycode, row, col):
|
||||
return {
|
||||
'type': KEY_DOWN_EVENT,
|
||||
'keycode': keycode,
|
||||
'row': row,
|
||||
'col': col,
|
||||
}
|
133
kmk/common/internal_state.py
Normal file
133
kmk/common/internal_state.py
Normal file
@ -0,0 +1,133 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from kmk.common.consts import DiodeOrientation
|
||||
from kmk.common.event_defs import (INIT_FIRMWARE_EVENT, KEY_DOWN_EVENT,
|
||||
KEY_UP_EVENT)
|
||||
|
||||
|
||||
class ReduxStore:
|
||||
def __init__(self, reducer, log_level=logging.NOTSET):
|
||||
self.reducer = reducer
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.logger.setLevel(log_level)
|
||||
self.state = self.reducer(logger=self.logger)
|
||||
self.callbacks = []
|
||||
|
||||
def dispatch(self, action):
|
||||
self.logger.debug('Dispatching action: {}'.format(action))
|
||||
self.state = self.reducer(self.state, action)
|
||||
self.logger.debug('Dispatching complete: {}'.format(action))
|
||||
|
||||
self.logger.debug('Calling subscriptions')
|
||||
|
||||
for cb in self.callbacks:
|
||||
if cb is not None:
|
||||
try:
|
||||
cb(self.state, action)
|
||||
except Exception as e:
|
||||
self.logger.error('Callback failed, moving on')
|
||||
print(sys.print_exception(e), file=sys.stderr)
|
||||
|
||||
self.logger.debug('Callbacks complete')
|
||||
|
||||
def get_state(self):
|
||||
return self.state
|
||||
|
||||
def subscribe(self, callback):
|
||||
self.callbacks.append(callback)
|
||||
return len(self.callbacks) - 1
|
||||
|
||||
def unsubscribe(self, idx):
|
||||
self.callbacks[idx] = None
|
||||
|
||||
|
||||
class InternalState:
|
||||
modifiers_pressed = frozenset()
|
||||
keys_pressed = frozenset()
|
||||
keymap = []
|
||||
row_pins = []
|
||||
col_pins = []
|
||||
matrix = []
|
||||
diode_orientation = DiodeOrientation.COLUMNS
|
||||
|
||||
@property
|
||||
def __dict__(self):
|
||||
return {
|
||||
'keys_pressed': self.keys_pressed,
|
||||
'modifiers_pressed': self.modifiers_pressed,
|
||||
'keymap': self.keymap,
|
||||
'col_pins': self.col_pins,
|
||||
'row_pins': self.row_pins,
|
||||
'diode_orientation': self.diode_orientation,
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return 'InternalState({})'.format(self.__dict__)
|
||||
|
||||
def copy(self, **kwargs):
|
||||
new_state = InternalState()
|
||||
|
||||
for k, v in self.__dict__.items():
|
||||
if hasattr(new_state, k):
|
||||
setattr(new_state, k, v)
|
||||
|
||||
for k, v in kwargs.items():
|
||||
if hasattr(new_state, k):
|
||||
setattr(new_state, k, v)
|
||||
|
||||
return new_state
|
||||
|
||||
|
||||
def kmk_reducer(state=None, action=None, logger=None):
|
||||
if state is None:
|
||||
state = InternalState()
|
||||
|
||||
if logger is not None:
|
||||
logger.debug('Reducer received state of None, creating new')
|
||||
|
||||
if action is None:
|
||||
if logger is not None:
|
||||
logger.debug('No action received, returning state unmodified')
|
||||
|
||||
return state
|
||||
|
||||
if action['type'] == KEY_UP_EVENT:
|
||||
return state.copy(
|
||||
keys_pressed=frozenset(
|
||||
key for key in state.keys_pressed if key != action['keycode']
|
||||
),
|
||||
matrix=[
|
||||
r if ridx != action['row'] else [
|
||||
c if cidx != action['col'] else False
|
||||
for cidx, c in enumerate(r)
|
||||
]
|
||||
for ridx, r in enumerate(state.matrix)
|
||||
],
|
||||
)
|
||||
|
||||
if action['type'] == KEY_DOWN_EVENT:
|
||||
return state.copy(
|
||||
keys_pressed=(
|
||||
state.keys_pressed | {action['keycode']}
|
||||
),
|
||||
matrix=[
|
||||
r if ridx != action['row'] else [
|
||||
c if cidx != action['col'] else True
|
||||
for cidx, c in enumerate(r)
|
||||
]
|
||||
for ridx, r in enumerate(state.matrix)
|
||||
],
|
||||
)
|
||||
|
||||
if action['type'] == INIT_FIRMWARE_EVENT:
|
||||
return state.copy(
|
||||
keymap=action['keymap'],
|
||||
row_pins=action['row_pins'],
|
||||
col_pins=action['col_pins'],
|
||||
diode_orientation=action['diode_orientation'],
|
||||
matrix=[
|
||||
[False for c in action['col_pins']]
|
||||
for r in action['row_pins']
|
||||
],
|
||||
)
|
@ -1,18 +1,25 @@
|
||||
from kmk.common.event_defs import key_down_event, key_up_event
|
||||
|
||||
|
||||
class Keymap:
|
||||
def __init__(self, map):
|
||||
self.map = map
|
||||
self.state = [
|
||||
[False for _ in row]
|
||||
for row in self.map
|
||||
]
|
||||
|
||||
def parse(self, matrix):
|
||||
def parse(self, matrix, store):
|
||||
state = store.get_state()
|
||||
|
||||
for ridx, row in enumerate(matrix):
|
||||
for cidx, col in enumerate(row):
|
||||
if col != self.state[ridx][cidx]:
|
||||
print('{}: {}'.format(
|
||||
'KEYDOWN' if col else 'KEYUP',
|
||||
self.map[ridx][cidx],
|
||||
))
|
||||
|
||||
self.state = matrix
|
||||
if col != state.matrix[ridx][cidx]:
|
||||
if col:
|
||||
store.dispatch(key_down_event(
|
||||
row=ridx,
|
||||
col=cidx,
|
||||
keycode=self.map[ridx][cidx],
|
||||
))
|
||||
else:
|
||||
store.dispatch(key_up_event(
|
||||
row=ridx,
|
||||
col=cidx,
|
||||
keycode=self.map[ridx][cidx],
|
||||
))
|
||||
|
46
kmk/firmware.py
Normal file
46
kmk/firmware.py
Normal file
@ -0,0 +1,46 @@
|
||||
import logging
|
||||
|
||||
from kmk.common.event_defs import init_firmware
|
||||
from kmk.common.internal_state import ReduxStore, kmk_reducer
|
||||
from kmk.common.keymap import Keymap
|
||||
|
||||
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,
|
||||
log_level=logging.NOTSET,
|
||||
):
|
||||
self.cached_state = None
|
||||
self.store = ReduxStore(kmk_reducer, log_level=log_level)
|
||||
self.store.subscribe(
|
||||
lambda state, action: self._subscription(state, action),
|
||||
)
|
||||
self.store.dispatch(init_firmware(
|
||||
keymap=keymap,
|
||||
row_pins=row_pins,
|
||||
col_pins=col_pins,
|
||||
diode_orientation=diode_orientation,
|
||||
))
|
||||
|
||||
def _subscription(self, state, action):
|
||||
if self.cached_state is None or self.cached_state.keymap != state.keymap:
|
||||
self.keymap = Keymap(state.keymap)
|
||||
|
||||
if self.cached_state is None or any(
|
||||
getattr(self.cached_state, k) != getattr(state, k)
|
||||
for k in state.__dict__.keys()
|
||||
):
|
||||
self.matrix = MatrixScanner(
|
||||
state.col_pins,
|
||||
state.row_pins,
|
||||
state.diode_orientation,
|
||||
)
|
||||
|
||||
def go(self):
|
||||
while True:
|
||||
self.keymap.parse(self.matrix.raw_scan(), store=self.store)
|
@ -1,2 +1,6 @@
|
||||
[flake8]
|
||||
exclude = .git,__pycache__,vendor
|
||||
max_line_length = 99
|
||||
|
||||
[isort]
|
||||
known_third_party = analogio,bitbangio,bleio,board,busio,digitalio,framebuf,gamepad,gc,microcontroller,micropython,pulseio,pyb,pydux,uio,ubluepy
|
||||
|
@ -1,4 +1 @@
|
||||
vendor/upy-lib/__future__/__future__.py
|
||||
vendor/upy-lib/functools/functools.py
|
||||
vendor/upy-lib/string/string.py
|
||||
vendor/pydux/pydux
|
||||
vendor/upy-lib/logging/logging.py
|
||||
|
1
vendor/pydux
vendored
1
vendor/pydux
vendored
@ -1 +0,0 @@
|
||||
Subproject commit 943ca1c75357b9289f55f17ff2d997a66a3313a4
|
Loading…
Reference in New Issue
Block a user