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.