As you probably noticed my blog has a new name – Area 73.
When I originally started this blog it was just a replacement for my aging personal homepage, and I wasn’t sure how long I would really stick with it. So, I brought the blog online in a subfolder of my personal family domain name. My intent was just to blog about random life things, and in fact my first post was about cooking.
Fast forward two and a half years, and now my blog has morphed into a place for me to showcase my hobby projects. With that in mind I decided it was time to give the blog a better name than just “Josh’s Blog”. I wanted a name that invoked images of a skunk works or secret lab, and also something easy to remember. So, I opened up my favorite DNS reseller and started looking at what was available.
After some thought I decided to parody the infamous Area 51, and so I settled on Area 73. Not only is it my birth year, it also happens to be the best number. It’s also unique enough that it won’t be confused with anything else (unlike my first choice, Area 313, which is also the name of a local DJ/musicians collective).
Along with the new name I’m going to be remodeling a bit (changing the WordPress theme). It may get a bit messy for a bit, so please watch your step. 😉
Progress for the past few days has been slower than I’d like, but hey, progress is progress right?
Hardware Updates
I’ve finally ironed out the remaining bugs in the hardware interface with the propeller. Previously I had thought things were stable, but when I attempted to read back the frame buffer in my scroll code I would get corrupted data, frequent crashes, and hard lockups. I spent two long nights trying to figure out what was wrong with my code; turns out it was a couple of hardware issue…
Bus Buffer Timing
The first problem was with the propeller’s data bus buffer, a 74LVC245 used for converting the 5V signals from the rest of the system down to 3.3V. When the propeller isn’t being accessed this buffer was being held in a high-Z state by a signal called /PROPBE generated by one of my GALs. I suspect that this signal, which was generated from the output of a second GAL, introduced enough extra lag that the buffer was not shutting off fast enough. Putting that signal back under direct propeller control solved the random crashes and data corruption, but not the lockups.
Clock instability
The second problem was with the /STP signal used for clock stretching. This signal had been giving me problems since day one due to voltage level differences. The obvious solution of using an ACT series part for the flip-flop did NOT work; despite the data sheet claiming the 74ACT74 is TTL-level compatible it would not recognize 3.3V on the /PRE input. So, I had the signal hooked up with a pull-up to 5V and was relying on the propeller to pull it low. As it turns out, this only sort of worked, and it was enough to cause the CPU clock to randomly start and stop at weird times, violating the timing specs and causing random lockups. I finally gave up and just tossed a 74HCT04 inverter in front of that signal, since I had a few on-hand, and voila! No more lockups.
The system is now rock-solid as far as I’ve been able to tell. I’ve had it running for more than 48 hours with zero issues. I may even swap out the crystal and see if I can push it up to 4 MHz; previously this made the system horrendously unstable, but with the /STP issue fixed it may finally work.
Video
With the hardware issues sorted out I was finally able to get a basic console driver working, including scrolling. To celebrate I prettied up the boot screen:
The console code is still in that “hacky but at least it works” state. Some of this is due to the fact that I have been dragging my feet on implementing a real driver architecture in my BIOS. Fixing that is high on my priority list for ROM enhancements.
Another thing the console driver is sorely lacking is a robust set of control codes. I briefly considered implementing VT-100 for compatibility with the old serial console, but VT-100 is pretty complex and way overkill for my needs (and it would require a decimal parser in the console driver…yuck). Instead I’ve decided to go more Apple II-style using control characters. Right now the only thing implemented is Ctrl-A + byte to change the text attribute byte.
Sound
Not much to report here. I would like to code up a boot sound soon, but I have zero experience with the SID. I am hoping to find some code I can borrow for that.
Keyboard
Absolutely no progress here. I did briefly work on hooking up the PS/2 connection directly through the VIA, but abandoned it; I just didn’t want the VIA doing that much polling during interrupts. So, I’m back to my original plan of using an ATmega to handle the user input devices.
SPI
After much internal debate I think I’m going to offload SPI onto the ATmega instead of the propeller. The driving force here is that my current propeller interface just doesn’t really provide for a clean way to implement SPI. The shortage of I/O pins contributed as well.
The new plan is to designate the ATmega as the I/O coprocessor; it will handle SPI in addition to the keyboard, mouse, and game controllers. I have not yet decided HOW to do this, as this means the ATmega<->VIA interface will need to be bidirectional. I do have some ideas though.
Next Steps
This week’s number one priority is to get the ATmega interface up and running. Once that’s done I will implement the keyboard code and then finally be free of the serial console for now.
Time permitting I’d also like to finally start tackling the mess that is the BIOS. In fact I may make another post this week just talking about the BIOS design, as I’m finding that these posts help me think through my issues.
I said I’d post an update (with a picture) when I got the VGA console working, so here it is:
It’s very preliminary; the screen does not scroll yet, and input is still coming from the serial port (there’s no keyboard port yet), but it does work.
One thing that is rather apparent in the above screen shot is that the weird issues with some of the punctuation symbols. I’ll dig into that after I get scrolling working.
Hardware Changes
The image above was taken using my updated hardware design that I talked about in my last post, which uses clock stretching in lieu of the 65816 RDY pin to pause the CPU during propeller accesses. The previous design rarely lasted more than a few minutes without random serial port garbage, and always crashed after a few hours at most. This new design has so far gone nearly 48 hours glitch-free, and probably would have gone longer had I not powered it down for a ROM update.
Next Steps
With basic video output working my next task is to make the console scrolling work so that it’s truly usable. After that I’ll be getting the keyboard port implemented, which I’m still planning to do using a small ATmega ‘328. It’s going to connect through the VIA so it should be a lot less work than it was getting the propeller implemented.
As I mentioned in my previous post I am using a Parallax Propeller in my COLE-2 SBC project. The propeller is a neat little chip. I won’t go into a whole lot of details about it here, since that is well-covered elsewhere, but the basics are that it’s an 8-core (or “cog”) processor running at 80 MHz, and is very hobbyist-friendly. It’s a bit of an odd duck from a programming point of view, but once you get used to it you can do some amazing things with it. There are literally hundreds of open source modules available implementing all sorts of software-designed peripherals so you can do a lot while writing little to no code of you own, if you so choose.
What initially drew me to the propeller is its ability to generate video signals, both composite (NTSC or PAL) and analog VGA. It’s able to do this thanks to some custom hardware included in each cog that facilitates the generation of the proper timing and the shifting out of pixel data based on that timing. You can do this with as little as one cog, although multiple cogs working together will allow you to get higher resolutions and/or better color depth. The only limitations are that is is limited to 6-bit RRGGBB color, and there is only 32 KB of shared RAM (called “hub RAM”) available for holding your frame buffer and other shared data.
Once you have the propeller integrated into your design, however, you might as well put all eight cogs to use; the only cost incurred is adding the extra driver code and maybe some I/O pins (more on that later). With that in mind I decided to see how much I could pack into that single chip.
The Bus Interface
To start with I need a way to interface the propeller to the rest of the system. Many projects just do this over a serial link; the chip can bit-bang serial at modest bit rates, and I could easily have attached it to the second port of the UART. But serial can be a real bottleneck for graphics modes, so I wanted some sort of parallel interface. In an ideal world I would let the chip pretend to be a 32 KB RAM chip, but this would eat up every single available I/O pin and would probably be overkill for the modest graphics modes the chip is capable of producing. So, I decided to borrow from the 1970s-era TMS9918, which did all configuration and VRAM access through just two addressable registers. This means I only need 1 address input instead of 15.
To implement this I connected the propeller to 14 signals bus: D0-D7, Φ2, /IOSEL2 (from my address decoder GAL), RWB, RDY, and /IRQ. The RDY line would be held low to halt the CPU while the propeller responds to a bus request, since it’s not fast enough to keep up with the CPU at full speed. All of these signals would be fed through a pair of 74LVC245 buffers, because the propeller is a 3.3V part but the rest of my system is 5V. So far so good, or so I thought…
RDY and Waiting
As it turns out using RDY with the 65816 is not quite as straightforward as I had hoped, due to the way it multiplexes the bank address onto the data bus. The bank address is emitted during the first half of the CPU cycle, when Φ2 is low. The data bus is connected to a 74ACT573 latch, which is kept open (transparent) during Φ2 low, but which closes and captures the bank address when Φ2 goes high.
Normally this setup works fine, and in fact it’s the exact design recommended by WDC. The problem comes in when you start trying to use RDY. When RDY is pulled low, the CPU halts as soon as Φ2 transitions from high to low. The actual Φ2 clock, however, does not stop. If RDY is kept low long enough for Φ2 to go high again, the bank address latch will capture some random data bus data as the bank address, and when the CPU finally resumes it will likely access the wrong memory address.
For my test implementation I solved this by using some extra lines on a GAL to construct a latch enable signal that such that the latch remains closed as long as RDY is low. Unfortunately this seems to have made the system slightly unstable, even when the propeller is not being accessed (during which times it’s not even on the bus, as its data bus buffer is disabled until /IOSEL2 goes low while Φ2 is high.) My GALs are fast (7 ns parts), but it’s possible the extra delay is the causing the strange behavior.
For my next attempt I am going to try a different approach: halting the CPU’s Φ2 clock during the high phase using a circuit like this:
My current clock generator is the top half of that circuit, which means I already have the second half of the flip-flop available to use to add the bottom half; I just need to do the wiring. Once that’s done the propeller will pull /STP low instead of RDY, and my bank address latch will go back to being directly qualified by Φ2. I am hoping this will result in a stable system.
As a bonus I am hoping this new setup will solve another issue I have so far ignored; when first powering on, the propeller takes a few seconds to boot, during which it is not properly asserting RDY. This causes boots to randomly fail. With the new setup /STP will be low during this time so the CPU will not even try to boot until the propeller is up and running.
The Software
Writing the PASM code to implement the 65xx bus interface turned out to be much easier than I was expecting; I was able to hack out a working proof-of-concept implementation in a few hours. It isn’t even a lot of code; the basic code forming the main loop is just this:
mainloop waitpeq Pin_PHI2, Pin_CS_PHI2 'Wait for /CS to go low with PHI2 high
andn outa, Pin_RDY 'Pull RDY low
mov _in, ina 'Capture the input port
and _in, Pin_RS WZ,NR 'Check RS bit (0 = vram, 1 = registers)
and _in, Pin_RWB 'Mask RWB bit for later
if_e jmp #:vram
tjz _in, #write_register
jmp #read_register
:vram tjz _in, #write_vram
jmp #read_vram
'' Common code for all ops; unhalts the CPU, waits for /CS to go high and then loops
finish_request
or outa, Pin_RDY 'Unpause the CPU
waitpeq Pin_CS, Pin_CS 'Wait for /CS to go high again
andn dira, Pins_Data 'Set data bus pins to high-Z (input state)
jmp #mainloop 'Rinse and repeat
In the end I had a working setup in which reading the propeller on either I/O port would return a constantly incrementing byte, which I could also change by writing to either port. This allowed me to verify that the bus interface was working properly.
With the test code working I’ve started adding useful functionality. So far I’ve gotten the VRAM read and write working, and I’ve been able to successfully fill the screen with characters using assembly code running on the main CPU.
Video
At the moment the video output is being driven by the “80×25 C0DF” driver from the waitvid.2048 repository. It generates 80×25 text using a 9×16 font; each character has an attribute byte associated with it that points to a 256-entry color palette. Each color entry in turn consists of a foreground/background color pair and a blink bit. The driver also supports two independent hardware cursors that can be a block or an underline, with or without blinking. The video buffer, color palette, and the font are all in hub RAM, so in theory they could all be made changeable by the main CPU.
I would like to offer the ability to switch to an alternate video driver (either a limited resolution bitmap, or perhaps a tiled driver with sprites), but as of yet I have not worked out how to accomplish this.
Sound
Since I have video the most natural choice for another thing to add is audio. As it turns out someone has written a Propeller module called SIDcog that emulates the C64 SID chip. It takes only a single cog to run, uses very little hub RAM (just a couple dozen bytes for registers) and only two I/O pins.
The SIDcog module is very simple to use; you tell it what pins to use for left/right audio, and it returns a pointer to a block of emulated SID registers in hub memory. Reading or writing those locations will affect the emulated SID just like it would a real one. So, in theory, once I’ve finished implementing the write_register function in my bus interface I will be able to play sound.
SPI
An SPI cog is the last piece I plan to add, since at that point I will be almost out of I/O pins. The propeller will handle the actual SPI transfers and signal the main CPU via interrupt once the transfer is complete. To allow reading or writing of entire SDcard blocks I plan to implement a small 512-byte buffer. I am not yet sure how this will be implemented at the bus interface side but I have some ideas.
Future Work
At the moment I’m hard at work getting the bus interface fully implemented. My focus is on getting the video registers implemented enough that I can try redirecting the console output to VGA. Expect another post (with pictures) once that VGA boot screen is working!
NOTE: I’m going to experiment with turning on commenting, starting with this post. We’ll see how it goes.
The nice thing about creating things as a hobby is that you can switch to a new thing whenever you get bored or frustrated with the current thing. That is exactly what happened to me a few months ago: I started getting frustrated with the limited features I had available on COLE-1, so I decided to just jump ahead and start the next iteration. The result is this:
That ugly mess of wires is actually a functional 2 MHz 65816-based SBC, called, not surprisingly, COLE-2.
Hardware Overview
COLE-2 will be a substantial upgrade from its predecessor:
6 MHz 65C816 CPU (largely limited by EEPROM speeds)
1 MB of RAM
256 KB of ROM w/ full system monitor, OS, and 16-bit BASIC
ATmega328p for PS/2 keyboard/mouse and two NES-compatible game ports
Dual 65C22 Versatile Interface Adapters
SD card reader
A user port
A single expansion slot
When I started this project my goal was to produce another headless SBC similar to COLE-1, but with an expansion slot that would act as a platform for developing my custom CPLD-based video system. However it became apparent that I was trying to take too big of a leap there, and so I decided to add basic on-board video as an interim step.
My current prototype is scaled down from the final specs, since it’s implemented on solderless breadboards. It only runs at 2 MHz, and has only 512 KB of RAM, 32 KB of ROM, one VIA, and no ATmega. Much of this is just due to lack of space on the breadboard; as you can see I’m already spilling out onto additional boards, and all those long wire runs take their toll on stability, especially at higher speeds. My plan is to squeeze the ATmega in there somewhere to get a working keyboard port, and then move on to designing the PCB and building a full prototype.
In my next post I will dive into how I integrated the Propeller into my design.
Yes, it’s been five months since my last post. As it turns out writing meaningful blog posts is not as easy as I thought!
Anyway, I’ve been pretty busy, not only with life and work, but also steadily making progress on COLE-1. Here’s a recap.
SPI Support
One of the first things I did once the board was operational was start hacking together support for SPI, which opens up the possibility of talking to a lot of interesting hardware. The 6522 VIA can almost, but not quite, do SPI in hardware, so I ended up bit-banging it myself. The protocol is fairly simple, and is very flexible on clock rate, so this was not hard to do. Owning a logic analyzer (a Saleae Logic 4) also helped immensely.
SD Card Support
Once the SPI was working my next task was to hook up one of those little $5 SD-to-SPI adapters that you can find just about anywhere on the net. The hardware hookup was easy; the driver software, not so much. It took me a week or so to finally get the driver to properly initialize the card and read blocks, despite having some official documentation and some sample code. Having a small bug in my SPI driver also did not help matters!
Operating System
With the low-level bits of the SD card driver working (at least for reading), I have begun to build a real operating system around that. Tentatively I’m calling it JR/OS (Josh’s Retro Operating System), at least until a better name comes to mind.
The plan is to embed the OS in ROM and allow booting applications from FAT-formatted SD cards. Some day I would like to be able to load the OS from disk at boot time, but COLE-1 only has 32K of RAM and a largely empty 32K ROM, so for now JR/OS will remain in ROM.
Video and Improved I/O
This is where I’ve spent most of my time lately and the part I’m most excited about. For the past few months I have been designing a custom video and I/O chip using a large CPLD (complex programmable logic device). The goal is to provide VGA output, PS/2 keyboard and mouse input, and possibly hardware SPI. It’s slated to eventually be the core of my next SBC, but first I’m going to be hooking it up to COLE-1.
Here’s the early prototype hardware. The board on the left is a Max V CPLD development board; it’s hooked up via a ribbon cable to my breadboard which contains the support circuitry to interface to the VGA monitor.
The two chips at the bottom of the breadboard are the VRAM, which is not yet connected. This board will get much more full soon.
And here’s a 16-color test pattern. The board can actually do up to 64 colors at 320×240 but this pattern was easy to generate.
I’ll be making a separate post about this chip soon, because it’s pretty cool and it deserves a post of its own. Stay tuned!
In my last post I talked about breadboarding a 6809 single-board computer; well, in the last three months I’ve taken that one step further, and I am proud to say I have finally achieved my goal of building an SBC based on an original design. Allow me to present COLE-1:
This SBC is based on a WD 65C02 clocked at 4 MHz, with 32K of RAM, 32K of ROM (implemented with an EEPROM, for easy updates), a 6522 VIA driving two 8-bit expansion ports, and a 6850 ACIA providing a TTL-level serial port running at 115.2k bps.
Board Design
All of my design work (schematic and board layout) was done using KiCad, and the boards were manufactured by PCBWay. It’s a four-layer board (my first) with internal ground and power planes. Here are a couple of photos of the bare board; as you can see the manufacturing quality is quite good:
Careful observers will notice that there is solder mask over the pads for my surface-mount components; this is NOT the board house’s fault, because I see the same thing in the gerber file; somehow I goofed up my board layout in KiCad. Fortunately solder mask is pretty easy to remove with a sharp flat-head screwdriver, so I was able to correct this mistake.
The solder mask problem, however, was nothing compared to the other mistake in this layout: I accidentally picked the wrong footprint for the ACIA, which is the chip at the lower left corner of the board. What should have been a 24-pin, 15mm wide footprint ended up being a 10mm wide variant!
Rather than waste the boards I hacked together an ugly but functional adapter using a 24-pin socket, some perf board, and two strips of male headers. This turned out to be more of a pain than I had planned, due to the need to solder in some tight spaces, but I finally got it working and stable.
The moral here is always print your layout on paper and do a test fitting with your actual components before sending your layout to the fab house!
Firmware
The firmware for this board is very much a work in progress. The system currently boots into a customized version of Enhanced BASIC; from there, you can run the ‘SYS’ command to drop into my home-brew system monitor.
The monitor is loosely based on the Apple II system monitor (since I grew up using it), but currently has only minimal functionality. You can view (but not modify) memory, disassemble code, and do Xmodem transfers to and from memory. The upload function is very handy for rapid testing of new firmware; I can upload and run test code from RAM instead of having to pull the EEPROM and re-program it.
Future Upgrades
At the moment I am working on designing an add-on board that will provide VGA output and PS/2 keyboard input through one of the VIA ports, using a custom CPLD and an Atmega microcontroller. I’ll be blogging more about that project soon.
I have also been experimenting with a microSD card adapter that interfaces via SPI, with the intent of eventually implementing a full disk operating system (probably based on FAT-16). The problem is that my current hardware design doesn’t directly support SPI; I have written some code that bit-bangs it on a VIA port, but it’s not exactly fast, so I am thinking about just tying one port to an atmega328p and using that as a parallel<=>SPI interface to get better throughput.
Eventually there will be a COLE-2, probably based on the 65816, and with more integrated peripherals. I had originally intended to start work on COLE-2 once COLE-1 was up and running, but I’ve decided to hold off for a bit and see how much I can add onto COLE-1 first.
Design Files
The schematics, board layout, and firmware source code are all available in my GitHub repository.
When I was a kid experimenting with electronics my big dream was always to build my own computer. Today, after thirty years, I finally realized that dream:
This is my breadboard implementation of Grant Searle’s 6809 SBC. His basic design is just six chips and a handful of miscellaneous components. If you use a USB to TTL serial cable like I did then you can cut that down to just five chips.
This SBC is powered by a 6809 with 32K of RAM, 16K of ROM, and a 68B50 ACIA for the serial I/O. The ROM contains a modified version of Microsoft Extended BASIC as used by the Tandy Color Computer 2. All I/O is via the serial port at 115200 bps. Here’s me running some really simple BASIC commands in Minicom:
I was able put this together in an afternoon over 2-3 hours, and surprisingly, it almost worked on the first try! The only reason it didn’t work right away was because I forgot to tie a couple of MPU pins to Vcc. Once the missing wires were added it booted right up.
Grant also has designs for basically the same SBC but with a Z-80 or a 6502 as the CPU, and i plan to build all three eventually. I had actually wanted to start with the Z-80 version, but it uses an 8k ROM (a 27C64) and my burner seems to be having problems burning those; more on that later.
Now that I’ve gotten my feet wet I’m to start designing my own SBC. I’d like to build something around a 65816 but I will probably start with a 6502 or 65802 to keep the first design simple. Whatever I do I’m sure I’ll have fun building it!
I’ve pushed up some more XGS changes today! Actually some of this stuff has been in since late spring but I’ve been a bit lax on my updates…sorry about that.
Video and GUI Updates
The video output in XGS is now done via OpenGL ES. SDL is still used to create the window and OpenGL context, but beyond that all video output is done via OpenGL. The reason for this change was to make it possible to integrate ImGui, which has become my GUI toolkit of choice for XGS.
Currently the GUI consists of a bottom bar that lists the current CPU speed and drive activity lights for slots 5 and 6, and a speed control you can toggle by pressing F1. The speed control lets you set the speed that the emulator runs when in “fast mode” to something other than the base 2.8 MHz.
Raspberry Pi Support
Early on I said that I wanted XGS to run an acceptable speed on a Raspberry Pi 2, and as of this update that’s now possible. By activating a compile-time feature it is now possible for XGS to run in “raw hardware” mode on the Pi. In this mode XGS will run full-screen, talking OpenGL ES direct to the GPU without requiring X11 to be running. Keyboard, mouse, and joystick events will be captured (via SDL) directly from the Linux input event interface.
To run in the special Raspberry Pi mode you will need (for the moment) a custom build of SDL2. As of this writing the build of SDL2 included with Raspbian does not appear to have the “rpi” driver enabled, or at least, I couldn’t get it working. Fortunately it’s very easy to build your own SDL2. Here’s what I did:
sudo apt-get remove -y --force-yes libsdl2-dev
tar -xzvf SDL2-2.0.7.tar.gz
cd SDL2-2.0.7
./configure --disable-pulseaudio --disable-esd --disable-video-mir \
--disable-video-wayland --disable-video-opengl --disable-video-x11 \
--host=arm-raspberry-linux-gnueabihf
make -j4
sudo make install
This will install the custom SDL2 libraries and include files under /usr/local. You can then just tell XGS to enable the RPI mode:
cmake -DCMAKE_BUILD_TYPE=Debug -DBCMHOST=yes <path to XGS source tree>
make -j4
On my Pi 2 I’ve found I can get a little over 3 MHz, which is not great, but it’s at least on par with a real GS. I’m sure I can speed that up down the road with some optimization.
3.5″ Drive Emulation
Finally, there is finally an implementation of the 3.5″ drive emulation again, albeit in read-only mode. This means you can now boot from slot 5, although you will still need to issue a manual “PR#5” for the moment because something about the emulated slot 7 Smartport is breaking boot scanning.
Weird Bug of the Week
While testing out my latest batch of changes I found an interesting bug. If I try to launch synthLAB from an image mounted on a Smartport device it will crash on startup, but it works fine if I mount the image on a 3.5″ drive. I’m a bit stumped here; the Smartport emulation seems to have no problem booting the OS so I’m not sure why it causes synthLAB to crash.
Traditionally I’ve never been one to put a great deal of effort into Halloween. This year, however, I decided I wanted to go as Rick from “Rick & Morty”, and i wanted the centerpiece of my costume to be a fully-functional portal gun prop (it could be argued that the costume was just an excuse to build the gun). So, I set out to build the Rickest portal gun of them all: a fully 3-D printed replica containing a micro projector powered by a Raspberry Pi that could project fully animated portals with sound.
The first thing I did was some research. I immediately came across this project, which is very impressive, but it was rather limited due to the use of a projector with an SD card slot. He can’t control the projector without it popping up its OSD, so the animation is always looping, and he uses some clever hacks to only show it when he wants it to be visible. I knew I could do better.
My next step was to get an idea of the dimensions for the gun I was going to build. After checking out dozens of other projects on the web I settled on having the main body of the gun be roughly 6″ long, 4″ wide, and 2″ high. This made the gun look aesthetically pleasing, and would just barely fit inside my printer’s build volume.
Choosing the Hardware
With the dimensions chosen and some rough sketches made I set out to select the parts to go inside the gun. A Raspberry Pi Zero was the obvious choice for the controller, since it’s cheap, tiny, and has enough horsepower to run the animation I wanted to display. I also settled on a Quad Alphanumeric Display from Adafruit for displaying the selected destination universe. The display is controlled via I2C which is perfect for interfacing to the Pi. It is available in red (and several other colors), but red was out of stock at the time so i went with the white version and a red plastic filter.
For the projector I settled on an APEMAN Mini Projector from Amazon. It has a decent output brightness, built-in speakers, and a USB output that can power external devices from its internal battery, meaning I wouldn’t need to wedge in another power source for the Pi and LEDs. It also happens to be 4″ x 4″ square which perfectly fit with the dimensions I had decided on for the body of the gun. At $200 it was by far the most expensive piece of this project!
Finally, I picked up a simple rotary encoder for the control knob, and some DIY HDMI cable parts from Adafruit to build a short, flexible cable for connecting the Pi and the projector.
The Prototype
With my parts acquired I began building a prototype. I framed out a basic version of the body with cardboard so that I could test fit everything. I then hooked up everything the way it would be connected in the final product and did some testing to make sure everything played nice together.On the left is the projector, connected via an HDMI ribbon cable and a home built USB power cable to the Pi, which is in turn wired to the display and the rotary encoder. Near the top center you can see my cardboard prototype for the gun body.
The Software
With the hardware proof of concept built I set out to design the software. My plan was to write some code that talked OpenGL directly to the GPU on the Pi, bypassing the need to run an X server. I was hoping to write a fancy shader that would actually generate the portal directly, but settled for a simpler setup with a static image and a modified version of a swirl filter shader that would rotate the static image. It also scales the image to create the open & close effects. This actually took surprisingly little time to create; I went from no code to a working prototype in a couple of hours.
Here’s the image that is used for the portal:
Next I had to add sound effects. The projector has two internal speakers so all I had to do was redirect the Pi’s audio output over the HDMI. The problem came when I tried to find some good sound samples to use. I found a decent “portal opening” sound, but was unable to find a matching “portal closing” sound. In the end I used Audacity to copy a portion of the opening sound and reverse it.
Last up was the alphanumeric display. This was simply a matter of using Adafruit’s sample code for the Arduino and converting it to run on the Pi. I had originally programmed it to just count sequentially as the knob was turned, but I realized that for a “real” portal gun that would be a horrible user experience, so I implemented a “favorites list” of known destinations from the show and had the knob scroll between those.
Once the software was finished I created a “pg” user on the Pi and set it to auto-login and run the app as soon as the Pi boots.
In the end I spent the better part of a week writing and tweaking the software. It would have gone faster if I’d had a cross-compile environment set up, but I don’t, so I had to do all my compiles on the Pi. It is NOT a fast process!
Building the Gun Body
By far the most time-consuming part of the project was designing and printing the gun body. I pushed my FreeCAD skills to their limits designing all the pieces, and ended up going through several iterations before settling on a final design that worked.
When I started out designing the body I had planned to print it in four pieces. The main (rectangular) part of the body would be printed as a box and a lid, with a black front panel held in place by slots, and the handle would be separate and attached to the back via screws. This turned out to be a bad idea for a couple of reasons:
1, My original design for the box (minus lid) was going to be an 11+ hour print. Every time I wanted to tweak anything would mean yet another 11 hours, not to mention all the wasted plastic.
2. I kept running into problems when trying to design complicated designs in FreeCAD; I would want to go back and change something and it would end up destroying my entire design. This is probably just a skill-level problem on my part, but in the end it was faster for me to design multiple smaller pieces instead of a few large ones.
So after a lot of thought I decided to print the top, bottom, front, and side pieces individually and then connect them with M1.5 screws and a little glue for added strength. Here’s my first prototype for the bottom panel:
The four square posts are for holding the projector, which needs to be centered vertically in the gun so that the projector output is lined up with the center line on the front panel, and to make sure the projector’s air vents can get some circulation. The small circular posts are for holding the Pi, and the slot along the front will hold the front panel. The large circular hole is where I was originally going to put the power switch, and all the tiny holes are the screw holes.
Here is my first test fitting of the electronics on the base:
The projector fit perfectly, but the Pi was hanging over the back edge just a hair. In addition I decided I wanted the Pi farther back so that I had more room to attach the handle to the top and bottom plates. Keen observers will also notice the missing mounting post for the Pi; it broke off while I was trying to screw the Pi down.
After a couple more tries I had a winner:Here you can also see the right-angle micro-USB connector I found on Amazon. By pure luck it is exactly the right size so that it lines up with right edge of the gun, meaning I would be able to charge the projector without opening the gun.
It’s at this point that I hit my first snag. The HDMI ribbon cable was built with right-angle connectors, which was fine on my original design, but with the Pi partially underneath the projector the connector on the Pi end did not have enough room. So, back to Adafruit i went for a straight connector.
Next up were the side pieces. These would need to have small tabs to allow them to be screwed to the top and bottom plates, and would also need some carefully positioned access slots in them for the projector’s side controls and the micro USB charging connector. This was a bit tricky because the corners and edges of the projector are rounded just enough to make it difficult to get an accurate measurement with my digital caliper. I went through several iterations of the side panels before finally coming up with ones that fit; fortunately they are small and printed fast.
Here’s the partially-assembled gun with the side panels attached:
Next up was the top panel. This was actually the simplest piece to print because it only had two openings beside the screw holes, and the placement of those two openings was rather arbitrary:
And here’s the top with the display and the portal fluid cylinder mounted:
Perfect! In fact the display actually just snaps in place on its own; originally I had intended to drill some small holes and secure it with screws, but i broke my 1.1mm drill bit so I just left it as-is.
Last, but not least, I printed the front plate. I did this last because it’s black and I didn’t want to change the filament in the printer until I was done printing the white pieces. I also printed two domes out of translucent green filament and mounted those in the front panel. The domes have a hollow space inside that is just large enough for an LED. Note that there are three holes but only two are filled; the third hole is the output for the projector.
Finally here’s the body mostly assembled, minus the yet-to-be-printed handle. The top isn’t screwed down yet because I still need to fill the portal fluid cylinder and also do all of the LED wiring.
The Handle
Going into this I knew what the handle should look like, but I had no idea how to design it. It took me the better part of a day to design this one piece, and it was the most frustrating day of the entire project. The handle, with its non-square angles and curves, was well beyond anything I had ever done in FreeCAD before.
I am not going to go into too much detail on the handle design because I was learning as I went and so what I did may not be the best way to do things (and I don’t even remember all the steps now!) But the simple version is that it consists of a half cylinder base with a hollow area for wiring, and then a cylinder set at an angle as the actual handle. Two angled cylindrical shafts, one from the top and one from the bottom, provide mounts for the control knob and power switch:
Unfortunately my measurements for those access shafts were a bit tight so I had to carefully drill them out to make room.
Wiring
With the software done and all the pieces designed and printed it was time for final assembly.
I started with the portal fluid. For this I used some clear Aloe Vera gel to provide a cool bubble effect. I also drilled two small holes in the lid for the leads of an LED (after making sure the lid was non-conductive of course!). I mounted a super-bright green LED on the inside of the lid and secured it with more Loctite.
Next I glued LEDs in each of the green domes on the front panel, and then wired them all in parallel along with some 200 ohm current-limiting resistors. All connections are soldered and covered in heat-shrink tubing for strength. The whole LED circuit is then connected to 5V and GND on the Pi. As long as the Pi is powered on the LEDs will be lit.
Once the wiring for the LEDs was done I permanently attached the portal fluid cylinder to the top panel with some Loctite:
Next I connected the top display to the I2C and power pins on the Pi. The display requires two power connections; a 5V supply, and a supply line tied to the desired voltage level of the I2C pins, which in the case of the Pi is 3.3V.
After the display was connected I turned to the handle. First up was the rotary encoder. It requires five wires: three for sensing rotation and one for the pushbutton function. Two of these would go to ground and the rest to GPIO inputs. Once it was connected I carefully mounted it in its access shaft on the handle, taking care not to break the connections.
Next I cut the ends off of a USB-A to micro-USB cable and wired them together with the power switch in series, then plugged one end into the projector and the other into the Pi. I then mounted the switch inside the shaft I made for it on the bottom of the handle.
Assembly
The final step was to assemble the remaining pieces and test the portal gun. I slotted the front panel in its mounting slots, and then screwed the lid down to the body. Then I carefully pushed the handle into place, taking care not to break any of the wiring coming from the controls on the handle, and screwed it to the top and bottom panels. Here is my first power on test:
As you can see there is one TINY thing missing, and that is the red filter over the display. I bought some red plastic sheets on Amazon and cut them to size, doubling them up to produce a deeper red. I also printed a simple black frame to go around the display, and carefully glued both in place with Loctite.