aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile15
-rw-r--r--boot.py10
-rw-r--r--code.py49
-rw-r--r--ff.json6
-rw-r--r--json_layer.py153
-rw-r--r--kmk_frnb.py150
-rw-r--r--layout.py46
-rw-r--r--oled.py69
-rw-r--r--readme.rst49
10 files changed, 548 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2db3406
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+deps/*
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..af3c25b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,15 @@
+SRC=$(wildcard *.py)
+MPY=$(patsubst %.py,build/%.mpy,$(SRC))
+
+all: $(MPY)
+
+.phony: build
+build:
+ mkdir build
+
+build/%.mpy: %.py | build
+ ../mpy-cross.static-amd64-linux-8.0.5 -O3 -o $@ $<
+
+.phony: clean
+clean:
+ rm -r build
diff --git a/boot.py b/boot.py
new file mode 100644
index 0000000..e1c58e0
--- /dev/null
+++ b/boot.py
@@ -0,0 +1,10 @@
+import usb_cdc
+usb_cdc.enable(data=True)
+
+import layout
+if layout.check_key():
+ print("Key pressed")
+else:
+ import storage
+ storage.disable_usb_drive()
+ storage.remount("/", False)
diff --git a/code.py b/code.py
new file mode 100644
index 0000000..e799760
--- /dev/null
+++ b/code.py
@@ -0,0 +1,49 @@
+import board
+
+from supervisor import runtime
+# Do not reload the application when the files are changed
+runtime.autoreload = False
+
+# Initialize the BEPO layout
+import kmk.handlers.stock as handlers
+#from kmk.key_validators import key_seq_sleep_validator
+from kmk import keys
+keys.KC.clear()
+keys.KEY_GENERATORS=(
+ #keys.maybe_make_argumented_key(
+ # key_seq_sleep_validator,
+ # ('MACRO_SLEEP_MS', 'SLEEP_IN_SEQ'),
+ # on_press=handlers.sleep_pressed,
+ #),
+ keys.maybe_make_no_key,
+ )
+KC=keys.KC
+import kmk_frnb
+
+from kmk.kmk_keyboard import KMKKeyboard
+keyboard = KMKKeyboard()
+
+import layout
+layout.set_keyboard(keyboard)
+
+import oled
+oled.main(keyboard)
+
+import json_layer
+def load_json(file):
+
+ with open("ff.json", "r") as ff_conf:
+ s = ff_conf.read()
+ layer = json_layer.Layer(s)
+ return layer
+
+from kmk.modules.macros import Macros
+keyboard.modules = [Macros(), json_layer.JsonLayer()]
+firefox = load_json("ff.json")
+
+keyboard.keymap = [
+ firefox
+]
+
+if __name__ == '__main__':
+ keyboard.go()
diff --git a/ff.json b/ff.json
new file mode 100644
index 0000000..ad8f44e
--- /dev/null
+++ b/ff.json
@@ -0,0 +1,6 @@
+{ "Firefox": [
+ {"seq": ["A", "B", "C"]}, ["^", "R"], ["^", "T"], ["^", "W"],
+ null, null, ["^", "PAGE_UP"], ["^", "PAGE_DOWN"],
+ null, null, ["^", "WINDOWS", "LEFT_ARROW"], ["^", "WINDOWS", "RIGHT_ARROW"],
+ null, null, null, null
+]}
diff --git a/json_layer.py b/json_layer.py
new file mode 100644
index 0000000..4d4c69c
--- /dev/null
+++ b/json_layer.py
@@ -0,0 +1,153 @@
+#
+# Read data in the json format from the serial connexion.
+# The json shall hold in a single line, and the following commands are
+# recognized :
+# {"layer": layer_name} : change the keyboard to the given layer
+# {"stack": layer_name} : stack a new layer on top on an existing one
+# {"get_layer"}
+#
+
+from usb_cdc import data
+import json
+
+from kmk.modules import Module
+from kmk.keys import Key, KC
+#from kmk.handlers.sequences import simple_key_sequence
+from kmk.modules.macros import Delay, Macros, Tap
+
+# This dictionnary is a sort of point of function.
+#
+# It gives a way to apply a specific function in the json
+#
+# There is mutually recursive calls between
+# - operators
+# - key_of_json
+# - sequence
+#
+# I do not define it yet, only declare it.
+operators = {}
+
+def key_of_json(element, inside=False):
+ """ Create the key from the key name.
+ The value can be either a string or a list. If the parameter is a
+ string, the associated key will be returned, otherwise this will be the
+ chain of all of them.
+ """
+ if isinstance(element, int):
+ return element
+ if isinstance(element, str):
+ return KC[element]
+ if isinstance(element, list):
+ hd, *tl = element
+ key = key_of_json(hd)
+ if len(tl) != 0:
+ # Chainable key / recursive function
+ return key( key_of_json(tl) )
+ else:
+ return key
+ if isinstance(element, dict):
+ for key, value in element.items():
+ return operators[key](value)
+ pass
+
+def encode_macro(element):
+ """ Encode the result of the function key_of_json in order to build a macro
+ of this
+ """
+ key = key_of_json(element)
+ if isinstance(key, int):
+ return Delay(key)
+ elif isinstance(key, Key):
+ return Tap(key)
+ return key
+
+def sequence(element):
+ """ Convert a list of keys into a sequence of them.
+ """
+ # Convert each element in the list as Tap function
+ seq = list(map(encode_macro, element))
+ # Send the list as arguments to the function
+ return KC.MACRO(*seq)
+
+def no_release(element):
+ key = key_of_json(element)
+ return key(no_release=True)
+
+def no_press(element):
+ key = key_of_json(element)
+ return key(no_press=True)
+
+# Now I can define the dictionnary
+operators["key"] = key_of_json
+operators["chain"] = key_of_json
+operators["seq"] = sequence
+operators["no_release"] = no_release
+operators["no_press"] = no_press
+
+class Layer(object):
+ """ Layer as an object.
+ This class gives the property name in addition of the keymap list
+ """
+ def __init__(self, jdata):
+ self.load(jdata)
+
+ def load(self, json_data):
+ jdata = json.loads(json_data.strip())
+ for name, keys in jdata.items():
+ self.name = name
+ self.keys = list(map(key_of_json, keys))
+
+ def __getitem__(self, idx):
+ """ When the layer is indexed, return the keymap associated with
+ """
+ return self.keys[idx]
+
+ def __eq__(self, other):
+ """ Compare two layer by the name only
+ """
+ return self.name == other.name
+
+class JsonLayer(Module):
+
+ def during_bootup(self, keyboard):
+ try:
+ # Do not set any timeout, we check before reading a string if there
+ # is any content to read, but block to be sure to read everything.
+ data.timeout = None
+ except AttributeError:
+ pass
+
+ def before_matrix_scan(self, keyboard):
+ pass
+
+ def after_matrix_scan(self, keyboard):
+ pass
+
+ def process_key(self, keyboard, key, is_pressed, int_coord):
+ return key
+
+ def before_hid_send(self, keyboard):
+ # Serial.data isn't initialized.
+ if not data:
+ return
+
+ # Nothing to parse.
+ if data.in_waiting <= 0:
+ return
+ line = data.readline()
+ if not line:
+ return
+
+ try:
+ keyboard.keymap[0].load(line.decode())
+ except Exception as err:
+ print(err)
+
+ def after_hid_send(self, keyboard):
+ pass
+
+ def on_powersave_enable(self, keyboard):
+ pass
+
+ def on_powersave_disable(self, keyboard):
+ pass
diff --git a/kmk_frnb.py b/kmk_frnb.py
new file mode 100644
index 0000000..ce01b37
--- /dev/null
+++ b/kmk_frnb.py
@@ -0,0 +1,150 @@
+from kmk.keys import make_key
+from kmk.keys import ModifierKey, KeyboardKey
+
+# Modifier keys
+make_key(code=0x01, names=("CONTROL", "^"), constructor=ModifierKey)
+make_key(code=0x02, names=("SHIFT",), constructor=ModifierKey)
+make_key(code=0x04, names=("ALT",), constructor=ModifierKey)
+make_key(code=0x08, names=("LEFT_GUI", "WINDOWS"), constructor=ModifierKey)
+make_key(code=0x40, names=("RIGHT_ALT", "ALTGR"), constructor=ModifierKey)
+make_key(code=0x20, names=("RIGHT_SHIFT",), constructor=ModifierKey)
+make_key(code=0x80, names=("RIGHT_GUI",), constructor=ModifierKey)
+#SHIFT=make_key(code=225, names=("SHIFT",))
+#WINDOWS=make_key(code=0x08, names=("WINDOWS",))
+
+make_key(code=39, names=("ZERO",), constructor=KeyboardKey)
+make_key(code=30, names=("ONE",), constructor=KeyboardKey)
+make_key(code=31, names=("TWO",), constructor=KeyboardKey)
+make_key(code=32, names=("THREE",), constructor=KeyboardKey)
+make_key(code=33, names=("FOUR",), constructor=KeyboardKey)
+make_key(code=34, names=("FIVE",), constructor=KeyboardKey)
+make_key(code=35, names=("SIX",), constructor=KeyboardKey)
+make_key(code=36, names=("SEVEN",), constructor=KeyboardKey)
+make_key(code=37, names=("EIGHT",), constructor=KeyboardKey)
+make_key(code=38, names=("NINE",), constructor=KeyboardKey)
+
+make_key(code=80, names=("LEFT_ARROW",), constructor=KeyboardKey)
+make_key(code=79, names=("RIGHT_ARROW",), constructor=KeyboardKey)
+make_key(code=82, names=("UP_ARROW",), constructor=KeyboardKey)
+make_key(code=81, names=("DOWN_ARROW",), constructor=KeyboardKey)
+
+make_key(code=4, names=("A",), constructor=KeyboardKey)
+make_key(code=20, names=("B",), constructor=KeyboardKey)
+make_key(code=11, names=("C",), constructor=KeyboardKey)
+make_key(code=12, names=("D",), constructor=KeyboardKey)
+make_key(code=9, names=("E",), constructor=KeyboardKey)
+make_key(code=56, names=("F",), constructor=KeyboardKey)
+make_key(code=54, names=("G",), constructor=KeyboardKey)
+make_key(code=55, names=("H",), constructor=KeyboardKey)
+make_key(code=7, names=("I",), constructor=KeyboardKey)
+make_key(code=19, names=("J",), constructor=KeyboardKey)
+make_key(code=5, names=("K",), constructor=KeyboardKey)
+make_key(code=18, names=("L",), constructor=KeyboardKey)
+make_key(code=52, names=("M",), constructor=KeyboardKey)
+make_key(code=51, names=("N",), constructor=KeyboardKey)
+make_key(code=21, names=("O",), constructor=KeyboardKey)
+make_key(code=8, names=("P",), constructor=KeyboardKey)
+make_key(code=16, names=("Q",), constructor=KeyboardKey)
+make_key(code=15, names=("R",), constructor=KeyboardKey)
+make_key(code=14, names=("S",), constructor=KeyboardKey)
+make_key(code=13, names=("T",), constructor=KeyboardKey)
+make_key(code=22, names=("U",), constructor=KeyboardKey)
+make_key(code=24, names=("V",), constructor=KeyboardKey)
+make_key(code=48, names=("W",), constructor=KeyboardKey)
+make_key(code=6, names=("X",), constructor=KeyboardKey)
+make_key(code=27, names=("Y",), constructor=KeyboardKey)
+make_key(code=47, names=("Z",), constructor=KeyboardKey)
+
+make_key(code=89, names=("KEYPAD_ONE",), constructor=KeyboardKey)
+make_key(code=98, names=("KEYPAD_ZERO",), constructor=KeyboardKey)
+make_key(code=90, names=("KEYPAD_TWO",), constructor=KeyboardKey)
+make_key(code=91, names=("KEYPAD_THREE",), constructor=KeyboardKey)
+make_key(code=92, names=("KEYPAD_FOUR",), constructor=KeyboardKey)
+make_key(code=93, names=("KEYPAD_FIVE",), constructor=KeyboardKey)
+make_key(code=94, names=("KEYPAD_SIX",), constructor=KeyboardKey)
+make_key(code=95, names=("KEYPAD_SEVEN",), constructor=KeyboardKey)
+make_key(code=96, names=("KEYPAD_EIGHT",), constructor=KeyboardKey)
+make_key(code=97, names=("KEYPAD_NINE",), constructor=KeyboardKey)
+make_key(code=86, names=("KEYPAD_MINUS",), constructor=KeyboardKey)
+make_key(code=83, names=("KEYPAD_NUMLOCK",), constructor=KeyboardKey)
+make_key(code=99, names=("KEYPAD_PERIOD",), constructor=KeyboardKey)
+make_key(code=87, names=("KEYPAD_PLUS",), constructor=KeyboardKey)
+make_key(code=85, names=("KEYPAD_ASTERISK",), constructor=KeyboardKey)
+make_key(code=84, names=("KEYPAD_FORWARD_SLASH",), constructor=KeyboardKey)
+
+make_key(code=101, names=("APPLICATION",), constructor=KeyboardKey)
+make_key(code=26, names=("Accent_aigu",), constructor=KeyboardKey)
+make_key(code=28, names=("Accent_circonflexe",), constructor=KeyboardKey)
+make_key(code=23, names=("Accent_grave",), constructor=KeyboardKey)
+make_key(code=49, names=("BACKSLASH",), constructor=KeyboardKey)
+make_key(code=42, names=("BACKSPACE",), constructor=KeyboardKey)
+make_key(code=47, names=("Barre_couvrante",), constructor=KeyboardKey)
+make_key(code=18, names=("Barre_oblique_couvrante",), constructor=KeyboardKey)
+make_key(code=15, names=("Brève",), constructor=KeyboardKey)
+make_key(code=57, names=("CAPS_LOCK",), constructor=KeyboardKey)
+make_key(code=10, names=("COMMA",), constructor=KeyboardKey)
+make_key(code=227, names=("COMMAND",), constructor=KeyboardKey)
+make_key(code=24, names=("Caron",), constructor=KeyboardKey)
+make_key(code=11, names=("Cédille",), constructor=KeyboardKey)
+make_key(code=76, names=("DELETE",), constructor=KeyboardKey)
+make_key(code=77, names=("END",), constructor=KeyboardKey)
+make_key(code=40, names=("ENTER",), constructor=KeyboardKey)
+make_key(code=46, names=("EQUALS",), constructor=KeyboardKey)
+make_key(code=41, names=("ESCAPE",), constructor=KeyboardKey)
+make_key(code=13, names=("Exposants",), constructor=KeyboardKey)
+make_key(code=23, names=("FORWARD_SLASH",), constructor=KeyboardKey)
+make_key(code=17, names=("GRAVE_ACCENT",), constructor=KeyboardKey)
+make_key(code=227, names=("GUI",), constructor=KeyboardKey)
+make_key(code=74, names=("HOME",), constructor=KeyboardKey)
+make_key(code=73, names=("INSERT",), constructor=KeyboardKey)
+make_key(code=29, names=("RIGHT_BRACKET",), constructor=KeyboardKey)
+make_key(code=28, names=("LEFT_BRACKET",), constructor=KeyboardKey)
+make_key(code=14, names=("Latin_et_ponctuation",), constructor=KeyboardKey)
+make_key(code=54, names=("Lettres_grecques",), constructor=KeyboardKey)
+make_key(code=45, names=("MINUS",), constructor=KeyboardKey)
+make_key(code=52, names=("Macron",), constructor=KeyboardKey)
+make_key(code=100, names=("OEM_102",), constructor=KeyboardKey)
+make_key(code=226, names=("OPTION",), constructor=KeyboardKey)
+make_key(code=56, names=("Ogonek",), constructor=KeyboardKey)
+make_key(code=78, names=("PAGE_DOWN",), constructor=KeyboardKey)
+make_key(code=75, names=("PAGE_UP",), constructor=KeyboardKey)
+make_key(code=72, names=("PAUSE",), constructor=KeyboardKey)
+make_key(code=25, names=("PERIOD",), constructor=KeyboardKey)
+make_key(code=70, names=("PRINT_SCREEN",), constructor=KeyboardKey)
+make_key(code=55, names=("Point_souscrit",), constructor=KeyboardKey)
+make_key(code=53, names=("QUOTE",), constructor=KeyboardKey)
+make_key(code=40, names=("RETURN",), constructor=KeyboardKey)
+make_key(code=16, names=("Rond_en_chef",), constructor=KeyboardKey)
+make_key(code=71, names=("SCROLL_LOCK",), constructor=KeyboardKey)
+make_key(code=26, names=("SEMICOLON",), constructor=KeyboardKey)
+make_key(code=44, names=("SPACE",), constructor=KeyboardKey)
+make_key(code=44, names=("SPACEBAR",), constructor=KeyboardKey)
+make_key(code=12, names=("Symboles_scientifiques",), constructor=KeyboardKey)
+make_key(code=43, names=("TAB",), constructor=KeyboardKey)
+make_key(code=51, names=("Tilde",), constructor=KeyboardKey)
+make_key(code=7, names=("Tréma",), constructor=KeyboardKey)
+
+make_key(code=58, names=("F1",), constructor=KeyboardKey)
+make_key(code=59, names=("F2",), constructor=KeyboardKey)
+make_key(code=60, names=("F3",), constructor=KeyboardKey)
+make_key(code=61, names=("F4",), constructor=KeyboardKey)
+make_key(code=62, names=("F5",), constructor=KeyboardKey)
+make_key(code=63, names=("F6",), constructor=KeyboardKey)
+make_key(code=64, names=("F7",), constructor=KeyboardKey)
+make_key(code=65, names=("F8",), constructor=KeyboardKey)
+make_key(code=66, names=("F9",), constructor=KeyboardKey)
+make_key(code=67, names=("F10",), constructor=KeyboardKey)
+make_key(code=68, names=("F11",), constructor=KeyboardKey)
+make_key(code=69, names=("F12",), constructor=KeyboardKey)
+make_key(code=104, names=("F13",), constructor=KeyboardKey)
+make_key(code=105, names=("F14",), constructor=KeyboardKey)
+make_key(code=106, names=("F15",), constructor=KeyboardKey)
+make_key(code=107, names=("F16",), constructor=KeyboardKey)
+make_key(code=108, names=("F17",), constructor=KeyboardKey)
+make_key(code=109, names=("F18",), constructor=KeyboardKey)
+make_key(code=110, names=("F19",), constructor=KeyboardKey)
+make_key(code=111, names=("F20",), constructor=KeyboardKey)
+make_key(code=112, names=("F21",), constructor=KeyboardKey)
+make_key(code=113, names=("F22",), constructor=KeyboardKey)
+make_key(code=114, names=("F23",), constructor=KeyboardKey)
+make_key(code=115, names=("F24",), constructor=KeyboardKey)
diff --git a/layout.py b/layout.py
new file mode 100644
index 0000000..3dfb828
--- /dev/null
+++ b/layout.py
@@ -0,0 +1,46 @@
+# Describe the layout used in the keyboard
+#
+import board
+from digitalio import Pull
+
+from kmk.scanners import DiodeOrientation
+col_pins = (board.GP26,board.GP27,board.GP28,board.GP29)
+row_pins = (board.GP22,board.GP20,board.GP23,board.GP21)
+
+
+diode_orientation = DiodeOrientation.ROW2COL
+
+from digitalio import DigitalInOut, Direction, Pull
+
+def check_key():
+ """ Check if a key is pressed and return True if so.
+ Scan all the keys only once, and return False if nothing where pressed.
+ """
+ if DiodeOrientation == DiodeOrientation.ROW2COL:
+ inputs = row_pins
+ outputs = col_pins
+ else:
+ inputs = col_pins
+ outputs = row_pins
+
+ for in_pin in inputs:
+ with DigitalInOut(in_pin) as in_io:
+ in_io.direction = Direction.INPUT
+ in_io.pull = Pull.DOWN
+
+ for out_pin in outputs:
+ with DigitalInOut(out_pin) as out_io:
+ out_io.direction = Direction.OUTPUT
+ out_io.value = True
+ result = in_io.value
+
+ if result:
+ return result
+ return False
+
+def set_keyboard(keyboard):
+ """ Initialize the kmk keyboard with the values defined here.
+ """
+ keyboard.row_pins = row_pins
+ keyboard.col_pins = col_pins
+ keyboard.diode_orientation = diode_orientation
diff --git a/oled.py b/oled.py
new file mode 100644
index 0000000..4db0436
--- /dev/null
+++ b/oled.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import board
+import busio
+
+from kmk.extensions.display import Display, TextEntry, ImageEntry
+from kmk.extensions.display.ssd1306 import SSD1306
+from supervisor import ticks_ms
+
+class NamedLayer(TextEntry):
+ """ This is a named layer which return the name of the current mappng as text.
+ """
+
+ def __init__(self, keyboard, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._text = None
+ self.keyboard = keyboard
+
+ @property
+ def text(self):
+ try:
+ return self.keyboard.keymap[0].name
+ except:
+ return self._text
+
+ @text.setter
+ def text(self, value):
+ self._text = value
+
+
+class CustomDisplay(Display):
+ """ Create a custom Display object which refresh the display everytime the
+ layout is updated
+ """
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._prevLayers = None
+
+ def during_bootup(self, keyboard):
+ super().during_bootup(keyboard)
+ self.keyboard = keyboard
+
+ def before_matrix_scan(self, sandbox):
+ """ Override the default function with the layer name as a comparaison
+ key
+ """
+ super().before_matrix_scan(sandbox)
+ if self.keyboard.keymap[0].name != self._prevLayers:
+ self.timer_start = ticks_ms()
+ self._prevLayers = self.keyboard.keymap[0].name
+ self.render(sandbox.active_layers[0])
+ return
+
+
+
+def main(keyboard):
+
+ driver = SSD1306(
+ i2c=busio.I2C(board.SCL, board.SDA),
+ )
+
+ display = CustomDisplay(
+ display=driver,
+ )
+ display.entries = [
+ NamedLayer(keyboard, text="", x=0, y=0),
+ ]
+ keyboard.extensions.append(display)
diff --git a/readme.rst b/readme.rst
new file mode 100644
index 0000000..404e2f4
--- /dev/null
+++ b/readme.rst
@@ -0,0 +1,49 @@
+.. -*- mode: rst -*-
+.. -*- coding: utf-8 -*-
+
+Ce projet contient le client pour le macropad dynamique.
+
+Installation
+============
+
+Préparation de la carte
+-----------------------
+
+Récupérer le firmware circuitpython sur la page adapté à la carte :
+
+https://circuitpython.org/board/0xcb_helios/
+
+Démarrer la carte en mode bootloader, et copier le fichier uf2 sur la
+partition.
+
+Installer les librairies
+------------------------
+
+Récupérer le bundle correspondant à la version du firmware :
+
+https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/tag/20240326
+
+Dans le répertoire `lib`, copier les dossiers :
+
+- adafruit_display_text
+- adafruit_displayio_ssd1306.mpy
+
+Télécharger la dernière version de KMK
+
+Dans le répertoire `lib`, copier le répertoire `KMK`.
+
+Installer l’application
+-----------------------
+
+Copier les fichiers présents dans le répertoire à la racine.
+
+Utilisation
+===========
+
+Démarrage
+---------
+
+Par défaut, la carte ne s’active pas avec le mode stockage USB. Cela permet de
+ne pas créer de lecteur sur le système. Pour que la carte se déclare comme un
+point de stockage, il faut appuyer sur une touche (n’importe laquelle) durant
+le démarrage.