Project GamePi: Raspberry Pi Based Portable Game Console

Hi!

DSC_3044

 

Hope you’re well. In this weeks episode of ‘Weird Electronic Devices You Can Make In Your Own Home’, we’ll be diving into a Raspberry Pi based handheld inside of a Game Boy (DMG). My inspiration to pick this one up was seeing the awesome SuperPiBoy,  as well as the wonderful PiGRRL from Adafruit. I took bits from both of those projects, and added my own small touch.

Parts:

My little bit of spice for this project were the additional 3 controls on the rear panel. Instead of using momentary switches,  I used capacitive touch sensors. I chose the standalone versions that Adafruit sells, because the form factor is favorable to this application, and for simplicity.

I started with a trashed original Game Boy. I found it in a lot buy with a broken Game Gear (I can see another project in my future… :)) for 15$. I handed off the internals to a chip tune obsessed friend, and began the conversion.

I chose not to retain any of the original parts, except for the plastics. I first measured the LCD window, and shaved it to the appropriate size for the 3.5 inch LCD. I shaved down as much of the internal plastics as I could. I then mounted the LCD in place with hot glue. The KB button PCB mounts using the original mounting hardware, no modification required.

DSC_3008

With that done, I soldered a ribbon cable onto the button PCB, and removed the 12v -> 5V regulator from the LCD PCB. I found a datasheet for the regulator on my board, but your results may vary. Apparently, there are many revisions of this device. Here’s a link to a thread which speaks out the conversion in detail (Thanks SuperPiBoy). You can see where I soldered on to the 5V OUTPUT pad on the chip footprint in the photo. Worst case, you can search the board for a chip that has 12V on one pin, and 5V on another, it is likely that’s the chip that needs to be removed.

DSC_3028

The LCD PCB ends up being slightly too large to fit into the case, but can be safely shaved down without having to move any traces. If you needed to keep going with the shaving, the buttons can be moved easily.

DSC_3007

With that done, the Mini USB breakout gets mounted where the old power switch was. It’s the perfect footprint for that spot, and with a tiny bit of shaving, gets supported by the plastic standoff post in that corner.

  • The USB 5v and GND output get tied to the LiPoly Charger 5V and GND.
  • The Charger BAT and GND get tied to the respective V+ and GND on your battery of choice. (Please though, only use LiPoly with the LiPoly charger, obviously.)
  • Pass the V+ from your battery through your SPDT switch, and then along to the PowerBoost V+ input.
  • GND on the battery can go straight to the PowerBoost GND.
  • 5V Output from the PowerBoost goes to the TP2 pin on the Raspberry Pi, and PowerBoost Output GND goes to TP1 pin on the Pi.

Congrats! Your Pi is powered. Your LCD board will also need 5V output from the PowerBoost, as well as the three capacitive sensor boards and audio amplifier.

DSC_3016

DSC_3017

DSC_3023

After a significant amount of shaving of the rear case, I was able to mount all of the aforementioned electronics. The chargers and power input devices are mounted on the recessed area of plastic for the cartridge. The capacitive pads are underneath the label area of the rear plastic. I mounted a female USB port where the old Link port was, and the SPDT switch where the volume knob used to be. I hotglued my snazzy Kitch Bent battery cover in place, and cut it’s locking tab to save space.

DSC_3020

I drilled holes in the center of the cap-pad plastics, so there can be a tactile feeling when searching for the buttons on the rear case. I also used a significant amount of nuts/bolts and standoffs to hold everything in place solidly, so the rear case is littered with bolts in different areas. I used a “Cell” format battery, so that it would sit nicely on the bottom of the case. The battery I chose also has an in-circuit thermal cutoff, so if there’s a problem charging it, it will (hopefully) prevent serious damage/fire. I cut the “fuel gauge” board off of the cell, as I didn’t need it.

DSC_3025

DSC_3026

DSC_3021

