Skip to content

Commit

Permalink
Add steno module (#935)
Browse files Browse the repository at this point in the history
Add steno module
  • Loading branch information
Cheetah26 authored Feb 2, 2024
1 parent e733c4d commit 2b18417
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/en/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Before you look further, you probably want to start with our [getting started gu
- [SerialACE](serialace.md): [DANGER - _see module README_] Arbitrary Code Execution over the data serial.
- [Split](split_keyboards.md): Keyboards split in two. Seems ergonomic!
- [TapDance](tapdance.md): Different key actions depending on how often it is pressed.
- [Steno](steno.md): Communicate with stenography software over serial.

### Peripherals

Expand Down
71 changes: 71 additions & 0 deletions docs/en/steno.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Steno

Communicate with stenography software such as [Plover](https://www.openstenoproject.org/plover/) over the Gemini PR protocol.

## Setup

You must include the following in `boot.py` to enable data serial.

```python
import usb_cdc
usb_cdc.enable(data=True)
```

Then, instantiate the module as usual and add steno keys to your keymap.

```python
from kmk.modules.steno import Steno
keyboard.modules.append(Steno())
```

## Keys

The following keys are created for use in your keymap:

| Keycode | Description |
|------------|---------------|
| `KC.STN_LS1` | S1- |
| `KC.STN_LS2` | S2- |
| `KC.STN_LT` | T- |
| `KC.STN_LK` | K- |
| `KC.STN_LP` | P- |
| `KC.STN_LW` | W- |
| `KC.STN_LH` | H- |
| `KC.STN_LR` | R- |
| `KC.STN_A` | A |
| `KC.STN_O` | O |
| `KC.STN_AS1` | * Top-left |
| `KC.STN_AS2` | * Lower-left |
| `KC.STN_AS3` | * Top-right |
| `KC.STN_AS4` | * Lower-right |
| `KC.STN_E` | E |
| `KC.STN_U` | U |
| `KC.STN_RF` | -F |
| `KC.STN_RR` | -R |
| `KC.STN_RP` | -P |
| `KC.STN_RB` | -B |
| `KC.STN_RL` | -L |
| `KC.STN_RT` | -T |
| `KC.STN_RS` | -S |
| `KC.STN_RD` | -D |
| `KC.STN_RZ` | -Z |
| `KC.STN_N1` | Number bar 1 |
| `KC.STN_N2` | Number bar 2 |
| `KC.STN_N3` | Number bar 3 |
| `KC.STN_N4` | Number bar 4 |
| `KC.STN_N5` | Number bar 5 |
| `KC.STN_N6` | Number bar 6 |
| `KC.STN_N7` | Number bar 7 |
| `KC.STN_N8` | Number bar 8 |
| `KC.STN_N9` | Number bar 9 |
| `KC.STN_NA` | Number bar A |
| `KC.STN_NB` | Number bar B |
| `KC.STN_NC` | Number bar C |
| `KC.STN_FN` | Function |
| `KC.STN_RES1` | Reset 2 |
| `KC.STN_RES2` | Reset 1 |
| `KC.STN_PWR` | Power |

## Connecting Plover

Open the Plover configuration to the Machine tab. Set Machine to Gemini PR. Then, under Connection set the Port to the keyboard's serial data interface (this may take some trial and error if you are unsure which one to use). All other settings can be left as their defaults.
107 changes: 107 additions & 0 deletions kmk/modules/steno.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import usb_cdc

from kmk.keys import make_key
from kmk.modules import Module

# key order from https://github.com/openstenoproject/plover/blob/main/plover/machine/geminipr.py
# do not rearrange
STENO_KEYS = (
'STN_FN',
'STN_N1',
'STN_N2',
'STN_N3',
'STN_N4',
'STN_N5',
'STN_N6',
'STN_LS1',
'STN_LS2',
'STN_LT',
'STN_LK',
'STN_LP',
'STN_LW',
'STN_LH',
'STN_LR',
'STN_A',
'STN_O',
'STN_AS1',
'STN_AS2',
'STN_RES1',
'STN_RES2',
'STN_PWR',
'STN_AS3',
'STN_AS4',
'STN_E',
'STN_U',
'STN_RF',
'STN_RR',
'STN_RP',
'STN_RB',
'STN_RL',
'STN_RG',
'STN_RT',
'STN_RS',
'STN_RD',
'STN_N7',
'STN_N8',
'STN_N9',
'STN_NA',
'STN_NB',
'STN_NC',
'STN_RZ',
)


class Steno(Module):
def __init__(self):
self._should_write = False

self._buffer = bytearray(6)
self._initialize_buffer()

for idx, key in enumerate(STENO_KEYS):
make_key(
code=((idx // 7) << 8) | (0x40 >> (idx % 7)),
names=(key,),
on_press=self._steno_press,
on_release=self._steno_release,
)

def _initialize_buffer(self):
self._buffer[:] = b'\x80\x00\x00\x00\x00\x00'

# flip a key's bit in the buffer
def _steno_press(self, key, *_):
self._should_write = True
self._buffer[key.code >> 8] |= key.code & 0xFF

# send all keys that were pressed, and reset the buffer
def _steno_release(self, *_):
if self._should_write:
usb_cdc.data.write(self._buffer)

self._should_write = False
self._initialize_buffer()

def during_bootup(self, keyboard):
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):
pass

def after_hid_send(self, keyboard):
pass

def on_powersave_enable(self, keyboard):
pass

def on_powersave_disable(self, keyboard):
pass

0 comments on commit 2b18417

Please sign in to comment.