# python-keyboard
**Repository Path**: forgo/python-keyboard
## Basic Information
- **Project Name**: python-keyboard
- **Description**: 手焊的、跑 Python 的 USB + 蓝牙双模键盘
- **Primary Language**: Python
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 15
- **Created**: 2020-07-02
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
Python Keyboard
===============
English | [中文][1]
---------|----------
A hand-wired USB & Bluetooth keyboard powered by Python


## Steps
1. [hand-wire the keyboard](hardware.md)
2. Follow [the guide - How to Program Pitaya Go](https://wiki.makerdiary.com/pitaya-go/programming/) to flash [CircuitPython firmware](circuitpython-5.3.0-for-pitaya-go.hex)
3. Download two CircuitPython libraries - [adafruit-ble](https://github.com/adafruit/Adafruit_CircuitPython_BLE) & [adafruit-hid](https://github.com/adafruit/Adafruit_CircuitPython_HID) and put them into the `lib` directory of the USB drive named CIRCUITPY.
```
CIRCUITPY
├───code.py
└───lib
├───adafruit_ble
└───adafruit_hid
```
4. Copy the Python code to `code.py`. When `code.py` is reloaded, you will get a keyboard with USB & Bluetooth
```python
import time
from board import *
import digitalio
import usb_hid
import adafruit_ble
from adafruit_ble.advertising import Advertisement
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.standard.hid import HIDService
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode as _
ROWS = (P27, P13, P30, P20, P3)
COLS = (P26, P31, P29, P28, P5, P4, P24, P25, P23, P22, P14, P15, P16, P17)
KEYMAP = (_.ESCAPE, _.ONE, _.TWO, _.THREE, _.FOUR, _.FIVE, _.SIX, _.SEVEN, _.EIGHT, _.NINE, _.ZERO, _.MINUS, _.EQUALS, _.BACKSPACE,
_.TAB, _.Q, _.W, _.E, _.R, _.T, _.Y, _.U, _.I, _.O, _.P, _.LEFT_BRACKET, _.RIGHT_BRACKET, _.BACKSLASH,
_.CAPS_LOCK, _.A, _.S, _.D, _.F, _.G, _.H, _.J, _.K, _.L, _.SEMICOLON, _.QUOTE, None, _.ENTER,
_.LEFT_SHIFT, _.Z, _.X, _.C, _.V, _.B, _.N, _.M, _.COMMA, _.PERIOD, _.FORWARD_SLASH, None, _.RIGHT_SHIFT, None,
_.LEFT_CONTROL, _.LEFT_ALT, _.LEFT_GUI, None, None, _.SPACE, None, None, _.RIGHT_ALT, _.RIGHT_GUI, _.APPLICATION, _.RIGHT_CONTROL, None, None)
class Matrix:
def __init__(self, rows=ROWS, cols=COLS):
self.rows = []
for pin in rows:
io = digitalio.DigitalInOut(pin)
io.direction = digitalio.Direction.OUTPUT
io.drive_mode = digitalio.DriveMode.PUSH_PULL
io.value = 0
self.rows.append(io)
self.cols = []
for pin in cols:
io = digitalio.DigitalInOut(pin)
io.direction = digitalio.Direction.INPUT
io.pull = digitalio.Pull.DOWN
self.cols.append(io)
self.pressed_keys = []
def scan(self):
new_keys = []
pressed_keys = []
released_keys = self.pressed_keys
for r in range(len(self.rows)):
self.rows[r].value = 1
for c in range(len(self.cols)):
if self.cols[c].value:
key = r * len(self.cols) + c
pressed_keys.append(key)
if key in released_keys:
released_keys.remove(key)
else:
new_keys.append(key)
self.rows[r].value = 0
self.pressed_keys = pressed_keys
return pressed_keys, released_keys, new_keys
def main():
hid = HIDService()
advertisement = ProvideServicesAdvertisement(hid)
advertisement.appearance = 961
ble = adafruit_ble.BLERadio()
if ble.connected:
for c in ble.connections:
c.disconnect()
ble.start_advertising(advertisement)
advertising = True
ble_keyboard = Keyboard(hid.devices)
matrix = Matrix()
usb_keyboard = Keyboard(usb_hid.devices)
while True:
pressed_keys, released_keys, new_keys = matrix.scan()
if released_keys:
released_keycodes = list(map(lambda i: KEYMAP[i], released_keys))
print('released keys {}'.format(released_keycodes))
usb_keyboard.release(*released_keycodes)
if ble.connected:
advertising = False
ble_keyboard.release(*released_keycodes)
if new_keys:
new_keycodes = list(map(lambda i: KEYMAP[i], new_keys))
print('new keys {}'.format(new_keycodes))
usb_keyboard.press(*new_keycodes)
if ble.connected:
advertising = False
ble_keyboard.press(*new_keycodes)
if not ble.connected and not advertising:
ble.start_advertising(advertisement)
advertising = True
time.sleep(0.001)
if __name__ == '__main__':
main()
```
If you have a different configuration of raws and columns, you must change `ROWS` and `COLS` in the code.
## To be a productive keyboard
As the 60% keyboard lacks a lot of keys (F1~F12, arrow keys and etc). We can add
[features like TMK's layers and composite keys](https://github.com/tmk/tmk_keyboard/blob/master/tmk_core/doc/keymap.md) to make the small keyboard much more powerful.
With the idea of [Toward a more useful keyboard](https://github.com/jasonrudolph/keyboard) to keep our fingers at the home row, we can optimize the keyboard to make us more productive.
Adding the Tap-key feature, which is holding a key down to activate an alternate function, can make a big difference.
### Using D for Navigation
Taping d outputs d (press & release quickly), holding d down activates navigation functions.

+ d + h as ←
+ d + j as ↓
+ d + k as ↑
+ d + l as →
+ d + u as PageUp
+ d + n as PageDown
To apply the navigation d, copy `keyboard.py` and `action_code.py` to `CIRCUITPY`, and then modify `code.py` to import the new keyboard
```python
# code.py
from keyboard import main
main()
```
### Using Pair-keys
Simultaneously pressing two keys (interval less than 25ms) activates an alternate function.
### Using ; as Ctrl
WIP - Holding ; down outputs Ctrl
## Todo
+ ; as Ctrl
+ add macro
+ add system keys and cosumer keys
+ add mouse keys
+ reduce latency
[1]: https://gitee.com/makerdiary/python-keyboard