Thursday, December 1, 2016

The story of those awesome "shaky benches" from TechWeek Dallas 2016

For all this time, I have touted myself as someone familiar and adept at working with hardware, and try to stay on top of all the latest & greatest gadgets in the arena.  Of course, that's very hard to do, and eventually you will find that you've ignored something that virtually everybody else in your circles seems to have already played with.  For me, that was the WS2812 LED strip.  Sure, I've used WS2812 lights recently in an IoT sensor product, but really Stacy wrote that code and we are only driving one of them per board, so it's not truly all that interesting.  Now, it's time for me to get serious with what the WS2812 lights are really meant for.


What is (was) TechWeek?


TechWeek is an event held in cities across the country. It attempts to discuss various tech trends by bringing together numerous panel discussions on things such as ed-tech [1] [2], healthcare, and smart cities, plus individual speakers talking about things like the path to driving a startup culture inside a large enterprise organization (in fact a "big bank", if "large enterprise organization" wasn't already scary enough for you).  The event began in earnest on 11/2, preceded by a Halloween party on Monday 10/31 and tours around various innovative corporate campuses on 11/1 (including at the aforementioned big bank, which I helped facilitate), and wrapped up by an epic concert featuring The Neighbourhood and others on Friday 11/4.  The keynote was given on Friday by Alexis Ohanian, co-founder of Reddit.  There are other things to do besides plenty of networking opportunities, such as startup pitch contests, startup job fairs, and lots of drinking & partying & concerts besides just the one on 11/4.

The highlight of TechWeek for me was the "Surround Sound" event at The Bomb Factory in Deep Ellum, which is where most of TechWeek took place.  At this event, attendees would be exposed to various ways in which sound could be transduced and manifested into other sensory experiences such as visual or haptic effects.  Among the pieces presented were an artist on-stage making a 3D interpretive painting with an HTC Vive, a large LED wall producing art based on live spectral analysis of the music coming from the DJ booth and an additional microphone, holographic screens where you could dance with people across the room... and my piece.

Getting in on this not just by who I work for


One of my friends from the Dallas Makerspace approached me sometime over the summer (maybe end of July or August) about working on a project that would fit the essence of what Surround Sound would be all about.  I told him to keep me posted, and it wasn't until mid-September (at the DMS Open House) when he mentioned with certainty that he'd be participating and building out his dream for the event.  It really had to do with getting paid the grant money; it would have been an expensive project to take on and have the potential of something failing or not working out.  Unfortunately that left only about six weeks to the show, thus I was recruited to assist given my knowledge of LEDs and electronics.

Nick owns a print shop and design studio, and is a very crafty fellow in the realm of everything from graphic design to embroidery as well as other unrelated hobbies such as shooting and ammunition reloading.  As such, he put himself in charge of fabricating the actual components of the piece.  This included two circular benches 4 feet in diameter with little tiny holes cut out in the top of the plywood for light to shine through.  The benches were covered by custom-cut white Spandex, which the lights from the holes projected nicely onto.  Speaking of the lights, there would be 240 WS2812 LEDs in each bench, broken up into strips of 10, 20, and 30, located in each quadrant of the circle.  At the base of each table would be a ButtKicker amp that could deliver 1500 watts at 2 ohms worth of frequencies between 1 and 200 Hz.  This amp was not really designed for propagating audio through the air, but through some solid medium like a chair that you sit on and really feel the bass tones.  Then, there would be a control table, also fashioned out of plywood and covered with Spandex, that would have 160 WS2812 LEDs in it in addition to two sonar sensors mounted into 3D-printed coverings over holes cut into the plywood.  All of the plywood was cut on the CNC router at the Dallas Makerspace and was designed to be press-fit -- no nails or staples were used to hold the furniture together.


Then there was Darcy, tasked with creating the audio effects to be used in this installation.  The idea was to allow party-goers to manipulate the audio signal into the benches somehow by using the sonar sensors.  She worked out calibrating the sonar sensors to respond to objects moving at various distances.  The sensor outputs were consumed by me through the PWM pins of the Teensy she was using (v3.2) to read the sensors, and also interpreted for the sake of performing sound synthesis with the Teensy Audio Adaptor Board.

My colleagues downstairs worked with a publicity company who managed the engagement with Nick and Darcy.  They were impressed at the scale of my network when I mentioned I knew them and they had spoken of this project with me, and then they thought it was really cool when I told them I would be helping out on the project too!

Affecting Effects


I was brought in to do the lighting effects, despite that I practically minored in sound synthesis in college.  It turns out I have a lot more LED experience of recent note, so that makes sense.  In fact, I briefly dappled in using mathematics to make art (or at least wonky patterns on 3D graphs) in college, so I applied the similar mindset (if not my long-lost knowledge of multi-variable calculus) into making the LED effects for this project.  Since this was not a true LED matrix, but rather 3 bands of lights around the circumference of the benches, it didn't make sense to do something over-complicated and two-dimensional.  A simple yet compelling one-dimensional effect that can go in a circle is simply an undulating sine wave.  Here is my first crack at such a wave:

video


This wave was built by calculating the value of sin²(x) at various points along 0 <= x < 2π.  Originally I used a step size of 2π/61 because my prototype had 61 LEDs, but later I changed the step size to be 2π/100 to be standard.  Now, sin(x) produces a very wide waveform relative to a single period, and doesn't make a lot of distinction between lights that should be on versus lights that should be totally dark.  By squaring the sine of x, values close to 1 become even closer to 1, and values close to 0 become even closer to 0, thus making the waveform even "peakier."  Actually, I avoided doing an annoying squaring operation by looking this function up in Wolfram Alpha and finding out I could calculate (1 - cos(2x))/2 instead.  And speaking of looking things up, I stuffed all the output values from this function into a lookup table for convenience later.  As the program would constantly be calculating the same values over and over (and wasting an untold amount of CPU cycles in doing so), I figured the lookup table would be best for performance in the long run.  (All the people who developed wavetable audio synthesis products would probably agree with me.)

I also coded additional lookup tables to vary the color of the LEDs over time.  Originally we would swing them from red to purple to blue based on the value of one of the sensors.  The sensor would influence which index of the color lookup tables we would access; the same index for both the red and blue channels would be used.  (For this case, green could always stay 0.)  These lookup tables were based on simple ramp functions that slowly eased the value of that color channel from 0 to 255, and then held it constant; one channel's graph looks like the other one reflected over the Y axis.  Then, the speed of the undulation (the wave spinning around the circle) was affected by interpolating the other sensor's value to a number between 1 and 14, which corresponded to the offset we would add to the lookup table to calculate each LED's intensity.  This way, the waves would appear to move around the benches and table.

Stressing Over the Finishing Touches -- Heck, Just Getting It To Work!


After a couple more weeks where Nick & Darcy were working on their parts, I got engaged again to make further modifications to the LED effects and to help with the assembly.  One enhancement was to change the colors to sweep from red to white to blue.  To change purple to white, I took advantage of the green color channel.  The function for green is simply the negative absolute value of x so the lookup table ramps from 0 to 255 and then back down to 0.  Now each RGB channel has its own lookup table.  We also continued to toy with the exact formula producing the sine wave, and I made some changes to the sine wave lookup table so that the same lookup table could be used among the three different-length LED strands below the bench.

The few days prior to the event were quite stressful, and we put in long hours to get the finished product working.  The hardware we were using wasn’t even nailed down, so I attempted to use a Raspberry Pi to drive the LEDs and perform audio signal analysis all from a single source.  It ended up being overly complicated; audio signal analysis doesn’t go incredibly fast on the Pi, and lots of things would need to be downloaded and compiled for it to work.  It turns out that doing such analysis probably wasn’t even necessary anyway because people really seemed to enjoy adding simply the bass drops; it is something concrete and distinct that folks can understand rather than having one of us standing around the whole time explaining to people how to compare some subtle property of the audio with the audio signal in general.  Lastly, I tried to implement some beat detection with the Arduino so as to synchronize the bass drops with the beat of the music and perhaps do one or more bass drops per beat.  Ultimately, time ran out for that enhancement.  The hardware we ended up using consisted of a Teensy in the control table for sensors and sounds, an Arduino Uno in the control table to drive the lights, and then the benches had either an Arduino Uno or Leonardo in them to run their lights, depending on whatever we had on hand.

Since the Teensy reading the sensors needed to drive signals consumed by the other Arduinos to produce the light effects, I had to be careful not to use outputs on the Teensy that overlapped with the pins that the audio board needed.  For this reason, I used three specific PWM pins to output to the three other Arduinos so that each pin would only have to drive one TTL load.  However, the Arduinos would need to read these signals in as analog, so I used specific values of resistors and capacitors to make an RC circuit with characteristics that made me happy.  The biggest thing to consider in an RC circuit is the tradeoff between signal rise time and ripple.  Too much ripple in the output would cause bizarre pulsing of the colors or a confusing change in the rate the sine wave would spin around the table.  However, too much rise time would cause the sensors not to react fast enough to make the effect make sense to people interacting with it.  I played around with some websites to calculate the theoretical values of resistors and capacitors I needed, ultimately settling on 100 ohms and 10 μF (if I recall correctly), and then verified their behavior with an oscilloscope.  Once I was satisfied, I added the components to the breadboard and wired up the RC circuit to the analog inputs on each device, and then the appropriate outputs on each device to the LEDs.

Wow, what a mess…

Apparently WS2812 LED strips are quite sensitive to noise.  This pernicious problem can come from all sorts of sources, including shoddy soldering/cabling or EMI through unshielded cables.  Thus, for our long cable runs, we spliced up twisted-pair CAT 5 cables to provide power, ground, and data to each device.  We twisted up regular wires using a power screwdriver/drill to provide the same type of cross-talk cancellation between our LED light strips.  The problem is there were so many LED strips under each table that it required soldering almost 40 wires per table.  There's not a lot of landing room on the pads once you've cut them up, and remember we're soldering in some cases upside-down right on a plywood surface!  Talk about a fire hazard.  And even with all this precaution taken, we still had a big issue with noise, but only on the art project.  The lights would work just fine on a regular uncut LED strip, but the exact same code would produce all kinds of odd rainbowing and strobing effects in the installation pieces.  We tried reducing the noise by putting a resistor on the data line, but that didn’t help.  We then brought in Stacy, resident capacitor expert, to solve the problem that way.  Since the power line was noisy, she simply placed a capacitor across power and ground on one LED strip in each art piece, and now all that weird flickering and strobing went away, leaving us with the desired effect.  Of course, all this messing around with wiring didn’t come without occasionally shorting our power supply or zapping ourselves with 5V at 60A.  I can’t claim to have felt anything, but Nick says he got zapped a few times.  Maybe I’m just immune to 5V shocks anymore, even at 60A!

The audio synthesis code was a bit suspect at times too due to various bugs writing the frequency and amplitude to the two different audio streams.  I found out that each sensor was writing to one single stream in turn, very rapidly, which was probably not the desired effect.  We played around with “glitches” by writing various sensor parameters as either amplitude or frequency to the audio stream until we found something we liked.  This made me think: when will they come out with SuperCollider for embedded systems?

Wastes of Time, Including That Mother-F***ing Map Function


One thing that really wasted my time was trying to use the map() function provided by Arduino.  Literally all this function does is solve y = mx + b by taking two points of x and two points of y.  That’s really how they should describe it in the documentation.  Instead, they go on about some hand-wavy type of description that doesn’t make it clear what kind of boundary conditions exist when you use this function.  Apparently, there are no boundary conditions, and I needed them.  The PWM pins from the Teensy were basically sending me values between [0, 32) but scaled to between [0, 1024) because that’s the range of the ADC on an Arduino.  As I received input, I needed it to consider what would be actually appropriate ranges for those input values because they were being directly used as the indices for our lookup tables.  By simply re-implementing the map function to do what I needed it to do in the first place, I could have saved having to debug program crashes from index-out-of-bounds errors, which resulted in hours of busting my head on why the LED effects would freeze.  Of course, I could have just added min()’s and max()’s to the output from map(), but by that time, I was ready to call up the university of whoever made that function (or at least the documentation for it) and tell them to revoke his or her degree.

Another thing that sank a lot of my time was chasing down the correct signal lines.  As we added all the devices to the breadboard in the control table, the circuitry became rather jammed and hard to work with.  Often, I would find myself probing the wrong output when trying to test why the analog signals were causing different or undesired results between the different pieces.  On the final night before the big show, I wasted about 80 minutes in this manner trying to debug one single problem.  By the time you’ve worked so much on something and are feeling a lot of pressure, it can be easy to overlook such simple things as you stress the technical issues that you think it could be.

But When It’s All Said And Done…


Nevertheless, despite what it could have been and what it ended up being, we all believed it worked out the best for what the show was.  Anything more and it could have been overwhelming or confusing (or even annoying) to people, despite how much fun it would have been to get geeky with signal processing to, say, add in sparkling effects, add more spinning waves, or even use a solenoid to bang a drum.  Here is a short video of what we produced:



The code for the project is on GitHub at https://github.com/Drc3p0/SurroundSound .  Some highlights from that code:

We ended up going with sin^6(x) as the function to produce the wave in our lookup table.  It was achieved with this for loop:

for (i = 0, x = 0.0; i < 100; i++, x += (PI / double(100))) {
    //sin^6(x):
    overallAmplitude[i] = ((-15 * cos(2.0 * x)) + (6.0 * cos(4.0 * x)) - cos(6.0 * x) + 10) / 32.0; 
}

The lookup table consists of 100 float values.  Originally the literal 100 was a constant called QUANTA.  So, no, the actual code does not have me writing double(100) like some kind of weirdo.

The benches had 240 LED lights apiece within three circles.  One circle had 40 lights, the second had 80, and the biggest had 120.  However, these three circles were treated as a single string, so the first 40 were drawn on the string, then the next 80, then the last 120.  But it’s in fact even more complicated than that: when the tables were wired up, each quadrant (i.e. section of 10, 20, and 30) were wired together before the string would go on to the next quadrant.  This is why the tableSide variable exists in the showFrame() function.  The variable p inside each for loop keeps track of specifically which pixels from each circle are being drawn, and the variable scaledIndex converts the proportion of (pixel index : string length) to find the pixel index relative to the lookup table of length 100.

for (int p = int(PIXELS_INNER * beginRatio); p < int(PIXELS_INNER * endRatio); p++, pp++) {
    scaledIndex = p * 100.0 / PIXELS_INNER;
    rVal[pp] = figureColor(scaledIndex, redColor[colorIndex]);
    //etc...

The lookup tables for red, green, and blue values were defined as bytes rather than floats in order to save memory.  Had I chosen floats, the Arduino Uno would not have enough memory to store all the lookup tables I desired.  Originally I wrote the functions providing the lookup tables to provide floating-point outputs between [0, 1].  When using a byte array, I rewrote them to output integers between [0, 100].  This allowed me to scale the final output by 2.55 rather than 255 when coming up with the final intensity values for each pixel.

Before new data would be pushed to the LED string, the color value for each LED would be stored into an array of type byte as well.  The value in the array was derived from solving an equation relating the LED’s position index in the strip and the motion offset of the sine wave to which value of the lookup table it should get for its intensity.  This intensity value was used to scale the color values from each channel’s lookup table, and the index used to look up the channel values was given by the most recent reading from one of the sensors.  The final color of each LED is given by this function:

byte figureColor(float scaledIndex, float colorAmount) {
  return overallAmplitude[int((waves * scaledIndex) + stringOffset) % QUANTA]
        * colorAmount
        * 2.55;

}

Thursday, November 24, 2016

Making a ROM hack of an old arcade game

An interesting work project, right?


I was invited to take my Giant NES Controller to a recruiting event for work taking place at a local brewery.  But you can't just have an NES controller without anything to play, right?  Thus, I pitched a couple ideas on custom games to go along with the giant controller since I didn't want participants playing anything standard either.  The thing they agreed to was to feature the Tapper arcade game, by Bally/Midway in 1983, but modified to show the brewery's logo instead of being Budweiser-branded like it was originally.


My handiwork, about 80% done, and with a tiny glitch.  Can you spot the remaining issues?

Now you might be thinking Tapper wasn't ever ported to NES, and that's correct.  However, with the help of the MAME arcade emulator and a Hyperkin USB->NES adapter, I could add the Giant NES Controller as an input to this old arcade game, and no one would be the wiser.  To facilitate this required lots of learning about MAME, though, because I assumed it would just work right away with my controller.  However, the inputs have to be set up to look for button presses coming from the controller; by default, it is only listening for the keyboard.  The Giant NES Controller is very unwieldy, so I set it up first with one of the regular NES controllers around the house.  The first one I tried was flaky, and I got scared for a moment that my project wouldn't work.  However, I grabbed a more reliable one that conveyed the correct button presses rock-solid, and luckily my Giant NES Controller worked just as well anyway.


Ready to look at lots and lots of hex code?


The Tapper hack was done thanks to the help of MAME's awesome tools and a hex editor.  I downloaded the ROMs for the game and figured out the naming convention they used to describe what the files are.  There are four program ROMs, two background graphics ROMs, at least one foreground graphics ROM, and several others including sound ROMs.  Among these, I only needed to edit 3 of the program ROMs and the background ROMs to change the logos on-screen.

The first thing to figure out was how the background tiles were arranged and how the colors were encoded in the background ROMs.  Luckily, MAME's tile viewer helped me see which tiles would be where in the memory.


A snapshot of tiles 0xF0 - 0x19F in Major Color Palette 1 using MAME's tile viewer.  By this time, I already replaced most of what I needed with Community.  Their original arrangement of tiles clearly doesn't lend well to seeing what the intended image was; maybe they were using something like Gray codes to optimize memory accesses.  I said "to hell with all that stuff" and opted for sequential tile addresses and image clarity.  I haven't noticed a performance difference. :-P

I opened up a hex editor (specifically Hexplorer, which has a color scheme like The Matrix which makes it really easy to detect large vs. small hex values in each byte) to look for similar patterns as what appeared in the tile viewer.  I discovered somewhat similar patterns, but not exactly 100% identical.  This is because each background ROM actually has a two-bit encoding per pixel of the tile.  One ROM contains a background color of index 0-3 and the other ROM contains the palette number of 0-3.  Thus, two different-colored pixels could have the same background color index but a different palette number, making them appear different in the game but indistinguishable from each other in the color-index file viewed in the hex editor.  I experimented with editing the tiles, and my first couple attempts did not yield the desired results, but eventually I got the hang of it.  It's a very tedious process to do by hand.


The first few bytes of both tile files.  One is already changing because it's the two-bit encoding of what color to use from the minor palette.  The other is not changing yet because the minor palette to use is desired to stay constant for the time being.

The first step is to see how many pixels each tile is in height, and then line up the hex editor so it seems like each change in data or each pattern seems to take up that many rows.  (I was afraid the graphics would be compressed with RLE at a minimum, but was extremely pleased that they used just a strange encoding scheme rather than any compression.)  This gives you a feel for how many bytes there are per row.  Note there are only two bytes per row, and with eight pixels per row, this means each pixel is encoded with just two bytes in each file.  It was easier to determine this by looking at the solid colors at the very beginning of the tile files rather than trying to spot a particularly distinctive tile with a letter or graphic because rendering in your head what (for instance) 0x55 looks like versus 0x17 just doesn't come quickly.


Really, it's all just a game of pattern recognition


The next thing was to rearrange the tile order on the screen so I could have it say Community.  Community's logo is shorter but wider than Budweiser, so I wanted the tiles remapped so I could maximize the space where I needed it.  Now on top of doing pattern recognition for the background ROMs to find the right tile number, I had to analyze the program ROMs to find out how they actually drew the backgrounds.  Luckily, this was fairly easy to do.  I found out that each tile is represented by two bytes, and there are 32*20 tiles on-screen per level.  Thus, there are 1280 bytes in each screen, and roughly 16K worth of screens.  But I didn't find that out until I used a bit of intuition while searching the ROMs for the clues from the tile viewer.


Think about this in terms of probability or from an entropy perspective.  The Zilog Z80 instruction set is comprised of a great many opcodes, making big chunks of code look random and uniform (as in a uniform distribution, where all byte values from 0 to 255 seem equally likely) to someone inspecting the machine code in a hex editor.  However, the background tiles will seem to take on much less of a uniform distribution, exhibiting tendencies to repeat the same or very similar numbers many times over.  You might also start to see some of the tile addresses called out from the tile viewer.  In fact, the byte highlighted in red is where the screen for the 1st level begins.

Because the tile information is in program ROMs 1 & 2 (zero-based, as in 0, [1, 2], 3), this presented yet another problem: whenever you change the contents of these ROMs without modifying the checksum in ROM 3, you will get a ROM error corresponding to whichever one(s) are wrong when you boot the game next.  I eventually learned how to modify the checksum, but not without using the MAME debugger.  This was a real trip, because I used the debugger to figure out when the memory address for any byte in the string "ROM ERROR" was read.  (I was extremely fortunate that they used ASCII encoding for this string, otherwise it might have never been apparent where this occurred!)  Once the debugger read this address, it continued to read the other addresses in sequence, so I stepped back in the debugger to find out what code caused all the bytes in the ROM to be read.  It ended up being somewhat of a hassle to figure the checksums each time, but then a mega hassle to program them in, especially for ROM 3, as you end up creating a circular reference.  For instance, if ROM 3 calculates its own checksum to be 1 more than what is programmed into itself, when you modify it to be 1 more, then it calculates it to still be 1 more than the new value you just wrote in. :-P  I knew I would be modifying the tile map in ROMs 1 & 2 a whole bunch before I was finally done, so I ended up replacing the code that verifies the checksum with a bunch of NOP instructions so that the game would never call the code that calculates the checksum at boot.

The final thing was to perfect the artwork.  Stacy hand-traced the Community logo scaled down to the right size and with the respective palette.  I wrote a program that would take a .BMP file consisting of this imagery, along with a "tile file" and "color file", and overwrite the part of the ROMs designated in the tile file with the appropriate 2-bit representation of the colors as dictated by the color file.  The color file mapped each color present in the bitmap to a background palette number for that ROM and a color index for the other ROM.  The reason I had to write this utility (in Python) was because the other tile editors on romhacking.net didn't seem to be up to the task of parsing old arcade ROMs correctly.  The closest I got was one that added an offset to each tile so that it was scooted left a couple pixels.  Also it seems like they aren't accustomed to having to deal with *two* asset files for a single tile.  I'll have to clean up this utility so it's a little bit more user-friendly and then share it.

Oh, and one other thing: there's yet another palette setting that gets you *four* groups of four groups of four. :-P  So now, besides the 2-bit color index and the 2-bit background palette number, there's actually a tile designator that selects which group of 16 colors applies to the tile.  This isn't in the background ROMs, but in the program ROMs.  What it seems like to me is that the tile ROMs start in a particular address in memory, and then get aliased (e.g. the contents are all the same between addresses 0x3500, 0x5500, 0x7500, and 0x9500) but the higher-order address bits actually indicate which set of 16 colors get used for that tile.  Part of that addressing scheme also seems to indicate if the tile should be drawn right-side up or upside-down, but I've only encountered about 6 spaces in the game where this happens and wasn't able to make it do exactly what I wanted.  Originally it had drawn in one tile the upper half of the Budweiser bow, and then mirrored it in the tile below, rather than basically duplicating that tile except drawing it upside-down.  I wanted it to be changed to a U shape, so I drew in a U into the background ROMs and then thought I changed the tile map to call the new tile un-reflected, but instead what I ended up with is two parallel bars (the top of the U).  So that still has yet to be figured out...


ROM Hacking Is For Players Only


As you can imagine, there was lots & lots of play testing along the way.  It is important to make sure all the graphics are working just as you expect, otherwise you might get embarrassed if someone gets farther along in the game than you did and sees tons of glitches.  The good news is you will have plenty of time to practice (assuming you're not up against a really close deadline).  I had saved states on MAME for Tapper all the way up to level 9, which I still haven't managed to beat by this article's posting date.  That's OK, because at the event, I was sure someone would get up even higher than that, but most people didn't even make it past level 1!  A couple of my coworkers at the recruiting event challenged me to see what kind of score I could get on Tapper, but by the time I finally lost, they clearly got sick of watching me and had wandered off. :-P  I managed about 25,000 as my highest score that night with the giant NES controller but the next best person only got about 6,000 or so.

I ended up installing RetroPie onto a 64GB SD card in a Raspberry Pi, and then I copied the Tapper hack from a USB stick into the right directory on the file system.  After restarting RetroPie, it recognized Tapper as a legitimate option, and I was grateful that MAME's own internal checksums did not stop me from running the game!  On my laptop where I was making the hack, I had to open MAME with the command line argument to start Tapper, because loading the hacked Tapper through the regular menu would cause a fatal error telling me the expected checksums mismatched.

So yeah, that's the long story of how a ROM hack gets done... Anyway, I have set up a class at the Dallas Makerspace to go over these techniques in greater detail on 12/4/2016 at 3 PM local time.  Join me if you're interested in learning more!

Thursday, October 6, 2016

Talk to me about Battleship… your job could depend on it!

The classic game “Battleship” has captivated military sailors and civilians alike for at least 100 years as players attempt to hone in on a strategy to destroy their opponent’s ships placed on a game board while avoiding detection and certain destruction of their own ships.  The game became one of the earliest home computer programs when it was released on the Z80 Compucolor back in 1979, according to Wikipedia’s sources.  And for more than 35 years since, it has been a concept one could use to test someone’s knowledge of programming a system in an elegant way.

Recently, I sat in with a colleague of mine while he interviewed a prospective employee.  The idea of using Battleship as the pseudo-coding exercise came to him while he was surfing articles on the Internet regarding good thought exercises to engage interviewees.  We work for a company where object-oriented programming is the flavor of choice, and so we were interested in how our prospie would arrange the battleship game to best leverage the features of object-oriented programming languages.

As this thought exercise was posited, I started thinking about how I would arrange the game to take advantage of the OO paradigm.  I imagined having a class Ship that would contain various parameters about itself, such as its size, X, Y, and orientation.  It could also potentially have an array consisting of the tiles it occupies rather than its own size, or Ship itself could even be an abstract class that has subclasses Size2Ship, Size3Ship, and Size4Ship.  The game board could consist of two ArrayList<Ship>s, one for each player, and the first player with an empty ArrayList would lose.  There seem to be a few valid ways to go about arranging the data, nevertheless.  Then, you have to consider exactly how you want to track what part of each ship has been hit, including iterating over each Ship to see if it has the desired point, not to mention checking for invalid inputs or configurations such as placing ships that overlap.

As we were watching our candidate think through the process of building a Battleship game using the tenets of OOP, we realized something fantastic about this particular problem.  Because of its grid nature, it lends well to being implemented with standard procedural paradigms where you simply track everything with a 2-dimensional array and don’t really pay a whole lot of attention to the concept of individual ships existing on the board.  To be honest, I think this sort of implementation for the Battleship game in particular is a bit easier since it reduces overhead (it’s certainly easier to implement it this way on a Zilog Z80 processor from 1979, for that matter).  Then instead of having to ask each ship if it has the desired square to hit, you just check one single foundation of truth — your 2-dimensional game board array.  Our prospie had many years’ experience with things like low-level firmware for embedded devices and writing drivers, so it’s understandable this would be their tendency too.  But since we did ask for something truly OO, we ended up witnessing something that really made us appreciate why Battleship was a good choice for an interview.

What came out of the thought experiment was a game where, instead of numerous Ship classes being generated, there was a single Ships class that would keep track of a fixed number of ships on the board using various arrays and tricks to ensure the validity of the data.  While the tricks were indeed cool, you can imagine this might as well have been a static class where one instance of it gets initialized and then procedural logic controls the game flow thereafter.  The nuances of objected-oriented design really weren’t explored at all during the thought experiment.

Battleship probably isn’t the only good test case.  There has got to be an entire set of problems or tasks one could solve easily in both procedural and object-oriented paradigms.  These problems, such as Battleship, make a great test to see if someone is truly an object-oriented thinker.  They “camouflage” themselves due to:
  • Potential to solve the problem by simply using familiar data structures such as n-dimensional arrays
  • Not having a hierarchical “is-a” or “has-a” structure that absolutely must be enforced for the logic to work


Now is where you can contribute!  Share your thoughts on any other good test problems you’ve come across that straddle this object-oriented versus procedural paradigm, or suggest additional bullet points for above as to what makes a good problem, or tell me how my design for object-oriented Battleship sucks because it wastes way more memory or CPU time than yours… in the comments.

Thursday, July 7, 2016

It's a Huge 100x Faithful & Functional NES Controller Coffee Table!

A great deal of my time was spent from Memorial Day weekend up through now trying to produce this gigantic piece of functional furniture, and now I am pleased to present an Instructable on how to make one for yourself!  Here is a link to the Instructable.

Hint: It is helpful to have a Makerspace handy.

I actually started this project back in January in an effort to get certified on the CNC table router at Dallas Makerspace and start cutting pinball playfields.  I made a post previously about my experiences and learnings from that particular endeavor, but as you can tell by the edits, I didn't quite get it right the first time.  It took a bit of time before my schedule could clear up in order to try 3D parts again, and lest I forget the procedures involved with using the table router, I thought it'd be prudent to try building an even bigger NES controller than what I did to get certified.  My proficiency test involved making a 16x-sized controller, but here I would be attempting a 100x-sized controller that would take up an entire 2'x4' sheet of MDF (medium-density fiberboard).

I measured everything incredibly accurately and made 3D models of various parts in Blender prior to even using the CNC mill, and this took me maybe 20 hours' worth of work for both the 16x and the 100x version.  I even brought my laptop into the woodshop and spent about 5 hours cutting and tweaking G-code to make the perfect controller face and route out channels for the wiring and a pocket for the controller PCB to live so it would actually work.  Over Memorial Day Weekend, I was out at the Makerspace cutting the controller face from about 9:30 PM until 3:00 AM one night.  Fortunately, I came home with a wonderful-looking piece of MDF.

However, the fun had only just begun...

Milling is easy... Sanding and painting take forever!!!


Stacy & I worked feverishly to sand down the controller face and buttons for their initial coat of primer, then sanded everything down again ahead of putting on paint, and then sanded everything down yet again in between each coat of paint.  The buttons were finished with copious amounts of lacquer so their finish would stay good.  However, the controller face itself was another story.  With the multiple colors of paint and lettering required, it took quite a bit of waiting around, placing tape-n'-drape, rearranging the tape-n'-drape, and using a stenc...oh wait... I need to make a stencil for the letters!

This led to another several hours in the CAD tool and with image processing tools.  I needed to get high-quality vectors of the letters, and could not trust the vector tracers that muddled pictures of a real controller.  Luckily, I found artwork representing the shapes of the various letters on the controller face (namely A, B, SELECT, START, and Nintendo).  I spent a while in Photoshop massaging these to lay on top of a blown-up image of the controller, then converted these edges to vectors before re-importing them into VCarve so I could render the stencil design.

Then, there was the dilemma as to what material to use for the stencil.  I planned to use Hardboard, but was concerned about the way it cuts leaving tonnes of little fibers that could affect the quality of the edges of the stencil.  My other choices were laser cutting or just using a vinyl cutter and making these things all decals.  Well, those two wouldn't work because:
  • I'm not trained on the laser cutter.
  • I didn't have a piece of acrylic handy that would be big enough for the stencil.
  • Vinyl decals don't give the same texture as the paint, can't be reused, and would look like garbage if you apply them wrong or get so much as one crinkle or air bubble in it when you apply it.
I wish I could have gotten a lasered stencil, since the edges wouldn't have needed any finishing, but I went with what I knew anyway.  This led to many, many more hours sanding the insides of the letters, then painting the letters, then sanding again with finer sandpaper in order to get rid of all the leftover "fuzz" from the hardboard.

Painting the letters was still not easy with the stencil, and they didn't really look good afterward.  Fortunately, Stacy has mad skills with knives and detailing brushes, and was able to scrape away excess paint or just paint over it.  The fun wasn't over yet, though; the sides of the controller still had to be put on, not to mention the wiring!

Wiring it up for the first time


I spent probably four hours carefully measuring 26AWG wire, splicing it, threading it through the holes of the switch contacts, and soldering it down to make a good connection.  Once all was said and done, I wanted to try it out on our NES.  The Retron 5 was conveniently located upstairs with Kirby all ready to boot, so all I had to do was plug it in and play.  However, I didn't bother putting the buttons on the switches since they weren't necessary to verify the electrical function.  It's really difficult to play this controller while sitting on the floor and trying to hit the exact switches.  Once I was through with the electronic testing, it was time to put the sides on.

Oh wait, I still need to do something about that PCB.  Originally I had cut all the wires so that the PCB would hang outside of the NES controller.  I wanted it really to be pocketed inside the controller, so I had to go and shorten all the wires I spent such a long time working on already.  After about another 4 hours of untangling wires that had commingled with each other and dealing with weak traces on the PCB that had broken off as a result, I was ready to actually mill out a PCB pocket and glue the Hardboard down.  This would permanently enclose the PCB into the Hardboard, as we would liberally apply epoxy to everything to ensure it would be durable and wouldn't budge.  We prayed that no damage would happen to the electronics inside the controller after this stage!

Edging and More Painting


Once this was done, I needed to install the edges of the controller in order to give it some depth.  I excitedly drilled out a hole that the controller wire would be threaded through, but forgot that the side I drilled into would be the side that faces inward, not outward.  So when I threaded the wire through this hole, it ended up being totally the wrong way -- my detailing (beveled edge) was pointed inward, the side piece wouldn't line up flush with the edge of the controller face, and the side piece naturally wanted to be placed above the controller face rather than below it.  Wrong, Wrong, WRONG!  I ended up having to cut a channel out of the side piece to liberate the wire, and then drill another wire which was also about 1" off from where it needed to be, so I drilled a third hole which was finally right.  Once this was done, I cut yet another channel out of the side piece and allowed it (and the other three side pieces) to set overnight.

This process was nerve racking because the controller face was already beautifully painted, yet here we were planting it face-down on a dirty work table getting who knows what kind of scratches on it while we try to put on the sides.  Meanwhile, the side with the wire coming out of it loved to fall down, and I was just hoping that no damage was happening to the electronics as a result.  I also had the controller face-down to mill out the PCB pocket, which I didn't do originally because I didn't have the exact dimensions of the PCB at the time I was cutting the controller face, and when I went to go do it, the table router was down (along with other big machines) for general electrical repairs to our 3-phase power system.  Thus, one of the other members guided me in using a hand-operated mill to mill out the PCB pocket.  That was actually kind of fun, as you just go to town on the piece -- no need to do much planning or CAD work or exact measurements on a computer screen.

Final Touches


After the edges were in place, it was time to make them look nice and smooth with the controller face.  Despite our best efforts, the edges weren't exactly flush with the controller face, meaning we would need to sand down the edges that were too high.  We also used Bondo to fill in the seams, because after we would sand, it would be obvious these were several boards fused together.  With the Bondo, it's way less obvious how we fabricated it and the controller face looks great (and seamless).  This involved carefully applying Bondo to the seams without getting any on the controller face, and then once it cured, we had to go outside to paint over the Bondo with gray.  This was a bit nerve-racking too, with the controller face already being so nicely painted, so we made sure to totally cover it with tape-n'-drape so as not to ruin any of our hard stenciling work.

Oh, and did I mention that by now, it's the night before Let's Play Gaming Expo?  We're supposed to load this piece in tomorrow for the exhibition starting the following day.  As the first round of gray paint dries, I go to work creating some paper stencils to fill in the "letter holes" -- the middles of characters like A, B, and O.  My first technique for dealing with these didn't work at all (where I was trying to keep the middles of the milled-out characters, sand them down carefully, glue them to skewers, and then hope I was holding them down hard enough and evenly while the spray paint is applied).  So once these were done, I taped them over the letters and Stacy went at them with a paintbrush.

As she was doing this, I decided to do one final electrical test.  I brought our real Nintendo downstairs with Mario Bros/Duck Hunt, and tried to go to town.

Oops...

The A & B buttons, and Start, don't work.  Sh*8*@%!!!!.

Luckily Stacy is a smart one, and figured maybe it was a problem with a ground wire becoming disconnected from one of the buttons.  I was about to be beside myself with fury and rage if this thing got broken somehow and it was one of the actual button signal wires that got messed up, since that would have been about impossible to fix that close to showtime.


Here we are burning the candle at both ends at the 11th hour.  It is almost midnight Friday, 6/17, the day of load-in for LPGE.  Look closely at the picture and you'll see a blue alligator wire linking the ground line on SELECT to START.  Fortunately, this solved our problem!  All the buttons worked fine with this one modification, so I made a wire for this and installed it the next morning (so we wouldn't have the ugly blue wire exposed and messing with gameplay).

Finally, our touch-up work was done, and our gray paint was dry, so we applied a coat of lacquer to dry overnight.  Then we had another coat of lacquer dry during the time we were at work.  Unfortunately, by this time, the weather was starting to become very hot and humid like it does here every year, so our lacquer has some nasty streaks and imperfections in it.  Fortunately, we should be able to hit it with a heat gun and smooth it out.

What a time!


We brought it to the show and it was a big hit.  The guys running the free play home console room were ecstatic to have it because apparently one of their other ideas for a big show piece fell through.  They plugged it into the old-school console TV lent by the National Videogame Museum in Frisco, and that made it extra-cool and really a sight to behold.  It drew all sorts of folks, from little kids who don't know how to do anything but mash buttons all the way to experienced Super Mario Bros. speed runners.  Some folks played it solo, others paired up to share the D-pad and A/B buttons.  Anyway, I wrote more about the Expo in general and some of my thoughts in the previous post, but didn't mention much about this controller because I knew I'd be writing this much more detailed article next.  After all, I put so much effort into this piece this whole year, especially in the past month; it deserves its own article.

And the fun didn't stop there... After the show, I spent a while getting together pictures, documentation, and recollections, while modifying various CAD files to reflect a more sane and logical set of steps (as well as to expunge any trademark violations therein).  Including this post, it's taken me about 5 nights' worth of work just for all these documentation and housekeeping types of steps in order to share the mechanics of the project with you!

And here's a little treat for making it to the end: a link to our GitHub page where you can find all the downloadables you would need to make this NES controller for yourself.

And I couldn't have done it without three other folks from the Makerspace: Patrick, Rodney, and Mike, who helped assist me with various tools such as the table saw, wood clamps/nail gun, and the hand router.

Thursday, June 23, 2016

More things lost on today's youth

I'm a collector.  My wife doesn't help me with my problem.  It's something I've done since I was young, and growing up as an only child, it brought me joy to share these things with others.  On the other hand, she comes from a big family but has also come to appreciate stuff.  There are things we collect together, such as retro video games, which led us to the Let's Play Gaming Expo which was in Plano, TX on 6/18 and 6/19.  We brought eight of our home console systems with at least one game per system from our personal stash, and two pinball machines.  (Not to mention the gigantic 100x NES controller.)

We have seen a staggering evolution in technology and aesthetics in our just shy of 30 years in existence, and what's more interesting to me about this evolution is the things we don't do anymore rather than the new things that have evolved.  For instance, print media has declined drastically over the past 15 years, pay-phone booths are virtually non-existent, and hardly anyone keeps a Mapsco in their car to get themselves around town anymore.  Some of us hardly remember how to hold a pencil, and are much faster and neater with Swype or even speech-to-text technology.

And time marches on.  The youngest kids old enough to truly enjoy Bob Barker's hosting style on The Price Is Right are now in college.  Soon, going out shopping or going to the post office (yes, some of us have businesses that ship goods, and some of you might still have landlords who are stuck in the '70s) will be a thing of the past.  In the far future, gas stations will be a thing of the past, and hopefully we will no longer be driving cars, thus buying us all sorts of time to enjoy the scenery or absorb ourselves in the latest gossip or games.

There are people in this world, young and old alike, who have missed out on these cultural phenomena.  And there are others who cherish them and to whom it all brings back fond memories.  At the expo, we shared our toys with kids of all ages whose experiences with these things varied greatly.  It was gratifying to watch a pinball wizard set a high score on one of my machines, which I will spend months trying to beat back at home.  It also felt good to teach a youngster how to use an Atari 7800 controller.  But when I wasn't around to guide people on the best ways to enjoy these things, I observed some awfully odd behavior.


You're One Person, But You Started Four Games


Pinball machines tend to have modes where a maximum of four people can take turns playing one ball at a time in a 3- or 5-ball game.  In the arcade, people would pay money for a "credit" which allowed one player to play.  Thus, four credits meant one player could play four games, four players could compete in a single game, or anything in between.  I've somewhat questioned the utility of this mode, since it doesn't really speed things up; if you're waiting in line to play a game, though, it can facilitate bonding with other pinheads and at least get more people in the queue playing rather than waiting.

Nevertheless, I wished I had made my games require people to pay for a credit in order to avert a really bizarre behavior enabled by free play.  Many people (including practically everyone I walked by on Sunday) thought that the "Start" button would actually launch the ball and begin the game.  Pinball players with any experience know that the Start button only puts a ball in the shooter lane for you, and you then need to pull back the plunger in order to try to make the skill shot.  So often, I saw a single person who had started four games all for themselves before figuring out how they messed up.  I don't even usually play four games in a row on any of my machines, so as you can imagine, most people got bored and walked away, leaving other players in the midst of a game in progress and no good path to getting a high score.  No one bothers resetting the machines for obvious reasons, but hopefully at least a couple more people in this world know what to do if they decide to play pinball.

I even found four quarters in my World Cup Soccer '94 game at the end of the expo!  Were these people tipping me, or did they think they really needed to put in money to start a game?  Imagine if I didn't check that before putting the game up on a dolly, which would cause the quarters to fall back and possibly short something.  Nevertheless, proof that it still pays to own old games. ;)

