Category: Arduino

  • Retro-Joystick to USB Keyboard Adapter

    Retro-Joystick to USB Keyboard Adapter

    Project Introduction

    I made an adapter to take classic video game controllers for Sega, Atari (2600, 7800) and Commodore (VIC20, C64, C128) and adapt them to work as a 5 button USB keyboard (4 directions and a fire/trigger key).

    The joystick now works like an external numeric keypad. Pressing the fire/trigger button will act like pressing the right-hand Control/Ctrl key. This was all written in the Arduino IDE, and can be easily changed (if for example you prefer using W,A,S,D for direction and space bar to fire, or the arrow keys for movement, it’s a quick change to make.

    I picked this configuration mostly because the C64 emulator VICE has it as an option.

    Hardware

    For the brains I used a board with an Atmel ATMega32U4, similar to the Arduino Pro Micro (see parts list below).

    As an enclosure I used a DE-9 adapter housing. This one had terminal blocks for quickly throwing cables together, which I removed to make room.

    I chose this particular DE-9 adapter because it looked like it might be roomy enough to put the microcontroller into, however it wasn’t quite long enough. So, with some heavy duty cutters I use for chomping up bits of PCB I cut the last two pin holes off the microcontroller board.

    After cutting I sanded the edge and visually inspected to try to make sure no obvious unexpected connections between layers were being made. Cutting this down gave me enough room to fit it into the enclosure. I’ve shown this modification in the microcontroller PCB image below as a red line through the PCB.

    I soldered up some wires between the DE-9 PCB and the microcontroller PCB. The silk screen of the DE-9 PCB made it easy to quickly identify which pins were which. The I/O pins I chose on the microcontroller were really just a matter of convenience to get the cables to cooperate nicely with the DE-9 PCB and leave enough room to close the adapter enclosure. I didn’t annotate it very well in the image below, but the DE-9 ground (GND) pin should be wired to any one of the available microcontroller GND pins.

    To prevent accidental electrical connections between microcontroller PCB and the DE-9 PCB I wrapped the microcontroller in some Kapton tape, but hot glue or something would probably work fine. The end result looks like a big mess, thankfully I don’t have to look at it with the enclosure closed.

    I ended up needing to use a hobby knife to carve out a little of the cable opening of the DE-9 enclosure to widen it for the USB micro connector end.

    Software

    To find the numeric keypad directional key values I referenced the USB HID Usage Tables found here:
    http://www.usb.org/developers/hidpage/Hut1_12v2.pdf

    A quirk of the Arduino USB keyboard report handling requires you to add 136 (decimal) to the value you find in the usage table. This value is later removed by other code in the USB library, but helps the source make some kind of determination on how to handle the key event (it seems pretty crazy to me too, but whatever, it works).

    Because the board I’m using has an ATmega32U4 with an Arduino bootloader it shares enough in common with the Arduino Leonardo that “Arduino Leonardo” should be selected as the target board in the Arduino IDE to properly compile and flash the software.

    download joykey.ino

    // Some more meaningful names for referencing applicable I/O pins
    #define FIRE_PIN  2
    #define UP_PIN  3
    #define DOWN_PIN  16
    #define RIGHT_PIN  15
    #define LEFT_PIN  14

    // Arduino-friendly (value+136) keyboard values of direction keys of numeric keypad
    #define KEY_PAD_UP 232
    #define KEY_PAD_DOWN 226
    #define KEY_PAD_RIGHT 230
    #define KEY_PAD_LEFT 228

    // Map a keyboard value to the input pin. First Pins[] value is first Keys[] value, etc
    byte Pins[] = {FIRE_PIN,        UP_PIN,     DOWN_PIN,     RIGHT_PIN,     LEFT_PIN    };
    byte Keys[] = {KEY_RIGHT_CTRL,  KEY_PAD_UP, KEY_PAD_DOWN, KEY_PAD_RIGHT, KEY_PAD_LEFT};

    void setup() {
      // pullup all button input pins to high
      for (int i = 0; i <= sizeof(Pins); i++) {
        pinMode(Pins[i], INPUT_PULLUP);
      }
     
      Keyboard.begin();
    }

    void loop() {

      {
        int keyStates = 0;
        for (int i = 0; i <= sizeof(Pins); i++) {
          if (digitalRead(Pins[i]) == LOW) {
            Keyboard.press(Keys[i]);
            keyStates = 1;
          }
        }
        if (keyStates == 1) {
          // pause a moment to give any pressed keys a chance to report,
          // and possibly ignore some joystick bounce as well
          delay(10);
        }
      }

      // Release any keys that need to be released
      for (int i = 0; i <= sizeof(Pins); i++) {
        if (digitalRead(Pins[i]) == HIGH) {
          Keyboard.release(Keys[i]);
        }
      }

      // If no buttons are being pressed, releaseAll() to ensure no keys are stuck down
      {
        int pinStates = 1;
        for (int i = 0; i <= sizeof(Pins); i++) {
          pinStates = pinStates & digitalRead(Pins[i]);
        }
          if (pinStates == HIGH) {
            Keyboard.releaseAll();
          }
      }

    }

    Here is a description of what the code is doing.

    #defines
    The source defines some names for the pins that connect to the corresponding DE-9 pins (see the “Hardware” notes above for clarification) and some names for the keypad values. The right-hand Ctrl key value(KEY_RIGHT_CTRL) is already defined in the Arduino USB library.

    setup()
    The connected pins are internally pulled high, and are considered activated when connected to ground through the normal use of the joystick. The pins to be monitored for these state changes are put into the Pins[] array and the corresponding key values to send in the USB report are put in the Keys[] array at the respective array index position.
    The function setup() gets ran at device power-up and loops through all the pins listed in Pins[] to configure the internal pullup to high state. Keyboard.begin() prepares the Arduino keyboard library.

    loop()
    The main loop() iterates through each pin in Pins[] looking for a low state and applicably sets the corresponding key. If any keys are found to be pressed a ~10ms pause occurs to allow the operating system ample time to see the USB report, but not offer too much delay as to cause the joystick to feel unresponsive to rapid changes. This delay also helps remove sporadic changes in pin state due to imperfect contact between the joystick internal connection plates. Another iteration through the Pins[] array occurs to remove the appropriate data from the USB report for any keys no longer being held down. A final (possibly unnecessary) check occurs which calls Keyboard.releaseAll() when no positions of the joystick are active (low). I had some issues during initial develop with keys getting stuck “down” and found it very irritating to have to physically unplug the adapter in order to resolve the problem. This check was added to make life a little easier, but may no longer be required. Keyboard.releaseAll() only removes reports from memory on the microcontroller and won’t cause the USB host to ignore held keys on other connected keyboards, so it seems harmless enough to leave in place.

    Bill of Materials

    Arduino Pro Micro-like board
    https://www.amazon.com/gp/product/B012FOV17O

    “DB9” / DE-9 9 pin adapter module (terminal blocks will need to be removed)
    https://www.amazon.com/gp/product/B00YM3W7OI

    Micro USB cable
    https://www.amazon.com/gp/product/B003YKX6WM

    Resources

    USB HID Usage Tables
    http://www.usb.org/developers/hidpage/Hut1_12v2.pdf

    -Have fun!

  • Arduino Leonardo (ATMega32u4) USB Power Button

    Arduino Leonardo (ATMega32u4) USB Power Button

    Project Introduction

    For fun I made this USB power button with an Arduino Micro/Leonardo-like board (by which I mean one using the Atmel ATMega32u4 MCU with USB controller).

    The project details are below in case you should want to read or watch how I did it.

    I used an ATMega32U “arduino”-like board from Amazon.com although there is an official Arduino Micro that should work just as well. For development/testing I used the Arduino Leonardo. Any of those could work in a similar project, the only notable difference being size and available I/O pins.

    I put mine inside of an “Emergency Stop” button. I decided to get a red USB cable too because hey, why not?

    USB HID control of desktop power management features is possible using the Generic Desktop / System Control Collection (Usage Page: 0x01Generic Desktop Page“, Usage Id: 0x80System Control“) with the Usage 0x81System Power Down“. For more details consult the USB HID Usage Tables.

    Software

    Initially I planned to use the Arduino USB Keyboard functions, but I came to realize the USB HID keyboard device doesn’t contain the power management buttons. Similarly media keys such as Play/Pause, Next, Previous, Volume Up/Down, are also not handled in the same way as other keyboard input. Most examples of using the Arduino Leonardo to send media key events to the computer reference some work by Stefan Jones. Following his blog post I updated my Arduino libraries to include his modifications. With his changes and a couple of small tweaks, I was able to send a system power down request over USB.

    I used the Arduino IDE version 1.6.5, I think the USB libraries were changed a lot in 1.6.6, so if you are using a later version things may require substantial adjustments.

    I left Stefan’s additions to the file hardware/arduino/avr/cores/arduino/USBAPI.h unchanged.

    As shown below, inside of the file hardware/arduino/avr/cores/arduino/HID.cpp I commented out the “Consumer Devices” (0x05, 0x0c) value and the “Consumer Control” (0x09, 0x01) value and added the “Desktop” (0x05, 0x01) and “System Control” (0x09, 0x80) values. Next I commented out the “Mute” value (0x09, 0xe2) and added the “System Power Down” value (0x09, 0x81), hijacking the Remote.mute() function to work as my “System Power Down” function.

    //—————————————————————————–

        /* Cross-platform support for controls found on IR Remotes */

        //0x05, 0x0c,                    // Usage Page (Consumer Devices)
        0x05, 0x01,                    //   Usage Page (Desktop)
        //0x09, 0x01,                    // Usage (Consumer Control)
        0x09, 0x80,                    //   Usage (System Control)
        0xa1, 0x01,                    //   Collection (Application)
        0x85, 0x04,                    //   REPORT_ID (4)
        0x15, 0x00,                    //   Logical Minimum (0)
        0x25, 0x01,                    //   Logical Maximum (1)

        0x09, 0xe9,                    //   Usage (Volume Up)
        0x09, 0xea,                    //   Usage (Volume Down)
        0x75, 0x01,                    //   Report Size (1)
        0x95, 0x02,                    //   Report Count (2)
        0x81, 0x06,                    //   Input (Data, Variable, Relative)

        //0x09, 0xe2,                    // Usage (Mute)
        0x09, 0x81,                    //   Usage (System Power Down)
        0x95, 0x01,                    //   Report Count (1)
        0x81, 0x06,                    //   Input (Data, Variable, Relative)

    The Arduino sketch is taken from Stefan’s example for using Remote.mute, (which I’ve re-purposed above). I’ve added a couple of extra lines to wait for the button press before sending the shutdown report to the USB host.

    void setup() {
      pinMode(2, INPUT_PULLUP);
    }

    void loop() {
      while(digitalRead(2) == HIGH) {
        // nop nap
        delay(1);
      }

      Remote.mute ();
      Remote.clear();

      delay(5000); /

    }

    In the Arduino IDE I selected “Arduino Leonardo” from Tools->Board menu and select the correct com port under Tools-Port, and flashed the new software to my board.

    Hardware

    With that done all that was left was soldering up some wires and connecting them to the button.

    I soldered a piece of yellow wire to pin 2 of my board and a piece of green wire to the near by ground pin as shown below.

    Then I just had to attach an exposed section of the other side of the wires to the screw terminals on the switch. On the switch I bought there was a side labeled “A” and a side labeled “B”.

    Using a multimeter set on continuity test I determined the conditions of the connections between terminals with the switch depressed and released. On my switch the two terminals within the green side labeled “A” were closed when the push button was pressed, and the two terminals on the orange side labeled “B” were opened once the button was pressed.

    Usage Notes

    The effect of the power button can be changed in the OS. In Windows 10 you can get to this under Power Options as shown below.

    I have mine set to sleep, which looks a bit cooler/quicker on the video above, and was less time consuming during testing and troubleshooting.

    Bill of Materials

    “Emergency Stop” Push Button
    https://www.amazon.com/gp/product/B00MJVMV32

    Red USB cable
    https://www.amazon.com/gp/product/B013DO561W

    “Pro Micro” ATmega32u4 “arduino Leonardo”-like board
    https://www.amazon.com/gp/product/B012FOV17O

    Resources

    Sending HID Media Key (modified this)
    http://stefanjones.ca/blog/arduino-leonardo-remote-multimedia-keys/

    USB HID Usage Tables
    http://www.usb.org/developers/hidpage/Hut1_12v2.pdf

  • Santa Hatifyer v1.0

    Santa Hatifyer v1.0

    Well, it’s Christmas time again. I really haven’t had a lot of blog posts since last Christmas, so my 2013 desk-decorations (deskorations?) are still fresh on my mind. I cheated this year, and didn’t stick to stuff I had on hand (although at the core is a Raspberry Pi, which the office had available).

    I purchased one of those cool new Raspberry Pi camera modules and stuck it in a [faux] Christmas tree. These use the Pi’s camera serial interface (CSI) port which may provide better frame rates than processing from some USB webcams. I didn’t benefit from any of that efficiency in my project, but that’s my own fault.

    rpi_cam_xmas_tree

    I used a 60 LED 1 meter “Neopixel” WS2812B series from Adafruit to decorate the folding table holding my tree and greeting cards. My coworker Eric programmed a Larson scanner sequence (think of the lights on the KITT car from the television show Knight Rider, or the cylons from Battlestar Galactica). He wrote some other sequences as well, but they were all pretty hard to capture with my camera due to the brightness of the LEDs.

    christmas_cylon

    We drove these with an Arduino compatible “Trinket Pro” also from Adafruit, although they could have been driven by the Pi itself with some extra effort. The Trinket route was pretty straight forward thanks to the Adafruit Neopixel library.

    arduino_trinket

    A monitor connected to the Raspberry Pi displays a festive youtube video of a fire and falling snow. This 2 hour long video plays on a loop, unless it is interrupted.

    While the video plays a Python script takes images from the Raspberry Pi camera module and uses OpenCV to detect faces. When a face is found a Santa hat is placed on the subject’s head (or thereabouts) and the picture is displayed to the viewer in a greeting card-esque format. The hat is scaled to fit the head of the individual subject, and multiple subjects are supported. The photos are of course saved for future embarrassment.

    xmascam_eric

    The script is ugly, and as I said earlier pretty inefficient (unnecessary disk I/O and format conversions for starters). The slowness is amplified by the limited resources available on the Pi. No doubt this can be done better, quicker, and probably as a real-time video. Here it is regardless. The background comes from scigola’s contribution over at openclipart.org (thank-you).

    https://github.com/ethertubes/santa_hatifyer

    xmasvision.py

    #!/usr/bin/env python

    # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
    import cv2
    import sys
    import io
    import time
    import picamera
    import picamera.array
    import os
    import pygame

    CAMERA_WIDTH = 640
    CAMERA_HEIGHT = 480

    # Taken from "Capturing to an OpenCV object"
    # http://picamera.readthedocs.org/en/latest/recipes1.html

    # Create the in-memory stream
    stream = io.BytesIO()

    face = 0
    while face == 0:
        # Acquiring pic
        with picamera.PiCamera() as camera:
            camera.resolution = (CAMERA_WIDTH, CAMERA_HEIGHT)
            camera.vflip = True
            time.sleep(1)
            with picamera.array.PiRGBArray(camera) as stream:
                camera.capture(stream, format=‘bgr’)
                # At this point the image is available as stream.array
                image = stream.array

        # Adapted from "Haar-cascade Detection in OpenCV"
        # http://docs.opencv.org/trunk/doc/py_tutorials/py_objdetect/py_face_detection/py_face_detection.html
        face_cascade = cv2.CascadeClassifier(‘haarcascade_frontalface_default.xml’)

        # Got pic, checking for faces
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        faces = face_cascade.detectMultiScale(image, 1.3, 5)

        if len(faces) > 0:
            face = 1
            print "Found Face! "
            cv2.imwrite(‘foundface.jpg’, image)

    cv2.destroyAllWindows()

    # driver selection routine borrowed from:
    # https://web.archive.org/web/20130601053413/http://www.karoltomala.com/blog/?p=679

    screen = None;

    drivers = [‘fbcon’, ‘directfb’, ‘svgalib’]

    for driver in drivers:
        if not os.getenv(‘SDL_VIDEODRIVER’):
            os.putenv(‘SDL_VIDEODRIVER’, driver)
        try:
            print "trying: " + driver
            pygame.display.init()
        except pygame.error:
            print ‘Driver: {0} failed.’.format(driver)
            continue
        found = True
        break

        if not found:
            raise Exception(‘No suitable video driver found!’)

    original_width = pygame.display.Info().current_w
    original_height = pygame.display.Info().current_h

    # match camera/image resolution
    width = CAMERA_WIDTH
    height = CAMERA_HEIGHT

    # Use the face photo as our canvas
    screen = pygame.display.set_mode((width, height))
    pygame.mouse.set_visible(False)
    bg = pygame.image.load(‘foundface.jpg’)
    bg = pygame.transform.scale(bg, (width, height))
    screen.blit(bg, bg.get_rect())

    # hat is longer on the right than the wearable
    # area (because of the little puff ball) tweak
    # value for your own hats
    hat_offset = 330

    # put the hat on the cat
    if len(faces) > 0:
        for (x,y,w,h) in faces:
            hat = pygame.image.load(‘hat.png’).convert_alpha()
            hat_size = int((hat.get_width() – hat_offset) / w)
            if hat_size < 1:
                hat_size = 1
            hat_offset = int(hat_offset * (1.0 / hat_size))
            hat = pygame.transform.scale(hat, (int(hat.get_width() * (1.0 / hat_size)), int(hat.get_height() * (1.0 / hat_size))))
            hat_w = hat.get_width()
            hat_h = hat.get_height()
            #pygame.draw.rect(screen, (255, 0, 0), (x, y – hat_h, hat_w, hat_h), 1) # hat border, helpful for debugging
            print "x: " + str(x)
            print "y: " + str(y)
            # fudge placement a little to put hat on, rather than over
            fx = int(x * 0.96)
            fy = int(y * 1.04)
            screen.blit(hat, (fx, fy – hat_h, hat_w, hat_h)) # fudge placement a little to put hat on, rather than over
            #pygame.draw.rect(screen, (0, 255, 0), (x, y, w, h), 1) # face border

    # Uncomment if you want to see the intermediary face + hat photo
    #pygame.display.update()
    pygame.image.save(screen, ‘hatted.png’)

    # Resize canvas to fit monitor
    width = original_width
    height = original_height

    # load background and photo (with hat) into objects
    # display background over photo, allowing transparent region to
    # show the photo behind it.
    screen = pygame.display.set_mode((width, height))
    bg = pygame.image.load(‘xmascam.png’).convert_alpha()
    bg = pygame.transform.scale(bg, (width, height))
    photo = pygame.image.load(‘hatted.png’)
    photo = pygame.transform.scale(photo, (int(1.339 * photo.get_width()), int(1.339 * photo.get_height())))
    screen.blit(photo, (622, 115, photo.get_width(), photo.get_height()))
    screen.blit(bg, bg.get_rect())
    pygame.display.update()

    time.sleep(10)
    sys.exit

    pygame.display.quit()

    This script is ran, the photos archived, and the fire video started/stopped by the following bash script

    watcher.sh

    #!/bin/bash

    facewatch() {
            if ! [ -d "./archive" ]; then
                    mkdir "./archive"
            fi

            while [ 1 ]
            do
                    ./xmasvision.py
                    stamp="$(date +%s)"
                    if [ -f "foundface.jpg" ]; then
                            mv foundface.jpg ./archive/foundface_$stamp.jpg
                    fi
                    if [ -f "hatted.png" ]; then
                            mv hatted.png ./archive/hatted_$stamp.png
                    fi
            done
    }

    facewatch &
    while [ 1 ]
    do
            # if "hatted.png" exists then the thing should display soon
            # so stop the fire video
            if [ -f "hatted.png" ]; then
                    if ! [ -z "$(ps aux |grep -i omxplayer.bin |grep -v grep)" ]; then
                            killall omxplayer.bin
                    fi
            fi
            # start the fire video if needed
            if [ -z "$(ps aux |grep omxplayer.bin |grep -v grep)" ]; then
                    omxplayer video/Christmas\ Yule\ Log\ Fireplace\ with\ Snow\ and\ Crackling\ Fire\ Sounds\ \(HD\)-y_VD92xKS5w.mp4 &
            fi
            sleep 1
    done

    Happy Holidays