Gemma M0

The Gemma M0 is just right for this project for a few reasons. It's inexpensive, has three digital input pins to read the two flippers and the plunger, it can output HID USB keyboard strokes over USB, and it draws very little power, so the iPad and iPhone won't mind powering it! Plus, it has an RGB DotStar LED that can be used as a multi-color indicator!

Coding Options

The Gemma M0 can currently be coded with either the Arduino IDE or CircuitPython. For this project, we'll use CircuitPython. It's very simple to set up -- all you need to do is plug it into your computer via USB and it shows up as a USB thumb drive. Then, you can write code in any text editor and save the file to the Gemma M0. That's all it takes, it immediately runs the code. Talk about fast iteration!

iCade Standard

Since the iPad doesn't have a game controller port on it like a video game console, game developers have come up with a few clever ways to interface physical controls with the device. One such standard is the iCade protocol, made by ION Audio for their line of iPad desktop arcade cabinets and controllers. 

The iCade standard interfaces with iOS as an HID keyboard, and is typically paired over Bluetooth. However, plugging in a wired HID keyboard works just as well, which makes it very straightforward to build your own controller!

The Gemma M0 is going to act as a USB HID keyboard so that it can "type" the keystrokes that the iCade standard uses.

Here's what the button mapping looks like for the standard iCade:

Keyboard Mapping

The first test I performed was to figure out the key mappings needed. I did so by pairing a Bluetooth keyboard to the iPad, launching Pinball Arcade, and setting the controller type to iCade in the game's settings. Then, I typed the key pairs seen in the diagram above until I knew which key combos did what.

The mapping for pinball looks like this:

Test Code

The next test I did was to have the Gemma M0 press the flipper buttons by telling it to "type" the "LVHR" combo over and over. Here's how you can make the Gemma M0 type the keystrokes.

First, make sure you've followed the basic guide to setting up the Gemma M0. Once you've successfully run the basic Blinky code example on your Gemma M0, move on to the next step.

If nothing happens when you plug in your Gemma M0, make sure the built-in ON/OFF switch is turned on!

The Gemma M0 ships with all of the libraries we'll need (HID library and DotStar library) already installed on the board, so we can immediately start coding this example!

With the Gemma M0 plugged into your computer, open the CIRCUTPY drive that shows up, and edit the code.py (or main.py, either will work) file in your text editor.

Copy and paste the code below into your text editor and then save the file, overwriting the existing code.py file on the root of the Gemma M0. Be careful where your cursor is when you save, since the program will cause the Gemma M0 to start spewing the letters "lhvr" as if you were banging on your keyboard!

If you want to test it out on your pinball game, go ahead and plug the Gemma M0 into the iPad using the USB to Lightning adapter. The iPad will autodetect the device and open a camera import window. You can ignore this and switch to your iCade compatible pinball game, such as Pinball Arcade. When you start a new game, it will immediately begin flipping the flippers for you automatically! Launch a ball onto the field with the on-screen plunger and watch it play by itself for a bit!

Button Code

Now it's time to rewrite the code so that the two flippers and the plunger aren't haunted by the ghost of Tommy, but instead, respond to your arcade buttons.

Download the Project Bundle

Your project will use a specific set of CircuitPython libraries and the code.py file. In order to get the libraries you need, click on the Download Project Bundle link below, and uncompress the .zip file.

Drag the contents of the uncompressed bundle directory onto your Gemma M0 board's CIRCUITPY drive, with the exception of the adafruit_hid library (see next section on that) replacing any existing files or directories with the same names, and adding any new ones that are necessary.

Library Pruning

You may run out of space on the Gemma M0 if you copy the entire adafruit_hid library over -- but you only need a few files, as shown on the left.

Delete unnecessary files from that folder before copying it over to the Gemma M0.

# SPDX-FileCopyrightText: 2022 john park & tod kurt for Adafruit Industries
# SPDX-License-Identifier: MIT
# Gemma IO demo - Keyboard emu
# iCade Pinball Edition

import board
from digitalio import DigitalInOut, Pull
from adafruit_debouncer import Debouncer
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode

# Allows three buttons on a Gemma M0 to control iCade standard Pinball Arcade
# game on iOS using USB to Lightning "camera connector"

# iCade keyboard mappings
# See developer doc at: http://www.ionaudio.com/products/details/icade

#    WE     YT UF IM OG
# AQ< -->DC
#    XZ     HR JN KP LV

#control key is triggered by a press, doesn't repeat, second control key is
#triggered by a release

# define buttons
num_keys = 3
pins = (
    board.D0, # D0
    board.D1, # D1
    board.D2 # D2
)

keys = []

# The keycode pair sent for each button:
# D0 is left flipper -  iCade key sequence (hold, release) is "hr"
# D1 is right flipper - iCade key sequence (hold, release) is "lv"
# D2 is plunger -       iCade key sequence (hold, release) is "xz"

for pin in pins:
    tmp_pin = DigitalInOut(pin)
    tmp_pin.pull = Pull.UP
    keys.append(Debouncer(tmp_pin))

keymap_pressed = {
    (0): ("Left Paddle", [Keycode.H]),
    (1): ("Right Paddle", [Keycode.L]),
    (2): ("Plunger", [Keycode.X])
}
keymap_released = {
    (0): ("Left Paddle", [Keycode.R]),
    (1): ("Right Paddle", [Keycode.V]),
    (2): ("Plunger", [Keycode.Z])
}

# the keyboard object
kbd = Keyboard(usb_hid.devices)

print("\nWelcome to keypad")
print("keymap:")
for k in range(num_keys):
    print("\t", (keymap_pressed[k][0]))
print("Waiting for button presses")


while True:
    for i in range(num_keys):
        keys[i].update()
        if keys[i].fell:
            print(keymap_pressed[i][0])
            kbd.send(*keymap_pressed[i][1])
        if keys[i].rose:
            print(keymap_released[i][0])
            kbd.send(*keymap_released[i][1])

Next, we'll connect the arcade buttons to the Gemma M0.

This guide was first published on Sep 05, 2017. It was last updated on Mar 28, 2024.

This page (Code the Gemma M0 with CircuitPython) was last updated on Mar 28, 2024.

Text editor powered by tinymce.