Batsocks TellyMate Shield

Thought you might like to see some pics of the Batsocks TellyMate Shield hooked up to my 47" LCD TV:-

The quality is really impressive. I am wondering if somebody already started implementing some old fashioned apps, like tennis, or even come up with a ZX Spectrum "replacement".

I'm going to give 'Pong' a go for starters :)

Hi Mike, I starting working on that as well, how far along is yours?

Not even started yet ::)

Here is a version of pong for the TellyMate. The gameplay is based on some code Tom Igoe posted here: http://www.tigoe.net/pcomp/code/category/Processing/454#more-454

/*
  TellyPong
  pong for the TellyMate shield
  based on gameplay created by Tom Igoe http://www.tigoe.net/pcomp/code/category/Processing/454#more-454
  modified by mem 20 May 2009 
 
  This example uses the TellyMate shield http://www.batsocks.co.uk/products/Other/TellyMate%20Shield.htm
 */

#define RIGHT_PIN 0  // pins connected to pots (2 and 0 are sliders 1 and 3 on dangershield)
#define LEFT_PIN  2 

// define the edges of the screen:
#define HEIGHT 25
#define WIDTH  38
#define LEFT    0
#define RIGHT  (WIDTH -1)
#define TOP     0
#define BOTTOM (HEIGHT-1)

#define PADDLESIZE  1      // this how many pixels the paddle extends top and bottom  
#define SPEED    4000      // how many milliseconds it takes the ball to move across the screen

#define BALL      'o'      // character code for ball 
#define PADDLE    219      // character for paddle (see http://en.wikipedia.org/wiki/Code_page_437)

#define CHAR_ESC "\x1B"    // escape character used in TellyMate commands 

int ballX = WIDTH/2;          // X position of the ball 
int ballY = HEIGHT/2;         // Y position of the ball
int ballDirectionY = 1;       // X direction of the ball
int ballDirectionX = 1;       // Y direction of the ball

int rightPaddleY = 0;         // X position of the center of the right paddle
int leftPaddleY = 0;          // Y position of the center of the right paddle

int prevRight = -1;           // holds previous paddle position
int prevLeft  = -1;           // set to -1 to force display on startup

long timeStamp = 0;           // time stamp to control the pauses between ball moves
long interval = SPEED/WIDTH ; // interval in milliseconds between ball moves
boolean gamePaused = false;   // state of the game

void setup() {
  Serial.begin(57600); //57k6 baud
  screen_clear();
  Serial.println("TellyPong!");
  cursor_show(false); // turn cursor off
  delay(1000);
  screen_clear();
}

void loop() {
  // read input:
  readSensors();
  // move the ball:
  if (gamePaused) {
    if (millis() - timeStamp > interval*10) {
      // if enough time has passed, start the game again:
      gamePaused = false;
    }
  } 
  // if the game isn't paused, and enough time between ball moves
  // has passed, move the ball and update the timestamp:
  else {
    if (millis() - timeStamp > interval) {
      moveBall();
      timeStamp = millis();
    }
  }
}

void readSensors() {
  // read the sensors for X and Y values:
  leftPaddleY = map(analogRead(LEFT_PIN), 0, 1023, 0, BOTTOM);
  if( leftPaddleY != prevLeft){
     drawPaddle(' ',0, prevLeft); // erase old paddle
     drawPaddle(PADDLE,0, leftPaddleY);
     prevLeft = leftPaddleY;
  }
  rightPaddleY = map(analogRead(RIGHT_PIN), 0, 1023, 0, BOTTOM);
  if( rightPaddleY != prevRight){
     drawPaddle(' ',RIGHT, prevRight); // erase old paddle
     drawPaddle(PADDLE,RIGHT, rightPaddleY);
     prevRight = rightPaddleY;
  }
}

