Page 1 of 2

[WIP] fdeluxes Game Boy Zero

Posted: Mon Nov 07, 2016 2:11 pm
by fdeluxe
Hi,

I've reached a state where I am very eager to present you my Game Boy Zero build! It comes with some gimmicks, like a customizable RGB LED behind the speaker grill :D The color can be changed by simultaneously pressing the small mode button in the upper left and the A/X/Y button. The color is saved to the teensys EPROM and is preserved even though the system is shut off. The teensy also features a Keyboard/Mouse-Mode for games like Quake 3 Arena and retroarch-configuration and a simple Gamepad-Mode.

Right now I am handling volume control via software. I hooked up the potentiometer to the teensy and assigned volume-hotkeys to its values using triggerhappy. Unfortunately the deamon crashes very often and I have to use the ingame menu to control the volume :( I gonna change my wiring in the future to directly control the volume through the potentiometer.

I also want to ditch USB Audio in the future and go for PWM. I simply cannot stand that my splashscreen video has no sound :D

Splashscreen video (link in description): https://www.youtube.com/watch?v=-7M2h-G ... e=youtu.be
Teensy Code (sorry for the german documentation): http://pastebin.com/F7ybmifS

Code: Select all

/* Teensy mit zwei Modi: Gamepad mit Analogstick und Tastatur mit Maus (PC Modus)
 *  
 *  Standardmäßig ist der Gamepad-Modus aktiviert.
 *  Durch die Tastenkombination: Mode-Button+Select lässt sich zwischen den Modi wechseln
 *  Im PC-Modus wird der Analogstick als Maus erkannt und die normalen Knöpfe werden als Buchstaben ausgegeben. R1 und R2 fungieren als Maustasten.
 *  Weitere Kombos:
 *  Mode+DPAD_UP = Pfeiltaste Hoch
 *  Mode+DPAD_DOWN = Pfeiltaste Runter
 *  Mode+DPAD_LEFT = Pfeiltaste Links
 *  Mode+DPAD_RIGHT = Pfeiltaste Rechts
 *  Mode+B = Enter
 *  Mode+START = Escape
 *  Mode+A/X/Y = Farbwert erhöhen (maximal auf 10, dann springt er wieder auf 0)
*/
#include <Bounce.h>
#include <EEPROM.h>
#define NUM_KEYS 15
#define COMMON_ANODE

struct Key {
    char keycode;
    Bounce * bounce;
};

Key keys[NUM_KEYS];
char volumeLVLs[21];
Key key(char keycode, int pin) {
    Key * ret = new Key;
    ret->keycode = keycode;
    ret->bounce = new Bounce(pin, 10);
    pinMode(pin, INPUT_PULLUP);
    return *ret;
}

//Keyboard-Tastenbelegung
void setupKeys() {
    keys[0] = key('a', 0);
    keys[1] = key('w', 1);
    keys[2] = key('s', 2);
    keys[3] = key('d', 3);
    keys[4] = key('m', 4);
    keys[5] = key('n', 5);
    keys[6] = key('b', 6);
    keys[7] = key('v', 7);
    keys[8] = key('y', 8);
    keys[9] = key('x', 9);
    keys[10] = key('q', 10);
    keys[11] = key('e', 11);
    keys[12] = key('u', 12);
    keys[13] = key('l', 13);
    keys[14] = key('k', 14);
}
// Pseudo-Tasten zur Lautstärkeregelung über triggerhappy. Jedem Potentiometer Wert wird ein Button zugeordnet (temporäre Lösung)
void setupVolumeKeys() {
    volumeLVLs[20] = KEY_MINUS;         // 0%
    volumeLVLs[19] = KEY_EQUAL;         // 5%
    volumeLVLs[18] = KEY_LEFT_BRACE;    // 10%
    volumeLVLs[17] = KEY_RIGHT_BRACE;   // 15%
    volumeLVLs[16] = KEY_BACKSLASH;     // 20%
    volumeLVLs[15] = KEY_SEMICOLON;     // 25%
    volumeLVLs[14] = KEY_QUOTE;         // 30%
    volumeLVLs[13] = KEY_TILDE;         // 35%
    volumeLVLs[12] = KEY_COMMA;         // 40%
    volumeLVLs[11] = KEY_PERIOD;        // 45%
    volumeLVLs[10] = KEY_SLASH;         // 50%
    volumeLVLs[9] = KEY_CAPS_LOCK;      // 55%
    volumeLVLs[8] = KEY_END;            // 60%
    volumeLVLs[7] = KEY_INSERT;         // 65%
    volumeLVLs[6] = KEY_V;              // 70%
    volumeLVLs[5] = KEY_T;              // 75%
    volumeLVLs[4] = KEY_P;              // 80%
    volumeLVLs[3] = KEY_SCROLL_LOCK;    // 85%
    volumeLVLs[2] = KEY_PRINTSCREEN;    // 90%
    volumeLVLs[1] = KEY_PAUSE;          // 95%
    volumeLVLs[0] = KEY_I;              // 100%
}

//RGB LED-Pins
int redPin = 16;
int greenPin = 17;
int bluePin = 20;

//Change LED Color
void setColor(int red, int green, int blue) {
    #ifdef COMMON_ANODE
    red = 255 - red;
    green = 255 - green;
    blue = 255 - blue;
    #endif
    analogWrite(redPin, red);
    analogWrite(greenPin, green);
    analogWrite(bluePin, blue);
}

//Mode-Button Kombo-Buttons
Bounce modeButton = Bounce(15, 10);
Bounce upButton = Bounce(1, 10);
Bounce downButton = Bounce(2, 10);
Bounce leftButton = Bounce(0, 10);
Bounce rightButton = Bounce(3, 10);
Bounce selectButton = Bounce(4, 10);
Bounce startButton = Bounce(5, 10);
Bounce xButton = Bounce(9, 10);
Bounce yButton = Bounce(8, 10);
Bounce aButton = Bounce(7, 10);
Bounce bButton = Bounce(6, 10);

boolean pcMode = false;
boolean modeButtonPressed = false;
boolean colorChanged = false; // Only write to EPROM if color has changed
const int numButtons = 16; // 16 for Teensy, 32 for Teensy++

void setup() {

    //Debug-Nachrichten über Serial.println("Nachricht") verschicken
    Serial.begin(9600);

    //Setup the LED-Pins
    pinMode(redPin, OUTPUT);
    pinMode(greenPin, OUTPUT);
    pinMode(bluePin, OUTPUT);

    setupKeys();
    setupVolumeKeys();
    pinMode(15, INPUT_PULLUP);
    pinMode(16, INPUT_PULLUP);
    Joystick.useManualSend(true);

    for (int i = 0; i < numButtons; i++) {
        pinMode(i, INPUT_PULLUP);
    }
}

byte allButtons[numButtons];
byte prevButtons[numButtons];
int mapX;
int mapY;
int deadzone = 60;
int volumeLVL;
int deltaVolume = 0;
int volumeKey = 0;
float volumeRel = 0;
int red = EEPROM.read(0);
int green = EEPROM.read(1);
int blue = EEPROM.read(2);

void loop() {

    //Read potentiometer for volumecontrol
    volumeLVL = analogRead(21);

    if (volumeLVL < 4) volumeLVL = 0;
    if (volumeLVL > 1019) volumeLVL = 1023;

    //Convert Potentiometer value to array index
    volumeRel = 0.0195503421309873 * volumeLVL;
    deltaVolume = volumeKey - (int) volumeRel;
    volumeKey = (int) volumeRel;

    //Check if potentiometer value has changed
    if (deltaVolume != 0) {
        Serial.print("Volume = ");
        Serial.print(volumeKey * 5);
        Serial.println(" %");
        Keyboard.set_key1(volumeLVLs[volumeKey]);
        Keyboard.send_now(); // send the button press
        Keyboard.set_key1(0);
        Keyboard.send_now(); // send the button release
    }

    //Update button status
    modeButton.update();
    upButton.update();
    downButton.update();
    leftButton.update();
    rightButton.update();
    selectButton.update();
    startButton.update();
    aButton.update();
    bButton.update();
    xButton.update();
    yButton.update();

    //Check if mode-button is pressed
    if (modeButton.fallingEdge()) modeButtonPressed = true;
    if (modeButton.risingEdge()) modeButtonPressed = false;

    if (modeButtonPressed && selectButton.fallingEdge()) { //Switch Mode
        Serial.println("Mode changed");
        pcMode = !pcMode;
    }
    if (modeButtonPressed && startButton.fallingEdge()) { //Escape Key
        Serial.println("Escape");
        Keyboard.set_key1(KEY_ESC);
        Keyboard.send_now(); // send the button press
        Keyboard.set_key1(0);
        Keyboard.send_now(); // send the button release
    }
    if (modeButtonPressed && bButton.fallingEdge()) { //Enter
        Serial.println("Enter");
        Keyboard.set_key1(KEY_ENTER);
        Keyboard.send_now(); // send the button press
        Keyboard.set_key1(0);
        Keyboard.send_now(); // send the button release
    }
    if (modeButtonPressed && upButton.fallingEdge()) { //Up
        Serial.println("up");
        Keyboard.set_key1(KEY_UP);
        Keyboard.send_now(); // send the button press
        Keyboard.set_key1(0);
        Keyboard.send_now(); // send the button release
    }
    if (modeButtonPressed && downButton.fallingEdge()) { //Down
        Serial.println("down");
        Keyboard.set_key1(KEY_DOWN);
        Keyboard.send_now(); // send the button press
        Keyboard.set_key1(0);
        Keyboard.send_now(); // send the button release
    }
    if (modeButtonPressed && leftButton.fallingEdge()) { //Left
        Serial.println("left");
        Keyboard.set_key1(KEY_LEFT);
        Keyboard.send_now(); // send the button press
        Keyboard.set_key1(0);
        Keyboard.send_now(); // send the button release
    }
    if (modeButtonPressed && leftButton.fallingEdge()) { //Right
        Serial.println("right");
        Keyboard.set_key1(KEY_RIGHT);
        Keyboard.send_now(); // send the button press
        Keyboard.set_key1(0);
        Keyboard.send_now(); // send the button release
    }
    if (modeButtonPressed && xButton.fallingEdge()) { //Red
        Serial.println("Red changed");
        red += 25;
        if (red > 255) red = 0;
        colorChanged = true;
    }
    if (modeButtonPressed && yButton.fallingEdge()) { //Green
        Serial.println("Green changed");
        green += 25;
        if (green > 255) green = 0;
        colorChanged = true;
    }
    if (modeButtonPressed && aButton.fallingEdge()) { //Blue
        Serial.println("Blue chganged");
        blue += 25;
        if (blue > 255) blue = 0;
        colorChanged = true;
    }

    //write RGB-values to EPROM
    if (colorChanged) {
        colorChanged = false;
        EEPROM.write(0, red);
        EEPROM.write(1, green);
        EEPROM.write(2, blue);
    }

    setColor(red, green, blue);


    // Analog Stick Kalibrierung
    // Zu faul meine Kommentare wieder einzudeutschen... :)

    //Remap the data range
    mapX = map(analogRead(23), 185, 839, 0, 1023); 
    mapY = map(analogRead(22), 125, 830, 0, 1023); 

    //Deadzones
    if (mapX < 512 + deadzone && mapX > 512 - deadzone) mapX = 512;
    if (mapY < 512 + deadzone && mapY > 512 - deadzone) mapY = 512;
    if (mapX < 24) mapX = 0;
    if (mapY < 24) mapY = 0;

/*
    //Non-linear correction
        mapX=(int)(0.00000302*mapX*mapX*mapX-0.004631*mapX*mapX+2.577*mapX+1.23);
      if(mapX>1023) mapX=1023;
      if(mapX<0) mapX=0;
      
      mapY=(int)(0.00000302*mapY*mapY*mapY-0.004631*mapY*mapY+2.577*mapY+1.23);
      if(mapY>1023) mapY=1023;
      if(mapY<0) mapY=0;
*/


    //Send final values
    Joystick.X(mapX);
    Joystick.Y(mapY);

    // PC mode
    //Analogstick as mouse
    if (pcMode == true) {
        float X = (float) mapX;
        float Y = (float) mapY;
        float dX = 6 * ((X - 512) / 512); //scale the mousespeed by 6.
        float dY = 6 * ((Y - 512) / 512);
        Mouse.move(dX, dY, 0);

        for (int i = 0; i < NUM_KEYS; i++) {
            keys[i].bounce->update();

            if (keys[i].bounce->fallingEdge()) {
                if (i == 13) Mouse.press(MOUSE_RIGHT);
                else if (i == 14) Mouse.press(MOUSE_LEFT);
                else Keyboard.press(keys[i].keycode);
            } else if (keys[i].bounce-> risingEdge()) {
                if (i == 13) Mouse.release(MOUSE_RIGHT);
                else if (i == 14) Mouse.release(MOUSE_LEFT);
                else Keyboard.release(keys[i].keycode);
            }
        }
    }

    // Gamepad mode
    else {
        // read digital pins and use them for the buttons
        for (int i = 0; i < numButtons; i++) {
            if (digitalRead(i)) {
                // when a pin reads high, the button is not pressed
                // the pullup resistor creates the "on" signal
                allButtons[i] = 0;
            } else {
                // when a pin reads low, the button is connecting to ground.
                allButtons[i] = 1;
            }
            Joystick.button(i + 1, allButtons[i]);
        }
        Joystick.send_now();
    }
}
Here's my parts list: Special thanks to
[mention]wermy[/mention] for inspiring me and your awesome guides!
[mention]dominator[/mention] for that kick-ass screen surround!
[mention]prerunnerseth[/mention] for the button PCB!

[spoiler="Front"]Image
Image[/spoiler]
[spoiler="Back"]Image[/spoiler]
[spoiler="Side"]Image
Image[/spoiler]
[spoiler="RGB LED"]Image
Image
Image
Image
Image
Image
Image[/spoiler]

Re: [WIP] fdeluxes Game Boy Zero

Posted: Mon Nov 07, 2016 2:47 pm
by wermy
Looks great! Love the RGB LED. :D

Re: [WIP] fdeluxes Game Boy Zero

Posted: Mon Nov 07, 2016 3:07 pm
by alexln
This is a really cool clean build. Nice job!

Re: [WIP] fdeluxes Game Boy Zero

Posted: Mon Nov 07, 2016 8:27 pm
by AustenFM
Looks great, awesome clean build!!

Love the L & R buttons~ I'm curious if you managed to keep the working cartridge reader with that snes L & R button setup?

Re: [WIP] fdeluxes Game Boy Zero

Posted: Tue Nov 08, 2016 12:21 am
by Lpoolm
I like that splash screen!

Re: [WIP] fdeluxes Game Boy Zero

Posted: Tue Nov 08, 2016 6:48 am
by fdeluxe
AustenFM wrote:Looks great, awesome clean build!!

Love the L & R buttons~ I'm curious if you managed to keep the working cartridge reader with that snes L & R button setup?
Unfortunatly not. I did a first build where I put the LR buttons a little deeper and could keep the cartridge reader. But I ditched this for ergonomic reasons. In the end the cartridge reader is just for nostalgia anyways :D

If you also want to use the SNES L&R buttons I would suggest that you put them like 1.00 - 1.50 cm higher than I did. In their current position I am still struggeling to find a comfortable way to press them.

Re: [WIP] fdeluxes Game Boy Zero

Posted: Tue Nov 08, 2016 9:29 am
by Apis
Looks like you didn't have to use the "vertical aligned buttons" like the Kite's build did to use the 3DS analog stick. Can I see the inside of your build to see how the psp stick is mounted?

Also, does anyone know if the Kite build will accept the psp analog stick? I can't ask in the Kite thread because it's locked.

Re: [WIP] fdeluxes Game Boy Zero

Posted: Tue Nov 08, 2016 9:43 am
by CaptainDurden
Apis wrote: Also, does anyone know if the Kite build will accept the psp analog stick? I can't ask in the Kite thread because it's locked.
I don't see why it couldn't.

Re: [WIP] fdeluxes Game Boy Zero

Posted: Tue Nov 08, 2016 10:02 am
by AustenFM
fdeluxe wrote:
Unfortunatly not. I did a first build where I put the LR buttons a little deeper and could keep the cartridge reader. But I ditched this for ergonomic reasons. In the end the cartridge reader is just for nostalgia anyways :D

If you also want to use the SNES L&R buttons I would suggest that you put them like 1.00 - 1.50 cm higher than I did. In their current position I am still struggeling to find a comfortable way to press them.
Thanks for the reply! Yeah, I was worried that may be your answer, haha. I'd love to use the SNES L&R buttons -and- have a working cartridge reader since I already shelled out for a cartridge pcb. But I may have to resort to smaller L&R buttons to get it to work. Thanks for your input on ergonomics as well, this is something I'm working with myself.

Keep up the great work!

Re: [WIP] fdeluxes Game Boy Zero

Posted: Tue Nov 08, 2016 1:01 pm
by fdeluxe
Apis wrote:Looks like you didn't have to use the "vertical aligned buttons" like the Kite's build did to use the 3DS analog stick. Can I see the inside of your build to see how the psp stick is mounted?

Also, does anyone know if the Kite build will accept the psp analog stick? I can't ask in the Kite thread because it's locked.
I will post some pics of the interior tomorrow. As for the analog stick, the placement is pretty easy since the stick fits right between the button pcb and the case. No need to leave a hole in the pcb! I just left the middle screw of the button pcb a little loose to prevent to much pressure on the stick so it wont get stuck.