# IBM PC Keyboard to USB HID with CircuitPython

## Overview

![](https://cdn-learn.adafruit.com/assets/assets/000/116/409/medium800thumb/projects_PXL_20221101_203931141.TS.mp4_2-opt.jpg?1667338231)

In this guide, you'll see how to convert an IBM PC keyboard for use on modern computers using Adafruit CircuitPython and an Adafruit QT Py RP2040 microcontroller board.

The software in this guide is only for original 83-key PC keyboard. There are also 101/102-key keyboards with the same connector, but they send multi-byte scancodes that aren't handled by the CircuitPython code.

The techniques in this guide may also be helpful in converting other classic keyboards that use a clocked serial protocol. The RP2040's "pio" peripheral is perfect for this kind of low level I/O task, and even provides a buffer for up to 8 key events so no keypresses get lost.

Since there are just a few pins used, the Adafruit QT Py RP2040 makes a solid choice of board for the project. However, there's no reason you couldn't adapt the code to the KB2040, Raspberry Pi Pico, or other board based on the same microcontroller.

Warning: The software in this guide is only for original 83-key PC keyboard (8 function keys on the side, labeled Model F. There are also 101/102-key keyboards with the same connector, but they send multi-byte scancodes that aren't handled by the CircuitPython code in this guide.

## Homework

When I saw this keyboard in a pile of stuff a friend was giving away, I recognized the classic IBM PC keyboard design with the function keys at the left hand side, and grabbed it with both hands. Good thing, too, because it weighs about 8lbs.

Being such a foundational piece of hardware, it is well documented and of course has an [extensive Wikipedia article](https://en.wikipedia.org/wiki/Model_F_keyboard). This keyboard is commonly called the "Model F", though so is the later keyboard for the IBM PC/AT. The AT Model F has a much more conventional layout and seems to be more common, so double check which one you have.

Basics of the protocol ([more detail in tmk\_keyboard](https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol)):

- keyboard drives clock and data lines
- unidirectional communication (keyboard to pc only)
- 2 start bits
- 8 data bits
- make/break bit (keydown/keyup)
- 1 stop bit
- keyboard reset via pin 3 to ground&nbsp;

## Jeff's Verdict

I've always liked typing on buckling spring keyboards, and the feel and sound of this one are no exception. I'd have to work out alternatives for F11/F2 and the "Windows" modifier key to make it an everyday keyboard, though.

Pros:

- That classic buckling spring feel
- One of the heaviest keyboards I own
- Rare & expensive with fragile and unique keyswitches
- Seems to support full NKRO (limited to 6 keys + 3 modifiers by CircuitPython)

Cons:

- One of the heaviest keyboards I own
- Unusual location of some keys, especially F-keys and modifiers
- No caps lock / num lock indicators
- Rare & expensive with fragile and unique keyswitches

## Parts
### Adafruit QT Py RP2040

[Adafruit QT Py RP2040](https://www.adafruit.com/product/4900)
What a cutie pie! Or is it... a QT Py?&nbsp;This diminutive dev board comes with one of our new favorite chip, the RP2040. It's been made famous in the new [Raspberry Pi Pico](https://www.adafruit.com/pico) _and_ our [Feather...](http://www.adafruit.com/product/4884)

Out of Stock
[Buy Now](https://www.adafruit.com/product/4900)
[Related Guides to the Product](https://learn.adafruit.com/products/4900/guides)
![Video of hand holding a QT Py PCB in their hand. An LED glows rainbow colors.](https://cdn-shop.adafruit.com/product-videos/640x480/4900-06.jpg)

### Breadboard-friendly MIDI Jack (5-pin DIN)

[Breadboard-friendly MIDI Jack (5-pin DIN)](https://www.adafruit.com/product/1134)
To celebrate the 30th Anniversary of the invention of MIDI we're carrying these handy 5-pin MIDI jacks. They're what you see on the back of nearly every synthesizer and drum machine. These are nice sturdy jacks, and breadboard-friendly, with all the pins on 0.1" spacing for easy...

In Stock
[Buy Now](https://www.adafruit.com/product/1134)
[Related Guides to the Product](https://learn.adafruit.com/products/1134/guides)
![Breadboard-friendly MIDI Jack](https://cdn-shop.adafruit.com/640x480/1134-08.jpg)

### Half Sized Premium Breadboard - 400 Tie Points

[Half Sized Premium Breadboard - 400 Tie Points](https://www.adafruit.com/product/64)
This is a cute, half-size breadboard with&nbsp;400 tie points, good for small projects. It's 3.25" x 2.2" / 8.3cm&nbsp;x 5.5cm&nbsp;with a standard double-strip in the middle and two power rails on both sides.&nbsp;You can pull the power rails off easily to make the breadboard as...

Out of Stock
[Buy Now](https://www.adafruit.com/product/64)
[Related Guides to the Product](https://learn.adafruit.com/products/64/guides)
![Angled shot of half-size solderless breadboard with red and black power lines.](https://cdn-shop.adafruit.com/640x480/64-06.jpg)

### Premium Male/Male Jumper Wires - 40 x 6" (150mm)

[Premium Male/Male Jumper Wires - 40 x 6" (150mm)](https://www.adafruit.com/product/758)
Handy for making wire harnesses or jumpering between headers on PCB's. These premium jumper wires are 6" (150mm) long and come in a 'strip' of 40 (4 pieces of each of ten rainbow colors). They have 0.1" male header contacts on either end and fit cleanly next to each other...

Out of Stock
[Buy Now](https://www.adafruit.com/product/758)
[Related Guides to the Product](https://learn.adafruit.com/products/758/guides)
![Angled shot of Premium Male/Male Jumper Wires - 40 x 6 (150mm)](https://cdn-shop.adafruit.com/640x480/758-04.jpg)

### USB Type A to Type C Cable - approx 1 meter / 3 ft long

[USB Type A to Type C Cable - approx 1 meter / 3 ft long](https://www.adafruit.com/product/4474)
As technology changes and adapts, so does Adafruit. This&nbsp;&nbsp; **USB Type A to Type C** cable will help you with the transition to USB C, even if you're still totin' around a USB Type A hub, computer or laptop.

USB C is the latest industry-standard connector for...

In Stock
[Buy Now](https://www.adafruit.com/product/4474)
[Related Guides to the Product](https://learn.adafruit.com/products/4474/guides)
![Angled shot of a coiled black, USB-C to USB-A cable.](https://cdn-shop.adafruit.com/640x480/4474-02.jpg)

### Through-Hole Resistors - 4.7K ohm 5% 1/4W - Pack of 25

[Through-Hole Resistors - 4.7K ohm 5% 1/4W - Pack of 25](https://www.adafruit.com/product/2783)
ΩMG! You're not going to be able to resist these handy resistor packs!&nbsp;Well, axially, they&nbsp;do all of the resisting for you!

This is a **25 Pack of 4.7K Ω Resistors.** More specifically, they are **carbon film** , through-hole...

In Stock
[Buy Now](https://www.adafruit.com/product/2783)
[Related Guides to the Product](https://learn.adafruit.com/products/2783/guides)
![Angled of shot 25 Through-Hole Resistors - 4.7K ohm 5% 1/4W.](https://cdn-shop.adafruit.com/640x480/2783-00.jpg)

# IBM PC Keyboard to USB HID with CircuitPython

## Wiring the Adapter

![](https://cdn-learn.adafruit.com/assets/assets/000/116/432/medium800/projects_PXL_20221102_152402404.MP.jpg?1667405559)

The IBM PC keyboard male plug happens to be physically the same as the "MIDI DIN connector", so the female connectors are easy to come by.

With Adafruit's breadboard friendly DIN-5 connector, you can just wire it up on a breadboard! For advanced users who like to solder, it's possible to create a super compact adapter instead, [in the style of this guide](https://learn.adafruit.com/qt-py-rp2040-usb-to-serial-midi-friends/circuit-diagram).

On the breadboard, make the following connections, noting the odd pin numbering of the DIN-5 connector:

- QT Py **5V** to DIN-5 **pin 5**
- QT Py **GND** to DIN-5 **pin 4**
- QT Py **MISO (MI)** to DIN-5 **pin 2 (data)**
- QT Py **SCK** to DIN-5 **PIN 1 (clock)**
- First resistor from **DIN-5 pin 2 (data)** to **GND**
- Second resistor from **DIN-5 pin 1 (clock)** to **GND**

Use the blue supply rail as an additional GND bus to simplify the wiring.

Numbered DIN connector image [from Wikipedia](https://upload.wikimedia.org/wikipedia/commons/3/31/DIN_connector_pinout.svg), rotated to match connector as shown in circuit diagram.

![projects_QTPy_PC_XT_Adapter_bb.png](https://cdn-learn.adafruit.com/assets/assets/000/116/431/medium640/projects_QTPy_PC_XT_Adapter_bb.png?1667405415)

![projects_Screenshot_2022-11-01_12-18-17.png](https://cdn-learn.adafruit.com/assets/assets/000/116/433/medium640/projects_Screenshot_2022-11-01_12-18-17.png?1667405774)

## A note on voltages

The keyboard is powered at +5V. Its outputs are "open collector" pins meaning that they are never driven to +5V, but are instead allowed to float up to it through a nominal 2kΩ pull- **up** resistor. However, the RP2040's input pins are not 5V tolerant, but are limited to about 3.8V maximum. By adding a pull- **down** resistor we create a voltage divider which decreases the maximum voltage seen at the RP2040's input pin. The goldilocks resistor value of 3.3kΩ would give a high voltage of approximately 3.1V at the RP2040's input pin. But nearby standard values of 2.2kΩ (2.6V) and 4.7kΩ (3.5V) are within spec too. I used 2.2kΩ resistors because I had them on hand.

# IBM PC Keyboard to USB HID with CircuitPython

## Install CircuitPython

[CircuitPython](https://github.com/adafruit/circuitpython) is a derivative of [MicroPython](https://micropython.org) designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the **CIRCUITPY** drive to iterate.

## CircuitPython Quickstart

Follow this step-by-step to quickly get CircuitPython running on your board.

[Download the latest version of CircuitPython for this board via circuitpython.org](https://circuitpython.org/board/adafruit_qtpy_rp2040/)
 **Click the link above to download the latest CircuitPython UF2 file.**

Save it wherever is convenient for you.

![install_circuitpython_on_rp2040_RP2040_UF2_downloaded.jpg](https://cdn-learn.adafruit.com/assets/assets/000/101/655/medium640/install_circuitpython_on_rp2040_RP2040_UF2_downloaded.jpg?1618943202)

![](https://cdn-learn.adafruit.com/assets/assets/000/101/680/medium800/adafruit_products_QTRP_buttons.jpg?1618956837)

To enter the bootloader, hold down the **BOOT/**** BOOTSEL button**(highlighted in red above), and while continuing to hold it (don't let go!), press and release the**reset button**(highlighted in red or blue above).&nbsp;**Continue to hold the BOOT/BOOTSEL button until the RPI-RP2 drive appears!**

If the drive does not appear, release all the buttons, and then repeat the process above.

You can also start with your board unplugged from USB, press and hold the BOOTSEL button (highlighted in red above), continue to hold it while plugging it into USB, and wait for the drive to appear before releasing the button.

A lot of people end up using charge-only USB cables and it is very frustrating! **Make sure you have a USB cable you know is good for data sync.**

You will see a new disk drive appear called **RPI-RP2**.

&nbsp;

Drag the **adafruit\_circuitpython\_etc.uf2** file to **RPI-RP2.**

![install_circuitpython_on_rp2040_RP2040_bootloader_drive.jpg](https://cdn-learn.adafruit.com/assets/assets/000/101/656/medium640/install_circuitpython_on_rp2040_RP2040_bootloader_drive.jpg?1618943666)

![install_circuitpython_on_rp2040_RP2040_drag_UF2.jpg](https://cdn-learn.adafruit.com/assets/assets/000/101/657/medium640/install_circuitpython_on_rp2040_RP2040_drag_UF2.jpg?1618943674)

The **RPI-RP2** drive will disappear and a new disk drive called **CIRCUITPY** will appear.

That's it, you're done! :)

![install_circuitpython_on_rp2040_RP2040_CIRCUITPY.jpg](https://cdn-learn.adafruit.com/assets/assets/000/101/658/medium640/install_circuitpython_on_rp2040_RP2040_CIRCUITPY.jpg?1618943864)

## Safe Mode

You want to edit your **code.py** or modify the files on your **CIRCUITPY** drive, but find that you can't. Perhaps your board has gotten into a state where **CIRCUITPY** is read-only. You may have turned off the **CIRCUITPY** drive altogether. Whatever the reason, safe mode can help.

Safe mode in CircuitPython does not run any user code on startup, and disables auto-reload. This means a few things. First, safe mode _bypasses any code in_ **boot.py** (where you can set **CIRCUITPY** read-only or turn it off completely). Second, _it does not run the code in_ **code.py**. And finally, _it does not automatically soft-reload when data is written to the_ **CIRCUITPY** _drive_.

Therefore, whatever you may have done to put your board in a non-interactive state, safe mode gives you the opportunity to correct it without losing all of the data on the **CIRCUITPY** drive.

### Entering Safe Mode
To enter safe mode when using CircuitPython, plug in your board or hit reset (highlighted in red above). Immediately after the board starts up or resets, it waits 1000ms. On some boards, the onboard status LED (highlighted in green above) will blink yellow during that time. If you press reset during that 1000ms, the board will start up in safe mode. It can be difficult to react to the yellow LED, so you may want to think of it simply as a slow double click of the reset button. (Remember, a fast double click of reset enters the bootloader.)

### In Safe Mode

If you successfully enter safe mode on CircuitPython, the LED will intermittently blink yellow three times.

If you connect to the serial console, you'll find the following message.

```terminal
Auto-reload is off.
Running in safe mode! Not running saved code.

CircuitPython is in safe mode because you pressed the reset button during boot. Press again to exit safe mode.

Press any key to enter the REPL. Use CTRL-D to reload.
```

You can now edit the contents of the **CIRCUITPY** drive. Remember, _your code will not run until you press the reset button, or unplug and plug in your board, to get out of safe mode._

## Flash Resetting UF2

If your board ever gets into a really _weird_ state and CIRCUITPY doesn't show up as a disk drive after installing CircuitPython, try loading this 'nuke' UF2 to RPI-RP2. which will do a 'deep clean' on your Flash Memory. **You will lose all the files on the board** , but at least you'll be able to revive it! After loading this UF2, follow the steps above to re-install CircuitPython.

[Download flash erasing "nuke" UF2](https://cdn-learn.adafruit.com/assets/assets/000/101/659/original/flash_nuke.uf2?1618945856)
# IBM PC Keyboard to USB HID with CircuitPython

## Coding the Keyboard

## Text Editor

Adafruit recommends using the **Mu** editor for editing your CircuitPython code. You can get more info in [this guide](https://learn.adafruit.com/welcome-to-circuitpython/installing-mu-editor).

Alternatively, you can use any text editor that saves simple text files.

## Download the Project Bundle

Your project will use a specific set of CircuitPython libraries and the&nbsp; **code.py** &nbsp;file. To get everything you need, click on the&nbsp; **Download Project Bundle** &nbsp;link below, and uncompress the .zip file.

Connect your computer to the QT Py via a known good data+power USB-C cable. The board should show up in your File Explorer/Finder (depending on operating system) as a drive named **CIRCUITPY**.

Drag the contents of the uncompressed bundle directory onto your board's **CIRCUITPY** &nbsp;drive, replacing any existing files or directories with the same names, and adding any new ones that are necessary.

Down below the full source code listing, skip to "[how it works](https://learn.adafruit.com/ibm-pc-keyboard-to-usb-hid-with-circuitpython?view=all#how-it-works-3130672)" to get an overview of the major parts of the program.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/CircuitPython_IBM_XT_Keyboard_QTPy_RP2040/code.py

## How it works

### Preliminaries

After the required `import` lines, there is a list of values to convert from IBM keycodes to USB keycodes:

```auto
import array
import board
import rp2pio
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode as K
import adafruit_pioasm

# from https://www.scs.stanford.edu/10wi-cs140/pintos/specs/kbd/scancodes-9.html
# translating from "Set 1" to USB using the adafruit_hid keycode names
# fmt: off
xt_keycodes = [
    None, K.ESCAPE, K.ONE, K.TWO, K.THREE, K.FOUR, K.FIVE, K.SIX, …
]                                                                               
# fmt: on
```

### Configure the PIO peripheral

These lines configure a program within the PIO peripheral. The program waits for the clock input to become false, then copies the data value, and waits for the clock input to become true.

The peripheral is configured to wait for 10 bits of data before making all 10 bits available to CircuitPython in a buffer.

```auto
program = adafruit_pioasm.Program("""                                           
    wait 0 pin 2                                                                
    in pins, 1                                                                  
    wait 1 pin 2                                                                
""",                                                                            
build_debuginfo=True)                                                           
                                                                                
sm = rp2pio.StateMachine(program.assembled,                                     
    first_in_pin = board.MISO,                                                  
    in_pin_count = 3,                                                           
    pull_in_pin_up = 0b111,                                                     
    auto_push=True,                                                             
    push_threshold=10,                                                          
    in_shift_right=True,                                                        
    frequency=8_000_000,                                                        
    **program.pio_kwargs)                                                       
                                                                                
buf = array.array('H', [0])
```

Finally, the program is ready to receive key events from the keyboard and translate them to USB. First, read one value from the PIO peripheral and turn it into a Boolean 'pressed' value and a Tandy key number value.

If the key number is not valid, something has gone wrong. Ignore the value and try again. Otherwise, send a fresh USB HID report with the key pressed or released.

Messages are shown on the serial REPL for each event read from the keyboard.

```auto
print("Ready to type")
kbd = Keyboard(usb_hid.devices)
while True:
    sm.readinto(buf, swap=False)
    val = buf[0]
    pressed = not val &amp; 0x8000
    key_number = (val &gt;&gt; 8) &amp; 0x7f
    if key_number &gt; len(xt_keycodes):
        print(f"invalid key number {key_number} pressed")
        continue
    keycode = xt_keycodes[key_number]
    print(f"{keycode} {'PRESSED' if pressed else 'released'}")
    if keycode is None:
        continue
    if pressed:
        kbd.press(keycode)
    else:
        kbd.release(keycode)
```


## Featured Products

### Adafruit QT Py RP2040

[Adafruit QT Py RP2040](https://www.adafruit.com/product/4900)
What a cutie pie! Or is it... a QT Py?&nbsp;This diminutive dev board comes with one of our new favorite chip, the RP2040. It's been made famous in the new [Raspberry Pi Pico](https://www.adafruit.com/pico) _and_ our [Feather...](http://www.adafruit.com/product/4884)

Out of Stock
[Buy Now](https://www.adafruit.com/product/4900)
[Related Guides to the Product](https://learn.adafruit.com/products/4900/guides)
### Breadboard-friendly MIDI Jack (5-pin DIN)

[Breadboard-friendly MIDI Jack (5-pin DIN)](https://www.adafruit.com/product/1134)
To celebrate the 30th Anniversary of the invention of MIDI we're carrying these handy 5-pin MIDI jacks. They're what you see on the back of nearly every synthesizer and drum machine. These are nice sturdy jacks, and breadboard-friendly, with all the pins on 0.1" spacing for easy...

In Stock
[Buy Now](https://www.adafruit.com/product/1134)
[Related Guides to the Product](https://learn.adafruit.com/products/1134/guides)
### Half Sized Premium Breadboard - 400 Tie Points

[Half Sized Premium Breadboard - 400 Tie Points](https://www.adafruit.com/product/64)
This is a cute, half-size breadboard with&nbsp;400 tie points, good for small projects. It's 3.25" x 2.2" / 8.3cm&nbsp;x 5.5cm&nbsp;with a standard double-strip in the middle and two power rails on both sides.&nbsp;You can pull the power rails off easily to make the breadboard as...

Out of Stock
[Buy Now](https://www.adafruit.com/product/64)
[Related Guides to the Product](https://learn.adafruit.com/products/64/guides)
### Premium Male/Male Jumper Wires - 40 x 6" (150mm)

[Premium Male/Male Jumper Wires - 40 x 6" (150mm)](https://www.adafruit.com/product/758)
Handy for making wire harnesses or jumpering between headers on PCB's. These premium jumper wires are 6" (150mm) long and come in a 'strip' of 40 (4 pieces of each of ten rainbow colors). They have 0.1" male header contacts on either end and fit cleanly next to each other...

Out of Stock
[Buy Now](https://www.adafruit.com/product/758)
[Related Guides to the Product](https://learn.adafruit.com/products/758/guides)
### USB Type A to Type C Cable - approx 1 meter / 3 ft long

[USB Type A to Type C Cable - approx 1 meter / 3 ft long](https://www.adafruit.com/product/4474)
As technology changes and adapts, so does Adafruit. This&nbsp;&nbsp; **USB Type A to Type C** cable will help you with the transition to USB C, even if you're still totin' around a USB Type A hub, computer or laptop.

USB C is the latest industry-standard connector for...

In Stock
[Buy Now](https://www.adafruit.com/product/4474)
[Related Guides to the Product](https://learn.adafruit.com/products/4474/guides)
### Through-Hole Resistors - 4.7K ohm 5% 1/4W - Pack of 25

[Through-Hole Resistors - 4.7K ohm 5% 1/4W - Pack of 25](https://www.adafruit.com/product/2783)
ΩMG! You're not going to be able to resist these handy resistor packs!&nbsp;Well, axially, they&nbsp;do all of the resisting for you!

This is a **25 Pack of 4.7K Ω Resistors.** More specifically, they are **carbon film** , through-hole...

In Stock
[Buy Now](https://www.adafruit.com/product/2783)
[Related Guides to the Product](https://learn.adafruit.com/products/2783/guides)

## Related Guides

- [Adafruit QT Py RP2040](https://learn.adafruit.com/adafruit-qt-py-2040.md)
- [NeoKey Emoji Keyboard](https://learn.adafruit.com/neokey-emoji-keyboard.md)
- [AS5600 Super Smooth Rotary Encoder](https://learn.adafruit.com/as5600-smooth-rotary-encoder.md)
- [MIDI for Makers](https://learn.adafruit.com/midi-for-makers.md)
- [Three Button Foot Switch](https://learn.adafruit.com/three-button-foot-switch.md)
- [Halloween Neon LED Signs](https://learn.adafruit.com/halloween-neon-led-signs.md)
- [How to Choose a Microcontroller](https://learn.adafruit.com/how-to-choose-a-microcontroller.md)
- [USB Rotary Media Dial](https://learn.adafruit.com/usb-rotary-media-dial.md)
- [Toddler Timer](https://learn.adafruit.com/toddler-timer.md)
- [Light Up your Costume with Noods](https://learn.adafruit.com/light-up-your-costume-with-noods.md)
- [An Introduction to RP2040 PIO with CircuitPython](https://learn.adafruit.com/intro-to-rp2040-pio-with-circuitpython.md)
- [MIDI Foot Pedal](https://learn.adafruit.com/midi-foot-pedal.md)
- [NeXT Bus Mouse to USB HID with CircuitPython](https://learn.adafruit.com/next-bus-mouse-to-usb-hid-with-circuitpython.md)
- [Hexpad](https://learn.adafruit.com/hexpad.md)
- [QT Py RP2040 USB to Serial MIDI Friends](https://learn.adafruit.com/qt-py-rp2040-usb-to-serial-midi-friends.md)
- [Adafruit I2S Amplifier BFF](https://learn.adafruit.com/i2s-amplifier-bff.md)
