An ATTiny85 based handheld game

WP_20161228_21_29_19_Pro (2)Yes, I love gaming.  And there is nothing more satisfying, to me, than building, sometimes coding and playing something I made.  Now, I don’t always WRITE the code, after all, time is a premium these days, but I don’t mind taking something someone else did and making it work with what I built.  For this project, I was very lazy: the design is also someone else’s.  I really wanted to do something with the ATTiny85, but have not really done anything outside of playing with the Adafruit Trinket or Digispark.

So, for this little project, I wanted to also use one of my cool little ssd1306 OLED screens.  While perusing the net, I came across Webboggles.com.  Here, they are selling a nifty little kit called the ATTiny Arcade Keychain. It looks to be of high quality and the author (Ilya Titov) goes through much detail in the design and build.  There are several posts about it and the games.  The game code and schematic have been made readily available. The first of the games was breakout and that is where I started. 

To build the little game, you will need the following:

  • Attiny85 + dip8 socket
  • SSD1306 OLED screen
  • 3x push buttons
  • 2x resistors (10kOhm optimal)
  • Piezo speaker
  • 3V 2032 coin cell battery
  • perf or vero board
  • I used a little speaker out of a toy cell phone instead of the piezo. I would also recommend socketing the screen instead of soldering it directly, you don’t have to, but I wish I had now.attiny85game_schem

    One other thing to keep in mind, you will need a way to program the ATtiny 85 chip, which I will describe in a follow up post. I actually built two programmers: one on breadboard and a quasi shield for the UNO.  I like that better.

    As you can see from the schematic, it is really simple. Even so, I made a few mistakes at first.  Not paying attention to the chip pinout, I got the pins reversed from pin 8 to pin 5. I, for whatever the reason, assumed the actual pin 8 was pin 5, instead of going from pin 4 to pin 5 at the bottom of the chip. Once I figured that out (I had yet to apply power) the rest was easy. I also got SCL and SDA backward (hey, I’m old).  Once I got my mistakes corrected, I was amazed that this simple circuit was now a little game machine.  Now, you aren’t going to play Call of Duty or even Doom, but you can play many classics on the devices.  I am going to build one or two more as this was a blast. I would also encourage ordering a kit from Webboggles as well.

    My next post will discuss creating an Attiny 85 programmer for the UNO.

    WP_20161231_15_20_15_Pro (2)

    Type in Game: PONG! (or, something close)

    WP_20160911_21_48_56_Pro (3)Today’s type in game for HB Tiny Basic is a PONG! variant.  I cannot take full credit for this one, I found the original on a Japanese educational site devoted to teaching microcontroller programming, using Half-Byte’s Tiny Basic(!) (a variation of it, anyway) and for basic electronics.  The original was written in a variant of HB Tiny Basic and also used a 10k potentiometer for the controller.  I fixed a couple of bugs, got it to work with Nunchuk AND squeezed into a somewhat smaller memory footprint.

    The game has a little bit of intelligence, it does a decent job of trying to guess where the ball will go, but, it is not perfect and it is possible to win the game.  There are some nice uses of the language, such as trying to include something like an OR statement when figuring out where the ball is going and takes advantage of an undocumented ‘feature’ of LINE: if you specify ‘2’ as the ‘color’ parameter, it simply inverses the pixels in the line.  This eliminates the need for multiple statements to draw and erase the paddles.  Quite clever.

    Gameplay is super simple: the computer ALWAYS serves, the score goes to nine and stops. You are always on the right. You use the thumb stick up and down to control your paddle.

    Weird things are likely to happen, it is not perfect and there’s no more room for improvement (challenge?)

    Anyway, have fun!

    10 CLS:A=0:B=0:W=48:H=32
    30 BOX 0,0,W,H,1
    40 U=H/2-3:V=U
    50 LINE W-5,U,W-5,U+5,2:LINE 4,V,4,V+5,2
    60 CURSOR 8,1:? A:IF A=9 STOP
    70 CURSOR 3,1:? B:IF B=9 STOP
    80 D=1:E=1:IF (U+V)&1 E=-1
    90 X=5:Y=V+3:SET X,Y
    100 C=50
    110 IF C>0 C=C-1:GOTO 240
    120 RESET X,Y
    130 X=X+D
    140 IF X=0 A=A+1:GOTO 60
    150 IF X=W B=B+1:GOTO 60
    160 IF X=W-6 IF Y>=U IF Y<=U+6 D=-D:TONE 440,100
    170 IF X=5 IF Y>=V IF Y<=V+6 D=-D:TONE 440,100
    180 Y=Y+E
    190 IF Y=1 E=-E
    200 IF Y=H-1 E=-E
    210 IF X=W-6 IF Y=U IF E=1 E=-1
    220 IF X=W-6 IF Y=U+5 IF E=-1 E=1
    230 SET X,Y
    240 LINE W-5,U,W-5,U+5,2
    250 U=H-2-PAD(1)/8
    260 IF U<0 U=0
    270 IF U>H-6 U=H-6
    280 LINE W-5,U,W-5,U+5,2:LINE 4,V,4,V+5,2
    300 IF D=1 GOTO 370
    310 IF X>=28 GOTO 370
    320 IF X=27 IF A<=B GOTO 370
    330 IF E=1 Q=Y+X-4:IF Q>=H Q=32-H
    340 IF E=0 Q=Y-X+4:IF Q<0 Q=-Q
    350 IF Q<V+3 IF V>1 V=V-1
    360 IF Q>V+3 IF V<25 V=V+1
    370 LINE 4,V,4,V+5,2
    380 RESET X,Y
    400 DELAY 20:GOTO 110

    HB Tiny Basic Type in Game: Hurkle

    For those of you who are old enough to know and remember the TRS-80, Cromemco or Altair will remember the game of Hurkle.

    WP_20160908_23_16_10_Pro (2)A Hurkle is a legendary beast that, even today, remains highly elusive creature.  So elusive, in fact, that few have seen a Hurkle and lived to tell about it.  Of course, you, our intrepid adventurer, are different.  For, you, you have HALF-BYTE’S Tiny Basic and an Arduino or compatible microcontroller at your disposal.  An arsenal worthy of such of hunt.

    Our Hurkle adventure takes place on a 10 by 10 grid.  You have to find the creature by deducing its where abouts on the 10 by 10 grid. Unfortunately for you, you will have from five to twenty moves in which to find the creature. Each time your adventure begins, your time is recalculated. This makes the level of difficulty even higher. You will, of course, through the use of the microcontroller, be told which direction you must travel.  Your grid follows a North-South, East-West pattern.  The X axis is West to East and Y axis is North to South. 

    This simple game is rather difficult to play.  Sure, there is a way to cheat, but I’ll let you figure that out. And, once you do, you should just type NEW and move on to something else.

    This game was originally published by the People’s Computer Company in Menlo Park California. I have adapted it from the Big Book of Computer Games, published in the early 1970’s.

    NOTE: I had originally posted a version of the game, as part of a sample code page. The listing was broken and the game did not work correctly, as published.  This one does.  Apologies for that.

    Below is the HB Tiny Basic listing.

    10  CLS: ?”HURKLE”
    20  ?”FOR HB TINY BASIC”
    99  # Converted to TINY BASIC by George Gray
    100 # HURKLE – PEOPLE’S COMPUTER COMPANY, MENLO PARK CA
    110 N=RND(10)+5
    120 G=10
    210 ?
    220 ? “A hurkle is hiding on a “,G,” by “,G,” grid.”
    230 A = RND(G)
    240 B = RND(G)
    310 FOR K=1 TO N
    320 ? “Guess #”,K
    330 ?”X=”;: INPUT X
    335 ?”Y=”;: INPUT Y
    340 IF ABS(X-A)+ABS(Y-B)=0 GOTO 500
    350 # ? INFO
    360 GOSUB 610
    380 NEXT K
    420 ? “Sorry, that’s “,N,” guesses.”
    430 ? “The hurkle is at “,A,”,”,B
    450 ? “Let’s play again. Hurkle is hiding.”
    470 GOTO 285
    500 ? “You found him in “,K,” guesses!”
    530 FOR I=1 TO 10
    532 TONE 1000,75
    534 NEXT I
    540 GOTO 440
    610 ? “Go “;
    620 IF Y=B GOTO 670
    630 IF Y<B GOTO 660
    640 ? “South “
    650 GOTO 670
    660 ? “North “
    670 IF X=A GOTO 720
    680 IF X<A GOTO 710
    690 ? “West “
    700 GOTO 720
    710 ? “East “
    720 ?””
    730 RETURN

    A Spark of Life: More DigiSpark fun

    WP_20151019_22_42_59_Pro__highresI recently wrote about the cool little DigiSpark ATTiny 85 controller board. This little USB wonder, like the AdaFruit Trinket, is based on the ATTiny85 Microcontroller and has limited I/O, memory and is cheap, very cheap.

    One thing it is not short on, however, is uses. This little thing can be used for a variety of things, including games and control applications.  For any use, though, you will need some kind of input and some kind of output.  In a previous post, I postulated about porting my ‘Battlestar Galactica’ game to the DigiSpark from the Trinket.  So, here are the results of that endeavor and some other things as well.

    For the backstory of the game and why I wrote it, check out this post. I’ll wait.

    Read it?  Cool.WP_20151019_22_44_34_Pro__highres

    Ok, so there are few differences between the Trinket and the DigiSpark, other than size.  The way you program it is very similar and the pins are nearly identical.  The way you talk to things, is, however, a little different.

    The Trinket needed SoftwareSerial. We don’t here, we just use DigitalRead and DigitalWrite and Serial.write to read the button and write to the screen.

    The display is a 16×2 Serial LCD from Parallax.  The button is actually a ‘controller’ I built a couple of years ago for my game console. I did not use it for that, opting for the Wii Nunchuck instead.  So, the controller has three buttons that would be used for left, right and ‘fire’ or action.  Here, they just return a 0 if pressed and a 1 when ‘open’.  The code periodically polls the controller for a press and then acts on the press to evaluate a hit or miss.

    WP_20151019_22_44_06_Pro__highres

    Now, one of the problems I had was the lack of GROUND pins. So, I made a ground bus, a four pin strip with a wire connecting all four pins. I then connected this to ground on the DS and the button and the LCD as well are connected to this bus. I have one free ground pin.  Well, what I did not realize was that you can use any pin for ground.  While I did not change my connections, I find this pretty cool.

    TIP! To use a free pin as ground:

    (From the wiki)

    You can use an I/O pin as a ground/+5V if you have some left and the current that flow in it is low (a few milliamps if you want the pin to stay around 0V/+5V. If you don’t care about the exact voltage you can go up to 40mA per pin according to the datasheet but it’s good to keep a safety margin).

    Configure the pin as an output and set its value to either LOW or HIGH (for ground or +5V

    #define button 0
    #define gnd 2
     
    void setup() {                
      pinMode (button, INPUT);
      digitalWrite (button, HIGH); // enable pullup
      pinMode(gnd, OUTPUT);
      digitalWrite (gnd, LOW); // use this pin as a ground
    }

    For much more information and tips, go to the wiki, located here.

    The game code is below.

    Come back for more on this cool little device.  Since it is capable of serial i/o, I’m thinking of using the Bluetooth Serial module and connecting the DS to one of my HalfByte Consoles running the graphical serial software for video display from the DS.

    WP_20151019_22_45_35_Pro__highresWP_20151019_22_43_27_Pro__highres

    Game Code:

    #include <TinyPinChange.h>
    
     // globals
     int  cylonpos;
     int  oldcylonpos;
     int  cyDirection;
     int  button;
     int  posStart;
     int  lin2Start;
     int  bsgpos;
     
     // special characters
     int  bsg;
     int  cylon;
     
     // total number of torpedos the ship has left
     int  tShots=10;
     
     // the setup routine runs once when you press reset:
     void waitforbutton();
     void moveCylon();
     
     void setup() {  // initialize the digital pin as an output.  
       Serial.begin(9600); 
       Serial.write(12);
       Serial.write(17);
       
       //define the ship character
       Serial.write(249);
       Serial.write(0x4);
       Serial.write(0xE);
       Serial.write(0xE);
       Serial.write(0x15);
       Serial.write(0x1F);
       Serial.write(0x15);
       Serial.write(0xE);
       Serial.write(0xE);
       
       // baddies
       Serial.write(251);
    
       Serial.write(uint8_t(0x0));//to handle a goofy issue with '0'
       Serial.write(uint8_t(0x0));
       Serial.write(0xE);
       Serial.write(0x15);
       Serial.write(0x1F);
       Serial.write(uint8_t(0x0));
       Serial.write(uint8_t(0x0));
       Serial.write(uint8_t(0x0));
     
     // the button is on pin 0  
       pinMode(0,INPUT);
       
     // the initial direction is right
       cyDirection=1;
       
     } // the loop routine runs over and over again forever:
     void loop() {
      int score;
      score=0;
      
      Serial.write(12); // clear the screen and show the instructions
      Serial.println("Shoot the Cylon\n");
      Serial.println("press btn strt");
      
      // wait for the button
      waitforbutton();
      
      // define somethings before we start the game loop
      tShots=10;
      cylon=3;
      bsg=1;
      lin2Start=148; // the baddies appear on line 1
      posStart=128;  // the good guys on line 2
      randomSeed(analogRead(0)); // gen a random seed
      
      bsgpos = 8;  // our ship starts in the middle of the sector
      cylonpos=random(0,15); // baddies warp in at a random spot
      oldcylonpos=cylonpos;
      
      Serial.write(12);  // clear screen
      Serial.write(17);  // turn on backlight so we can see
      Serial.write(posStart + cylonpos);  // uh oh...the Cylon appeared!
     
      Serial.write(cylon);
      Serial.write(lin2Start+bsgpos);
      Serial.write(bsg); // and here we are
      
      // begin the game 
      while(1){
        /* Game loop */
        int button;
        Serial.write(22); // kill the cursor
        moveCylon();
        delay(200);
        button=digitalRead(0);  // check the button
        
        if(button==0){ // torpedo fired?
          if(cylonpos==bsgpos){ // Hit?
            Serial.write(132); // center 'Hit' on screen
            Serial.print("** HIT **");
            score ++;  // update score
            Serial.write(213); // noise (C at half note, 3rd scale)
            Serial.write(215); // play the note
            Serial.write(224);
            Serial.write(225);
            if(score==10){  // did we win?
               Serial.write(12); // clear screen
               Serial.write(128);// start at beginning
               Serial.print("You have saved\nthe Galactica.");
               delay(2000);
               loop();  // start game again
            }
            delay(3000); // did not win yet
            Serial.write(12); // clear screen
            Serial.write(lin2Start); // move cursor to beginning of line
            Serial.write("Score:");  // put up score
            Serial.print(score);
            Serial.write(lin2Start+bsgpos); // Since we shot the Cylon, we have to leave the sector, so...
            Serial.write(0x20); // go into hyperdrive and erase the BSG
            bsgpos=random(9,15);  // calculate new position
            Serial.write(lin2Start+bsgpos); //...and, we are here
            Serial.write(bsg);
            Serial.write(posStart+cylonpos);
            Serial.write(0x20);
            cylonpos=random(0,15); // Damn, they found us!
            oldcylonpos=cylonpos;
            moveCylon();
          } 
          
          tShots --; // decrement our torpedo inventory
          if(tShots<1){ // out of ammo?
            Serial.write(12);  // clear screen
            Serial.write(posStart); // start at beginning of line
            Serial.print("The Cylons win.\n");
            int count=1;
            // show animation of our ship exploding
            while (count<10){
              Serial.write(lin2Start+bsgpos);
              Serial.write(bsg);
              delay(500);
              Serial.write(lin2Start+bsgpos);
              Serial.write('*');
              delay(500);
              count++;
            }        
            loop(); // Play again
          }
        }  
      }
    }
    void moveCylon(){
      // sneaky Cylons...they show up anywhere!
      Serial.write(posStart+oldcylonpos);
      Serial.write(0x20);
      if (random(0,6)>3){
          //Cylon warped
          cylonpos=random(0,15);
          oldcylonpos=cylonpos;
      }
      
      cylonpos += cyDirection; // figure out which way to move
      if (cylonpos>15){
       cylonpos=15;
       cyDirection=-1;
      }
      
      if (cylonpos<0){
        cylonpos=0;
        cyDirection=1;
      }
      // erase from old spot
      Serial.write(posStart+oldcylonpos);
      Serial.write(0x20);
      Serial.write(posStart+cylonpos);
      Serial.write(cylon);
      oldcylonpos=cylonpos;
    } 
    
    void waitforbutton(){
       
      int button;
      button=digitalRead(0);
      while(button==1){     //wait for button press
        button=digitalRead(0);
        delay(1000);
      }  
      Serial.write(1);  
      delay(1000);
      Serial.write(12); 
    }