A Video Game on my Trinket, take 2

Several posts ago, I wrote about writing a video game for the Trinket from Adafruit. Well, since then, I have added scoring, sound and a bit more animation (very crude, mind you, I can only do so much in the small memory and limited screen.)

The setup uses the Trinket, Parallax’s 2×16 LCD panel and a switch connected to Pin 0. The LCD panel is connected to Pin 2.  You will need to import the SoftwareSerial library for the LCD.

The premise of the game is based on the Battlestar Galactica TV show from 2003-2010.  The Galactica was engaged in heavy battle and barely managed to escape before certain destruction. All of her gun turrets, save the forward torpedo launcher, are damaged and non-functional, meaning she can only fight head on.  The Viper squads are still engaging the enemy.  A group of Cylon raiders have found the mighty Battlestar and are taking her on, one by one. Oh, you only have ten torpedo’s left, so use them wisely.

The game ends when you use up your ten torpedo’s.

Below is the revised listing for the game.  Apologies to Glen Larsen.

 

Listing 1.

#include <SoftwareSerial.h>

SoftwareSerial s(0,2);
 
// 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. 
   s.begin(9600);
   s.write(12);
   s.write(17);
  
   //define the ship character
   s.write(249);
   s.write(0×4);
   s.write(0xE);
   s.write(0xE);
   s.write(0×15);
   s.write(0x1F);
   s.write(0×15);
   s.write(0xE);
   s.write(0xE);
  
   // baddies
   s.write(251);

   s.write(uint8_t(0×0));//to handle a goofy issue with ’0′
   s.write(uint8_t(0×0));
   s.write(0xE);
   s.write(0×15);
   s.write(0x1F);
   s.write(uint8_t(0×0));
   s.write(uint8_t(0×0));
   s.write(uint8_t(0×0));
 
// 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;
 
  s.write(12); // clear the screen and show the instructions
  s.println("Shoot the Cylon\n");
  s.println("press btn 2 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;
 
  s.write(12);  // clear screen
  s.write(17);  // turn on backlight so we can see
  s.write(posStart + cylonpos);  // uh oh…the Cylon appeared!
 
  s.write(cylon);
  s.write(lin2Start+bsgpos);
  s.write(bsg); // and here we are
 
  // begin the game
  while(1){
    /* Game loop */
    int button;
    s.write(22); // kill the cursor
    moveCylon();
    delay(200);
    button=digitalRead(0);  // check the button
    if(button==HIGH){ // torpedo fired?
      if(cylonpos==bsgpos){ // Hit?
        s.write(132); // center ‘Hit’ on screen
        s.print("** HIT **");
        score ++;  // update score
        s.write(213); // noise (C at half note, 3rd scale)
        s.write(215); // play the note
        s.write(224);
        s.write(225);
        if(score==10){  // did we win?
           s.write(12); // clear screen
           s.write(128);// start at beginning
           s.print("You have saved\nthe Galactica.");
           delay(2000);
           loop();  // start game again
        }
        delay(3000); // did not win yet
        s.write(12); // clear screen
        s.write(lin2Start); // move cursor to beginning of line
        s.write("Score:");  // put up score
        s.print(score);
        s.write(lin2Start+bsgpos); // Since we shot the Cylon, we have to leave the sector, so…
        s.write(0×20); // go into hyperdrive and erase the BSG
        bsgpos=random(9,15);  // calculate new position
        s.write(lin2Start+bsgpos); //…and, we are here
        s.write(bsg);
        s.write(posStart+cylonpos);
        s.write(0×20);
        cylonpos=random(0,15); // Damn, they found us!
        oldcylonpos=cylonpos;
        moveCylon();
      }
     
      tShots –; // decrement our torpedo inventory
      if(tShots<1){ // out of ammo?
        s.write(12);  // clear screen
        s.write(posStart); // start at beginning of line
        s.print("The Cylons win.\n");
        int count=1;
        // show animation of our ship exploding
        while (count<10){
          s.write(lin2Start+bsgpos);
          s.write(bsg);
          delay(500);
          s.write(lin2Start+bsgpos);
          s.write(‘*’);
          delay(500);
          count++;
        }       
        loop(); // Play again
      }
    } 
  }
}
void moveCylon(){
  // sneaky Cylons…they show up anywhere!
  s.write(posStart+oldcylonpos);
  s.write(0×20);
  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
  s.write(posStart+oldcylonpos);
  s.write(0×20);
  s.write(posStart+cylonpos);
  s.write(cylon);
  oldcylonpos=cylonpos;
}

