Journey to a Fully Custom Pinball Machine - Part 2
From walking the show floor at Texas Pinball Fest 2016, I couldn't help but get the vibe that something novel and big would be in store for TPF 2017 -- something beyond the big but also typical/expected releases of commercial games such as The Big Lebowski and Ghostbusters (more on those later), but in fact the ushering in of a new era: totally home-brew and open-source pinball. As the re-themed games became more impressive from 2015 to 2016, and with easy access to leaning about hardware, fabrication techniques to develop new things and restore/renew/improve on old things, and a rejuvenated fascination with pinball in general, it was not surprising to me in the least that we would see someone totally knock it out of the park like Scott Danesi did at TPF 2017 with Total Nuclear Annihilation.
However, just in case Scott wasn't there with his amazing game (for which I placed one of the pre-orders slated to ship sometime in 2018), I wanted to produce some work as well in order to show what could be done in this realm by just two people working hard together over a short period of time. Unfortunately, while this article accounting my activities around the Wylie 1-Flip custom pinball machine is long overdue and probably should have been published way back in May, something big transpired that really made me put it off for a long time. The basis for the electronics in Wylie 1-Flip was the Intel Edison development kit, since it was a convenient mix of an x86-based chip running Linux combined with an interface supporting Arduino sketches without having to wait as long as a Raspberry Pi does to boot. However, as you may know, Intel decided to discontinue much of its 2017 hobbyist IoT line, leaving me lamenting the significant time invested into learning a dead platform and lots of memory-hogging tabs open in Chrome for my research. (Well, I'm not really lamenting the time; after all, I did study Latin, a famously dead language, and continue to tinker with retro-computers that haven't been manufactured nor supported in decades. However, using a discontinued platform doesn't exactly usher the art of pinball into the cutting edge.)
As for those two pins mentioned at the top of this article, neither has fared well: Dutch Pinball has been facing many difficulties shipping TBL to those who pre-ordered it, despite the passage of many years since the initial hype, and the value of Ghostbusters and many other games designed by John Trudeau has taken a hit (if only temporarily) since he was arrested for possessing child pornography outside Chicago in August just as Hurricane Harvey was rolling into the Texas coast.Meanwhile, if anyone needs to just drop their Ghostbusters LE edition quickly, you know how to get a hold of me... ;) Sorry, I managed to find a Pro edition for cheap, and it's holding me over just fine.
However, just in case Scott wasn't there with his amazing game (for which I placed one of the pre-orders slated to ship sometime in 2018), I wanted to produce some work as well in order to show what could be done in this realm by just two people working hard together over a short period of time. Unfortunately, while this article accounting my activities around the Wylie 1-Flip custom pinball machine is long overdue and probably should have been published way back in May, something big transpired that really made me put it off for a long time. The basis for the electronics in Wylie 1-Flip was the Intel Edison development kit, since it was a convenient mix of an x86-based chip running Linux combined with an interface supporting Arduino sketches without having to wait as long as a Raspberry Pi does to boot. However, as you may know, Intel decided to discontinue much of its 2017 hobbyist IoT line, leaving me lamenting the significant time invested into learning a dead platform and lots of memory-hogging tabs open in Chrome for my research. (Well, I'm not really lamenting the time; after all, I did study Latin, a famously dead language, and continue to tinker with retro-computers that haven't been manufactured nor supported in decades. However, using a discontinued platform doesn't exactly usher the art of pinball into the cutting edge.)
Where We Left Off
In case you missed Part 1 of this series, there was another goal besides making an awesome custom game to go along with the trend I predicted for TPF 2017: it was also to impress my coworkers and continue producing mind-blowing projects to show off alongside their top creative talent at various internal and external events. You got a slight peek at the CAD design process of the game, and the frustration around installing the various mechanisms that go on top and below the playfield, but then also learned at a high level the enhancements and innovations that went into it. Here is where I start describing the innovations at a lower level.
So I can finally close those Chrome tabs...
Despite that Intel Edison is no longer a thing, I wanted to still describe for you the stumbling blocks in working with the Edison platform that cost me so much time and trouble. Granted, there's always a learning curve with anything, but here I was biting off a whole lot at once by trying to basically hand-route all the electronics for the game and write controlling logic for it using a platform I hadn't explored too deeply for its hardware capabilities before in the two weeks or so I had left between finishing the cabinet and actually taking the game to shows. Yes, it was pretty insane, given that a "Makers Gonna Make" event was to be held on 3/2, followed quickly by TPF 2017 starting on 3/24. However, Stacy decided to take a buyout package from her employer at the time and took a couple months off work, and believe it or not, she spent a great deal of her time off dealing with artwork and 3D modeling the various parts for this machine.
As the Edison supported a couple different modes of development (one involving the Arduino IDE and another in standard C++ with gnu/gcc through MRAA), I had to choose which one would suit me the best. It looked like, at first, the Arduino approach would be simple because it was a familiar programming style and way less verbose than the C++ constructs of MRAA. My first approach was to utilize interrupts to watch for changes in state on any of the sensors, but if I recall correctly, it was really only feasible to set up a whole bunch of rising-edge and falling-edge interrupts using gnu C++. I did experiments for a long time in just trying to get reading a pin to work, but it is confusing how the pin numbers are laid out between the GPIO numbering scheme on the board, the Arduino IDE's view of the 20 standard I/O pins, and what the GPIO "files" are named on the file system.
// Arduino | Edison | MRAA
// 0 | 26 | 130
// 1 | 35 | 131
// 2 | 13 | 128
// 3 | 20 | 12
// 4 | 25 | 129
// 5 | 14 | 13
// 6 | 0 | 182
// 7 | 33 | 48
// 8 | 47 | 49
// 9 | ??? | ???
// 10 | 51 | 41
// 11 | 38 | 43
// 12 | 50 | 42
// 13 | 37 | 40
// 14 | 31 | 44
// 15 | 45 | 45
// 16 | 32 | 46
// 17 | 46 | 47
// 18 | 36 | 14
// 19 | 15 | 165
The next big annoyance was that the event loop didn't even work properly when there were rising- or falling-edge interrupts triggered. The basic premise here is simple; when an interrupt is triggered, raise a flag. Then, when the event loop runs a condition to check if the flag has been set, run the desired action (e.g. score points, flash an animation, increment the ball counter...) and clear the flag. By using rising- and falling-edge interrupts, I can monitor for the side of the button press I really care about -- the actuation, rather than the release. However, by using such interrupts on the Edison, it would for some reason only pick up on the very first pin being monitored -- the left lane rollover switch. At the time, I was only trying to wire up the three rollover lanes on top, and coded it up to read from these switches in this manner, but I obviously didn't proceed like that with the rest of the switches because functions for each rising edge on each specific I/O pin are not named explicitly in the rest of the code. Instead, I resorted to pin change interrupts, monitoring all the I/O pins for any change whatsoever. At least this way, it'll tell me which pin changed as a function argument which can get passed directly into an array, saving me from explicitly naming each pin. The downside was that I had to get serious about my debouncing code, since interrupts were being triggered on the actuation and the release of the switch, and if you know anything about switches, it's possible there were 2 or 3 such toggle cycles registered by the I/O pin before the ball moved away from the area.
I figured that there's no point in using pin change interrupts; I might as well just read all the switches at once during the event loop, setting all the flags at once before they each get analyzed one at a time (acting accordingly for whoever is pressed). It's not quite as pretty as using interrupts, but:
In the table above, you might have noticed those Edison pin numbers, and especially the MRAA pin numbers, get pretty high. This is because there are a whole bunch of other GPIO pins available on the system to be configured. I spent a great deal of time, energy, and effort trying to figure out how to tap into all these extra pins, but was ultimately disappointed that all these extra GPIO pins were only there to feed into various multiplexers to change the purpose of the 20 standard Arduino I/O pins. Because the processor inside the Edison wasn't engineered with exactly the same types of I/O registers as, say, the ATmega328, functionality such as serial UART, PWM, SPI, and even setting up pull-up or pull-down resistors in front of the I/O pins. The ATmega chips handle this all internally, but the Intel processor had to externalize this into a ton of extra GPIO pins I thought I could hack to read from more sensors, but alas not without compromising functionality I need in order to keep the rest of the system behaving as expected. To see what all the extra GPIO pins control and where the table above is codified, read this code, this article, and this thorough writeup.
In short, given that:
Then came the next pitfall: Edison Arduino C++ code can't send serial data, despite the best advice from here and here. As I was using BriteBlox LED displays as my DMD of choice (also not a great idea for quality purposes, as they tend to flake out at times, probably due to voltage fluctuations in the presence of unstable power, which is largely but not 100% helped by attaching a huge capacitor between power & ground), they must be driven by serial signals in order to show anything meaningful. I already had lots of experience writing Arduino serial routines to deal with BriteBlox as that's their native environment, but the Arduino implementation of Serial.write() on Edison just wasn't having anything to do with me. This means I had to go back to gnu C++ once again (just for the graphics & serial routines), write a routine in there to parse the .BMP graphics files I utilized for DMD artwork, and then promptly send this over serial. I ended up finding a way from within Arduino C++ to execute binaries with arguments, so each time I needed something put on the DMD (whether it's graphics or just a simple score change), I'd use something akin to this, explained here:
String hi = "/home/root/dmd score ";
hi.concat(ballInPlay);
hi.concat(" ");
hi.concat(score[player]);
hi.concat(" &");
system(hi.buffer);
Unfortunately, based on the few times I've gotten to play the game thus far, it doesn't really seem all that fun anyway. There are still some issues with the ball getting stuck and the shooter lane not working well that really hamper it (not to mention by far the most annoying electrical issues mentioned earlier), but maybe once I solve those issues, it would actually be something I would play. As you can see, the legs are built in a special way so that the machine can really be expertly nudged, because while play-testing it in Visual Pinball, the game was much more fun if you pushed on the cabinet.
I don't anticipate you'll be seeing a Part 3 of this series anytime soon -- maybe after TPF 2018 in March at the earliest, if I manage to switch successfully to Android Things and happen to solve problems in a noteworthy fashion.
As the Edison supported a couple different modes of development (one involving the Arduino IDE and another in standard C++ with gnu/gcc through MRAA), I had to choose which one would suit me the best. It looked like, at first, the Arduino approach would be simple because it was a familiar programming style and way less verbose than the C++ constructs of MRAA. My first approach was to utilize interrupts to watch for changes in state on any of the sensors, but if I recall correctly, it was really only feasible to set up a whole bunch of rising-edge and falling-edge interrupts using gnu C++. I did experiments for a long time in just trying to get reading a pin to work, but it is confusing how the pin numbers are laid out between the GPIO numbering scheme on the board, the Arduino IDE's view of the 20 standard I/O pins, and what the GPIO "files" are named on the file system.
// Arduino | Edison | MRAA
// 0 | 26 | 130
// 1 | 35 | 131
// 2 | 13 | 128
// 3 | 20 | 12
// 4 | 25 | 129
// 5 | 14 | 13
// 6 | 0 | 182
// 7 | 33 | 48
// 8 | 47 | 49
// 9 | ??? | ???
// 10 | 51 | 41
// 11 | 38 | 43
// 12 | 50 | 42
// 13 | 37 | 40
// 14 | 31 | 44
// 15 | 45 | 45
// 16 | 32 | 46
// 17 | 46 | 47
// 18 | 36 | 14
// 19 | 15 | 165
Sheer quackery.
The next big annoyance was that the event loop didn't even work properly when there were rising- or falling-edge interrupts triggered. The basic premise here is simple; when an interrupt is triggered, raise a flag. Then, when the event loop runs a condition to check if the flag has been set, run the desired action (e.g. score points, flash an animation, increment the ball counter...) and clear the flag. By using rising- and falling-edge interrupts, I can monitor for the side of the button press I really care about -- the actuation, rather than the release. However, by using such interrupts on the Edison, it would for some reason only pick up on the very first pin being monitored -- the left lane rollover switch. At the time, I was only trying to wire up the three rollover lanes on top, and coded it up to read from these switches in this manner, but I obviously didn't proceed like that with the rest of the switches because functions for each rising edge on each specific I/O pin are not named explicitly in the rest of the code. Instead, I resorted to pin change interrupts, monitoring all the I/O pins for any change whatsoever. At least this way, it'll tell me which pin changed as a function argument which can get passed directly into an array, saving me from explicitly naming each pin. The downside was that I had to get serious about my debouncing code, since interrupts were being triggered on the actuation and the release of the switch, and if you know anything about switches, it's possible there were 2 or 3 such toggle cycles registered by the I/O pin before the ball moved away from the area.
I figured that there's no point in using pin change interrupts; I might as well just read all the switches at once during the event loop, setting all the flags at once before they each get analyzed one at a time (acting accordingly for whoever is pressed). It's not quite as pretty as using interrupts, but:
- My early understanding of the disassembled code for Gottlieb's Gold Wings (1986) indicates they only use interrupts for countdown & event timers, and that they read pin statuses at some point in the event loop like this anyway
- MRAA interrupt frequency is only about 100Hz anyway due to the complexity of what's involved in checking for interrupts on the Edison, so if my event loop runs faster than 100 times per second, I'm able to react faster than the interrupts anyway
In the table above, you might have noticed those Edison pin numbers, and especially the MRAA pin numbers, get pretty high. This is because there are a whole bunch of other GPIO pins available on the system to be configured. I spent a great deal of time, energy, and effort trying to figure out how to tap into all these extra pins, but was ultimately disappointed that all these extra GPIO pins were only there to feed into various multiplexers to change the purpose of the 20 standard Arduino I/O pins. Because the processor inside the Edison wasn't engineered with exactly the same types of I/O registers as, say, the ATmega328, functionality such as serial UART, PWM, SPI, and even setting up pull-up or pull-down resistors in front of the I/O pins. The ATmega chips handle this all internally, but the Intel processor had to externalize this into a ton of extra GPIO pins I thought I could hack to read from more sensors, but alas not without compromising functionality I need in order to keep the rest of the system behaving as expected. To see what all the extra GPIO pins control and where the table above is codified, read this code, this article, and this thorough writeup.
In short, given that:
- It's unfeasible to access GPIO pins outside of the Arduino realm for your own uses
- The gnu C++ coding style requires a whole lot more variables to be created, casting to be performed, and just longer lines of code to be written than the Arduino C++ style
- Despite the documentation here and even from Intel's own site, attempts at making an input pin also utilize an internal pull-up resistor through MRAA code (and possibly the initial line states if I recall, for that matter, meaning solenoids might randomly fire upon starting the system) never seemed to work, leading me to have to solder on my own bank of resistors to the board by hand and possibly compromise electrical reliability of the system
- Evidently I was trying to do something with timer interrupts or just pure waiting around for some amount of time that didn't work in gnu C++ either, whereas in Arduino I could use a very simple delay() function
I ended up porting my pinball code back to Arduino C++ after doing all this work in gnu C++.
String hi = "/home/root/dmd score ";
hi.concat(ballInPlay);
hi.concat(" ");
hi.concat(score[player]);
hi.concat(" &");
system(hi.buffer);
Updating the score on Wylie 1-Flip.
Where do we go from here?
The next endeavor would likely have been to launch the Wylie 1-Flip game software upon powering up the Edison. (Right now, you have to reflash the Arduino side of the processor with the program in order for it to start.) However, considering that:
- Intel Edison is discontinued
- There are still electrical gremlins in the system causing random switches to appear toggled when nothing in the game is happening, meaning the pop bumper constantly goes off, the score & flipper changes at will, and the ball in play counter moves up on its own until your game is terminated
I'm keen on switching this project to the Android Things framework and hope that it'll bring about a less buggy, more electrically isolated hardware platform where I can write all my code in one place without so many confusing or deceiving constructs.
Nevertheless, here's what I have so far:
Nevertheless, here's what I have so far:
Unfortunately, based on the few times I've gotten to play the game thus far, it doesn't really seem all that fun anyway. There are still some issues with the ball getting stuck and the shooter lane not working well that really hamper it (not to mention by far the most annoying electrical issues mentioned earlier), but maybe once I solve those issues, it would actually be something I would play. As you can see, the legs are built in a special way so that the machine can really be expertly nudged, because while play-testing it in Visual Pinball, the game was much more fun if you pushed on the cabinet.
That's already a lot of hand-cut wiring, and there's probably still a ways to go! (At least judging by how the leg plates hadn't been put on yet, so there was probably still a lot being worked on)
I don't anticipate you'll be seeing a Part 3 of this series anytime soon -- maybe after TPF 2018 in March at the earliest, if I manage to switch successfully to Android Things and happen to solve problems in a noteworthy fashion.
Epilogue - And what of Ghostbusters or The Big Lebowski?
As for those two pins mentioned at the top of this article, neither has fared well: Dutch Pinball has been facing many difficulties shipping TBL to those who pre-ordered it, despite the passage of many years since the initial hype, and the value of Ghostbusters and many other games designed by John Trudeau has taken a hit (if only temporarily) since he was arrested for possessing child pornography outside Chicago in August just as Hurricane Harvey was rolling into the Texas coast.
Comments
Post a Comment