void moveBall() {
  // check to see if the ball is in the horizontal range 
  // of the paddles:

  // right:
  if (ballX >= RIGHT - 1) {
    // if the ball's next Y position is between 
    // the top and bottom of the paddle, reverse its  X direction:
    if ((ballY + ballDirectionY >= rightPaddleY - PADDLESIZE) 
      && (ballY + ballDirectionY <= rightPaddleY + PADDLESIZE)) {
      // reverse the ball horizontal direction:
      ballDirectionX = -ballDirectionX; 
    }
  }

  // left:
  if (ballX <= LEFT + 1) {
    // if the ball's next Y position is between 
    // the top and bottom of the paddle, reverse its  X direction:
    if ((ballY + ballDirectionY >= leftPaddleY - PADDLESIZE ) 
      && (ballY + ballDirectionY <= leftPaddleY + PADDLESIZE )) {
      // reverse the ball horizontal direction:
      ballDirectionX = -ballDirectionX;  
    }
  }

  // if the ball goes off the screen bottom,
  // reverse its Y direction:
  if (ballY == BOTTOM) {
    ballDirectionY = -ballDirectionY;
  }
  // if the ball goes off the screen top, 
  // reverse its X direction:
  if (ballY == TOP) {
    ballDirectionY = -ballDirectionY;
  }

  // clear the ball's previous position:
    screenShowXY(' ', ballX, ballY);

  // if the ball goes off the screen left or right:
  if ((ballX == LEFT) || (ballX == RIGHT)) {
    // reset the ball:
    ballX = WIDTH/2;
    ;
    ballY = HEIGHT/2;
    // pause  and note the time you paused:
    gamePaused = true;
    timeStamp = millis();
  }
  // increment the ball's position in both directions:
  ballX = ballX + ballDirectionX;
  ballY = ballY + ballDirectionY;
  
  // if the game isn't paused, set the ball
  // in its new position:
  if (!gamePaused) {
    // set the new position:
     screenShowXY(BALL, ballX, ballY);
  }
}

void drawPaddle(char paddleChar, int x, int y)
{
  for(int i =-PADDLESIZE; i <= PADDLESIZE; i++)
  {
    screenShowXY(paddleChar, x, y + i);
  }
}

// TellyMate helper functions

void screenShowXY( char ch, int x, int y){
  // display the given character at the screen x and y location  
  cursor_move(y,x);
  Serial.print(ch);
}
void screen_clear( void )
{ // <ESC>E
  Serial.print( CHAR_ESC "E" );
}

void cursor_move( uint8_t row , uint8_t col )
{ // <ESC>Yrc
  Serial.print( CHAR_ESC "Y" ) ;
  Serial.print((unsigned char)(32 + row)) ;
  Serial.print((unsigned char)(32 + col)) ;
}

void cursor_show( bool show )
{ // <ESC>e or <ESC>f
  Serial.print( CHAR_ESC ) ;
  Serial.print( show?'e':'f' ) ;
}

Nice one. I haven't got that shield but will modify it for my kit. Thanks!

I haven’t got that shield but will modify it for my kit

what modificaitons ? it looks like we use the same shield.

The 'dangershield' mentioned in your code.

You only need to set the analog pin assignments for your pots. I am not using any of the other features of the DangerShield - but it would be cool to use the piezo to add sound for ball contacts, and perhaps use the swtiches to start and pause the game. hmmm

here a version that uses a speaker or piezo on pin 3 for sound.

/*
  TellyPong
  pong for the TellyMate shield
  based on gameplay created by Tom Igoe http://www.tigoe.net/pcomp/code/category/Processing/454#more-454
  modified by mem 20 May 2009 
 
  This example uses the TellyMate shield http://www.batsocks.co.uk/products/Other/TellyMate%20Shield.htm
 */

#define RIGHT_PIN 0  // pins connected to pots (2 and 0 are sliders 1 and 3 on dangershield)
#define LEFT_PIN  2 

#define BUZZER_PIN 3 // digital pin connected to a piezo buzzer or speaker
#define PADDLE_SOUND 2000 
#define EDGE_SOUND   500

// define the edges of the screen:
#define HEIGHT 25
#define WIDTH  38
#define LEFT    0
#define RIGHT  (WIDTH -1)
#define TOP     0
#define BOTTOM (HEIGHT-1)

#define PADDLESIZE  1      // this how many pixels the paddle extends top and bottom  
#define SPEED    4000      // how many milliseconds it takes the ball to move across the screen

#define BALL      'o'      // character code for ball 
#define PADDLE    219      // character for paddle (see http://en.wikipedia.org/wiki/Code_page_437)

#define CHAR_ESC "\x1B"    // escape character used in TellyMate commands 

int ballX = WIDTH/2;          // X position of the ball 
int ballY = HEIGHT/2;         // Y position of the ball
int ballDirectionY = 1;       // X direction of the ball
int ballDirectionX = 1;       // Y direction of the ball

int rightPaddleY = 0;         // X position of the center of the right paddle
int leftPaddleY = 0;          // Y position of the center of the right paddle

int prevRight = -1;           // holds previous paddle position
int prevLeft  = -1;           // set to -1 to force display on startup

long timeStamp = 0;           // time stamp to control the pauses between ball moves
long interval = SPEED/WIDTH ; // interval in milliseconds between ball moves
boolean gamePaused = false;   // state of the game

void setup() {
  pinMode(BUZZER_PIN, OUTPUT); // if using speaker
  Serial.begin(57600); //57k6 baud
  screen_clear();
  Serial.println("TellyPong!");
  cursor_show(false); // turn cursor off
  delay(1000);
  screen_clear();
}