Long Times To Boot


Among the consoles, I brought systems that were generally modded to boot faster than normal.  Raymond Jett sold me a special BIOS chip for the ColecoVision that eliminates the ungodly long time you have to wait for it to start up normally.  Same deal with our Sega Master System.  However, the poor old Amiga 500 had no such love; we still had to put in our Workbench 1.3 floppy disk and wait about 5 minutes for it to boot up each time it needed to be rebooted (and so many people messed with it that reboots were frequently necessitated).  When doing this, people would often try to bang on the keyboard during the lengthy boot cycle to see if the system did something.  Then, they were surprised to see that such an old system actually had a desktop-style GUI.  Evidently, no one remembers waiting around!  My Nexus 6P seems to take an ungodly long time to cold boot, so I guess most folks are either never letting their batteries die or never installing critical system updates.

Someday, I will see about getting a ROM with various versions of Workbench programmed onto it.  This'll save me the hassle of floppy disks and buy me the convenience of loading whatever Workbench version works nicest with whatever I want to do.  Similarly, I put a 1GB SCSI hard drive onto my Mac Plus from 1986 in order to help with loading System 6 and large programs such as Photoshop.  While everyone should go through at least once what people back in the day had to suffer through, even those before us had limits and would eventually spend $1,000 on a device (usually a 40MB hard drive) that saved them from having to swap the Photoshop floppy with the System floppy several times just to draw a gradient, not to mention how many times required to load the program!

