{"id":15142134,"url":"https://github.com/pimoroni/keybow2040-circuitpython","last_synced_at":"2025-09-29T10:31:35.714Z","repository":{"id":44536858,"uuid":"346485921","full_name":"pimoroni/keybow2040-circuitpython","owner":"pimoroni","description":"CircuitPython library for the Pimoroni Keybow 2040","archived":true,"fork":false,"pushed_at":"2022-02-09T10:14:25.000Z","size":522,"stargazers_count":68,"open_issues_count":0,"forks_count":23,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-09-21T15:02:05.304Z","etag":null,"topics":["circuitpython","hid","keyboard","keyboards","macro-pad","midi","python","usb-hid"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pimoroni.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-03-10T20:39:24.000Z","updated_at":"2024-05-18T05:01:03.000Z","dependencies_parsed_at":"2022-09-01T16:12:37.053Z","dependency_job_id":null,"html_url":"https://github.com/pimoroni/keybow2040-circuitpython","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pimoroni%2Fkeybow2040-circuitpython","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pimoroni%2Fkeybow2040-circuitpython/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pimoroni%2Fkeybow2040-circuitpython/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pimoroni%2Fkeybow2040-circuitpython/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pimoroni","download_url":"https://codeload.github.com/pimoroni/keybow2040-circuitpython/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219874510,"owners_count":16554585,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["circuitpython","hid","keyboard","keyboards","macro-pad","midi","python","usb-hid"],"created_at":"2024-09-26T09:23:25.937Z","updated_at":"2025-09-29T10:31:35.094Z","avatar_url":"https://github.com/pimoroni.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Keybow 2040 CircuitPython\n\n:warning: This library is now part of PMK- see: https://github.com/pimoroni/pmk-circuitpython ⚠️\n\nkeybow2040-circuitpython is preserved here for posterity.\n\n---\n\nThis CircuitPython library is for the RP2040-powered Keybow 2040 from Pimoroni,\na 16-key mini mechanical keyboard with RGB backlit keys. Find out more about \nKeybow 2040 at the link below.\n\n[Learn more about Keybow 2040 at pimoroni.com](https://shop.pimoroni.com/products/keybow-2040)\n\nIt also works on Raspberry Pi Pico mounted in RGB Keypad Base from Pimoroni\na 16-key mini rubber keyboard with RGB backlit keys. Find out more about\nPico RGB Keypad Base at the link below.\n\n[Learn more about Pico RGB Keypad Base at pimoroni.com](https://shop.pimoroni.com/products/pico-rgb-keypad-base)\n\nThe library abstracts away most of the complexity of having to check pin states,\nand interact with the LED driver library, and exposes classes for\nindividual keys and the whole Keybow (a collection of Key instances).\n\n![Keybow 2040 with backlit keys on marble background](keybow-2040-github-1.jpg)\n\n# Getting started quickly!\n\n## Hardware-dependent part\n\n### Keybow 2040\n\nYou'll need to grab the latest version of Adafruit's Keybow 2040-flavoured\nCircuitPython, from the link below.\n\n[Download the Adafruit CircuitPython binary for Keybow 2040](https://circuitpython.org/board/pimoroni_keybow2040/)\n\nUnplug your Keybow 2040's USB-C cable, press and hold the button on the top edge\nof Keybow 2040 while plugging the USB-C cable back into your computer to mount\nit as a drive (it should show up as `RPI-RP2` or something similar).\n\nDrag and drop the `adafruit-circuitpython-pimoroni_keybow2040-en_US-XXXXX.uf2`\nfile that you downloaded onto the drive and it should reboot and load the \nCircuitPython firmware. The drive should now show up as `CIRCUITPY`.\n\nThe Adafruit IS31FL3731 LED driver library for CircuitPython is a prequisite for\nthis Keybow 2040 library, so you'll need to download it from GitHub at the link\nbelow, and then drop the `adafruit_is31fl3731` folder into the `lib` folder on\nyour `CIRCUITPY` drive.\n\n[Download the Adafruit IS31FL3731 CircuitPython library](https://github.com/adafruit/Adafruit_CircuitPython_IS31FL3731)\n\n### Pico RGB Keypad\n\nYou'll need to grab the latest version of Adafruit's Raspberry Pi Pico-flavoured\nCircuitPython, from the link below.\n\n[Download the Adafruit CircuitPython binary for Raspberry Pi Pico](https://circuitpython.org/board/raspberry_pi_pico/)\n\nUnplug your Pi Pico's micro USB cable, press and hold the BOOTSEL button on the top\nof Pi Pico while plugging the micro USB cable back into your computer to mount\nit as a drive (it should show up as `RPI-RP2` or something similar).\n\nDrag and drop the `adafruit-circuitpython-raspberry_pi_pico-en_US-XXXXX.uf2`\nfile that you downloaded onto the drive and it should reboot and load the \nCircuitPython firmware. The drive should now show up as `CIRCUITPY`.\n\nThe Adafruit DotStar LED driver library for CircuitPython is a prequisite for\nthis Keybow 2040 library, so you'll need to download it from GitHub at the link\nbelow, and then drop the `adafruit_dotstar.py` file into the `lib` folder on\nyour `CIRCUITPY` drive.\n\n[Download the Adafruit DotStar CircuitPython library](\u003chttps://github.com/adafruit/Adafruit_CircuitPython_DotStar)\n\n## Hardware-independent part\n\nFinally, drop the `lib` contents (`keybow2040.py` file and `keybow_hardware` folder) from this library into the `lib` folder\non your `CIRCUITPY` drive also, and you're all set!\n\nPick one of the [examples](examples) (I'd suggest the \n[reactive.press.py](examples/reactive-press.py) example to begin), copy the \ncode, and save it in the `code.py` file on your `CIRCUITPY` drive using your \nfavourite text editor. As soon as you save the `code.py` file, or make any other\nchanges, then it should load up and run the code!\n\nExamples are by default using Keybow 2040 hardware, if you want to run them\non Pico RGB Keypad, you need to change the hardware. Comment out the line:\n```\nfrom keybow_hardware.pim56x import PIM56X as Hardware\n```\nand uncomment the line:\n```\nfrom keybow_hardware.pim551 import PIM551 as Hardware\n```\n\n## Index\n\n* [Library functionality](#library-functionality)\n  * [Imports and setup](#imports-and-setup)\n  * [The Keybow class](#the-keybow-class)\n  * [An interlude on timing!](#an-interlude-on-timing)\n  * [Key presses](#key-presses)\n  * [LEDs!](#leds)\n  * [LED sleep](#led-sleep)\n  * [Attaching functions to keys with decorators](#attaching-functions-to-keys-with-decorators)\n  * [Key combos](#key-combos)\n* [USB HID](#usb-hid)\n  * [Setup](#setup)\n  * [Sending key presses](#sending-key-presses)\n  * [Sending strings of text](#sending-strings-of-text)\n* [USB MIDI](#usb-midi)\n  * [Setup](#setup-1)\n  * [Sending MIDI notes](#sending-midi-notes)\n\n# Library functionality\n\nThis section covers most of the functionality of the library itself, without\ndelving into additional functions like USB MIDI or HID (they're both covered\nlater!)\n\n## Imports and setup\n\nAll of your programs will need to start with the following:\n\n```\nfrom keybow_hardware.pim56x import PIM56X as Hardware\nfrom keybow2040 import Keybow2040\n\nhardware = Hardware()\nkeybow = Keybow2040(hardware)\n```\n\nFirst, this imports a hardware object representing the board. A hardware object\nhides technical details on how keys and LEDs are connected and exposes them\nvia uniform interface. You need to choose the correct hardware object for\nyour hardware. If you're curious, hardware differences are explained below,\nbut all you need to know is that for Keybow 2040 you need an import:\n```\nfrom keybow_hardware.pim56x import PIM56X as Hardware\n```\nand for Pico RGB Keypad Base:\n```\nfrom keybow_hardware.pim551 import PIM551 as Hardware\n```\n\nOn Keybow 2040 (`PIM56X`) keys are read directly via GPIO, and LEDs are set\nvia IS31FL3731 LED driver connected over I2C bus.\n\nOn Pico RGB Keypad Base (`PIM551`) keys are connected via TCA9555 GPIO extender\nconnected over I2C bus and LEDs are DotStar LEDs connected via SPI bus.\n\nSince both boards use I2C bus, hardware object also exposes it in case you\nneed to access it (Keybow 2040 has even I2C connecting pads exposed):\ni2c = hardware.i2c()\n\nIn the rest of this file examples of the code will use `PIM56X` hardware object.\nIf you're running them on Pico RGB Keypad Base, don't forget to change it accordingly.\n\nThe `Keybow2040()` class, imported from the `keybow2040` module, is instantiated\nand passed the hardware object. Instantiating this sets up all of the pins, keys,\nand LEDs, and provides access to all of the attributes and methods associated \nwith it.\n\n## The Keybow class\n\nThe Keybow class exposes a number of handy attributes and methods. The main one\nyou'll be interested in is the `.keys` attribute, which is a list of `Key` \nclass instances, one for each key.\n\n```\nkeys = keybow.keys\n```\n\nThe indices of the keys in that list correspond to their position on the keypad,\nstaring from the bottom left corner (when the USB connector is at the top), \nwhich is key 0, going upwards in columns, and ending at the top right corner,\nwhich is key 15.\n\nMore about the `Key` class later...\n\nA **super** important method of the `Keybow` class is `.update()` method. It \nupdates all of the keys, key states, and other attributes like the time of the\nlast key press, and sleep state of the LEDs.\n\n**You need to call this method on your `Keybow` class at the very start of each\niteration of your program's main loop, as follows:**\n\n```\nwhile True:\n    keybow.update()\n```\n\n## An interlude on timing!\n\nAnother **super** important thing is **not to include any `time.sleep()`s in \nyour main loop!** Doing so will ruin the latency and mean that you'll miss key\npress events. Just don't do it.\n\nIf you need introduce timed events, then you have to go about it in a slightly\n(!!) roundabout fashion, by using `time.monotonic()` a constantly incremented\ncount of seconds elapsed, and use it to check the time elapsed since your last\nevent, for example you could do this inside your `while True` loop:\n\n```\ntime_interval = 10\n\n# An event just happened!\n\ntime_last_fired = time.monotonic()\ntime_elapsed = 0\n\n# ... some iterations later\n\ntime_elapsed = time.monotonic() - time_last_fired\n\nif time_elapsed \u003e time_interval:\n    # Fire your event again!\n```\n\nThere's a handy `keybow.time_of_last_press` attribute that allows you to quickly\ncheck if a certain amount of time has elapsed since any key press, and that\nattribute gets updated every time `keybow.update()` is called.\n\n## Key presses\n\nThere are a few ways that you can go about detecting key presses, some\nglobal methods on the `Keybow` class instance, and some on the `Key` class\ninstances themselves.\n\n### Keybow class methods for detecting presses and key states\n\n`keybow.get_states()` will return a list of the state of all of the keys, in \norder, with a state of `0` being not pressed, and `1` being pressed. You can\nthen loop through that list to do whatever you like.\n\n`keybow.get_pressed()` will return a list of the key numbers (indices in the \nlist of keys) that are currently pressed. If you only care about key presses,\nthen this is an efficient way to do things, especially since you have all the\nkey numbers in a list.\n\n`keybow.any_pressed()` returns a Boolean (`True`/`False`) that tells you whether\nany keys are currently being pressed. Handy if you want to attach a behaviour to\nall of the keys, which this is effectively a proxy for.\n\n`keybow.none_pressed()` is similar to `.any_pressed()`, in that it returns a \nBoolean also, but... you guessed it, it returns `True` if no keys are being\npressed, and `False` if any keys are pressed.\n\n### Key class methods for detecting key presses\n\nIf we want to check whether key 0 is pressed, we can do so as follows:\n\n```\nkeys = keybow.keys()\n\nwhile True:\n    keybow.update()\n\n    if keys[0].pressed:\n        # Do something!\n```\n\nThe `.pressed` attribute returns a Boolean that is `True` if the key is pressed\nand `False` if it is not pressed.\n\n`key.state` is another way to check the state of a key. It will equal `1` if the\nkey is pressed and `0` if it is not pressed.\n\nIf you want to attach an additional behaviour to your key, you can use \n`key.held` to check if a key is being key rather than being pressed and released\nquickly. It returns `True` if the key is held and `False` if it is not.\n\nThe default hold time (after which `key.held` is `True`) for all of the keys is\n0.75 seconds, but you can change `key.hold_time` to adjust this to your liking, \non a per key basis.\n\nThis means that we could extend the example above to be:\n\n```\nkeys = keybow.keys()\n\nwhile True:\n    keybow.update()\n\n    if keys[0].pressed:\n        # Do something!\n\n    if keys[0].held:\n        # Do something else!\n```\n\nThe [reactive-press.py example](examples/reactive-press.py) shows in more detail\nhow to handle key presses.\n\n## LEDs!\n\nLEDs can be set either globally for all keys, using the `Keybow` class instance,\nor on a per-key basis, either through the `Keybow` class, or using a `Key` class\ninstance.\n\nTo set all of the keys to the same colour, you can use the `.set_all()` method\nof the `Keybow` class, to which you pass three 0-255 integers for red, green, \nand blue. For example, to set all of the keys to magenta:\n\n```\nkeybow.set_all(255, 0, 255)\n```\n\nTo set an individal key through your `Keybow` class instance, you can do as\nfollows, to set key 0 to white:\n\n```\nkeybow.set_led(0, 255, 255, 255)\n```\n\nTo set the colour on the key itself, you could do as follows, again to set key\n0 to white:\n\n```\nkeybow.keys[0].set_led(255, 255, 255)\n```\n\nA key retains its RGB value, even if it is turned off, so once a key has its \ncolour set with `key.rgb = (255, 0, 0)` for example, you can turn it off using\n`key.led_off()` or even `key.set_led(0, 0, 0)` and then when you turn it back on\nwith `key.led_on()`, then it will still be red when it comes back on.\n\nAs a convenience, and to avoid having to check `key.lit`, there is a \n`key.toggle_led()` method that will toggle the current state of the key's LED \n(on to off, and _vice versa_).\n\nThere's a handy `hsv_to_rgb()` function that can be imported from the \n`keybow2040` module to convert an HSV colour (a tuple of floats from 0.0 to 1.0)\nto an RGB colour (a tuple of integers from 0 to 255), as follows:\n\n```\nfrom keybow2040 import hsv_to_rgb\n\nh = 0.5  # Hue\ns = 1.0  # Saturation\nv = 1.0  # Value\n\nr, g, b = hsv_to_rgb(h, s, v)\n```\n\nThe [rainbow.py example](examples/rainbow.py) shows a more complex example of\nhow to animate the keys' LEDs, including the use of the `hsv_to_rgb()` function.\n\n## LED sleep\n\nThe `Keybow` class has an `.led_sleep_enabled` attribute that is disabled (set to\n`False`) by default, and an `.led_sleep_time` attribute (set to 60 seconds by\ndefault) that determines how many seconds need to elapse before LED sleep is \ntriggered and the LEDs turn off.\n \nThe time elapsed since the last key press is constantly updated when \n`keybow.update()` is called in your main loop, and if the `.led_sleep_time` is\nexceeded then LED sleep is triggered.\n\nBecause keys retain their RGB values when toggled off, when asleep, a tap on any\nkey will wake all of the LEDs up at their last state before sleep.\n\nEnabling LED sleep with a sleep time of 10 seconds could be done as simply as:\n\n```\nkeybow.led_sleep_enabled = True\nkeybow.led_sleep_time = 10\n```\n\nThere's also a `.sleeping` attribute that returns a Boolean, that you can check \nto see whether the LEDs are sleeping or not.\n\n## Attaching functions to keys with decorators\n\nThere are three decorators that can be attached to functions to link that \nfunction to, i) a key press, ii) a key release, or iii) a key hold.\n\nHere's an example of how you could attach a decorator to a function that lights\nup that key yellow when it is pressed, turns all of the LEDs on when held, and \nturns them all off when released:\n\n```\nfrom keybow_hardware.pim56x import PIM56X as Hardware\nfrom keybow2040 import Keybow2040\n\nkeybow = Keybow2040(Hardware())\nkeys = keybow.keys\n\nkey = keys[0]\nrgb = (255, 255, 0)\nkey.rgb = rgb\n\n@keybow.on_press(key)\ndef press_handler(key):\n    key.led_on()\n\n@keybow.on_release(key)\ndef release_handler(key):\n    keybow.set_all(0, 0, 0)\n\n@keybow.on_hold(key)\ndef hold_handler(key):\n    keybow.set_all(*rgb)\n\nwhile True:\n    keybow.update()\n```\n\nThe [decorators.py example](examples/decorators.py) has another example of how\nto use the `.on_hold()` decorator to toggle LEDs on and off when a key is held.\n\n## Key combos\n\nKey combos can provide a way to add additional behaviours to keys that only get\ntriggered if a combination of keys is pressed. The best way to achieve this is\nusing the `.held` attribute of a key, meaning that the key can also have a \n`.pressed` behaviour too.\n\nHere's a brief example of how you could do this inside your main loop, with key\n0 as the modifier key, and key 1 as the action key:\n\n```\nkeys = keybow.keys\n\nmodifier_key = keys[0]\naction_key = keys[1]\n\nwhile True:\n    keybow.update()\n\n    if modifier_key.held and action_key.pressed:\n        # Do something!\n```\n\nOf course, you could chain these together, to require two modifer keys to be\nheld and a third to be pressed, and so on...\n\nThe [colour-picker.py example](examples/colour-picker.py) has an example of\nusing a modifier key to change the hue of the keys.\n\n# USB HID\n\nThis covers setting up a USB HID keyboard and linking physical key presses to \nkeyboard key presses on a connected computer.\n\n## Setup\n\nUSB HID requires the `adafruit_hid` CircuitPython library. Download it from the\nlink below and drop the `adafruit_hid` folder into the `lib` folder on your \n`CIRCUITPY` drive.\n\n[Download the Adafruit HID CircuitPython library](https://github.com/adafruit/Adafruit_CircuitPython_HID)\n\nYou'll need to connect your Keybow to a computer using a USB cable, just like\nyou would with a regular USB keyboard.\n\n## Sending key presses\n\nHere's an example of setting up a keyboard object and sending a `0` key press\nwhen key 0 is pressed, using an `.on_press()` decorator:\n\n```\nfrom keybow_hardware.pim56x import PIM56X as Hardware\nfrom keybow2040 import Keybow2040\n\nimport usb_hid\nfrom adafruit_hid.keyboard import Keyboard\nfrom adafruit_hid.keyboard_layout_us import KeyboardLayoutUS\nfrom adafruit_hid.keycode import Keycode\n\nkeybow = Keybow2040(Hardware())\nkeys = keybow.keys\n\nkeyboard = Keyboard(usb_hid.devices)\nlayout = KeyboardLayoutUS(keyboard)\n\nkey = keys[0]\n\n@keybow.on_press(key)\ndef press_handler(key):\n    keyboard.send(Keycode.ZERO)\n\nwhile True:\n    keybow.update()\n```\n\nYou can find a list of all of the keycodes available at the\n[HID CircuitPython library documentation here](https://circuitpython.readthedocs.io/projects/hid/en/latest/api.html#adafruit-hid-keycode-keycode).\n\nIf you wanted to take this a bit further and make a full keymap for your\nkeyboard, then you could create a list of 16 different keycodes and then use the\nnumber of the key press registered by the `press_handler` function as an index\ninto your keymap to get the keycode to send for each key.\n\n```\nfrom keybow_hardware.pim56x import PIM56X as Hardware\nfrom keybow2040 import Keybow2040\n\nimport usb_hid\nfrom adafruit_hid.keyboard import Keyboard\nfrom adafruit_hid.keyboard_layout_us import KeyboardLayoutUS\nfrom adafruit_hid.keycode import Keycode\n\nkeybow = Keybow2040(Hardware())\nkeys = keybow.keys\n\nkeyboard = Keyboard(usb_hid.devices)\nlayout = KeyboardLayoutUS(keyboard)\n\nkeymap =    [Keycode.ZERO,\n             Keycode.ONE,\n             Keycode.TWO,\n             Keycode.THREE,\n             Keycode.FOUR,\n             Keycode.FIVE,\n             Keycode.SIX,\n             Keycode.SEVEN,\n             Keycode.EIGHT,\n             Keycode.NINE,\n             Keycode.A,\n             Keycode.B,\n             Keycode.C,\n             Keycode.D,\n             Keycode.E,\n             Keycode.F]\n\nfor key in keys:\n    @keybow.on_press(key)\n    def press_handler(key):\n        keycode = keymap[key.number]\n        keyboard.send(keycode)\n\nwhile True:\n    keybow.update()\n```\n\nThis code is available in the \n[hid-keys-simple.py example](examples/hid-keys-simple.py).\n\nAs well as sending a single keypress, you can send multiple keypresses at once,\nsimply by adding them as additional argumemnts to `keyboard.send()`, e.g. \n`keyboard.send(Keycode.A, Keycode.B)` and so on.\n\n## Sending strings of text\n\nRather than the incovenience of sending multiple keycodes using\n`keyboard.send()`, there's a different method to send whole strings of text at\nonce, using the `layout` object we created.\n\n```\nfrom keybow_hardware.pim56x import PIM56X as Hardware\nfrom keybow2040 import Keybow2040\n\nimport usb_hid\nfrom adafruit_hid.keyboard import Keyboard\nfrom adafruit_hid.keyboard_layout_us import KeyboardLayoutUS\nfrom adafruit_hid.keycode import Keycode\n\nkeybow = Keybow2040(Hardware())\nkeys = keybow.keys\n\nkeyboard = Keyboard(usb_hid.devices)\nlayout = KeyboardLayoutUS(keyboard)\n\nkey = keys[0]\n\n@keybow.on_press(key)\ndef press_handler(key):\n    layout.write(\"Pack my box with five dozen liquor jugs.\")\n\nwhile True:\n    keybow.update()\n```\n\nA press of key 0 will send that whole string of text at once!\n\nBe aware that strings sent like that take a little while to  virtually \"type\",\nso you might want to incorporate a delay using  `keybow.time_of_last_press`, \nand then check against a `time_elapsed` variable created with \n`time_elapsed = time.monotonic() - keybow.time_of_last_press`.\n\nAlso, be aware that the Adafruit HID CircuitPython library only currently \nsupports US Keyboard layouts, so you'll have to work around that and map any\nkeycodes that differ from their US counterpart to whatever your is.\n\n# USB MIDI\n\nThis covers basic MIDI note messages and how to link them to key presses.\n\n## Setup\n\nUSB MIDI requires the `adafruit_midi` CircuitPython library. Download it from\nthe link below and then drop the `adafruit_midi` folder into the `lib` folder on\nyour `CIRCUITPY` drive.\n\n[Download the Adafruit MIDI CircuitPython library](https://github.com/adafruit/Adafruit_CircuitPython_MIDI)\n\nYou'll need to connect your Keybow 2040 with a USB cable to a computer running a\nsoftware synth or DAW like Ableton Live, to a hardware synth that accepts USB\nMIDI, or through a MIDI interface that will convert the USB MIDI messages to\nregular serial MIDI through a DIN connector.\n\nUsing USB MIDI, Keybow 2040 shows up as a device with the name \n`Keybow 2040 (CircuitPython usb midi.ports[1])`\n\nIn my testing, Keybow 2040 works with the Teenage Engineering OP-Z quite nicely.\n\n## Sending MIDI notes\n\nHere's a complete, minimal example of how to send a single MIDI note (middle C,\nor MIDI note number 60) when key 0 is pressed, sending a note on message when\npressed and a note off message when released.\n\n```\nfrom keybow_hardware.pim56x import PIM56X as Hardware\nfrom keybow2040 import Keybow2040\n\nimport usb_midi\nimport adafruit_midi\nfrom adafruit_midi.note_off import NoteOff\nfrom adafruit_midi.note_on import NoteOn\n\nkeybow = Keybow2040(Hardware())\nkeys = keybow.keys\n\nmidi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0)\n\nkey = keys[0]\nnote = 60\nvelocity = 127\n\nwas_pressed = False\n\nwhile True:\n    keybow.update()\n\n    if key.pressed:\n        midi.send(NoteOn(note, velocity))\n        was_pressed = True\n    elif not key.pressed and was_pressed:\n        midi.send(NoteOff(note, 0))\n        was_pressed = False\n```\n\nThere'a more complete example of how to set up all of Keybow's keys with \nassociated MIDI notes using decorators in the \n[midi-keys.py example](examples/midi-keys.py).\n\nThe example above, and the `midi-keys.py` example both send notes on MIDI\nchannel 0 (all channels), but you can set this to a specific channel, if you\nlike, by changing `out_channel=` when you instantiate your `midi` object.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpimoroni%2Fkeybow2040-circuitpython","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpimoroni%2Fkeybow2040-circuitpython","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpimoroni%2Fkeybow2040-circuitpython/lists"}