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.  | 