Miscellania


There seems to be an age where one actually understands what's going on rather than just mashing the controller or watching the game basically play itself.  I was trying to teach one poor youngster how to play Joust on the Atari 7800, but he was notably bad at pressing the Fire button on the side in order to get his character to fly.  Another kid was getting a lesson in Food Fight from me, but he had a hard time aiming to sling deadly food at the attacking chefs.  Others would hold the joystick sideways or upside-down, causing their character to move in unexpected ways.  How much user research was done on these things back in the day to find out how intuitive they were?  Did kids fumble with controllers as much back then?  It would make me sad to see my own kids fumble with everything in my own collection, and would hope I could coach them enough so that eventually they're adept at even the things I fumble with (basically anything like a SNES controller or newer).

I was probably the only person working on systems live in-person in the free play room.  I still hadn't reassembled my ColecoVision from attempting the composite mod (so it would have composite video out rather than only RF out, which looks terrible), so I had to spend time taking care of that.  I was frequently removing the top cover of the Amiga to point out the Indivision ECS scan doubler / flicker fixer I installed into it so it could output VGA rather than to some weird unobtainium 23-pin RGB connector or to the black-and-white composite port.  It was extremely lucky that I got that card in from FedEx just hours before the show started, and I had lots of fun showing it off.

The other thing is my Amiga case is actually broken -- it doesn't really snap shut anymore, and one of the standoffs that you're supposed to screw the disk drive into is cracked.  Thus, it was hard for people to eject disks from it, and some folks even assumed that since they couldn't press the Eject button, the disk drive was empty.  I walked upon it once where there were two disks in the disk drive!  Argh.  Sad, but I will definitely have to rethink taking the Amiga with me to any public shows unattended.  That, and it developed a habit of screaming "Guru Meditation: Software Failure" at me, which is usually more a sign of hardware failure than software failure.  Hopefully I can whack that gremlin out of the system before long...