void waitforbutton(){
  SoftwareSerial s(0,1);
 
  int button;
  button=digitalRead(0);
  while(button==LOW){
    button=digitalRead(0);
    delay(1);
  } 
  s.write(1); 
  delay(1000);
  s.write(12);
}

Coding for small platforms is nearly a lost art OR why I love tiny computers

There was a time when a computer with 4K of memory was something to behold. Indeed, 4K was enough for a BASIC interpreter AND have room left over for user programs. Most I/O was serial, so ALL of that space was for code. The ‘operating system’ was something called a ‘monitor’ or the BASIC itself. Either way, the I/O code was ‘built’ into either the BASIC or the monitor. And, if you had a video terminal, well, you were THE stuff.

Just a few years later, that 4K grew to 16K then 64K and, suddenly, we are talking MEGAbytes. Wow. Well, funny thing happened, code went from small, tight and mostly efficient to BIG, heavy and bloated.  Code in that 4K system that put a character on the screen took, maybe six bytes suddenly took a DLL, which was probably ten times that or more.  Programmers got complacent, including this one.

I recall one job I had, I was maybe 17, that required me to write an accounting screen for a paging system (remember the pocket pager?) I had 2k for the data entry screen AND the billing code. Plus, the datastream to/from the paging system was like 32 bytes.  In that 32 bytes, I had to squeeze in the client number (10 digits), number of pages, and about two dozen status bits and more. I did it. The software went into a dozen or ‘head end’ systems (hey, it was a SMALL market) but it was my first ‘professional’ gig.

My career took me down the Microsoft/Lotus path. I had all kinds of wonders to code with, Visual Studio, Visual Basic, Lotus Notes, web.  My code, while functional and often clever, was sloppy. I forgot what it was to REALLY code.

Well, I had a shot a programming for the Palm Pilot, which reminded me of my old days, but it didn’t last long and I was back to programming for machines with memory measured in the gigabytes.

Imagine my delight when I discovered the world of Microcontrollers.  These things have big capability, but tiny (and I mean tiny) memory spaces. My current microcontroller project, the Adafruit Trinket, based on the ATMel ATtiny85, has 5,2k of available programming memory and 512 BYTES of RAM (as well as 512 bytes of EEPROM.)

Having found some cool 2×16 LCD panels for five bucks each at Radio Shack, these Trinkets and a bunch of parts, I set out to make a game for my step son.  I’m still working on a game concept that will keep his interest, but I did a proof of concept game based on Space Invaders (my all time favorite game.) So, how can one make a Space Invaders on a TWO line by SIXTEEN character display? Well…you need to be creative. AND…you have to fit it, along with a necessary library, into under 5.1K.

So…what did I do? Well, found an old notebook I kept from my TRS-80 days. Looked at some old Tiny Basic game code and…coded.

BSG001First, I needed an idea. The display I am using, from Parallax, allows for eight special characters.  So, I played around with a few designs and one of them looked like the Battlestar Galactica. After some thought, I had the idea: The Galactica’s Viper squadrons are out on patrol. The Galactica was in a fierce battle that has rendered it nearly immovable, it can only hyperjump. It’s turrets are all damaged, save the forward bank, she can only shoot from her forward cannons.  A Cylon has discovered the Galactica and is taking runs at it.  Problem is, the Cylon is jumping randomly, making a direct hit nearly impossible.  If, however, the Galactica does manage to destroy the Cylon, the Galactica then hyperjumps too.  Then, it all happens again.

Cool, huh?IMG_2758

So, I set out to code it. I put code together to read a switch, move a character back and forth and come up with an interrupt mechanism for the button.

Turns out, it was simpler than I thought.  I had some hiccups with the Arduino IDE (which involved upgrading the toolchain, upgrading part of it again, some choice words…) but got them ironed out and, now, the Colonials are fighting the nasty Cylons again.

First, the source code:

#include <SoftwareSerial.h>

SoftwareSerial s(0,2);
 
// globals
int  cylonpos;
int  oldcylonpos;
int  cyDirection;
int  button;
int  posStart;
int  lin2Start;
int  bsgpos;
// special characters
int  bsg;
int  cylon;
 
// the setup routine runs once when you press reset:
void waitforbutton();
void moveCylon();
 
