Arduino Pong

has anybody got this to work on the atmega8 ? I tried the cli method and the pong intro screen shows fine now, but when the game starts it gets out of sync again .

Question

Can video output from Arduino, be mixed with a web cam or other live video camera source?

I know there's video mixers that can do it, but they cost lots.

If anyone's experimenting in this field, please let me know :slight_smile:

Have a look at the lm1881 (National semiconductor if I am correct).

I think I have seen it mentioned a couple of times here too. It can be mixed with a normal video source, not sure about webcam.

I tried it with an atmega8 and atmega168 (arduinoBT) on my trusty self made beamer (slide-projector with pocket TV screen). While arduino pong seems to be just the perfect new companion for this beamer (which is not really usable for anything fine grained like a movie...) I see a second ghost picture. Also none picture has the right height. Is it maybe the cable lenght? I have roughly 1,5 meter.
I see many settings in the header, but when I change e.g. WIDTH, HEIGHT or DISPLAY_LINES it only gets worse, showing out-of sync artefacts...
Any hints?
I haven't looked to close at the code, but I assume that touching e.g. DISPLAY_LINES must mess up the timing, right?

regarding the resistors: I simply use three 1K potentiometers for all values and I see most changes when truning the 75 ohm one. But it looks not that picky... 100 ohm could also work I guess.
Hmmm, maybe I have some naughty analog filter effect due to the pots (acting as inductors) which causes ghost images?

I looked around for a TV test-pattern an found this very simple and clean one:
http://hklab.net/wiki/TV_Video_Signal_Generator_with_Arduino

Its done on an ardunio board, but supprisingly not compiled within the IDE, so I changed the code
to run in Arduino 0009. I also changed it to support the same circuit and pins that Al uses in arduino pong.
So video out is still generated on digital pin 8+9.

Now, I can see 3 nice vertical bars, which tells me that the ghost pictures I get from the arduino
pong code is probably not a hardware issue of my circuit or beamer...
Although the ghost pictures I see in pong are all exactly vertically shifted... maybe I just don't see them in
the 3 vertical bars pattern?

Anyways, I think this code is a good test! And maybe an even better starting point for proper
video generation. It also looks like the _delay_us() function is more accurate than delayMicroseconds...
When I've time and muse to pull that clunky scope from my shelf, I'll compare the signal patterns of this 3 bar test and arduino pong. :slight_smile:

/* 
   Vertical Bars Pattern PAL TV Signal Generator with Arduino 
   Use this code as you want
   2007 Javier Valcarce Garcia, <javier.valcarce@gmail.com>
   
   
   ---
   
   copied from http://hklab.net/wiki/TV_Video_Signal_Generator_with_Arduino
   This pattern should work on PAL and NTSC TV sets, as mentioned on the webpage.
   
   It's slightly changed to compile inside the standard Arduino IDE (Version 0009)
   Note that the left and right bar are usually not exact the same size, as 
   there are slight offsets in most TVs. 
   See reference: http://www.retroleum.co.uk/PALTVtimingandvoltages.html
   
     2007.09.11 Oliver Keller <oli.keller@gmail.com>
   
*/
 
// yes, ther is an avrlibc inside arduino...
#include <util/delay.h>
 
/////////////////////////////////////////////////////////////////////////////////////////////////
// Pins where the 2-bit DAC is connected, this is the composite video-out ;)
// PINB0 is DigitalPin 8, PINB1 is DigitalPin 9 
#define PINB0 0 // LSB, 1 kOhm resistor 
#define PINB1 1 // MSB, 330 Ohm resistor
 
// PINB1 PINB0 OUTPUT 
// 0     0     0.0V   - Sync level
// 0     1     0.3V   - Black level
// 1     0     0.6V   - Gray level
// 1     1     1.0V   - White level
 
#define LEVEL_SYNC   PORTB &= ~(1 << PINB1); PORTB &= ~(1 << PINB0);
#define LEVEL_BLACK  PORTB &= ~(1 << PINB1); PORTB |=   1 << PINB0; 
#define LEVEL_GRAY   PORTB |=   1 << PINB1;  PORTB &= ~(1 << PINB0);
#define LEVEL_WHITE  PORTB |=   1 << PINB1;  PORTB |=   1 << PINB0;
/////////////////////////////////////////////////////////////////////////////////////////////////
 
 
/////////////////////////////////////////////////////////////////////////////////////////////////
inline void vsync_pulse()
{
      LEVEL_SYNC; 
      _delay_us(30); 
      LEVEL_BLACK; 
      _delay_us(2); 
}
 