Not sure what other truly home-brew hardware people whipped up for the Expo (probably none), but besides building my 100-times-scale NES controller (which will be described in more detail here soon), I used the rest of the off-brand controller I harvested to make a Vectrex controller.  Controllers for the Vectrex are getting outrageously expensive, and I didn't feel like modifying one of our name-brand Sega controllers, so I decided to spend roughly double that value worth of my own time building one myself. Here's the guts:

The "component side" of this piece of prototyping board used in the Vectrex controller.  It's nothing more than hand-cut wires and several resistors, and on the other side, there are some short wires exposed that get shorted together each time you press down one of the buttons.

A few astute people noticed I was walking around with a Nintendo controller that had a Sega plug on the end of it!  It was nice that people were paying such close attention, but soon I solved that problem (and the other problem of "Why's this knock-off NES controller hooked up to this Vectrex?") by commissioning this decal from the folks at Muffin Bros. Graphics, basically taking the Vectrex controller graphics and massaging it onto their NES template:

Ooh, shiny!  And nothing like reproduction multi-carts for the Vectrex.

Thursday, May 19, 2016

Moments Inside Google I/O 2016

I was invited to attend Google I/O 2016 at pretty much the last second -- only about 10 days before the start of the event.  This is my first time here, though my wife DoesItPew has been here twice before already.  We got to attend the convention together for once, and now I am dodging the cold wind blowing in over the hill here at dusk from the Shoreline Amphitheater in Mountain View, CA.