void setup() {  // initialize the digital pin as an output. 
   s.begin(9600);
   s.write(12);
   s.write(17);
   //define the character
   s.write(249);
   s.write(0×4);
   s.write(0xE);
   s.write(0xE);
   s.write(0×15);
   s.write(0x1F);
   s.write(0×15);
   s.write(0xE);
   s.write(0xE);
  
   // baddies
   s.write(251);

   s.write(uint8_t(0×0));//to handle a goofy issue with ’0′
   s.write(uint8_t(0×0));
   s.write(0xE);
   s.write(0×15);
   s.write(0x1F);
   s.write(uint8_t(0×0));
   s.write(uint8_t(0×0));
   s.write(uint8_t(0×0));
  
   pinMode(0,INPUT);
   cyDirection=1;
  
} // the loop routine runs over and over again forever:
void loop() {
  int score;
  score=0;
 
  s.write(12);
  s.println("Shoot the Cylon\n");
  s.println("press btn 2 strt");
 
  waitforbutton();
 
  cylon=3;
  bsg=1;
  lin2Start=148;
  posStart=128;
  randomSeed(analogRead(0)); // gen a random seed
 
  bsgpos = 8;
  cylonpos=random(0,15);
  oldcylonpos=cylonpos;
 
  s.write(12);
  s.write(17);
  s.write(posStart + cylonpos);
 
  s.write(cylon);
  s.write(lin2Start+bsgpos);
  s.write(bsg);
 
  while(1){
    /* Game loop */
    int button;
    s.write(22); // kill the cursor
    moveCylon();
    delay(200);
    button=digitalRead(0);
    if(button==HIGH){
      if(cylonpos==bsgpos){
        s.write(132); // center ‘Hit’ on screen
        s.print("** HIT **");
        score ++;  // update score
        delay(3000);
        s.write(12); // clear screen
        s.write(lin2Start); // move cursor to beginning of line
        s.write("Score:");  // put up score
        s.print(score);
        s.write(lin2Start+bsgpos); // Since we shot the Cylon, we have to leave the sector, so…
        s.write(0×20); // go into hyperdrive and erase the BSG
        bsgpos=random(9,15);  // calculate new position
        s.write(lin2Start+bsgpos); //…and, we are here
        s.write(bsg);
        s.write(posStart+cylonpos);
        s.write(0×20);
        cylonpos=random(0,15); // Damn, they found us!
        oldcylonpos=cylonpos;
        moveCylon();
      }
    } 
  }
}
void moveCylon(){
  // sneaky Cylons…they show up anywhere!
  s.write(posStart+oldcylonpos);
  s.write(0×20);
  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
  s.write(posStart+oldcylonpos);
  s.write(0×20);
  s.write(posStart+cylonpos);
  s.write(cylon);
  oldcylonpos=cylonpos;
}

void waitforbutton(){
  SoftwareSerial s(0,1);
 
  int button;
  button=digitalRead(0);
  while(button==LOW){
    button=digitalRead(0);
    delay(1);
  } 
  s.write(1); 
  delay(1000);
  s.write(12);
}

SInce the Trinket is limited, I/O wise, I have only five pins, two of which are shared with the USB programmer and one is shared with the red programming LED, it really limits what you can add. At least, for those of us who are electronically challenged. I have my LCD on pin 2 and the switch on pin 0.  The LCD is controlled with a single line, it also has a five volt line (which goes to the USB power) and ground. The switch is connected to power and a 10k ohm resistor. It connects to pin 0. In the LOOP code, there is a While loop that handles the game logic. Inside the loop, it checks the pin for HIGH, which means the button was pressed. It then checks the position of the BSG and the Cylon. If there is a hit, it tells the user that they hit the Cylon and then increments the score, recalculates the positions and does it all again.  Simple, but kind of hard since the Cylon jumps so much.

While the game is not sophisticated, it does show what you can do in 5K of memory, a limited input mechanism and very limited display.  Now, about the game for my step son…

 

Adafruit, USBTinyISP, Digital Signature and Windows 8: hoop jumping time

I do development work on a variety of PC’s in my home, Windows 7, Windows 8 and, yes, even on a Windows Vista laptop.  So far, it’s not been a problem. Oh, sure, my ancient copy of Visual Studio 2005 tells me it is not compatible with Windows 8, but ignore the warning and keep on going…no issues yet and I’ve been running it for awhile now.  However, drivers for things like USB programmers have not been an issue (well, OK, there was the one Radio Shack Serial to USB cable that Windows 8 hates) until now.

