In the beginning...
Back in my college years, I had seen someone put an oscilloscope into XY mode and then draw patterns on the screen by controlling the input voltage on channels 1 & 2. I had played around with that a little back then, but only drew squares and rectangles.
Then I thought, "Can I make a pong game?"
My first attempt was building some simple RC lowpass filters on a Pi Hat, and using the Pi's PWMs to make a quick and dirty D2A. The first iteration was horrible! Smearing, trails, tails, flickering, blobby morphing. I tried adding a second stage to the RC filtering, and increased the PWM frequency. It did improve it, but not enough for a playable game.
I rummaged through my parts kits and found my old single board computer, complete is A2Ds and DACs. Luckily the datasheet was still available for the MAX506, and I was able to wire up enough GPIOs to run it's 8 bit data bus, 2 address bits and WR control.
// configure our IO mappings for the DAC
#define D0 (4)
#define D1 (5)
#define D2 (6)
#define D3 (12)
#define D4 (13)
#define D5 (16)
#define D6 (17)
#define D7 (18)
#define WR (19)
#define A0 (20)
#define A1 (21)
It's Alive!
Once I got that wired up, I added some extra capacitors to the DAC chip to clean up the USB 5V power. What were the results? Clean, clear distinct images, some call fonts or sprites.
In making this project I wanted to expand my knowledge of graphics a little bit, so instead of making a square "ball", I figured out how to make it a round ball. Since I'm using a Pi (rather than a tiny uController), I figured I have the CPU power to calculate the circle as it translates from side to side.
For a little added efficiently I used a double for the FOR loop, since that is a native data type for the Pi. h,k are the offsets as the ball is translated around the screen. Once the values are calculated, the X value is written to channel X and the Y value is written to channel Y.
// trig functions are in radians
// input X, Y location and a radius
void drawCircle( unsigned h, unsigned k, unsigned r )
{
float x,y;
// x = h + r cosθ
// y = k + r sinθ
for( double idx = 0; idx <= 2*M_PI; idx += 0.05 )
{
// calculate the points along the circumference
x = h + r * cos( idx );
y = k + r * sin( idx );
// write the point
setData( (unsigned)x, X );
setData( (unsigned)y, Y );
}
}
The other parts of the game coded up are to check if the paddle missed the ball, restart/serve the ball after a miss, calculate a trajectory for the ball, and of course a splash screen on startup.
The Future...
All the code for Pi-O-Scope-Pong is available on github.com/alager/Pi-o-scope-pong, allowing anyone to enhance or fork it. As noted in the README on github, there are a few enhancements that still need some work, like score keeping and altering the bounce angle based on paddle contact location.
Now I remember an idea I had many years ago.
You know QuadraPONG (aka Elimination)? It's PONG, but the top and bottom walls are paddles too, each player has 4 misses and once you used them up, you turn into a wall until only one player is left. This is much more fun than PONG, too (I played both on an arcade machine, our regular PONG machine is 4 player so we're doing an apples-to-apples comparison).
On a rectangular / raster display, the number 4 makes sense.
On an oscilloscope (especially an ancient one with a round CRT), any number would work, as a circle is essentially just a polygon with infinite edges.
So you can have 2 to 8 players in a crazy PONG Battle Royale
(more is possible, but probably gets too confusing. Also old scopes usually have very small screens, so getting 8 people to look at a 5 inch circle screen, especially with everyone nowadays used to screens measured in feet, will be difficult)!
I hereby grant you permission to use and implement this idea under the condition that you actually play it on an ancient oscilloscope that has a round CRT screen :-D