void loop() {
  // read input:
  readSensors();
  // move the ball:
  if (gamePaused) {
    if (millis() - timeStamp > interval*10) {
      // if enough time has passed, start the game again:
      gamePaused = false;
    }
  } 
  // if the game isn't paused, and enough time between ball moves
  // has passed, move the ball and update the timestamp:
  else {
    if (millis() - timeStamp > interval) {
      moveBall();
      timeStamp = millis();
    }
  }
}

void readSensors() {
  // read the sensors for X and Y values:
  leftPaddleY = map(analogRead(LEFT_PIN), 0, 1023, 0, BOTTOM);
  if( leftPaddleY != prevLeft){
     drawPaddle(' ',0, prevLeft); // erase old paddle
     drawPaddle(PADDLE,0, leftPaddleY);
     prevLeft = leftPaddleY;
  }
  rightPaddleY = map(analogRead(RIGHT_PIN), 0, 1023, 0, BOTTOM);
  if( rightPaddleY != prevRight){
     drawPaddle(' ',RIGHT, prevRight); // erase old paddle
     drawPaddle(PADDLE,RIGHT, rightPaddleY);
     prevRight = rightPaddleY;
  }
}

void moveBall() {
  // check to see if the ball is in the horizontal range 
  // of the paddles:

  // right:
  if (ballX >= RIGHT - 1) {
    // if the ball's next Y position is between 
    // the top and bottom of the paddle, reverse its  X direction:
    if ((ballY + ballDirectionY >= rightPaddleY - PADDLESIZE) 
      && (ballY + ballDirectionY <= rightPaddleY + PADDLESIZE)) {
      // reverse the ball horizontal direction:
      ballDirectionX = -ballDirectionX; 
      playSound(PADDLE_SOUND);
    }
  }

  // left:
  if (ballX <= LEFT + 1) {
    // if the ball's next Y position is between 
    // the top and bottom of the paddle, reverse its  X direction:
    if ((ballY + ballDirectionY >= leftPaddleY - PADDLESIZE ) 
      && (ballY + ballDirectionY <= leftPaddleY + PADDLESIZE )) {
      // reverse the ball horizontal direction:
      ballDirectionX = -ballDirectionX;  
      playSound(PADDLE_SOUND);
    }
  }

  // if the ball goes off the screen bottom,
  // reverse its Y direction:
  if (ballY == BOTTOM) {
    ballDirectionY = -ballDirectionY;
  }
  // if the ball goes off the screen top, 
  // reverse its X direction:
  if (ballY == TOP) {
    ballDirectionY = -ballDirectionY;
  }

  // clear the ball's previous position:
    screenShowXY(' ', ballX, ballY);

  // if the ball goes off the screen left or right:
  if ((ballX == LEFT) || (ballX == RIGHT)) {
    playMiss();
    // reset the ball:
    ballX = WIDTH/2;
    ;
    ballY = HEIGHT/2;
    // pause  and note the time you paused:
    gamePaused = true;
    timeStamp = millis();
  }
  // increment the ball's position in both directions:
  ballX = ballX + ballDirectionX;
  ballY = ballY + ballDirectionY;
  
  // if the game isn't paused, set the ball
  // in its new position:
  if (!gamePaused) {
    // set the new position:
     screenShowXY(BALL, ballX, ballY);
  }
}

void drawPaddle(char paddleChar, int x, int y)
{
  for(int i =-PADDLESIZE; i <= PADDLESIZE; i++)
  {
    screenShowXY(paddleChar, x, y + i);
  }
}

void playSound(int period ){
  digitalWrite(BUZZER_PIN,HIGH);
  delayMicroseconds(period);
  digitalWrite(BUZZER_PIN, LOW);
  delayMicroseconds(period); 
}

void playMiss(){
  for(int i=0; i < 100; i++)
     playSound(400);  
  for(int i=0; i < 100; i++)
     playSound(3000 + i);   
}

// TellyMate helper functions

void screenShowXY( char ch, int x, int y){
  // display the given character at the screen x and y location  
  cursor_move(y,x);
  Serial.print(ch);
}
void screen_clear( void )
{ // <ESC>E
  Serial.print( CHAR_ESC "E" );
}

void cursor_move( uint8_t row , uint8_t col )
{ // <ESC>Yrc
  Serial.print( CHAR_ESC "Y" ) ;
  Serial.print((unsigned char)(32 + row)) ;
  Serial.print((unsigned char)(32 + col)) ;
}

void cursor_show( bool show )
{ // <ESC>e or <ESC>f
  Serial.print( CHAR_ESC ) ;
  Serial.print( show?'e':'f' ) ;
}

Nice. I'll give that a try some time this week when I get a chance.