IMG_2603I go to setup one of my Windows 8 desktops with the Arduino IDE so I can do some work with the Adafruit Trinket. The IDE installs just fine. I make all of the changes that Adafruit says I need to make to the IDE so it can support the Trinket.  Everything is great…until I try to install USBTinyISP. It’s drivers are not digitally signed.  Windows 8 does not allow unsigned drivers.  Well, damn. So, off to the ‘net I go.

I BING ‘USBTinyISP and Windows 8’ and get this site, Next of Windows. Here, they have a short tutorial on how to enable the ability to install unsigned drivers. Now, I understand why Microsoft did this…it was designed to protect consumers and help ensure only good stuff gets on your computer.

Rather than retread what Next of Windows has already done, just go there and follow the instructions. There are a few restarts involved as you are accessing settings that are only mean to be accessed if your PC is having issues with Windows.

Now, while Microsoft may be partly to blame, I also wonder why Adafruit does not supply a signed driver or, at least, tell you how to install it under Windows 8. It is, after all, on the LEARNING System.

Once I got the driver installed, Arduino IDE was able to program the Trinket and all is well with the universe.

UPDATE: It seems that Adafruit DID, in fact, post instructions for installing on Windows 8. Apologies to Adafruit are in order [wipes egg from face.] Here’s the link to the page that actually says ‘Don’t forget, for Windows 8, you will have to turn off driver signing checking..’. I don’t know how I missed that.

The Trinket: small, versatile and cute

IMG_2603I’ve become a fan of microcontrollers. These little marvels can do a lot: control motors, lights, LED’s,LCD’s, servo mechanisms, keyboards, mice…you name it, it probably has one of these things in it.  Some of them, such as the PIC24, are as powerful as yesterday’s microprocessor and can even run real operating systems or video games (the PIC24 is the heart of the XGS PIC 16 bit board I’ve been porting BASIC-the same BASIC will also run on the Arduino line of microcontroller based development boards.)

Adafruit Industries is one of the leaders in the burgeoning hobby that has sprung up around IMG_2631these little beasts.  They recently introduced the Trinket. The Trinket, based on the ATtiny85 controller, is about an inch long by half inch wide (approx.) and contains a sole USB connection and five I/O lines.

The Trinket is easy to use and program. Using Arduino IDE (free from the Arduino web site) you write using C++ and then upload the resulting code to the device. This is the only thing about the device that I have issue with: You have a very short time in which the device is programmable. The IDE is rather slow, and the interval with which the device will program is about ten seconds. You have to repeatedly press the reset button on the device and hope that you have time to program it, otherwise, you have to start over and the IDE does not appear to be smart enough to know that the source has already been compiled. 

IMG_2637The upload process aside, everything else is rather straight forward. There are a number of guides on the Adafruit site to explain how to set to set up the IDE.  Basically, download and install the IDE, then USBTiny, the driver, then there are two config files you need to download and copy into directories under Arduino IDE and in the Documents directory. Complete instructions are here. I suggest you read through the entire article and then do the install.

Once installed, you would code as if it were an Arduino.  Keep in mind, there is no Serial port, but there is a library that ‘maps’ one to one of the I/O pins. It works, great. I used it to talk to one of the Parallax 2×16 LCD panels I picked up from Radio Shack. See my source code snippet below for more.

One of the really nice features is that you can power this thing using three 1.5 volt AAA batteries. Input voltage can be from 3.3 to 16 volts.  The device comes in 3v and 5v flavors, meaning you have to be careful about what you connect to the device. I got two 3v boards as the 5v boards were sold out. I tried with both a 9v battery and three 1.5 volt AAA cells. Of course, you can also power it from the USB connections. And anything connected will, likely, need its own power source.

At $7.95, you cannot really go wrong.  Check out Adafruit’s site for project ideas. I have a couple in mind, including that Star Trek game I wrote of, several posts ago.  In a future post, I will share what I have done with these as well as how my homebrew PIC16 project turns out. And look for more on that Tiny Basic port to the XGS.

#include <SoftwareSerial.h>

SoftwareSerial s(0,1); //receive on "0", and transmit on "1" aka "PB1" aka pin 6

void setup() {   s.begin(9600);// set baud rate to 9600 baud   }    void loop() {   s.write(12);// clear screen   s.write(17);// turn on backlight   s.println("Adafruit Trinket");   s.println("2x16LCD Parallax");      }