For the Raspberry Pi, I removed the Audio Jack, Composite Jack and relocated the USB port. Being a Model A, it did not have the Ethernet jack or second USB port. I soldered wires onto the respective areas to pull the signals I needed. Take care when soldering to the Composite Video pads, as it’s easy to damage the Pi’s composite output with too much heat. For the audio, I routed the Audio Jack output direct to the headphone port, as well as to the amplifier board. Since I did not have a “switched” variety headphone jack around for this project, I came up with the following arrangement. When booting, the Pi runs a program to check the state of the 3 Cap Pads. If they’re all HIGH, it will boot into “Headphone Mode” and drive the Shutdown pin on the amplifier board, freeing the headphone jack. For the speakers, I stuffed a set of Macbook (3Ohm) speakers into the area where the old GB speaker used to be. I tied them to the outputs on the amp board.

I trimmed down the GPIO pins down to half height, and soldered the Common Ground PCB and OUTPUT pins (from the cap-sensors) to their respective BCM GPIOs. I then cleaned up the wiring with a couple of wire ties, and routed the wires as best I could.

DSC_3033

DSC_3036

DSC_3035

And, that’s it!

As far as software goes, running on a Model A means that running RetroPie 2.0+ is out of the question (as ES 2.0 is apparently more graphically intense, and requires a 256/256 GPU split). I loaded RetroPie 1.10 onto a high speed SD card, and shimmed in my custom bits of software to glue everything together. Cave Story runs great! 😀

 Post-Mortem:

When shaving the case down to fit the LCD, I neglected to do something about the standoffs for securing the two halves of the case together. This lead to complications at the very end of the project, because there was no reliable way to join the halves together. I fixed this by hot gluing a bolt down in two corners of the case. I then fed a machine screw up into the case, and into the bolt. My screws ended up being the perfect length to pull the bolt, without going all the way through. It hurts the project’s aesthetics, as it’s clear the halves no longer join seamlessly, but it has an acceptable amount of mechanical strength.

The Capacitive Sensors are HIGH when active, and LOW when dormant. The opposite is true for the Common Ground Button PCB, which (when the internal pullups are activated) are HIGH when dormant and LOW when active. This lead to complications using Retrogame (the Adafruit C utility used to convert the physical button presses into keyboard events), as the ‘always-active’ buttons would pin it. I’m in the process of writing a program that is compatible with mixed button conditions, and will update this post when I push it to my git.

DSC_3042

 

 

 

Adventures in armel – Debian Wheezy – udevd[XXX]: unable to receive ctrl connection: Function not implemented

Hi!

I’m running a tiny specially designed ARM system with a heavily patched 2.6.3x kernel on an armel install of Debian Squeeze. It was my goal to bring this system into the realm of security updates, and Debian Wheezy. Simple dist-upgrade right? Wrong. After the udev package was updated to the Wheezy version and I rebooted, udev immediately started spitting out this log message:

udevd[PID]: unable to receive ctrl connection: Function not implemented

Login either never started, or was just getting completely stomped by udev, because I couldn’t do so much as login to the console. I rebooted my machine in Single User mode, which succeeded. (If you’re having trouble getting into Single User mode on your Debian box, you can change your init kernel parameter to: init=/bin/bash You’ll get access to basic functions, such as file movement and such, which might save you in the event of a complete meltdown.)

I double checked the minimum kernel requirements for udev in Wheezy (2.6.18+), and double-checked that I wasn’t missing any critical udev components in the kernel. Both checked out, which didn’t leave me much to go on. (If you’re able to kill udev and login, you could strace the calls to udev during startup to see which ones are causing the failure.) After checking around online, I found a couple of threads where people had the same problem. One of them talked about lack of the accept4 syscall mapping in the kernel on ia64 devices. In that thread, there was a reference to a similar bug for armel. Hmmm.

Here‘s the ia64 thread. Here‘s the armel bug filing.

Sounds like exactly what I’m experiencing. Digging a little further, I was able to find the patch referenced there, as well as a C program that tests whether your kernel is able to use the accept4 syscall. Unfortunately, if you’ve gotten to this point, your system is quite broken, so the accept4 test might not work for you.