Prior to the event were many different Day Zero parties including a really large one thrown by Intel.  As an Intel Innovator (though I rarely speak about that because I suck at publicizing myself and/or I mostly use their products on projects covered by NDAs), I got to show off some new hardware prototypes made recently and impressed several people who are fond of homemade hardware and/or LED products.  Unfortunately, my favorite prototype died on Wednesday after the Google I/O keynote; it probably got shorted when one of the power wires poking through my shirt decided to come loose and touch the other power wire.  Now all it does is burn my finger really badly whenever I try to touch the microcontroller. after plugging it in  Oh well, I can just replace it when I get home.  Meanwhile, no extra attention for me... :-(  I also got to meet some fellow employees of my company from different sites I don't usually interact with (and tour their two offices in the city), and met a couple people from a relatively recent acquisition we made in San Francisco.

As you may have heard, the lines to attend many of the sessions were absolutely ridiculous and caused people to miss out on things they wanted to see.  That combined with the nearly 90-degree weather here in the Bay Area led to a lot of unhappy, hot, and sunburned folks.  The folks I met from our acquisition were so disappointed with the lines that they bailed after the first hour and vowed to watch I/O only via the live streams!  They would not even bother showing up in person on Thursday nor Friday.  But, as an amelioration for those of us who suffered through that and those who were not selected for the conference, you can (for probably the first time) experience the sessions of this I/O on the Google Developers YouTube Channel.  Thus, there's not really a reason to attend a talk unless you want to see something demonstrated live.  The real reason one would spend $900 on a conference pass now is to come out and schmooze with Googlers and other enthusiastic developers deemed worthy and discuss lofty ideas one could develop on the backbone of all the stuff Google has come out with now and will release in the near future.  Plenty of them are around and enthusiastic to talk to you, showing off intriguing demos on anything from mobile app "point-and-click" testing to a music box made out of a revolving wooden disc and sticky notes with "Do", "Re", "Mi", etc. on them being analyzed in real-time by a camera and translated into text that feeds into a synthesizer.  A Google Maps API guy clarified the Terms of Service for me, and stated my idea for "Maker's Markers" wouldn't violate it.  Goodbye OpenStreetMaps.  We have had previews of Android Auto in various scenarios where DoesItPew tore apart the UI with various Googlers for about 10 minutes while they took diligent notes.  (It's good to have someone so opinionated, but can you tell who wears the pants at our house? :-P)  I have spoken with people about Firebase, Project Tango augmented reality, various machine learning ideas, things I want to do relating to what I've seen here the last two days (which you can join on the new Google Spaces app), and of course on progressive Web design and improving the user experience for my company's mobile offerings.


NFC "tattoos" representing my three ideas on the Big Idea Wall.  One may or may not be to rename it the Big Idea Board... doesn't that have a nicer ring to it? :-P

To see for yourself the ideas I added to the Big Idea Wall, check out these links and please join the conversation:

Don't Die Watching Android TV -- Emergency Notification Overlays
Order Fast Food on Android Auto
Optimize Meetups By Travel Time

As if I/O 2015 wasn't inspirational enough just from watching the keynote, now I will have a whole 'nother year of projects to keep myself busy after work, not to mention plenty of stuff to share with my coworkers and help build new and innovative aspects of the financial industry as it pertains to auto finance and home loans.

Tomorrow is the final day of I/O 2016, and we have yet to see what that will bring.  After that on my agenda is to hit the Bay Area Maker Faire in San Mateo and the Computer History Museum, not to mention see some family and friends who live out here on the "Left Coast."  And if that's not enough to do, once I get back late Sunday night, there's yet another conference for software engineers the following Tuesday at my workplace and then I'm playing a gig with the corporate band to celebrate the recent expansion of my workspace -- the awesome innovation lab known as The Garage.

Meanwhile, it's so friggin' cold out here...