Since I was trying to utilise the existing keyboard in my TRS-80 Model 100, I needed some way to connect it to the Pine A64. While I could have added something like an Arduino Pro Micro to run QMK, this would have added more complexity. Instead I decided to connect the keyboard directly to the GPIO pins on the Pine A64 and poll them using Python. This would also be applicable to the Raspberry Pi – same GPIO pinout and I’m using a Raspberry Pi GPIO library. I found some code here which was a useful start on using UInput, but the logic behind that didn’t make much sense to me and also didn’t work for me – I think maybe an early/non-working version of the script was uploaded by accident. I have made some updates and resolved the issues, as well as adding something to reduce the polling rate if the keyboard isn’t touched for a while – just saving battery life 🙂 By the way, not saying I am a coding master by any means, but someone may find this useful. Still need to add a second layer for a couple of keys that are missing from the Model 100 keyboard.
#!/usr/bin/python3
import RPi.GPIO as GPIO
from time import sleep
from evdev import UInput, ecodes as e
import logging
# TODO:
# Add an exception for handling Shift + [
# Add a new keymap for when CODE is held. Need to add chars: \ | ` ~ { }
#logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
#logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
ui = UInput(name = "TRS-80 Model 100 Keyboard", vendor = 0x01, product = 0x01)
# TRS-80 Keyboard pin to GPIO pin map
# 1 : 11, 11 : 0,
# 2 : 12, 12 : 23,
# 3 : 13, 13 : 29,
# 4 : 15, 14 : 31,
# 5 : 16, 15 : 32,
# 6 : 18, 16 : 33,
# 7 : 19, 17 : 35,
# 8 : 21, 18 : 36,
# 9 : 22, 19 : 37,
# 10 : 0, 20 : 0
cols = [11,12,13,15,16,18,19,21,22]
rows = [23,29,31,32,33,35,36,37]
keymap = [
e.KEY_Z, e.KEY_A, e.KEY_Q, e.KEY_O, e.KEY_1, e.KEY_9, e.KEY_SPACE, e.KEY_F1, e.KEY_LEFTSHIFT,
e.KEY_X, e.KEY_S, e.KEY_W, e.KEY_P, e.KEY_2, e.KEY_0, e.KEY_BACKSPACE, e.KEY_F2, e.KEY_LEFTCTRL,
e.KEY_C, e.KEY_D, e.KEY_E, e.KEY_LEFTBRACE, e.KEY_3, e.KEY_MINUS, e.KEY_TAB, e.KEY_F3, e.KEY_LEFTALT,
e.KEY_V, e.KEY_F, e.KEY_R, e.KEY_SEMICOLON, e.KEY_4, e.KEY_EQUAL, e.KEY_ESC, e.KEY_F4, e.KEY_FN,
e.KEY_B, e.KEY_G, e.KEY_T, e.KEY_APOSTROPHE, e.KEY_5, e.KEY_LEFT, e.KEY_GRAVE, e.KEY_F5, e.KEY_NUMLOCK,
e.KEY_N, e.KEY_H, e.KEY_Y, e.KEY_COMMA, e.KEY_6, e.KEY_RIGHT, e.KEY_COPY, e.KEY_F6, e.KEY_CAPSLOCK,
e.KEY_M, e.KEY_J, e.KEY_U, e.KEY_DOT, e.KEY_7, e.KEY_UP, e.KEY_CLEAR, e.KEY_F7, e.KEY_RESERVED,
e.KEY_L, e.KEY_K, e.KEY_I, e.KEY_SLASH, e.KEY_8, e.KEY_DOWN, e.KEY_ENTER, e.KEY_F8, e.KEY_PAUSE,
]
GPIO.setmode(GPIO.BOARD)
for row in rows:
logging.info(f"Setting pin {row} as an output")
GPIO.setup(row, GPIO.OUT)
for col in cols:
logging.info(f"Setting pin {col} as an input")
GPIO.setup(col, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
pressed = set()
sleep_time = 1/60
polls_since_press = 0
while True:
sleep(sleep_time)
syn = False
for i in range(len(rows)):
#logging.debug(f"Setting row {i} high, pin {rows[i]}")
GPIO.output(rows[i], GPIO.HIGH)
for j in range(len(cols)):
keycode = i * (len(rows) + 1) + j
#logging.debug(f"Checking column {j}, pin {cols[j]} which results in key {keymap[keycode]}")
newval = GPIO.input(cols[j]) == GPIO.HIGH
if newval and not keycode in pressed:
pressed.add(keycode)
logging.info(f"Pressed {keycode} which results in key {e.KEY[keymap[keycode]]} Column {i} Row {j}")
ui.write(e.EV_KEY, keymap[keycode], 1)
syn = True
elif not newval and keycode in pressed:
pressed.discard(keycode)
logging.info(f"Released {keycode} which results in key {e.KEY[keymap[keycode]]}")
ui.write(e.EV_KEY, keymap[keycode], 0)
syn = True
GPIO.output(rows[i], GPIO.LOW)
if syn:
ui.syn()
polls_since_press = 0
sleep_time = 1/60
else:
polls_since_press = polls_since_press + 1
if polls_since_press == 600:
logging.info(f"Reducing polling rate")
sleep_time = 1/10
elif polls_since_press == 1200:
logging.info(f"Reducing polling rate again")
sleep_time = 1/5
I then threw a quick start script into /etc/rc.local so it starts on boot.

Im going to build one of these! I already ordered a broken TRS 80 model 100. I might consider RPi not sure yet. Do you have a pin out or more closeup pictures of where you wired up to the keyboard?
Nice! I didn’t use the Raspberry Pi just because then you need separate boards for power supply. With the Pine I can poll battery voltage and charge status from the CLI 🙂
If you check the code comments you’ll see a mapping from the GPIO pin number (common to Pine and RPi) and the Model 100 keyboard pinout. For the keyboard pinout, do a google for “TRS-80 Model 100 Service Manual” and you’ll find what you need 🙂