Here is the patch to fix 2.6.3x armel kernels to use the accept4 syscall:

From e3b4f8cdd8d2a83e8ffaed2a8f682959150365d1 Mon Sep 17 00:00:00 2001
From: Ingo Albrecht <prom@berlin.ccc.de>
Date: Sun, 18 Sep 2011 02:53:04 +0200
Subject: [PATCH 01/15] backport: wire up sys_accept4() on ARM

 * This is required to run current debian unstable (because of udev)
 * Original commit from kernel git: 21d93e2e29722d7832f61cc56d73fb953ee6578e
---
 arch/arm/include/asm/unistd.h |    1 +
 arch/arm/kernel/calls.S       |    1 +
 2 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h
index cf9cdaa..8f32b6b 100644
--- a/arch/arm/include/asm/unistd.h
+++ b/arch/arm/include/asm/unistd.h
@@ -392,6 +392,7 @@
 #define __NR_rt_tgsigqueueinfo    	(__NR_SYSCALL_BASE+363)
 #define __NR_perf_event_open		(__NR_SYSCALL_BASE+364)
 #define __NR_recvmmsg			(__NR_SYSCALL_BASE+365)
+#define __NR_accept4			(__NR_SYSCALL_BASE+366)
 
 /*
  * The following SWIs are ARM private.
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
index 9314a2d..1dff6a0 100644
--- a/arch/arm/kernel/calls.S
+++ b/arch/arm/kernel/calls.S
@@ -375,6 +375,7 @@
 		CALL(sys_rt_tgsigqueueinfo)
 		CALL(sys_perf_event_open)
 /* 365 */	CALL(sys_recvmmsg)
+		CALL(sys_accept4)
 #ifndef syscalls_counted
 .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
 #define syscalls_counted
-- 
1.7.6.3

Here is the test_accept4.c program:

/* test_accept4.c

  Copyright (C) 2008, Linux Foundation, written by Michael Kerrisk
       <mtk.manpages@gmail.com>

  Licensed under the GNU GPLv2 or later.
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

#define PORT_NUM 33333

#define die(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)

/**********************************************************************/

static int
do_test(int lfd, struct sockaddr_in *conn_addr,
       int closeonexec_flag, int nonblock_flag)
{
   int connfd, acceptfd;
   int fdf, flf, fdf_pass, flf_pass;
   struct sockaddr_in claddr;
   socklen_t addrlen;

   printf("=======================================\n");

   connfd = socket(AF_INET, SOCK_STREAM, 0);
   if (connfd == -1)
       die("socket");
   if (connect(connfd, (struct sockaddr *) conn_addr,
               sizeof(struct sockaddr_in)) == -1)
       die("connect");

   addrlen = sizeof(struct sockaddr_in);
   acceptfd = accept4(lfd, (struct sockaddr *) &claddr, &addrlen,
                      closeonexec_flag | nonblock_flag);
   if (acceptfd == -1) {
       perror("accept4()");
       close(connfd);
       return 0;
   }

   fdf = fcntl(acceptfd, F_GETFD);
   if (fdf == -1)
       die("fcntl:F_GETFD");
   fdf_pass = ((fdf & FD_CLOEXEC) != 0) ==
              ((closeonexec_flag & SOCK_CLOEXEC) != 0);
   printf("Close-on-exec flag is %sset (%s); ",
           (fdf & FD_CLOEXEC) ? "" : "not ",
           fdf_pass ? "OK" : "failed");

   flf = fcntl(acceptfd, F_GETFL);
   if (flf == -1)
       die("fcntl:F_GETFD");
   flf_pass = ((flf & O_NONBLOCK) != 0) ==
              ((nonblock_flag & SOCK_NONBLOCK) !=0);
   printf("nonblock flag is %sset (%s)\n",
           (flf & O_NONBLOCK) ? "" : "not ",
           flf_pass ? "OK" : "failed");

   close(acceptfd);
   close(connfd);

   printf("Test result: %s\n", (fdf_pass && flf_pass) ? "PASS" : "FAIL");
   return fdf_pass && flf_pass;
}

