Skip to content

Instantly share code, notes, and snippets.

@joric

joric/code.py Secret

Last active August 9, 2022 15:07
Show Gist options
  • Save joric/327fafcf40a8a8ccb93c9b7eb631100b to your computer and use it in GitHub Desktop.
Save joric/327fafcf40a8a8ccb93c9b7eb631100b to your computer and use it in GitHub Desktop.
code.py
# see https://github.com/joric/nrfmicro/wiki/Circuitpython
import time
import board
import adafruit_ssd1306
import busio
import digitalio
import json
ready_str = "Hello from Python!"
try:
f = open('config.json')
config = json.load(f)
ready_str = config['config']['device_info']['name']
f.close()
except Exception as e:
print(e)
# enable power
power_pin = digitalio.DigitalInOut(board.P1_09)
power_pin.direction = digitalio.Direction.OUTPUT
power_pin.value = False
try:
i2c = busio.I2C(board.SCL, board.SDA)
oled = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c)
oled.fill(0)
oled.text(ready_str, 0, 0, 1)
oled.show()
except Exception as e:
print('No OLED (wrong pins?)', e)
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode
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_ble.services.standard.device_info import DeviceInfoService
class Matrix:
ROWS = ()
COLS = ()
ROW2COL = False
def __init__(self):
self.keys = len(self.ROWS) * len(self.COLS)
self.queue = bytearray(self.keys)
self.head = 0
self.tail = 0
self.length = 0
self.rows = [] # row as output
for pin in self.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 = [] # col as input
for pin in self.COLS:
io = digitalio.DigitalInOut(pin)
io.direction = digitalio.Direction.INPUT
io.pull = digitalio.Pull.DOWN if self.ROW2COL else digitalio.Pull.UP
self.cols.append(io)
# row selected value depends on diodes' direction
self.pressed = bool(self.ROW2COL)
self.t0 = [0] * self.keys # key pressed time
self.t1 = [0] * self.keys # key released time
self.mask = 0
self.count = 0
self._debounce_time = 20000000
def scan(self):
"""
Scan keyboard matrix and save key event into the queue.
:return: length of the key event queue.
"""
t = time.monotonic_ns()
# use local variables to speed up
pressed = self.pressed
last_mask = self.mask
cols = self.cols
mask = 0
count = 0
key_index = -1
for row in self.rows:
row.value = pressed # select row
for col in cols:
key_index += 1
if col.value == pressed:
key_mask = 1 << key_index
if not (last_mask & key_mask):
if t - self.t1[key_index] < self._debounce_time:
continue
self.t0[key_index] = t
self.put(key_index)
mask |= key_mask
count += 1
elif last_mask and (last_mask & (1 << key_index)):
if t - self.t0[key_index] < self._debounce_time:
mask |= 1 << key_index
continue
self.t1[key_index] = t
self.put(0x80 | key_index)
row.value = not pressed
self.mask = mask
self.count = count
return self.length
def wait(self, timeout=1000):
"""Wait for a new key event or timeout"""
last = self.length
if timeout:
end_time = time.monotonic_ns() + timeout * 1000000
while True:
n = self.scan()
if n > last or time.monotonic_ns() > end_time:
return n
else:
while True:
n = self.scan()
if n > last:
return n
def put(self, data):
"""Put a key event into the queue"""
self.queue[self.head] = data
self.head += 1
if self.head >= self.keys:
self.head = 0
self.length += 1
def get(self):
"""Remove and return the first event from the queue."""
data = self.queue[self.tail]
self.tail += 1
if self.tail >= self.keys:
self.tail = 0
self.length -= 1
return data
def view(self, n):
"""Return the specified event"""
return self.queue[(self.tail + n) % self.keys]
def __getitem__(self, n):
"""Return the specified event"""
return self.queue[(self.tail + n) % self.keys]
def __len__(self):
"""Return the number of events in the queue"""
return self.length
def get_keydown_time(self, key):
"""Return the key pressed time"""
return self.t0[key]
def get_keyup_time(self, key):
"""Return the key released time"""
return self.t1[key]
def time(self):
"""Return current time"""
return time.monotonic_ns()
def ms(self, t):
"""Convert time to milliseconds"""
return t // 1000000
@property
def debounce_time(self):
return self._debounce_time // 1000000
@debounce_time.setter
def debounce_time(self, t):
"""Set debounce time"""
self._debounce_time = t * 1000000
def suspend(self):
"""Suspend keyboard"""
pass
hid = HIDService()
advertisement = ProvideServicesAdvertisement(hid)
advertisement.appearance = 961
scan_response = Advertisement()
ble = adafruit_ble.BLERadio()
ble.name = 'Jorne BLE'
if ble.connected:
for c in ble.connections:
c.disconnect()
print("advertising")
ble.start_advertising(advertisement, scan_response)
k = Keyboard(hid.devices)
kl = KeyboardLayoutUS(k)
Matrix.ROWS = (board.P0_20, board.P0_13, board.P0_24, board.P0_09)
Matrix.COLS = (board.P0_30, board.P0_31, board.P0_29, board.P0_02, board.P1_13, board.P0_03)
Matrix.ROW2COL = False
matrix = Matrix()
import neopixel
leds = 6
pixels = neopixel.NeoPixel(board.P0_06, leds, auto_write=False)
def wheel(p):
p = 255 - p
if p < 85:
return 255-p*3, 0, p*3
if p < 170:
p -= 85
return 0, p*3, 255-p*3
p -= 170
return p*3, 255-p*3, 0
def rgb_update(pixels):
for i in range(leds):
pixels[i] = (wheel((int(time.monotonic()*100.0) + i*256//leds) & 255))
pixels.show()
usb_k = False
import usb_hid
try:
usb_k = Keyboard(usb_hid.devices)
except:
pass
def send_key(key, pressed):
if pressed:
row, col = divmod(key, len(matrix.ROWS))
if row==0:
if usb_k: usb_k.send(Keycode.LEFT_GUI)
else:
k.send(Keycode.ALT, Keycode.TAB)
while True:
rgb_update(pixels)
n = matrix.wait(20)
if n == 0:
continue
for i in range(n):
event = matrix.get()
key = event & 0x7F
pressed = event < 0x80
row, col = divmod(key, len(matrix.ROWS))
print(row, col, pressed)
send_key(key, pressed)
#oled.fill(0)
#oled.text("%dx%d %s" %(row, col, "pressed" if pressed else "released"), 0, 0, 1)
#oled.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment