Build your own ATtiny85 programmer using an UNO

attiny85programmerAs part of my ATTiny 85 learning adventure, I had to create some way to actually load code on to the 85 itself. My first go ’round was a breadboard monster. The thing I hate about bread boarding are the bloody wires.  What a mess. Once I got my game working, I set out for a better way to program the chip.  I could have bought one, but what’s the fun in that? As it turns out, it is simple to build, provided you have an Arduino UNO handy.

I decided to make a shield for the UNO. I wasn’t concerned with passing through all of the pins, so only the ones I needed are exposed.  This is something I won’t do often, so I made no attempt to pretty it up either.  The whole thing consists of an 8 pin socket, one six pin header and one ten pin header and a 10 uf capacitor. Oh, a small perf board to mount it all.

Wiring is tedious and made the same mistake as I did with the game (see my last post) as I got the four pins (5 to 8) reversed. I know, I know.

Mistake aside, it took about a half hour for me to wire it up.

One thing that was a bit problematic for me, as I have a vision impairment, was getting the pins lined up to the proper UNO pins for the headers.  That took a few tries, but I got it.

The connections are below for using an UNO:

UNO Pins

ATTiny 85 Pins (actual pin)

+5v Vcc (8)
Gnd Gnd (4)
Pin 13 pb2 (7)
Pin 12 pb1 (6)
Pin 11 pb0 (5)
Pin 10 reset pb5 (1)

In the table above, the left is the Uno, the right is the ATTiny 85 socket. You need a 10uf cap between the Uno gnd and reset.

Before you can do anything with the Arduino IDE and the 85, you must first install the support…

By default Arduino IDE doesn’t support ATtiny85 so we should add ATtiny boards to Arduino IDE. Open File -> Preferences and in the Additional Boards Manager URLs give this url Arduino IDE Attiny support. Then, open Tools -> Board -> Board Manager, scroll down the list where it says “attiny by Davis A. Mellis”. Click toinstall it. Once installed, you can select ATtiny as the board type. You will also have to select chip type (45,85,etc.) Along with the processor, you will also need to specify the speed.  1 Mhz is the default, I used 8 Mhz for the code I downloaded for the game. Adjust to suit your needs.

You also need to upload the ‘ArduinoISP’ sketch to the UNO before programming the ATTiny 85. Once you upload this sketch, the UNO will pass along to the 85 what ever you send.

For my little handheld, I had to burn the bootloader first, then upload the game sketch. Don’t forget to  set the Arduino IDE to ‘Programmer ‘Arduino as ISP’. Once the bootloader is burned to ATtiny, you are ready to upload your code.

You should be good to go.  Here is a site (from Arduino) that goes into far greater detail.

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)

    Using a Max7219 8×8 LED Matrix with an ATTINY85, like Trinket or DigiSpark

    While waiting for part two of the handheld construction article, I thought I’d give you a little distraction about interfacing an ATTINY85 to an 8×8 LED Matrix using the Max7219 chip.

    ATTINY85LEDMatrix1Been working on getting either a DigiSpark or Trinket to work with one of my 8×8 LED matrix displays. Connection is simple, but the maxmatrix library doesn’t seem to like the ATTINY85 processor. So, I went looking for something that did or, at least, getting enough knowledge to bit bang my way to displaying SOMETHING on the little display. Well, I found some code to do this, but it is not very pretty and doesn’t do enough. However, it does give a good demo of how to make these arrays do something and, even better, work with the confines of the ATTINY85 processor.
    The code shows how turn on and off pixels (a specific LED in the matrix, to be more precise) and send a ‘bitmap’ to the display.
    Cool stuff. Could be another game or, perhaps, a temperature display, mood indicator, etc.

    You can grab the code I used (middle of the page) here…http://www.avrfreaks.net/…/max7219-8×8-dot-matrix-led-drive…

    FYI-To connect it:
    PB0 -> DIN
    PB1 -> CS
    PB2 -> CLK
    +5 -> VCC
    GND -> GND

    Pretty easy. ATTINY85LEDMatrix21

    The code I found puts a smiley face, inverts the screen, frowny face, etc. up on the display. I took out the inversion as it takes too much time.  This code, which is unattributed as the poster on the AVR forum did not credit the code, is a good start, but needs work.  I thought about modifying it to handle ASCII text, but that will be a ways off yet. I have several things to finish up before then. 

    I’d love to see what you can do with this, so drop us a note in the comments or send me an email and I’d be happy to write it up and post it here.

    Code:

    /*
      One MAX7219 connected to an 8x8 LED matrix.
     */
    
    #include <avr/io.h>
    #include <util/delay.h>
    
    #define CLK_HIGH()  PORTB |= (1<<PB2)
    #define CLK_LOW()   PORTB &= ~(1<<PB2)
    #define CS_HIGH()   PORTB |= (1<<PB1)
    #define CS_LOW()    PORTB &= ~(1<<PB1)
    #define DATA_HIGH() PORTB |= (1<<PB0)
    #define DATA_LOW()  PORTB &= ~(1<<PB0)
    #define INIT_PORT() DDRB |= (1<<PB0) | (1<<PB1) | (1<<PB2)
    
    uint8_t smile[8] = {
            0b00000000,
            0b01100110,
            0b01100110,
            0b00011000,
            0b00011000,
            0b10000001,
            0b01000010,
            0b00111100};
    
    uint8_t sad[8] = {
            0b00000000,
            0b01100110,
            0b01100110,
            0b00011000,
            0b00011000,
            0b00000000,
            0b00111100,
            0b01000010,
    };
    
    
    void spi_send(uint8_t data)
    {
        uint8_t i;
    
        for (i = 0; i < 8; i++, data <<= 1)
        {
      CLK_LOW();
      if (data & 0x80)
          DATA_HIGH();
      else
          DATA_LOW();
      CLK_HIGH();
        }
        
    }
    
    void max7219_writec(uint8_t high_byte, uint8_t low_byte)
    {
        CS_LOW();
        spi_send(high_byte);
        spi_send(low_byte);
        CS_HIGH();
    }
    
    void max7219_clear(void)
    {
        uint8_t i;
        for (i = 0; i < 8; i++)
        {
      max7219_writec(i+1, 0);
        }
    }
    
    void max7219_init(void)
    {
        INIT_PORT();
        // Decode mode: none
        max7219_writec(0x09, 0);
        // Intensity: 3 (0-15)
        max7219_writec(0x0A, 1);
        // Scan limit: All "digits" (rows) on
        max7219_writec(0x0B, 7);
        // Shutdown register: Display on
        max7219_writec(0x0C, 1);
        // Display test: off
        max7219_writec(0x0F, 0);
        max7219_clear();
    }
    
    
    uint8_t display[8];
    
    void update_display(void)
    {
        uint8_t i;
    
        for (i = 0; i < 8; i++)
        {
      max7219_writec(i+1, display[i]);
        }
    }
    
    void image(uint8_t im[8])
    {
        uint8_t i;
    
        for (i = 0; i < 8; i++)
      display[i] = im[i];
    }
    
    void set_pixel(uint8_t r, uint8_t c, uint8_t value)
    {
        switch (value)
        {
        case 0: // Clear bit
      display[r] &= (uint8_t) ~(0x80 >> c);
      break;
        case 1: // Set bit
      display[r] |= (0x80 >> c);
      break;
        default: // XOR bit
      display[r] ^= (0x80 >> c);
      break;
        }
    }
    
    
    int main(void)
    {
        uint8_t i;
        
        max7219_init();
        
        while(1)
        {
      image(sad);
      update_display();
      _delay_ms(500);
      image(smile);
      update_display();
      _delay_ms(500);
    
      // Invert display
    //  for (i = 0 ; i < 8*8; i++)
    //  {
    //      set_pixel(i / 8, i % 8, 2);
    //      update_display();
    //      _delay_ms(10);
    //  }
      _delay_ms(500);
        }
    }

     

     

    Getting video out from your Adafruit Trinket or DigiSpark (or ANY ATTINY85)

    11224347_913681192034970_90837413382715523_oWhile pouring through some videos and posts on just what the tiny little ATTiny85 chip can do, I ran across a HackADay post about someone generating NTSC video on VHF channel 3 (US channel 3.)  I was intrigued.  So, I tried it and got mixed results and, as I have only a couple of old analog sets, I thought it was kind of impractical to pursue, but cool anyway.  So, I wondered if just composite out would be better and, to my delight, the same person who posted the video also had done composite out with an ATTiny84.  It should work on the 85 as well, and, it does.

    So, I found his code and downloaded it.  Mr. Lohr is a GENIUS, plain and simple.  Thank you, sir!

    The code did not work well at first. I had to tweak it a bit to work with my hardware, especially the little monitor.  It still is not perfect, the video is not centered, but is fairly steady and bright…my first attempts were not very bright and not very steady. I am guessing it needs to be tweaked for the monitor it will be used on, but I have not tested on more than one.

    All you need is something like a Trinket from Adafruit or a digispark. I used both, but destroyed the digispark when I attempted to use an external power source.  It does not like more than five volts.  Specs say 3.5 to 12volts, I used 9. My mistake. 12182942_913681798701576_4759339394040268204_o

    The Trinket works great. I am using the 5volt version with USB.  Any ATTiny85 based controller should work, though.

    Connecting it is simple: Pin 3 to video ground, Pin 4 to video center pin.  Download the code to your device, connect it to the monitor and reset it. You should see the video demo.

    Now, there are some major caveats:

    • it, so far, only does text
    • you have 13 characters by 6 lines
    • character spacing is mono
    • there’s no video memory, each frame is drawn on the fly
    • the ‘video memory’ is simply a string array
    • there is no formatting
    • the character set is stored in flash

    So, why bother with such limitations? Well, for starters, cheap composite monitors are easy to get WP_20151025_11_19_10_Rich_LIand use and the Trinket/digispark are both under ten bucks, so you can have an application with video out for very little money.  If nothing else, this is a great lesson in how video works. Charles Lohr did a fantastic job with the code.  He’s a genius. Did I mention that?

    Any way, the code is posted below.  Let us know if you get it working and what you do with it.

     

     

    /*
        Copyright (C) 2014 <>< Charles Lohr


        Permission is hereby granted, free of charge, to any person obtaining a
        copy of this software and associated documentation files (the "Software"),
        to deal in the Software without restriction, including without limitation
        the rights to use, copy, modify, merge, publish, distribute, sublicense,
        and/or sell copies of the Software, and to permit persons to whom the
        Software is furnished to do so, subject to the following conditions:

        The above copyright notice and this permission notice shall be included
        in all copies or substantial portions of the Software.

        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
        OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
        MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
        IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
        CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
        TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
        SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */


    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <avr/sleep.h>
    #include <util/delay.h>
    #include <avr/pgmspace.h>
    #include "ntscfont.h"

     

    void delay_ms(uint32_t time) {
      uint32_t i;
      for (i = 0; i < time; i++) {
        _delay_ms(1);
      }
    }

    #define NOOP asm volatile("nop" ::)

    void NumToText( char * c, uint8_t a )
    {
        c[0] = (a/100)+'0';
        c[1] = ((a/10)%10)+'0';
        c[2] = (a%10)+'0';
        c[3] = 0;
    }
    void NumToText4( char * c, uint16_t a )
    {
        c[0] = (a/1000)+'0';
        c[1] = ((a/100)%10)+'0';
        c[2] = ((a/10)%10)+'0';
        c[3] = (a%10)+'0';
        c[4] = 0;
    }


    EMPTY_INTERRUPT(TIM0_OVF_vect );


    int main( )
    {
        cli();

        CLKPR = 0x80;    /*Setup CLKPCE to be receptive*/
        CLKPR = 0x00;    /*No scalar*/

        PLLCSR = _BV(PLLE) | _BV( PCKE );
    //    PLLCSR |= _BV(LSM);

        DDRB = _BV(1);

        DDRB |= _BV(3);
        DDRB |= _BV(4);
        PORTB |= _BV(1);

        TCCR1 = _BV(CS10);// | _BV(CTC1); //Clear on trigger.
        GTCCR |= _BV(PWM1B) |  _BV(COM1B0);// | _BV(COM1B1);
        OCR1B = 2;
        OCR1C = 3;
        DTPS1 = 0;
        DT1B = _BV(0) | _BV(4);

        TCCR0A = 0;
        TCCR0B = _BV(CS01);
        TIMSK |= _BV(TOIE0);

    OSCCAL = 215;

    //    OSCCAL=186;


    #define POWERSET

    #ifdef POWERSET
    #define NTSC_HI  {    DDRB=0;}
    #define NTSC_LOW {    DDRB=_BV(4); }
    #define NTSC_VH  {    DDRB=_BV(3); }
    #elif defined( POWERSET2 )
    #define NTSC_VH  {    DDRB=0;}
    #define NTSC_LOW {    DDRB=_BV(4)|_BV(3); }
    #define NTSC_HI  {    DDRB=_BV(3); }
    #elif defined( POWERSET3 )
    #define NTSC_VH  {    DDRB=0; }
    #define NTSC_HI   { DDRB=_BV(3); }
    #define NTSC_LOW   {    DDRB=_BV(4)|_BV(3); }
    #else

    //Experimental mechanisms for changing power. Don't work.
    #define NTSC_VH  {    OCR1C = 3; TCNT1 = 0; }
    #define NTSC_HI  {    OCR1C = 6; TCNT1 = 0;}
    #define NTSC_LOW {    OCR1C = 0; TCNT1 = 0;}

    #endif
        uint8_t line, i;

        #define TIMEOFFSET .12 //.12
        #define CLKOFS .12

        uint8_t frame = 0, k, ctll;
        char stdsr[8*13];
        sprintf( stdsr, "Fr: " );
        sprintf( stdsr+8,  "HalfByte" );
        sprintf( stdsr+16, "  Blog  " );
        sprintf( stdsr+24, "        " );
        sprintf( stdsr+32, " Trinket" );
        sprintf( stdsr+40, "AdaFruit" );
        sprintf( stdsr+48, "        " );
        sprintf( stdsr+56, "        " );
        sprintf( stdsr+64, "        " );
        sprintf( stdsr+72, "        " );
        sprintf( stdsr+80, "        " );
        sprintf( stdsr+88, "        " );


        ADMUX =/* _BV(REFS1)  |  _BV(ADLAR) | */ 1; //1 = PB2
        ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADPS2) | _BV(ADPS1);

    #define LINETIME 21 //Linetime of 7..20 is barely valid. So,
    //#define WAITTCNT while(TCNT0);
    #define RESETCNT {TCNT0 = LINETIME; TIFR|=_BV(TOV0); GTCCR|=PSR0;sei();}

    //#define WAITTCNT while(!(TIFR&_BV(TOV0)));
    #define WAITTCNT sleep_cpu();

    //#define WAITTCNT fintcnt();

         sleep_enable();
        sei();

        uint16_t ovax; //0..1024 = 0...5v
        uint8_t  msd;
        uint8_t  lsd;

        while(1)
        {

            frame++;
            //H = 1./15734.264 = 63.555 / 2 = 31.7775
            for( line = 0; line < 6; line++ )
            { NTSC_LOW; _delay_us(2.3-TIMEOFFSET); NTSC_HI; _delay_us(29.5-TIMEOFFSET-CLKOFS); }
            for( line = 0; line < 6; line++ )
            { NTSC_LOW; _delay_us(27.1-TIMEOFFSET); NTSC_HI; _delay_us(4.7-TIMEOFFSET-CLKOFS); }
            for( line = 0; line < 6; line++ )
            { NTSC_LOW; _delay_us(2.3-TIMEOFFSET); NTSC_HI; _delay_us(29.5-TIMEOFFSET-CLKOFS); }

            for( line = 0; line < 39; line++ )
            {
                RESETCNT;
                NTSC_LOW;
                _delay_us(4.7-TIMEOFFSET);
                NTSC_HI;

                //Do whatever you want.
                //sprintf( stdsr, "%d", frame );
                switch (line)
                {
                case 0:
                    NumToText( stdsr+4, frame );
                    break;
                case 1:
                    ovax = ADC;
                    ovax = ovax * 49 + (ovax>>1);
                    ovax/=10;
                    break;
                case 2:
                    NumToText( stdsr+24, ovax/1000 );
                    stdsr[27] = '.';
                    break;
                case 5:
                    NumToText4( stdsr+27, ovax );
                    stdsr[27] = '.';

                    break;
                }

                WAITTCNT;
                //_delay_us(58.8-TIMEOFFSET-CLKOFS);
            }

            for( line = 0; line < 2; line++ )
            {
                RESETCNT;
                NTSC_LOW;
                _delay_us(4.7-TIMEOFFSET);
                NTSC_HI;
                WAITTCNT;
            }

            for( line = 0; line < 220; line++ )
            {
                RESETCNT;
                NTSC_LOW; _delay_us(4.7-TIMEOFFSET);
                NTSC_HI; _delay_us(8-TIMEOFFSET-CLKOFS);

    //#define LINETEST
    #ifdef LINETEST
                NTSC_VH; _delay_us(8-TIMEOFFSET-CLKOFS);
                NTSC_HI; _delay_us(44.5);
    #else
                ctll = line>>2;
        
                for( k = 0; k < 8; k++ )
                {
          // draw the character, one pixel at a time
                uint8_t ch = pgm_read_byte( &font_8x8_data[(stdsr[k+((ctll>>3)<<3)]<<3)] + (ctll&0x07) );
                for( i = 0; i < 8; i++ )
                {
                    if( (ch&1) ) //if pixel is dark...
                    {
                        NTSC_VH;
                    }
                    else
                    {
                        NTSC_HI;  // pixel is lit
                        NOOP;
                    }
                    ch>>=1;
                    NOOP; NOOP; NOOP; NOOP;
                }
                        NTSC_HI;

                }

                NTSC_HI; //_delay_us(4.7-TIMEOFFSET-CLKOFS);
                WAITTCNT;
    #endif

    //        NTSC_HI; _delay_us(46-TIMEOFFSET-CLKOFS);

    //            NTSC_VH; _delay_us(32-TIMEOFFSET-CLKOFS);
    //            NTSC_HI; _delay_us(19.8-TIMEOFFSET-CLKOFS);
            }
        }
       
        return 0;
    }

     

    Download the original code and the font file here. You will need the font file to make this work.

    Mr. Lohr’s YouTube Channel

    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); 
    }

    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");      }