static int
create_listening_socket(int port_num)
{
   struct sockaddr_in svaddr;
   int lfd;
   int optval;

   memset(&svaddr, 0, sizeof(struct sockaddr_in));
   svaddr.sin_family = AF_INET;
   svaddr.sin_addr.s_addr = htonl(INADDR_ANY);
   svaddr.sin_port = htons(port_num);

   lfd = socket(AF_INET, SOCK_STREAM, 0);
   if (lfd == -1)
       die("socket");

   optval = 1;
   if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &optval,
                  sizeof(optval)) == -1)
       die("setsockopt");

   if (bind(lfd, (struct sockaddr *) &svaddr,
            sizeof(struct sockaddr_in)) == -1)
       die("bind");

   if (listen(lfd, 5) == -1)
       die("listen");

   return lfd;
}

int
main(int argc, char *argv[])
{
   struct sockaddr_in conn_addr;
   int lfd;
   int port_num;
   int passed;

   passed = 1;

   port_num = (argc > 1) ? atoi(argv[1]) : PORT_NUM;

   memset(&conn_addr, 0, sizeof(struct sockaddr_in));
   conn_addr.sin_family = AF_INET;
   conn_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
   conn_addr.sin_port = htons(port_num);

   lfd = create_listening_socket(port_num);

   if (!do_test(lfd, &conn_addr, 0, 0))
       passed = 0;
   if (!do_test(lfd, &conn_addr, SOCK_CLOEXEC, 0))
       passed = 0;
   if (!do_test(lfd, &conn_addr, 0, SOCK_NONBLOCK))
       passed = 0;
   if (!do_test(lfd, &conn_addr, SOCK_CLOEXEC, SOCK_NONBLOCK))
       passed = 0;

   close(lfd);

   exit(passed ? EXIT_SUCCESS : EXIT_FAILURE);
}

Just to be clear, these aren’t my patches, I simply found them, and I’m mirroring them for historical purposes. All credit goes to the original authors referenced in both snippets. And, that’s that! Apply the patch, rebuild your kernel, and reboot into your Wheezy installation. Udev should now work like a charm.

If you’d like to do this the opposite way, you could patch the udev source with the following patch found here: (But, I have not personally tested this — This will also make future updates a pain.)

diff -Nru udev-177.orig/src/udev-ctrl.c udev-177/src/udev-ctrl.c
--- udev-177.orig/src/udev-ctrl.c    2012-01-10 01:43:22.125518772 +0100
+++ udev-177/src/udev-ctrl.c	2012-01-22 16:46:31.339378651 +0100
@@ -15,6 +15,7 @@
 #include <stddef.h>
 #include <string.h>
 #include <unistd.h>
+#include <fcntl.h>
 #include <sys/types.h>
 #include <sys/poll.h>
 #include <sys/socket.h>
@@ -182,6 +183,7 @@
         struct ucred ucred;
         socklen_t slen;
         const int on = 1;
+        int flgs;
 
         conn = calloc(1, sizeof(struct udev_ctrl_connection));
         if (conn == NULL)
@@ -189,13 +191,18 @@
         conn->refcount = 1;
         conn->uctrl = uctrl;
 
-        conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK);
+        conn->sock = accept(uctrl->sock, NULL, NULL);
         if (conn->sock < 0) {
                 if (errno != EINTR)
                         err(uctrl->udev, "unable to receive ctrl connection: %m\n");
                 goto err;
         }
 
