diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | boot.py | 10 | ||||
-rw-r--r-- | code.py | 49 | ||||
-rw-r--r-- | ff.json | 6 | ||||
-rw-r--r-- | json_layer.py | 153 | ||||
-rw-r--r-- | kmk_frnb.py | 150 | ||||
-rw-r--r-- | layout.py | 46 | ||||
-rw-r--r-- | oled.py | 69 | ||||
-rw-r--r-- | readme.rst | 49 |
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 @@ -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) @@ -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() @@ -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 @@ -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. |