/////////////////////////////////////////////////////////////////////////////////////////////////
inline void equal_pulse()
{
      LEVEL_SYNC; 
      _delay_us(2); 
      LEVEL_BLACK; 
      _delay_us(30);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
inline void hsync_pulse()
{
      LEVEL_SYNC; 
      _delay_us(5); //4.7us
      LEVEL_BLACK; 
      _delay_us(7); //7.3us
}
 
       unsigned int line = 0;
 
/////////////////////////////////////////////////////////////////////////////////////////////////
void setup()
{

 
      /* NOTE THAT THE SIGNAL GENERATED BY THIS PROGRAM HAS A NOT VERY ACCURATE TIMING SO 
       IT IS POSSIBLE THAT THE IMAGE BLINKS ON YOUR TV SCREEN OR DOESN'T SHOW AT ALL, 
       THIS PROGRAM WRITTEN IN C (INSTEAD OF ASSEMBLER) IS ONLY A PROOF OF CONCEPT */
 

      DDRB = 0xFF; // PORTB, all pins are outputs
      cli();
}

void loop()
{
          if (line == 626) 
          {
              line = 1;
          }
          else
          {
              line++;
          }
 
 
          switch(line)
          {
          case 1:
          case 2:
          case 314:
          case 315:
              vsync_pulse();
              vsync_pulse();
              break;
 
          case 3:
              vsync_pulse();
              equal_pulse();
              break;
 
          case 4:
          case 5:
          case 311:
          case 312:
          case 316:
          case 317:
          case 623:
          case 624:
          case 625:  
              equal_pulse();
              equal_pulse();
              break;
 
          case 313:
              equal_pulse();
              vsync_pulse();
              break;
          default:
              // Image scanline (not a sync line)
 
              hsync_pulse(); // Horizontal Sync, lenght = 12us                 
 
              LEVEL_GRAY;   
              _delay_us(8);
              LEVEL_BLACK;
              _delay_us(14);
              LEVEL_WHITE;
              _delay_us(8);
              LEVEL_BLACK;
              _delay_us(14);
              LEVEL_GRAY;
              _delay_us(8);
              //52us in total
          }
}

Note the code works on ATmega168 as well as on ATmega8 arduino boards. Its even smaller than the arduino bootloader (1k) :slight_smile:

I have problem with arduino pong :frowning:
I use atmega8 and arduino009.

Pictures about problem:

Please help me some idea what is the problem with my arduino pong.

Wow. That's extremely impressive!!! :o
GREAT Work!

Very great work !!

May be it is a stupid question, but it is difficult to do the same thing in color or this is not possible with the artuino ? What is missing to do it ??

I gotta try this :3

can you find the parts necessery to make this work in radioshack? becuase i'd love to be able to have a tv screen as a display...

Yeah, just a couple of resistors (the values don't have to be exact) and an RCA jack.

Make sure you have the 75() resistor correct or else you may not see anything on your tv! also check the voltages with a multi meter can save your tv from more current then nesserary or worse! Have fun

Hi everybody,

here a code derivated of arduino pong,

enjoy it,

Ops,

sorry: http://organismo.art.br/interfaces/wikka.php?wakka=ArduinoTvanalogica

Hello, I am working on an interface that converts midi signals into visuals. I based this on the arduino pong code. Now I am on the end of my ideas. the 2 parts: Midi Input Signal and Arduino pong works seperately perfekt but its so hard for me to connect them. I hope there is someone out who can help me
I did the midi input shown here: it works

#include <MIDI.h>
/*
  Basic I/O MIDI tutorial
  by Franky
  28/07/2009
*/

#define LED 13               // LED pin on Arduino board

void setup() {
  pinMode(LED, OUTPUT);
  MIDI.begin(4);                  // Launch MIDI with default options
                        // input channel is set to 4
}

void loop() {
  if (MIDI.read()) {
    digitalWrite(LED,HIGH);     // Blink the LED
    MIDI.sendNoteOn(42,127,1);  // Send a Note (pitch 42, velo 127 on channel 1)
    delay(1000);            // Wait for a second
    MIDI.sendNoteOff(42,0,1);   // Stop the note
    digitalWrite(LED,LOW);          
  }
}

here is now my code that I build :frowning:
The midi request doesn 't work. My goal is to show different pictures with different midi nots from my drumcomputer.
If Midi Note 1 and Velocity 1 arrives show Picture 1
If Midi Note 1 and Velocity 1 arrives show Picture 12
its important that the programm asks the whole time if there is a new Midi note.
....
here is the code mixed with the arduino pong code. Now I can just see 2 does because the programm steps in the following else if part

else if(midi < 40 & midi < 30)
{
digitalWrite(ledPin, HIGH);
grayPixel(5,10);
setPixel(10, 20);
//clearScreen();
}

this is the code

//Arduino Tv framebuffer
//Alastair Parker
//2007
#include <MIDI.h>
// Video out voltage levels
#define _SYNC 0x00
#define _BLACK 0x01
#define _GRAY 0x02
#define _WHITE 0x03
// dimensions of the screen
#define WIDTH 38
#define HEIGHT 14
//number of lines to display
#define DISPLAY_LINES 240
// update speed for the main loop of the game
#define UPDATE_INTERVAL 1
// maximum velocity of the ball
#define MAX_VEL 10
// time to wait while paused
#define DONE_WAITING 30
// input pins for the player pots
#define PLAYER_LEFT_PIN 2
// currently the same pin
#define PLAYER_RIGHT_PIN 3
//video pins
#define DATA_PIN 9
#define SYNC_PIN 8

int ledPin = 13;
int midi = 0;

// the video frameBuffer
byte frameBuffer[WIDTH][HEIGHT];
// loop indices
byte index, index2;
// pal video line loop
byte line;
// current drawing line in framebuffer
byte newLine;
// loop counter to for the main loop delay
int waitingCount = 0;
// if we should be waiting for something to happen
boolean waiting=true;
// value of the counter controlling the freq of updates
byte updateCounter=0;

// positions and velocity of the ball
byte ballXPos = 14;
byte ballYPos = 7;

int readMidi(){
  int midi_type = 0;

  if (MIDI.read()) {
    switch(MIDI.getType()) {            // Get the type of the message we caught
    case PC:
      midi_type = MIDI.getData1();
      return midi;
      //BlinkLed(MIDI.getData1());      // Blink the LED a number of times 
      // correponding to the program number 
      // (0 to 127, it can last a while..)
      break;
      // See the online reference for other types
    default:
      break;
    }
  }
}
void drawVisualisation(){
  int midi = 0;
  midi = readMidi();

  if(midi = 0){
    //digitalWrite(ledPin, HIGH);
    setPixel(10, 2);
  }
  else if(midi < 10 & midi > 0 )
  {
    //digitalWrite(ledPin, HIGH);
    //clearScreen();
    setPixel(10, 3);
  }
  else if(midi < 20 & midi > 10)
  {
    //digitalWrite(ledPin, HIGH);
    //clearScreen();
    setPixel(10, 4);
  }  
  else if(midi < 30 & midi > 20)
  {
    //digitalWrite(ledPin, HIGH);
    //clearScreen();
    setPixel(10, 5);
  }  
  else if(midi < 40 & midi < 30)
  {
    digitalWrite(ledPin, HIGH);
    grayPixel(5,10);
    setPixel(10, 20);
    //clearScreen();
  }
  else if(midi > 100)
  {
    //digitalWrite(ledPin, HIGH);
    //clearScreen();
    setPixel(10, 20);
    grayPixel(30,30);
  }      
}




// draw a pixel to the buffer
void setPixel(byte x,byte y)
{
  frameBuffer[x][y]= _WHITE;
}

void grayPixel(byte x, byte y)
{
  frameBuffer[x][y]= _GRAY;
}

// draw a black pixel to the buffer
void clearPixel(byte x,byte y)
{
  frameBuffer[x][y]= _BLACK;
}

// draw a paddle
void drawPaddle(byte x,byte y,byte col)
{
  frameBuffer[x][y]= col;
  frameBuffer[x][y+1]= col;
}

void drawBall(byte col)
{
  frameBuffer[ballXPos][ballYPos]=col;
}

//draw the title message
void drawArduinoPong()
{
  //arduino
  /* int counter;
   for(counter=1; counter<32;counter++){
   setPixel(counter, 10);
   //clearPixel(counter-1, 10);
   }
   
   for(counter=1; counter<32;counter++){
   setPixel(2, counter);
   //clearPixel(counter-1, 10);
   }
   for(counter=1; counter<32;counter++){
   grayPixel(4, counter);
   //clearPixel(counter-1, 10);
   } 
   for(counter=1; counter<32;counter++){
   setPixel(8, counter);
   //clearPixel(counter-1, 10);
   }
   for(counter=1; counter<32;counter++){
   grayPixel(12, counter);
   //clearPixel(counter-1, 10);
   } */
}

// clear the screen
void clearScreen()
{
  for (index = 0; index < WIDTH; index++)
    for (index2=0;index2<=HEIGHT;++index2)
    {
      frameBuffer[index][index2] = _BLACK;
    }
}



// the setup routine
void setup()
{
  MIDI.begin();                  // Launch MIDI with default options
  cli();
  pinMode (SYNC_PIN, OUTPUT);
  pinMode (DATA_PIN, OUTPUT);
  digitalWrite (SYNC_PIN, HIGH);
  digitalWrite (DATA_PIN, HIGH);
  loop();
  clearScreen();
  //drawArduinoPong();
}

void loop()
{
  // iterate over the lines on the tv
  drawVisualisation();
  for ( line =0;line< DISPLAY_LINES;++line)
  {

    // HSync
    // front porch (1.5 us)
    PORTB = _BLACK;
    delayMicroseconds(1.5);
    //sync (4.7 us)
    PORTB = _SYNC;
    delayMicroseconds(4.7);
    // breezeway (.6us) + burst (2.5us) + colour back borch (1.6 us)
    PORTB = _BLACK;
    delayMicroseconds(0.6+2.5+1.6);


    //calculate which line to draw to
    newLine = line >>4;
    delayMicroseconds(1);

    //display the array for this line
    // a loop would have been smaller, but it messes the timing up
    PORTB = frameBuffer[0][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[1][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[2][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[3][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[4][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[5][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[6][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[7][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[8][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[9][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[10][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[11][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[12][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[13][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[14][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[15][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[16][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[17][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[18][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[19][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[20][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[21][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[22][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[23][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[24][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[25][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[26][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[27][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[28][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[29][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[30][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[31][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[32][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[33][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[34][newLine];
    delayMicroseconds(1);
    PORTB = frameBuffer[35][newLine];
    delayMicroseconds(1);

    // klugdge to correct timings
    PORTB = frameBuffer[36][newLine];
    PORTB=PORTB;
    PORTB=PORTB;
    PORTB=PORTB;
    delayMicroseconds(3);
  }


  //vsync
  PORTB = _SYNC;

  // if delaying for some reason
  if(waiting)
  {
    // increment the waiting counter
    waitingCount++;
    //digitalWrite(ledPin, HIGH);
    // delay(200);
    // digitalWrite(ledPin, HIGH);
    readMidi();
    drawVisualisation();



    // wait for the remainder of the sync period
    delayMicroseconds(610);
  }
  // not waiting
  else
  {
    // increment the update delay counter
    updateCounter++;
    if( updateCounter >= UPDATE_INTERVAL)
    {

      // remainder of sync period
      delayMicroseconds(10);
      updateCounter=0;
    }
    else
      // remainder of sync period
      delayMicroseconds(250);
  }

  drawVisualisation();

}

ok i think i am very near. THX lg munki

i will try this for sure

Hi,
I'm working with an Arduino Mega which has a 16mHz Quartz, do you think it will work well, or do I have to change the code?
(Sorry for my bad English, I'm French ;D)

this is awesome!

Interesting Project...

Thank you ;D

Absolutely amazing!!!