+        /* Since we don't have accept4 */
+        flgs = fcntl(conn->sock, F_GETFL, NULL);
+        if (flgs >= 0) fcntl(conn->sock, F_SETFL, flgs | O_NONBLOCK);
+        fcntl(conn->sock, F_SETFD, FD_CLOEXEC);
+
         /* check peer credential of connection */
         slen = sizeof(ucred);
         if (getsockopt(conn->sock, SOL_SOCKET, SO_PEERCRED, &ucred, &slen) < 0) {

UNIX Domain Sockets (Datagram)

Hello friends!

I am here in Internetland to talk to you about my adventures with UNIX domain sockets. I’m designing a collection of separate programs that all need to communicate. The communication has to be multiclient, single server, and easy to implement. In this case, a datagram is the preferred mode due to it’s tiny connectionless nature. UDP seems like a good option at first, but it has problems with reliability. Dropping packets in stateful IPC can cause issues with synchronization, etc. TCP could work, but why use the networking stack when there are APIs dedicated to IPC?

Sounds like a job for UNIX domain sockets. These wonderful little things use the file system for their endpoint identifiers, but the actual communication happens within the kernel. You can configure them as a pipe, or use datagrams. With datagrams, it’s in a single packet context, rather than a stream. There’s no established connection to speak of (but you do call connect()). A bit confusing. You create a file handle for your client and connect to the server’s file handle. If there’s already a file on the filesystem with the same name, the socket’s bind will fail, so cleaning the handles up when done (or before you init) is important too.

If you’re just getting into IPC, then I highly suggest reading Beej’s Guide To Unix Interprocess Communication. It’s great. I should say, there are many different types of IPC, and domain sockets are just one. I prefer domain sockets to shared memory, for the sake of simplicity. For your implementation, look at the many methods available, and see what works best for you.

Without further ado, here’s the code! This first part is the server.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

char * server_filename = "/tmp/socket-server";

int main(void)
{
    int s;
    struct sockaddr_un srv_un = {0};

    if ((s = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
        perror("socket server");
        exit(1);
    }

    srv_un.sun_family = AF_UNIX;
    strncpy(srv_un.sun_path, server_filename, sizeof(srv_un.sun_path));
    /*If you leave the file behind when you're finished, or perhaps crash after binding, the next bind will fail
    / with "address in use". Which just means, the file is already there.*/
    unlink(srv_un.sun_path);

    if (bind(s, (struct sockaddr *)&srv_un, sizeof(srv_un)) == -1) {
        perror("bind server");
        exit(1);
    }

    for(;;) {

        char buf[1024] = {0};
        read(s, buf, sizeof(buf));
        printf("RECEIVED: %s", buf);

    }

    close(s);

    return 0;
}

And, here’s the client.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

char * server_filename = "/tmp/socket-server";
char * client_filename = "/tmp/socket-client";

int main(void)
{

    int s;
    char obuf[100];
    struct sockaddr_un srv_un, cli_un = { 0 };
    
    srv_un.sun_family = AF_UNIX;
    strncpy(srv_un.sun_path, server_filename, sizeof(srv_un.sun_path));

    cli_un.sun_family = AF_UNIX;
    strncpy(cli_un.sun_path, client_filename, sizeof(cli_un.sun_path));
    unlink(cli_un.sun_path);

    if ((s = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
        perror("socket server");
        exit(1);
    }

    /* (http://stackoverflow.com/questions/3324619/unix-domain-socket-using-datagram-communication-between-one-server-process-and)
    Here, we bind to our client node, and connect to the server node. As Unix domain sockets need to have endpoints on either end
    of the connection. For more info, visit the URL.*/
    if (bind(s, (struct sockaddr *)&cli_un, sizeof(cli_un)) == -1) {
        perror("bind client");
        exit(1);
    }

    if (connect(s, (struct sockaddr *) &srv_un, sizeof(srv_un)) == -1) {
        perror("connect client");
        exit(1);
    }

    //printf("Connected.\n");

    while(printf("> "), fgets(obuf, 100, stdin), !feof(stdin)) {
        if (send(s, obuf, strlen(obuf), 0) == -1) {
            perror("send");
            exit(1);
        }
        break;
    }

    //printf("Sent successfully.\n");

    close(s);

    return 0;
}